fine-tune drag'n'drop in a tree
- From: Ionutz Borcoman <iborco gmail com>
- To: gtkmm-list gnome org
- Subject: fine-tune drag'n'drop in a tree
- Date: Sat, 3 May 2008 13:29:26 +0300
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]