1#!/usr/bin/env python
2'''Drag and Drop
3
4This is a test of the drag and drop capabilities of gtk.  It is a
5fairly straight forward port of the example distributed with gtk.
6'''
7
8import pygtk
9pygtk.require('2.0')
10import gtk
11import gobject
12
13from dndpixmap import drag_icon_xpm, trashcan_open_xpm, trashcan_closed_xpm
14
15TARGET_STRING = 0
16TARGET_ROOTWIN = 1
17
18target = [
19    ('STRING', 0, TARGET_STRING),
20    ('text/plain', 0, TARGET_STRING),
21    ('application/x-rootwin-drop', 0, TARGET_ROOTWIN)
22]
23
24def create_pixmap(widget, xpm_data):
25    return \
26        gtk.gdk.pixmap_colormap_create_from_xpm_d(
27            None, widget.get_colormap(), None, xpm_data)
28
29class DragAndDropDemo(gtk.Window):
30    trashcan_open = None
31    trashcan_open_mask = None
32    trashcan_closed = None
33    trashcan_closed_mask = None
34    drag_icon = None
35    drag_mask = None
36    have_drag = False
37    popped_up = False
38    in_popup = False
39    popup_timer = 0
40    popdown_timer = 0
41    popup_win = None
42
43    def __init__(self, parent=None):
44        gtk.Window.__init__(self)
45        try:
46            self.set_screen(parent.get_screen())
47        except AttributeError:
48            self.connect('destroy', lambda *w: gtk.main_quit())
49        self.set_title(self.__class__.__name__)
50
51        table = gtk.Table(2,2)
52        self.add(table)
53
54        self.drag_icon, self.drag_mask = \
55            create_pixmap(self, drag_icon_xpm)
56        self.trashcan_open, self.trashcan_open_mask = \
57            create_pixmap(self, trashcan_open_xpm)
58        self.trashcan_closed, self.trashcan_closed_mask = \
59            create_pixmap(self, trashcan_closed_xpm)
60
61        label = gtk.Label('Drop to Trashcan!\n')
62        label.drag_dest_set(gtk.DEST_DEFAULT_ALL, target[:-1],
63                gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
64        label.connect('drag_data_received', self.label_drag_data_received)
65        table.attach(label, 0, 1, 0, 1)
66
67        label = gtk.Label('Popup\n')
68        label.drag_dest_set(gtk.DEST_DEFAULT_ALL, target[:-1],
69                gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
70        table.attach(label, 1, 2, 1, 2)
71        label.connect('drag_motion', self.popsite_motion)
72        label.connect('drag_leave', self.popsite_leave)
73
74        image = gtk.Image()
75        image.set_from_pixmap(self.trashcan_closed, self.trashcan_closed_mask)
76        image.drag_dest_set(0, [], 0)
77        table.attach(image, 1, 2, 0, 1)
78        image.connect('drag_leave', self.target_drag_leave)
79        image.connect('drag_motion', self.target_drag_motion)
80        image.connect('drag_drop', self.target_drag_drop)
81        image.connect('drag_data_received', self.target_drag_data_received)
82
83        b = gtk.Button('Drag from Here\n')
84        b.drag_source_set(gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON3_MASK,
85                  target, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
86        b.drag_source_set_icon(self.get_colormap(), self.drag_icon, self.drag_mask)
87        table.attach(b, 0, 1, 1, 2)
88        b.connect('drag_data_get', self.source_drag_data_get)
89        b.connect('drag_data_delete', self.source_drag_data_delete)
90        self.show_all()
91
92    def label_drag_data_received(self, w, context, x, y, data, info, time):
93        if data and data.format == 8:
94            print 'Received "%s" in label' % data.data
95            context.finish(True, False, time)
96        else:
97            context.finish(False, False, time)
98
99    def popsite_motion(self, w, context, x, y, time):
100        if not self.popup_timer:
101            self.popup_timer = gobject.timeout_add(500, self.popup_cb)
102        return True
103
104    def popsite_leave(self, w, context, time):
105        if self.popup_timer:
106            gobject.source_remove(self.popup_timer)
107            self.popup_timer = 0
108
109    def popup_motion(self, w, context, x, y, time):
110        print 'popup_motion'
111        if not self.in_popup:
112            self.in_popup = True
113            if self.popdown_timer:
114                print 'removed popdown'
115                gobject.source_remove(self.popdown_timer)
116                self.popdown_timer = 0
117        return True
118
119    def popup_leave(self, w, context, time):
120        print 'popup_leave'
121        if self.in_popup:
122            self.in_popup = False
123            if not self.popdown_timer:
124                print 'added popdown'
125                self.popdown_timer = gobject.timeout_add(500, self.popdown_cb)
126
127    def popup_cb(self):
128        if not self.popped_up:
129            if self.popup_win is None:
130                self.popup_win = gtk.Window(gtk.WINDOW_POPUP)
131                self.popup_win.set_position(gtk.WIN_POS_MOUSE)
132                table = gtk.Table(3, 3)
133                for k in range(9):
134                    i, j = divmod(k, 3)
135                    b = gtk.Button("%d,%d" % (i,j))
136                    b.drag_dest_set(gtk.DEST_DEFAULT_ALL, target[:-1],
137                        gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
138                    b.connect('drag_motion', self.popup_motion)
139                    b.connect('drag_leave', self.popup_leave)
140                    table.attach(b, i, i+1, j, j+1)
141                table.show_all()
142                self.popup_win.add(table)
143            self.popup_win.present()
144            self.popped_up = True
145        self.popdown_timer = gobject.timeout_add(500, self.popdown_cb)
146        print 'added popdown'
147        self.popup_timer = 0
148        return False
149
150    def popdown_cb(self):
151        print 'popdown'
152        #if self.in_popup:
153        #    return True
154        self.popdown_timer = 0
155        self.popup_win.hide()
156        self.popped_up = False
157        return False
158
159    def target_drag_leave(self, img, context, time):
160        print 'leave'
161        self.have_drag = False
162        img.set_from_pixmap(self.trashcan_closed, self.trashcan_closed_mask)
163
164    def target_drag_motion(self, img, context, x, y, time):
165        if self.have_drag is False:
166            self.have_drag = True
167            img.set_from_pixmap(self.trashcan_open, self.trashcan_open_mask)
168        source_widget = context.get_source_widget()
169        print 'motion, source ',
170        if source_widget:
171            print source_widget.__class__.__name__
172        else:
173            print 'unknown'
174        context.drag_status(context.suggested_action, time)
175        return True
176
177    def target_drag_drop(self, img, context, x, y, time):
178        print 'drop'
179        self.have_drag = False
180        img.set_from_pixmap(self.trashcan_closed, self.trashcan_closed_mask)
181        if context.targets:
182            img.drag_get_data(context, context.targets[0], time)
183            return True
184        return False
185
186    def target_drag_data_received(self, img, context, x, y, data, info, time):
187        if data.format == 8:
188            print 'Received "%s" in trashcan' % data.data
189            context.finish(True, False, time)
190        else:
191            context.finish(False, False, time)
192
193    def source_drag_data_get(self, btn, context, selection_data, info, time):
194        if info == TARGET_ROOTWIN:
195            print 'I was dropped on the rootwin'
196        else:
197            selection_data.set(selection_data.target, 8, "I'm Data!")
198
199    def source_drag_data_delete(self, btn, context, data):
200        print 'Delete the data!'
201
202def main():
203    DragAndDropDemo()
204    gtk.main()
205
206if __name__ == '__main__':
207    main()
208