Hi all,

I'm using pygtk-1.99.16 (packaged for Debian Unstable).

I'm trying to extend the row reordering using drag and drop, but I'm
stuck with some odd behaviours.

First: I've tried to use the GtkTreeView::enable_model_drag_source, but,
although the ChangeLog says that it has been included inside gtk.defs
sometime in 2001, here's the error that I get:

AttributeError: 'ScheduleView' object has no attribute 'enable_model_drag_source'

[ScheduleView is a subclassed gtk.TreeView, but this error also appears
when I use a plain gtk.TreeView].

Since I do not know how to wrap the gtk_tree_view_enable_model_drag_*
functions, I've tryed to work them around, and so I wrote a working
example, using the list_store.py example inside pygtk-demo as a
template. I cannot stress this too much: the dnd_treeview.py program
works correctly.

Unfortunately, when I try to use the same workaround inside my code,
this is what happens:

        1. dragging every row *after* the fourth from the end to a
           position *before* the fourth from the end results in the row
           being set to the fourth from the end; e.g.

           row 1          row 1          row 1
           row 2          row 2 <- drop  row 2
           row 3          row 3          row 3
           row 4          row 4          row 7 <- result
           row 5          row 5          row 4
           row 6          row 6          row 5
           row 7 <- drag  row 7          row 6
        
        2. dragging every row *before* the fourth from the end produces
           no result whatsoever.

I know that it sounds strange, and believe me: I've tried every
concievable option and method, before giving up and asking here.

I've attached the working dnd example, and the two callbacks I use for
drag_data_get and drag_data_received.

Thanks in advance,
 Emmanuele.

-- 
Emmanuele Bassi (Zefram)           [ http://digilander.libero.it/ebassi/ ]
GnuPG Key fingerprint = 4DD0 C90D 4070 F071 5738  08BD 8ECC DB8F A432 0FF4
#!/usr/bin/env python
'''Tree View/List Store

The GtkListStore is used to store data in list form, to be used 
later on by a GtkTreeView to display it. This demo builds a
simple GtkListStore and displays it. See the Stock Browser
demo for a more advanced example.''' # "

description = 'List Store'

import pygtk
pygtk.require('2.0')

import gobject
import gtk
from gtk import TRUE, FALSE

COLUMN_FIXED       = 0
COLUMN_NUMBER      = 1
COLUMN_SEVERITY    = 2
COLUMN_DESCRIPTION = 3

data = \
[[FALSE, 60482, 'Normal', 'scrollable notebooks and hidden tabs'],
 [FALSE, 60620, 'Critical',
  'gdk_window_clear_area (gdkwindow-win32.c) is not thread-safe'],
 [FALSE, 50214, 'Major', 'Xft support does not clean up correctly'],
 [TRUE,  52877, 'Major', 'GtkFileSelection needs a refresh method. '],
 [FALSE, 56070, 'Normal', "Can't click button after setting in sensitive"],
 [TRUE,  56355, 'Normal', 'GtkLabel - Not all changes propagate correctly'],
 [FALSE, 50055, 'Normal', 'Rework width/height computations for TreeView'],
 [FALSE, 58278, 'Normal', "gtk_dialog_set_response_sensitive () doesn't work"],
 [FALSE, 55767, 'Normal', 'Getters for all setters'],
 [FALSE, 56925, 'Normal', 'Gtkcalender size'],
 [FALSE, 56221, 'Normal', 'Selectable label needs right-click copy menu'],
 [TRUE,  50939, 'Normal', 'Add shift clicking to GtkTextView'],
 [FALSE, 6112,  'Enhancement', 'netscape-like collapsable toolbars'],
 [FALSE, 1,     'Normal', 'First bug :=)']]

def serialize(model, iter):
    state = model.get_value(iter, COLUMN_FIXED)
    number = model.get_value(iter, COLUMN_NUMBER)
    severity = model.get_value(iter, COLUMN_SEVERITY)
    description = model.get_value(iter, COLUMN_DESCRIPTION)

    s = ('%d�%d�%s�%s' % (state, number, severity, description))
    return s

def unserialize(s, model, iter):
    state, number, severity, description = s.split('�', 4)
    
    model.set(iter,
        COLUMN_FIXED, int(state),
        COLUMN_NUMBER, int(number),
        COLUMN_SEVERITY, severity,
        COLUMN_DESCRIPTION, description)

def on_drag_begin(treeview, context):
    print 'on_drag_begin'

def on_drag_motion(treeview, context, x, y, time):
    #print 'on_drag_motion'
    pass

def on_drag_data_get(treeview, context, selection, info, time):
    print 'on_drag_data_get'
    
    s = treeview.get_selection()
    model, s_iter = s.get_selected()

    payload = serialize(model, s_iter)
    print 'Source: %s' % payload

    selection.set(selection.target, 8, payload)
    model.remove(s_iter)

def on_drag_data_received(treeview, context, x, y, selection, info, time):
    print 'on_drag_data_received'
    
    model = treeview.get_model()
    
    try:
        path, column, cell_x, cell_y = treeview.get_path_at_pos(x, y)
    except TypeError, e:
        print 'out of boundaries'
        context.finish(gtk.FALSE, gtk.FALSE, time)
        return
    
    print 'path := %s' % path
    new_path = (path[0] - 1,)
    print 'new_path := %s' % new_path
    target_iter = model.get_iter(new_path)
        
    if selection and selection.format == 8:
        print 'Target: %s' % selection.data
        iter = model.insert_before(target_iter)
        unserialize(selection.data, model, iter)
        
        context.finish(gtk.TRUE, gtk.FALSE, time)
    else:
        context.finish(gtk.FALSE, gtk.FALSE, time)

def on_drag_drop(treeview, context, x, y, time):
    return gtk.FALSE

def create_model():
    store = gtk.ListStore(gobject.TYPE_BOOLEAN,
                          gobject.TYPE_UINT,
                          gobject.TYPE_STRING,
                          gobject.TYPE_STRING)
    for item in data:
            iter = store.append()
            store.set(iter, COLUMN_FIXED, item[0],
                  COLUMN_NUMBER, item[1],
                  COLUMN_SEVERITY, item[2],
                  COLUMN_DESCRIPTION, item[3])
    return store

def fixed_toggled(cell, path, model):
    # get toggled iter
    iter = model.get_iter((int(path),))
    fixed = model.get_value(iter, COLUMN_FIXED)
    
    # do something with the value
    fixed = not fixed
    
    # set new value
    model.set(iter, COLUMN_FIXED, fixed)
        
def add_columns(treeview):
    model = treeview.get_model()
    
    # column for fixed toggles
    renderer = gtk.CellRendererToggle()
    renderer.connect('toggled', fixed_toggled, model)
    
    column = gtk.TreeViewColumn('Fixed?', renderer, active=COLUMN_FIXED)
    column.set_clickable(TRUE)
    
    # set this column to a fixed sizing(of 50 pixels)
    column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
    column.set_fixed_width(50)
    column.set_clickable(TRUE)
    
    treeview.append_column(column)

    # column for bug numbers
    column = gtk.TreeViewColumn('Bug Number', gtk.CellRendererText(),
                                text=COLUMN_NUMBER)
    treeview.append_column(column)
    
    # columns for severities
    column = gtk.TreeViewColumn('Severity', gtk.CellRendererText(),
                                text=COLUMN_SEVERITY)
    treeview.append_column(column)

    # column for description
    column = gtk.TreeViewColumn('Description', gtk.CellRendererText(),
                                 text=COLUMN_DESCRIPTION)
    treeview.append_column(column)
    
def main():
    TARGET_STRING = 0
    TARGET_ROOTWIN = 1
    DND_TARGETS = [
        ('STRING', 0, TARGET_STRING),
        ('text/plain', 0, TARGET_STRING),
    ]
    
    win = gtk.Window()
    win.connect('destroy', lambda win: gtk.main_quit())
        
    win.set_title('GtkListStore demo')
    win.set_border_width(8)
    
    vbox = gtk.VBox(FALSE, 8)
    win.add(vbox)
    
    label = gtk.Label('This is the bug list (note: not based on real data, '+\
  'it would be nice to have a nice ODBC interface to bugzilla or so, though).')
    vbox.pack_start(label, FALSE, FALSE)
    
    sw = gtk.ScrolledWindow()
    sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
    sw.set_policy(gtk.POLICY_NEVER,
                   gtk.POLICY_AUTOMATIC)
    vbox.pack_start(sw)
    
    model = create_model()
    
    treeview = gtk.TreeView(model)
    treeview.set_rules_hint(TRUE)
    treeview.set_search_column(COLUMN_DESCRIPTION)
    
    treeview.drag_source_set(gtk.gdk.BUTTON1_MASK, DND_TARGETS,
        gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
    treeview.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_TARGETS[:-1],
        gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
    
    treeview.connect('drag_begin', on_drag_begin)
    treeview.connect('drag_data_get', on_drag_data_get)
    treeview.connect('drag_data_received', on_drag_data_received)
    treeview.connect('drag_motion', on_drag_motion)
    treeview.connect('drag_drop', on_drag_drop)
    
    sw.add(treeview)
    
    add_columns(treeview)
    
    win.set_default_size(280, 250)
    
    win.show_all()
    gtk.main()
                                     
if __name__ == '__main__':
    main()
            
def on_drag_data_get(sview, context, selection, info, time):
    print 'on_drag_data_get'
    
    s = sview.get_selection()
    model, s_iter = s.get_selected()
    
    # retrieve the selected task
    task = sview.get_task(s_iter)
    
    # insert path before the task.
    s_path = int(model.get_path(s_iter)[0])
    
    # create the payload string.
    payload  = '%d�' % path
    payload += '<?xml version="1.0" encoding="UTF-8"?>'
    payload += '<gnome-schedule>'
    payload += task.serialize()
    payload += '</gnome-schedule>'
    print 'payload := %s' % payload

    selection.set(selection.target, 8, payload)
    
def on_drag_data_received(sview, context, x, y, selection, info, time):
    print 'on_drag_data_received'
    
    if selection and selection.format == 8:
        # retrieve both the path and the payload
        path, payload = selection.data.split('�', 2)
        start_path = (int(path),)
        
        parser = ScheduleParser()
        parser.parseString(payload)

        tasks = parser.get_entries()
        assert len(tasks) == 1
        
        model = sview.get_model()
        try:
            path, column, cell_x, cell_y = sview.get_path_at_pos(x, y)
        except TypeError, e:
            print 'out of boundaries... aborting.'
            context.finish(gtk.FALSE, gtk.FALSE, time)
            return
        
        # the path returned from the get_path_at_pos method is always shifted by
        # one. Is this wanted or is it a bug?
        drop_path = (path[0] - 1,)
        
        print 'start path := %s (type: %s)' % (start_path, type(start_path))
        print 'drop path  := %s (type: %s)' % (drop_path, type(drop_path))
        
        # used for future multiple drag'n'drop.
        for t in tasks:
            drop_iter = model.get_iter(drop_path)
            iter = model.insert_before(drop_iter)
            
            # fill the model.
            model.set(iter,
                COLUMN_PRIORITY, t.get_priority_string(),
                COLUMN_DATE_START, t.get_date_start(),
                COLUMN_DATE_END, t.get_date_end(),
                COLUMN_COMPLETED, t.is_complete(),
                COLUMN_DESCRIPTION, t.get_description(),
                COLUMN_NOTES, t.get_notes(),
                COLUMN_ACTIVE, not t.is_complete())
            
            start_iter = model.get_iter(start_path)
            model.remove(start_iter)
        
        context.finish(gtk.TRUE, gtk.FALSE, time)
    else:
        context.finish(gtk.FALSE, gtk.FALSE, time)
_______________________________________________
pygtk mailing list   [EMAIL PROTECTED]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/

Reply via email to