package MyTreeModelSort;

use strict;
use Gtk3 -init;

use Glib::Object::Subclass
    'Gtk3::TreeModelSort',
    interfaces => [ 'Gtk3::TreeDragSource', 'Gtk3::TreeDragDest' ];

sub ROW_DRAGGABLE {
    my ($dest_path, $selection_data) = @_;
    print("row draggable : $dest_path ; $selection_data ;\n");
    return 1;
}

sub DRAG_DATA_GET {
    my ($dest_path, $selection_data) = @_;
    print("drag data get : $dest_path ; $selection_data ;\n");
    return 1;
}

sub DRAG_DATA_DELETE {
    my ($dest_path, $selection_data) = @_;
    print("drag data delete : $dest_path ; $selection_data ;\n");
    return 1;
}

sub DRAG_DATA_RECEIVED {
    my ($dest_path, $selection_data) = @_;
    print("drag data received : $dest_path ; $selection_data ;\n");
    return 1;
}

sub ROW_DROP_POSSIBLE {
    my ($dest_path, $selection_data) = @_;
    print("row drop possible : $dest_path ; $selection_data ;\n");
    return 1;
}

# ------------------------------------------------------------

package MyTreeStore;

use strict;
use Gtk3 -init;

use Glib::Object::Subclass
    'Gtk3::TreeStore',
    interfaces => [ 'Gtk3::TreeDragSource', 'Gtk3::TreeDragDest' ];

sub ROW_DRAGGABLE {
    my ($dest_path, $selection_data) = @_;
    print("row draggable : $dest_path ; $selection_data ;\n");
    return 1;
}

sub DRAG_DATA_GET {
    my ($dest_path, $selection_data) = @_;
    print("drag data get : $dest_path ; $selection_data ;\n");
    return 1;
}

sub DRAG_DATA_DELETE {
    my ($dest_path, $selection_data) = @_;
    print("drag data delete : $dest_path ; $selection_data ;\n");
    return 1;
}

sub DRAG_DATA_RECEIVED {
    my ($dest_path, $selection_data) = @_;
    print("drag data received : $dest_path ; $selection_data ;\n");
    return 1;
}

sub ROW_DROP_POSSIBLE {
    my ($dest_path, $selection_data) = @_;
    print("row drop possible : $dest_path ; $selection_data ;\n");
    return 1;
}

# ------------------------------------------------------------

package MainWindow;

use strict;
use Glib::Object::Introspection;
use Gtk3 -init;

# Window
my $window = Gtk3::Window->new();
$window->set_size_request(300, 200);
$window->signal_connect('delete_event' => sub {
    Gtk3->main_quit();
});

# TreeView
my $tree = Gtk3::TreeView->new();
$window->add($tree);

# Standard TreeStore
my $model = MyTreeStore->new();
$model->set_column_types(['Glib::String']);
for (my $i=4; $i>=0; $i--) {
    $model->insert_with_values(undef, -1, 0 => "Item $i");
}
# Sorted TreeStore
my $model_sort = MyTreeModelSort->new(model => $model);
$model_sort->set_default_sort_func(sub {
    my ($treestore, $a_iter, $b_iter) = @_;
    my $a = $treestore->get_value($a_iter, 0);
    my $b = $treestore->get_value($b_iter, 0);
    return lc($a) cmp lc($b);
});

## Use store
# If using the standard TreeStore, drag_data_received and drag_data_get are correctly raised
#$tree->set_model($model);

# If using the sorted TreeStore, drag_data events are *NOT* raised
$tree->set_model($model_sort);

# Build columns
my $colA = Gtk3::TreeViewColumn->new_with_attributes('ColA', Gtk3::CellRendererText->new(), 'markup', 0);
$tree->append_column($colA);

# Setup DnD
my $target_entry = Gtk3::TargetEntry->new('text/plain', ${Gtk3::TargetFlags->new(qw/same-widget/)}, 0);
$tree->enable_model_drag_source('button1-mask', [ $target_entry ], [ 'default', 'move' ]);
$tree->enable_model_drag_dest([ $target_entry ], [ 'default', 'move' ]);

# Handle DnD events
$tree->signal_connect('drag_begin' => sub {
    my ($tree, $context) = @_;
    print("Drag begin\n");
    return $context;
});
$tree->signal_connect('drag_data_received' => sub {
    my ($tree, $context, $x, $y, $data) = @_;
    print("Drag data received : $tree ; $context ; $x ; $y ; $data\n");
    return 1;
});

$tree->signal_connect('drag_data_get' => sub {
    my ($tree, $context, $data) = @_;
    print("Drag data get : $tree ; $context ; $data\n");
    return 1;
});

$window->show_all();
Gtk3->main();
