Re: multiple-item drag-and-drop



On 31/08/2007, muppet <scott asofyet org> wrote:
In theory, then,  header height = widget allocation height - bin
window drawable height.  The drag-motion callback doesn't give you an
event, so you'd have to capture this value in the button-press-event
that starts the drag, as, i'm thinking, that button-press-event will
happen with $event->window == treeview->private->bin_window.

You're a genius. That works a treat.

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.

Looking at the tied array, the first pixbuf seems to be real and the
following ones seem to be references to the first one. I don't know if
this is the reason for my problem, but it seems strange nonetheless.

Any ideas?

Jeff

#!/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;
use Storable qw(freeze thaw);

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) = @_;
 my $model = $tree->get_model;
 my @data = $tree->get_selection->get_selected_rows or return;
 my @valid;

 for (@data) {
  my $iter = $model->get_iter($_);
  my @info = $model->get($iter);
  push @valid, [ @info ];
 }

 return unless @valid;

  $sel->set($sel->target, 8, freeze(\ valid));
});

$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;

 $data = thaw($data);

 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 }

  $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]