#!/usr/bin/env python

# example treeviewdnd.py

import pygtk
pygtk.require('2.0')
import gtk


class Store(gtk.GenericTreeModel):

    def __init__(self):
        gtk.GenericTreeModel.__init__(self)
        self.store = []

    def append(self, o):
        self.store.append(o)

    def on_get_flags(self):
        return gtk.TREE_MODEL_LIST_ONLY

    def on_get_n_columns(self):
        return len(self.store)

    def on_get_column_type(self, n):
        return str

    def on_get_iter(self, path):
        return self.store[path[0]]

    def on_get_path(self, rowref):
        return self.store.index(rowref)

    def on_get_value(self, rowref, column):
        return rowref[column]

    def on_iter_next(self, rowref):
        try:
            i = self.store.index(rowref) + 1
            return self.store[i]
        except IndexError:
            return None

    def on_iter_children(self, rowref):
        return False

    def on_iter_n_children(self, rowref):
        if rowref:
            return 0
        return len(self.store)

    def on_iter_nth_child(self, rowref, n):
        if rowref:
            return None
        try:
            return self.store[n]
        except IndexError:
            return None

    def on_iter_parent(child):
        return None

    def insert_before(self, iter, o):
        i = self.get_path(iter)[0]
        self.store.insert(i, o)

    def insert_after(self, iter, o):
        i = self.get_path(iter)[0] + 1
        self.store.insert(i, o)


class TreeViewDnDExample:

    TARGETS = [
        ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
        ]

    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_size_request(200, 200)
        self.window.connect("delete_event", self.delete_event)

        self.scrolledwindow = gtk.ScrolledWindow()
        self.vbox = gtk.VBox()
        self.vbox.pack_start(self.scrolledwindow, True)

        self.liststore = Store()
        #self.liststore = gtk.ListStore(str)

        for i in range(10):
            self.liststore.append([str(i)])

        self.treeview = gtk.TreeView(self.liststore)
        self.cell = gtk.CellRendererText()
        self.tvcolumn = gtk.TreeViewColumn('Column', self.cell, text=0)
        self.treeview.append_column(self.tvcolumn)

        self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
                                               self.TARGETS,
                                               gtk.gdk.ACTION_DEFAULT|
                                               gtk.gdk.ACTION_MOVE)
        self.treeview.drag_source_set(gtk.gdk.BUTTON1_MASK,
                                      self.TARGETS,
                                      gtk.gdk.ACTION_DEFAULT|
                                      gtk.gdk.ACTION_MOVE)

        # Drag doesn't start without this
        #self.treeview.enable_model_drag_dest(self.TARGETS,
        #                                     gtk.gdk.ACTION_DEFAULT)

        self.treeview.connect("drag_data_get", self.drag_data_get_data)
        self.treeview.connect("drag_data_received",
                              self.drag_data_received_data)
        self.treeview.connect("drag-drop", self.drag_drop)
        self.treeview.connect("drag-data-delete", self.drag_data_delete)

        self.scrolledwindow.add(self.treeview)
        self.window.add(self.vbox)
        self.window.show_all()

    def drag_data_get_data(self, treeview, context, selection, target_id,
                           etime):
        treeview.emit_stop_by_name("drag-data-get")
        treeselection = treeview.get_selection()
        model, iter = treeselection.get_selected()
        data = model.get_value(iter, 0)
        selection.set(selection.target, 8, data)
        return True

    def drag_data_received_data(self, treeview, context, x, y, selection,
                                info, etime):
        treeview.emit_stop_by_name("drag-data-received")
        model = treeview.get_model()
        data = selection.data
        drop_info = treeview.get_dest_row_at_pos(x, y)
        if drop_info:
            path, position = drop_info
            iter = model.get_iter(path)
            if (position == gtk.TREE_VIEW_DROP_BEFORE
                    or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
                model.insert_before(iter, [data])
            else:
                model.insert_after(iter, [data])
        else:
            model.append([data])
        if context.action == gtk.gdk.ACTION_MOVE:
            context.finish(True, True, etime)
        return True

    def drag_drop(self, treeview, context, x, y, time):
        treeview.emit_stop_by_name("drag-drop")
        treeview.drag_get_data(context, context.targets[-1], time)
        return True

    def drag_data_delete(self, treeview, context):
        treeview.emit_stop_by_name("drag-data-delete")


def main():
    gtk.main()

if __name__ == "__main__":
    treeviewdndex = TreeViewDnDExample()
    main()
