fine-tune drag'n'drop in a tree



Hi,

I'm playing around with the TreeStore, but I'm unable to obtain the effects I 
want.

Let's assume I have this tree:

-- 1
   -- 1.1
      -- 1.1.1
      -- 1.1.2
   -- 1.2
      -- 1.2.1
      -- 1.2.2
-- 2

I want to be able to do these:
* move (1) after (2) or (2) in front of (1)
* move (1.1) after (1.2) or (1.2) in front of (1.1)
* move (1.1) or (1.2) under (2)
* (1.1.1), (1.1.2), (1.2.1) and (1.2.2) should not be draggable
* (1.1) and (1.2) should always be attached to (1) or (2)
* (2) or (1) should always stay under the root

I have so far created a TreeStore with a model that has a 
Gtk::TreeModelColumn<int> m_level member.

I have set m_level to 1 for (1) and (2) and with 2 for (1.1) and (1.2). 
The "unmovable" rows has this set to 0.

I was able to restrict this way what rows can be dragged via 
row_draggable_vfunc. But I'm unable to controll the dropping mechanism 
correctly. That is:
* I can't attach (1.1) or (1.2)
* move (2) in front of (1) if I drop it over (1)
* move (1) after (2) if I drop (1) over (2)
* move (1.1) as the last item if I drop it over (1)

Any help would be appreciated. I have attached the code I've wrote so far.

Thanx,

Johnny
#include "treewindow.h"
#include <iostream>

TreeModel::TreeModel()
{
	// From the GTMM Book:
	// 
	// "We can't just call Gtk::TreeModel(m_columns) in the initializer list
	//  because m_columns does not exist when the base class constructor runs.
	//  And we can't have a static m_columns instance, because that would be
	//  instantiated before the gtk type system.
	//  So, we use this method, which should only be used just after creation!"
	//
	set_column_types(m_columns);
}

Glib::RefPtr<TreeModel> TreeModel::create()
{
	return Glib::RefPtr<TreeModel> (new TreeModel);
}

bool TreeModel::row_draggable_vfunc(const Gtk::TreeModel::Path& path) const
{
	TreeModel* model = const_cast<TreeModel*>(this);
	const_iterator iter = model->get_iter(path);
	if(iter)
	{
		Row row = *iter;
		std::cout << "row_draggable_vfunc:"
			<< " id: " << row[m_columns.m_id]
			<< " level: " << row[m_columns.m_level]
			<< std::endl;
		return (row[m_columns.m_level] > 0);
	}

	std::cout << "row_draggable_vfunc: false - no row" << std::endl;
	
	return false;
}

bool TreeModel::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data) const
{
	// obtain the row being dragged
	Glib::RefPtr<Gtk::TreeModel> ref_model = Glib::RefPtr<Gtk::TreeModel>(const_cast<TreeModel*>(this));
	ref_model->reference();
	
	Gtk::TreeModel::Path orig;
	Gtk::TreeModel::Path::get_from_selection_data(selection_data, ref_model, orig);

	const_iterator orig_iter = ref_model->get_iter(orig);
	if(orig_iter)
	{
		Row orig_row = *orig_iter;
		std::cout << "\torig: "
			<< " id: " << orig_row[m_columns.m_id]
			<< " level: " << orig_row[m_columns.m_level]
			<< std::endl;
			
		TreeModel* model = const_cast<TreeModel*>(this);
	
		const_iterator dest_iter = model->get_iter(dest);
		if(dest_iter)
		{
			Row dest_row = *dest_iter;
			std::cout << "\tdest: "
				<< " id: " << dest_row[m_columns.m_id]
				<< " level: " << dest_row[m_columns.m_level]
				<< std::endl;

			return (orig_row[m_columns.m_level] > 0 && dest_row[m_columns.m_level] > 0);
		}
	}

	std::cout << "row_drop_possible_vfunc: false - no row" << std::endl;
	
	return false;
}

// bool TreeModel::drag_data_received_vfunc(const Gtk::TreeModel::Path & dest, const Gtk::SelectionData & selection_data)
// {
// 	return true;
// }

TreeWindow::TreeWindow(BaseObjectType* object,
	const Glib::RefPtr<Gnome::Glade::Xml>& refGlade)
: Gtk::Window(object)
{
	// connect button signals
	Gtk::Button* btn = 0;
	refGlade->get_widget("quitbtn", btn);
	if(!btn)
		throw std::runtime_error("quitbtn not found");
	btn->signal_clicked().connect(sigc::mem_fun(*this, &TreeWindow::on_quitbtn));

	btn = 0;
	refGlade->get_widget("dumpbtn", btn);
	if(!btn)
		throw std::runtime_error("dumpbtn not found");
	
	btn->signal_clicked().connect(sigc::mem_fun(*this, &TreeWindow::on_dumpbtn));

	Gtk::TreeView* view = 0;
	refGlade->get_widget("treeview", view);
	if(!view)
		throw std::runtime_error("treeview not found");
		
	// create model and connect to the view
	m_model = TreeModel::create();
	TreeModel::ModelColumns& cols = m_model->m_columns;
	
	view->set_model(m_model);
	
	// set model data
	Gtk::TreeModel::Row row = *(m_model->append());
	row[cols.m_id] = 1;
	row[cols.m_name] = "foo1";
	row[cols.m_level] = 1;
	
	Gtk::TreeModel::Row row2 = *(m_model->append(row.children()));
	row2[cols.m_id] = 11;
	row2[cols.m_name] = "foo1.1";
	row2[cols.m_level] = 2;

	row2 = *(m_model->append(row.children()));
	row2[cols.m_id] = 12;
	row2[cols.m_name] = "foo1.2";
	row2[cols.m_level] = 2;
	
	Gtk::TreeModel::Row row3 = *(m_model->append(row2.children()));
	row3[cols.m_id] = 121;
	row3[cols.m_name] = "foo1.2.1";
	row3[cols.m_level] = 0;

	row3 = *(m_model->append(row2.children()));
	row3[cols.m_id] = 122;
	row3[cols.m_name] = "foo1.2.2";
	row3[cols.m_level] = 0;
	
	row2 = *(m_model->append(row.children()));
	row2[cols.m_id] = 13;
	row2[cols.m_name] = "foo1.3";
	row2[cols.m_level] = 2;
	
	row = *(m_model->append());
	row[cols.m_id] = 2;
	row[cols.m_name] = "bar2";
	row[cols.m_level] = 1;

	// show the columns
	view->append_column("ID", cols.m_id);
	view->append_column("Name", cols.m_name);

	for(int i=0; i< view->get_columns().size(); ++i)
	{
		Gtk::TreeViewColumn* p = view->get_column(i);
		p->set_reorderable();
	}
}

TreeWindow::~TreeWindow()
{
}

void TreeWindow::on_quitbtn()
{
	hide();
}

void TreeWindow::on_dumpbtn()
{
	std::cout << "Dumping the model" << std::endl;
	typedef Gtk::TreeModel::Children TChildren;
	TChildren children = m_model->children();
	for(TChildren::iterator it = children.begin(); it != children.end(); ++it)
	{
		Gtk::TreeModel::Row row = *it;
		std::cout << "\t" << row[m_model->m_columns.m_id] << ", " << row[m_model->m_columns.m_name] << std::endl;
	}
}
#ifndef TREEWINDOW_H
#define TREEWINDOW_H

#include <gtkmm.h>
#include <libglademm.h>

class TreeModel: public Gtk::TreeStore
{
public:
	class ModelColumns : public Gtk::TreeModel::ColumnRecord
	{
	public:
		ModelColumns()
		{
			add(m_id);
			add(m_name);
			add(m_level);
		}

		Gtk::TreeModelColumn<int> m_id;
		Gtk::TreeModelColumn<Glib::ustring> m_name;
		Gtk::TreeModelColumn<int> m_level;
	};

	static Glib::RefPtr<TreeModel> create();
	ModelColumns m_columns;
	
private:
	TreeModel();

	// overide drag_n_drop virtual functions
	bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const;
	bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data) const;
// 	bool drag_data_received_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data);
};

class TreeWindow: public Gtk::Window
{
public:
	TreeWindow(BaseObjectType* object, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
	~TreeWindow();
private:
	Glib::RefPtr<TreeModel> m_model;

	void on_quitbtn();
	void on_dumpbtn();
};

#endif
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.4 on Wed Apr 30 20:56:12 2008 -->
<glade-interface>
  <widget class="GtkWindow" id="TreeWindow">
    <property name="default_width">400</property>
    <property name="default_height">200</property>
    <child>
      <widget class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <child>
          <widget class="GtkScrolledWindow" id="scrolledwindow1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
            <child>
              <widget class="GtkTreeView" id="treeview">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="headers_clickable">True</property>
                <property name="reorderable">True</property>
                <property name="rules_hint">True</property>
                <property name="enable_tree_lines">True</property>
              </widget>
            </child>
          </widget>
        </child>
        <child>
          <widget class="GtkHButtonBox" id="hbuttonbox1">
            <property name="visible">True</property>
            <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
            <child>
              <widget class="GtkButton" id="dumpbtn">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="label" translatable="yes">Dump Model</property>
                <property name="response_id">0</property>
              </widget>
            </child>
            <child>
              <widget class="GtkButton" id="quitbtn">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="label" translatable="yes">Quit</property>
                <property name="response_id">0</property>
              </widget>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="expand">False</property>
            <property name="padding">5</property>
            <property name="position">1</property>
          </packing>
        </child>
      </widget>
    </child>
  </widget>
</glade-interface>
#include "treewindow.h"
#include <iostream>

int main(int argc, char *argv[])
{
	Gtk::Main kit(argc, argv);
	Glib::RefPtr<Gnome::Glade::Xml> refXml;

	try
	{
		refXml = Gnome::Glade::Xml::create(SOURCE_DIR "/treemain.glade");
	}
	catch(const Gnome::Glade::XmlError& ex)
	{
		std::cerr << ex.what() << std::endl;
		return 1;
	}

	TreeWindow* window = 0;
	refXml->get_widget_derived("TreeWindow", window);
	if(window)
	{
		kit.run(*window);
	}
	else
	{
		std::cerr << "Window template not found." << std::endl;
		return 1;
	}

	return 0;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]