« Return to Thread: fine-tune drag'n'drop in a tree

fine-tune drag'n'drop in a tree

by Ionutz Borcoman :: Rate this Message:

Reply to Author | View in Thread

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

[treewindow.cc]

#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;
        }
}


[treewindow.h]

#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>

[treemain.cc]

#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;
}


_______________________________________________
gtkmm-list mailing list
gtkmm-list@...
http://mail.gnome.org/mailman/listinfo/gtkmm-list

 « Return to Thread: fine-tune drag'n'drop in a tree