Re: multiple-item drag-and-drop



On 31/08/2007, Jeffrey Ratcliffe <jeffrey ratcliffe gmail com> wrote:
There is one last part of this not working - I have added a pixbuf and
a text column to the list. Bizarrely, although the first and third
(int and text) columns are moved or copied properly, the second, the
pixbuf always ends up with undef.

The reason for this was thaw/freeze and SelectionData. In this
example, as Emmanuele points out in
http://mail.gnome.org/archives/gtk-perl-list/2004-January/msg00291.html,
as there is only one tree involved, SelectionData is not needed, but
I'd love to know what the general case using SelectionData would look
like.

For those interested, here is the working example with dnd, scrolling,
move, copy and multiple columns:

#!/usr/bin/perl
use warnings;
use strict;
use Glib qw(TRUE FALSE);             # To get TRUE and FALSE
use Gtk2 -init;
use Gtk2::Ex::Simple::List;

my $win = Gtk2::Window->new;
$win->signal_connect (delete_event => sub { Gtk2->main_quit; });

my $scwin = Gtk2::ScrolledWindow -> new;
$win->add ($scwin);
$scwin -> set_policy ('automatic', 'automatic');

my $slist = Gtk2::Ex::Simple::List->new ( 'Int' => 'int', 'Pic' =>
'pixbuf', 'Txt' => 'text' );
for (my $i = 1; $i < 21; $i++) {
  push @{$slist->{data}}, [ $i,
Gtk2::Image->new->render_icon('gtk-jump-to', 'menu'), 'text' ];
}
$slist -> set_reorderable( TRUE );
#$slist -> set_headers_visible(FALSE);
$slist -> get_selection -> set_mode ('multiple');

my $target_entry = {
 target => 'Glib::Scalar', # some string representing the drag type
  flags => 'same-widget',   # Gtk2::TargetFlags
 info => 1,                # some app-defined integer identifier
};
$slist->drag_source_set('button1-mask', ['copy', 'move'], $target_entry);
$slist->drag_dest_set(['motion', 'highlight'], ['copy', 'move'], $target_entry);

$slist->signal_connect('drag-data-get' => sub {
  my ($tree, $context, $sel) = @_;
  $sel->set($sel->target, 8, 'data');
});

$slist->signal_connect('drag-data-delete' => sub {
 my ($tree, $context) = @_;
 my $model = $tree->get_model;
 my @data = $tree->get_selection->get_selected_rows;

 for (reverse @data) {
  my $iter = $model->get_iter($_);
  my $info = $model->get($iter, 0);
  $model->remove($iter);
 }

 $tree->get_selection->unselect_all;
});

$slist->signal_connect('drag-data-received' => sub {
  my ($tree, $context, $x, $y, $sel) = @_;
 my ($path, $how) = $tree->get_dest_row_at_pos($x, $y);
 my $model = $tree->get_model;
  my $data = $sel->data or return;
 my $delete = $context->action == 'move';
  my $iter;

 if ($path) {
  $iter = $model->get_iter($path);
  if ($how eq 'after' or $how eq 'into-or-after') {
   $iter = $model->insert_after($iter);
  }
  else {
   $iter = $model->insert_before($iter)
  }
 }
  else { $iter = $model->append }

 my @rows = $tree->get_selection->get_selected_rows or return;
 my @data;
 for (@rows) {
  my $iter = $model->get_iter($_);
  my @info = $model->get($iter);
  push @data, [ @info ];
 }
  $model->set($iter, data2model(shift @data));
  $model->set($iter = $model->insert_after($iter), data2model($_)) for (@data);
 $context->finish(1, $delete, time);
});

# Callback for dropped signal.
$slist -> signal_connect(drag_drop => sub {
 my ($tree, $context, $x, $y, $when) = @_;
  if (my $targ = $context->targets) {
  $tree->drag_get_data($context, $targ, $when);
  return TRUE;
 }
 return FALSE;
});

# when the user clicks, store the current header height, in case this
# click starts a drag.
$slist -> signal_connect (button_press_event => sub {
 my ($widget, $event) = @_;
  my (undef, $bin_window_height) = $event->window->get_size;
 $widget->{header_height} = $widget->allocation->height - $bin_window_height;
 return FALSE;
});

# If dragged below the bottom of the window, scroll it.
$slist->signal_connect('drag-motion' => sub {
 my ($tree, $context, $x, $y, $t) = @_;
 my ($path, $how) = $tree->get_dest_row_at_pos($x, $y) or return;
 my $scroll = $tree->parent;
 $tree->set_drag_dest_row($path, $how);

# Make move the default
 my @action;
 if ($context->actions == 'copy') {
  @action = ( 'copy' );
 }
 else {
  @action = ( 'move' );
 }
 $context->status(@action, $t);

 my $adj = $scroll->get_vadjustment;
 my ($value, $step) = ($adj->value, $adj->step_increment);

 if ($y > $adj->page_size - $step/2) {
  my $v = $value + $step;
  my $m = $adj->upper - $adj->page_size;
  $adj->set_value($v > $m ? $m : $v);
 }
  elsif ($y < $tree->{header_height} + $step ) {
  my $v = $value - $step;
  my $m = $adj->lower;
  $adj->set_value($v < $m ? $m : $v);
 }

 return FALSE;
});

$scwin -> add($slist);

$win->show_all;
Gtk2->main;


# Take an array reference and return it as an array with the indices inserted

sub data2model {
 my ($input) = @_;
 my @output;
 my $i = 0;
 for (@$input) {
 push @output, $i++, $_;
 }
 return @output;
}



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