050. How to drag and drop between 2 GtkTreeViews - Part 1 - left to right?

Problem

You would like to drag and drop items from the treeview on the left to the treeview on the right as shown below:

How to drag and drop between 2 GtkTreeViews - Part 1 - left to right?


Solution


Sample Code

1   
2   
3   
4   
5   
6   
7   
8   
9   
10   
11   
12   
13   
14   
15   
16   
20   
21   
22   
23   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
34   
38   
39   
40   
41   
42   
44   
45   
46   
47   
48   
49   
50   
51   
52   
53   
54   
55   
56   
57   
58   
59   
60   
61   
62   
63   
64   
65   
66   
67   
68   
69   
70   
71   
72   
73   
74   
75   
76   
77   
78   
79   
80   
81   
82   
83   
84   
85   
86   
87   
88   
89   
90   
91   
92   
93   
94   
95   
96   
97   
98   
99   
100   
101   
102   
103   
104   
105   
106   
107   
108   
109   
110   
111   
112   
113   
114   
115   
116   
117   
118   
119   
120   
121   
122   
123   
124   
125   
126   
127   
128   
129   
130   
131   
132   
141   
142   
143   
144   
145   
146   
147   
149   
151   
152   
153   
155   
156   
157   
158   
159   
160   
162   
164   
165   
166   
167   
168   
<?php
$window = new GtkWindow();
$window->set_size_request(600, 240);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("Drag and Drop between 2 TreeViews - Part 1 (left to right)");
$title->modify_font(new PangoFontDescription("Times New Roman Italic 10"));
$title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000ff"));
$title->set_size_request(-1, 40);
$vbox->pack_start($title, 0, 0);
$vbox->pack_start(new GtkLabel(''), 0, 0);
$vbox->pack_start($hbox = new GtkHBox());

// Set up treeview1
$data1 = array(
    array('row0', 'item 42', 2, 3.1),
    array('row1', 'item 36', 20, 6.21),
    array('row2', 'item 21', 8, 9.36),
    array('row3', 'item 10', 11, 12.4),
    array('row4', 'item 7', 5, 15.5),
    array('row5', 'item 4', 17, 18.6),
    array('row6', 'item 3', 20, 21.73));
$view1 = display_table ($hbox, $data1);
$view1->drag_source_set(Gdk::BUTTON1_MASK,  
    array( array( 'text/plain', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE); // note 1
$view1->connect('drag-data-get', 'on_drag'); // note 2
$hbox->pack_start(new GtkLabel('  '), 0, 0);

// Set up treeview2
$data2 = array(
    array('row7', 'item 127', 105, 115.5),
    array('row8', 'item 124', 117, 118.6),
    array('row9', 'item 123', 120, 121.73));
$view2 = display_table ($hbox, $data2);
$view2->drag_dest_set(Gtk::DEST_DEFAULT_ALL, 
    array( array( 'text/plain', 0, 0)), Gdk::ACTION_COPY|Gdk::ACTION_MOVE); // note 3
$view2->connect('drag-data-received', 'on_drop', $view1); // note 4

$window->show_all();
Gtk::main();

function display_table($container, $data) {

    // Set up a scroll window
    $scrolled_win = new GtkScrolledWindow();
    $scrolled_win->set_policy( Gtk::POLICY_AUTOMATIC,
        Gtk::POLICY_AUTOMATIC);
    $container->pack_start($scrolled_win);

    // Creates the list store
    if (defined("GObject::TYPE_STRING")) {
        $model = new GtkListStore(GObject::TYPE_STRING, GObject::TYPE_STRING,
                GObject::TYPE_LONG, GObject::TYPE_DOUBLE);
    } else {
        $model = new GtkListStore(Gtk::TYPE_STRING, Gtk::TYPE_STRING,
                    Gtk::TYPE_LONG, Gtk::TYPE_DOUBLE);
    }
    $field_header = array('Row #', 'Description', 'Qty', 'Price');
    $field_justification = array(0.0, 0.0, 0.5, 1.0);

    // Creates the view to display the list store
    $view = new GtkTreeView($model);
    $scrolled_win->add($view);

    // Creates the columns
    for ($col=0; $col<count($field_header); ++$col) {
        $cell_renderer = new GtkCellRendererText();
        $cell_renderer->set_property("xalign", $field_justification[$col]);
        $column = new GtkTreeViewColumn($field_header[$col],
            $cell_renderer, 'text', $col);
        $column->set_alignment($field_justification[$col]);
        $column->set_sort_column_id($col);

        // set the header font and color
        $label = new GtkLabel($field_header[$col]);
        $label->modify_font(new PangoFontDescription("Arial Bold"));
        $label->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000FF"));
        $column->set_widget($label);
        $label->show();

        // setup self-defined function to display alternate row color
        $column->set_cell_data_func($cell_renderer, "format_col", $col);
        $view->append_column($column);
    }

    // pupulates the data
    for ($row=0; $row<count($data); ++$row) {
        $values = array();
        for ($col=0; $col<count($data[$row]); ++$col) {
            $values[] = $data[$row][$col];
        }
        $model->append($values);
    }

    // setup selection
    $selection = $view->get_selection();
    $selection->connect('changed', 'on_selection');
    return $view;
}

// display alternate row color
function format_col($column, $cell, $model, $iter, $col_num) {
    $path = $model->get_path($iter); // get the current path
    $row_num = $path[0]; // get the row number
    if ($col_num==3) {
        $amt = $model->get_value($iter, 3);
        $cell->set_property('text', '$'.number_format($amt,2));
    }
    $row_color = ($row_num%2==1) ? '#dddddd' : '#ffffff';
    $cell->set_property('cell-background', $row_color);
}

// process user selection
function on_selection($selection) {
    list($model, $iter) = $selection->get_selected();
    if ($iter==null) return;
    $desc = $model->get_value($iter, 1);
    $qty = $model->get_value($iter, 2);
    $price = $model->get_value($iter, 3);
    echo "You have selected $desc: $qty ($price)\n";
}

function on_drag($widget, $context, $data, $info, $time) {
    $selection = $widget->get_selection();
    list($model, $iter) = $selection->get_selected();
    if ($iter==null) return;
    $id = $model->get_value($iter, 0);
    $desc = $model->get_value($iter, 1);
    $qty = $model->get_value($iter, 2);
    $price = $model->get_value($iter, 3);
    $data->set_text(serialize(array($id, $desc, $qty, $price)));  // note 5
}

function on_drop($widget, $context, $x, $y,    $data, $info, $time, $src_view) {  // note 6
    // remove the item from src first
    $src_selection = $src_view->get_selection();
    list($src_model, $src_iter) = $src_selection->get_selected();
    $src_model->remove($src_iter);

    // append the dragged item to the destination view
    $data2 = unserialize($data->data);
    $model = $widget->get_model();
    $model->append($data2);
}

?>

Output

As shown above.

 

Explanation

  1. To enable drag on the source treeview, we use the method drag_source_set(start_button_mask, targets, actions).
    • start_button_mask: just a combination of GDK Modifier Constants
    • targets: an array containing information about the targets. For each target,
      1. the first element is the MIME type of the drag source. For treeview items, we use 'text/plain'.
      2. The second element is the target flags, which could be a combination of gtk.TARGET_SAME_APP and gtk.TARGET_SAME_WIDGET or neither. Here we just use 0 which means no restriction.
      3. The last element is an integer ID used for identification purpose. Here we also just put a 0.
    • actions: a combination of GDK Drag Action Constants. Here we use Gdk::ACTION_COPY|Gdk::ACTION_MOVE.
  2. The signal drag-data-get allows us to know when user start dragging.
  3. To enable drop on the destination treeview, we use the method drag_dest_set(flags, targets, actions).
    • flags is one of four GtkDestDefaults values which define how the widget reacts when the drag is over the widget. Here we use GTK_DEST_DEFAULT_ALL.
    • targets and actions are exactly the same as in note 1.
  4. The signal drag-data-received allows us to know when user "drops" an item.
  5. Store the item to be dragged in the data field.
  6. Remove the item from the source first, and append the dragged item to the end of the destination tree view.

Related Links

Add comment


Security code
Refresh