Working drag and drop from listviews to clipboard

svn: r20087
This commit is contained in:
Benny Malengier
2012-07-26 13:42:10 +00:00
parent bd15d69989
commit d9710bdcc2
3 changed files with 71 additions and 43 deletions

View File

@ -893,9 +893,9 @@ class ClipboardListModel(Gtk.ListStore):
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class ClipboardListView(object): class ClipboardListView(object):
LOCAL_DRAG_TARGET = Gtk.TargetEntry.new('MY_TREE_MODEL_ROW',
Gtk.TargetFlags.SAME_WIDGET, 0)
LOCAL_DRAG_TYPE = 'MY_TREE_MODEL_ROW' LOCAL_DRAG_TYPE = 'MY_TREE_MODEL_ROW'
LOCAL_DRAG_ATOM_TYPE = Gdk.atom_intern(LOCAL_DRAG_TYPE, False)
LOCAL_DRAG_TARGET = (LOCAL_DRAG_TYPE, Gtk.TargetFlags.SAME_WIDGET, 0)
def __init__(self, dbstate, widget): def __init__(self, dbstate, widget):
@ -952,16 +952,25 @@ class ClipboardListView(object):
self._widget.set_enable_search(True) self._widget.set_enable_search(True)
#self._widget.set_search_column(3) #self._widget.set_search_column(3)
targ_data = (ClipboardListView.LOCAL_DRAG_TARGET,) + \ targ_data = DdTargets.all_dtype()
DdTargets.all_targets() tglist = Gtk.TargetList.new([])
self._widget.drag_dest_set(Gtk.DestDefaults.ALL, targ_data, tglist.add(ClipboardListView.LOCAL_DRAG_ATOM_TYPE,
ClipboardListView.LOCAL_DRAG_TARGET[1],
ClipboardListView.LOCAL_DRAG_TARGET[2])
for tg in targ_data:
tglist.add(tg.atom_drag_type, tg.target_flags, tg.app_id)
self._widget.enable_model_drag_dest([],
Gdk.DragAction.COPY) Gdk.DragAction.COPY)
#TODO GTK3: wourkaround here for bug https://bugzilla.gnome.org/show_bug.cgi?id=680638
self._widget.drag_dest_set_target_list(tglist)
#self._widget.drag_dest_set(Gtk.DestDefaults.ALL, targ_data,
# Gdk.DragAction.COPY)
self._widget.connect('drag_data_get', self.object_drag_data_get) self._widget.connect('drag-data-get', self.object_drag_data_get)
self._widget.connect('drag_begin', self.object_drag_begin) self._widget.connect('drag-begin', self.object_drag_begin)
self._widget.connect('drag_data_received', self._widget.connect('drag-data-received',
self.object_drag_data_received) self.object_drag_data_received)
self._widget.connect('drag_end', self.object_drag_end) self._widget.connect('drag-end', self.object_drag_end)
self.register_wrapper_classes() self.register_wrapper_classes()
@ -1127,12 +1136,13 @@ class ClipboardListView(object):
node = model.get_iter(path) node = model.get_iter(path)
if node is not None: if node is not None:
o = model.get_value(node,1) o = model.get_value(node,1)
targets += [target.target() for target in o.__class__.DROP_TARGETS] targets += [target.target_data() for target in o.__class__.DROP_TARGETS]
self._widget.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, targets, self._widget.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK,
targets,
Gdk.DragAction.COPY | Gdk.DragAction.MOVE) Gdk.DragAction.COPY | Gdk.DragAction.MOVE)
def object_drag_begin(self, context, a): def object_drag_begin(self, widget, drag_context):
""" Handle the beginning of a drag operation. """ """ Handle the beginning of a drag operation. """
pass pass
@ -1155,13 +1165,12 @@ class ClipboardListView(object):
o = model.get_value(node,1) o = model.get_value(node,1)
raw_list.append(o.pack()) raw_list.append(o.pack())
sel_data.set(sel_data.target, 8, pickle.dumps(raw_list)) sel_data.set(sel_data.target, 8, pickle.dumps(raw_list))
return True
def object_drag_data_received(self,widget,context,x,y,selection,info,time, def object_drag_data_received(self, widget, context, x, y, selection, info,
title=None, value=None, dbid=None, time, title=None, value=None, dbid=None,
dbname=None): dbname=None):
model = widget.get_model() model = widget.get_model()
sel_data = selection.data sel_data = selection.get_data()
# In Windows time is always zero. Until that is fixed, use the seconds # In Windows time is always zero. Until that is fixed, use the seconds
# of the local time to filter out double drops. # of the local time to filter out double drops.
realTime = strftime("%S") realTime = strftime("%S")
@ -1192,7 +1201,8 @@ class ClipboardListView(object):
if dragtype in self._target_type_to_wrapper_class_map: if dragtype in self._target_type_to_wrapper_class_map:
possible_wrappers = [dragtype] possible_wrappers = [dragtype]
else: else:
possible_wrappers = [target for target in context.targets tgs = [atm.name() for atm in context.list_targets()]
possible_wrappers = [target for target in tgs
if target in self._target_type_to_wrapper_class_map] if target in self._target_type_to_wrapper_class_map]
if len(possible_wrappers) == 0: if len(possible_wrappers) == 0:
@ -1225,7 +1235,7 @@ class ClipboardListView(object):
data = [o.__class__.DRAG_TARGET.drag_type, o, None, data = [o.__class__.DRAG_TARGET.drag_type, o, None,
o._type, o._value, o._dbid, o._dbname] o._type, o._value, o._dbid, o._dbname]
contains = model_contains(model, data) contains = model_contains(model, data)
if context.action != Gdk.DragAction.MOVE and contains: if context.get_actions() != Gdk.DragAction.MOVE and contains:
continue continue
drop_info = widget.get_dest_row_at_pos(x, y) drop_info = widget.get_dest_row_at_pos(x, y)
if drop_info: if drop_info:
@ -1242,7 +1252,7 @@ class ClipboardListView(object):
# FIXME: there is one bug here: if you multi-select and drop # FIXME: there is one bug here: if you multi-select and drop
# on self, then it moves the first, and copies the rest. # on self, then it moves the first, and copies the rest.
if context.action == Gdk.DragAction.MOVE: if context.get_actions() == Gdk.DragAction.MOVE:
context.finish(True, True, time) context.finish(True, True, time)
# remember time for double drop workaround. # remember time for double drop workaround.
@ -1333,7 +1343,6 @@ class ClipboardWindow(ManagedWindow):
ClipboardWindow.otree.connect('row-inserted', ClipboardWindow.otree.connect('row-inserted',
self.set_clear_all_btn_sensitivity) self.set_clear_all_btn_sensitivity)
self.object_list.set_model(ClipboardWindow.otree) self.object_list.set_model(ClipboardWindow.otree)
#Database might have changed, objects might have been removed, #Database might have changed, objects might have been removed,
@ -1398,7 +1407,7 @@ class MultiTreeView(Gtk.TreeView):
self.dbstate = dbstate self.dbstate = dbstate
self.uistate = uistate self.uistate = uistate
self.title = title if title else _("Clipboard") self.title = title if title else _("Clipboard")
super(MultiTreeView, self).__init__() Gtk.TreeView.__init__(self)
self.connect('button_press_event', self.on_button_press) self.connect('button_press_event', self.on_button_press)
self.connect('button_release_event', self.on_button_release) self.connect('button_release_event', self.on_button_release)
self.connect('key_press_event', self.key_press_event) self.connect('key_press_event', self.key_press_event)
@ -1486,12 +1495,12 @@ class MultiTreeView(Gtk.TreeView):
and not (event.get_state() & (Gdk.ModifierType.CONTROL_MASK|Gdk.ModifierType.SHIFT_MASK)) and not (event.get_state() & (Gdk.ModifierType.CONTROL_MASK|Gdk.ModifierType.SHIFT_MASK))
and self.get_selection().path_is_selected(target[0])): and self.get_selection().path_is_selected(target[0])):
# disable selection # disable selection
self.get_selection().set_select_function(lambda *ignore: False) self.get_selection().set_select_function(lambda *ignore: False, None)
self.defer_select = target[0] self.defer_select = target[0]
def on_button_release(self, widget, event): def on_button_release(self, widget, event):
# re-enable selection # re-enable selection
self.get_selection().set_select_function(lambda *ignore: True) self.get_selection().set_select_function(lambda *ignore: True, None)
target = self.get_path_at_pos(int(event.x), int(event.y)) target = self.get_path_at_pos(int(event.x), int(event.y))
if (self.defer_select and target if (self.defer_select and target

View File

@ -55,14 +55,16 @@
import logging import logging
log = logging.getLogger(".DdTargets") log = logging.getLogger(".DdTargets")
from gi.repository import Gdk
from gi.repository import Gtk from gi.repository import Gtk
class _DdType: class _DdType:
"""Represents the fields needed by a drag and drop target.""" """Represents the fields needed by a drag and drop target."""
_APP_ID_OFFSET = 40 # Starting value of app_ids _APP_ID_OFFSET = 40L # Starting value of app_ids
def __init__(self, container, drag_type, target_flags=0, app_id=None): def __init__(self, container, drag_type,
target_flags=0L, app_id=None):
"""Create a new DdType: """Create a new DdType:
drag_type: string holding the name of the type. drag_type: string holding the name of the type.
@ -71,6 +73,7 @@ class _DdType:
""" """
self.drag_type = drag_type self.drag_type = drag_type
self.atom_drag_type = Gdk.atom_intern(drag_type, False)
self.target_flags = target_flags self.target_flags = target_flags
self.app_id = app_id or self._calculate_id() self.app_id = app_id or self._calculate_id()
container.insert(self) container.insert(self)
@ -176,13 +179,13 @@ class _DdTargets(object):
self.CHILD = _DdType(self, 'child') self.CHILD = _DdType(self, 'child')
self.SPOUSE = _DdType(self, 'spouse') self.SPOUSE = _DdType(self, 'spouse')
self.TEXT_MIME = _DdType(self, 'text/plain', 0, 0) self.TEXT_MIME = _DdType(self, 'text/plain', 0L, 0L)
self.TEXT = _DdType(self, 'TEXT', 0, 1) self.TEXT = _DdType(self, 'TEXT', 0L, 1L)
self.STRING = _DdType(self, 'STRING', 0, 2) self.STRING = _DdType(self, 'STRING', 0L, 2L)
self.COMPOUND_TEXT = _DdType(self, 'COMPOUND_TEXT', 0, 3) self.COMPOUND_TEXT = _DdType(self, 'COMPOUND_TEXT', 0L, 3L)
self.UTF8_STRING = _DdType(self, 'UTF8_STRING', 0, 4) self.UTF8_STRING = _DdType(self, 'UTF8_STRING', 0L, 4L)
self.URI_LIST = _DdType(self, 'text/uri-list', 0, 5) self.URI_LIST = _DdType(self, 'text/uri-list', 0L, 5L)
self.APP_ROOT = _DdType(self, 'application/x-rootwin-drop', 0, 6) self.APP_ROOT = _DdType(self, 'application/x-rootwin-drop', 0L, 6L)
# List of all the text types. These are types # List of all the text types. These are types
# that can be interpreted as text. # that can be interpreted as text.
@ -234,7 +237,6 @@ class _DdTargets(object):
return tuple([t.target() for t in self._all_text_types]) return tuple([t.target() for t in self._all_text_types])
def all_gramps_targets(self): def all_gramps_targets(self):
"""Return a list off the internal gramps targets.""" """Return a list off the internal gramps targets."""
@ -244,6 +246,11 @@ class _DdTargets(object):
"""Return a list of all the known targets.""" """Return a list of all the known targets."""
return self.all_gramps_targets() + self.all_text_targets() return self.all_gramps_targets() + self.all_text_targets()
def all_dtype(self):
"""Return all known Ddtype"""
return [x for x in self._all_gramps_types] + \
[x for x in self._all_text_types]
# Create the singleton instance. # Create the singleton instance.
DdTargets = _DdTargets() DdTargets = _DdTargets()

View File

@ -446,21 +446,23 @@ class ListView(NavigationView):
def drag_begin(self, widget, context): def drag_begin(self, widget, context):
widget.drag_source_set_icon_stock(self.get_stock()) widget.drag_source_set_icon_stock(self.get_stock())
return True
def drag_data_get(self, widget, context, sel_data, info, time): def drag_data_get(self, widget, context, sel_data, info, time):
selected_ids = self.selected_handles() selected_ids = self.selected_handles()
#Gtk.selection_add_target(widget, sel_data.get_selection(),
# Gdk.atom_intern(self.drag_info().drag_type, False),
# self.drag_info().app_id)
if len(selected_ids) == 1: if len(selected_ids) == 1:
data = (self.drag_info().drag_type, id(self), selected_ids[0], 0) data = (self.drag_info().drag_type, id(self), selected_ids[0], 0)
sel_data.set(sel_data.target, 8, pickle.dumps(data)) sel_data.set(self.drag_info().atom_drag_type, 8, pickle.dumps(data))
elif len(selected_ids) > 1: elif len(selected_ids) > 1:
data = (self.drag_list_info().drag_type, id(self), data = (self.drag_list_info().drag_type, id(self),
[(self.drag_info().drag_type, handle) [(self.drag_list_info().drag_type, handle)
for handle in selected_ids], for handle in selected_ids],
0) 0)
sel_data.set(sel_data.target, 8, pickle.dumps(data)) sel_data.set(self.drag_list_info().atom_drag_type, 8, pickle.dumps(data))
return True
def set_column_order(self): def set_column_order(self):
""" """
@ -665,13 +667,23 @@ class ListView(NavigationView):
if len(selected_ids) == 1: if len(selected_ids) == 1:
if self.drag_info(): if self.drag_info():
self.list.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, self.list.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
[self.drag_info().target()], [],
Gdk.DragAction.COPY) Gdk.DragAction.COPY)
#TODO GTK3: wourkaround here for bug https://bugzilla.gnome.org/show_bug.cgi?id=680638
tglist = Gtk.TargetList.new([])
dtype = self.drag_info()
tglist.add(dtype.atom_drag_type, dtype.target_flags, dtype.app_id)
self.list.drag_source_set_target_list(tglist)
elif len(selected_ids) > 1: elif len(selected_ids) > 1:
if self.drag_list_info(): if self.drag_list_info():
self.list.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, self.list.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
[self.drag_list_info().target()], [],
Gdk.DragAction.COPY) Gdk.DragAction.COPY)
#TODO GTK3: wourkaround here for bug https://bugzilla.gnome.org/show_bug.cgi?id=680638
tglist = Gtk.TargetList.new([])
dtype = self.drag_list_info()
tglist.add(dtype.atom_drag_type, dtype.target_flags, dtype.app_id)
self.list.drag_source_set_target_list(tglist)
self.uistate.modify_statusbar(self.dbstate) self.uistate.modify_statusbar(self.dbstate)