Skip to content

Commit f993a19

Browse files
committed
add drag and drop function
1 parent e55a072 commit f993a19

File tree

1 file changed

+81
-1
lines changed

1 file changed

+81
-1
lines changed

GraphView/graphview.py

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from html import escape
4444
import gi
4545
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib
46+
import pickle
4647

4748
#-------------------------------------------------------------------------
4849
#
@@ -66,6 +67,7 @@
6667
from gramps.gen.utils.libformatting import FormattingHelper
6768
from gramps.gen.utils.thumbnails import get_thumbnail_path
6869

70+
from gramps.gui.ddtargets import DdTargets
6971
from gramps.gui.dialog import OptionDialog, ErrorDialog, QuestionDialog2
7072
from gramps.gui.display import display_url
7173
from gramps.gui.editors import EditPerson, EditFamily, EditTagList
@@ -715,11 +717,13 @@ def __init__(self, view, dbstate, uistate):
715717
self._last_x = 0
716718
self._last_y = 0
717719
self._in_move = False
720+
self._in_drag = False
718721
self.view = view
719722
self.dbstate = dbstate
720723
self.uistate = uistate
721724
self.parser = None
722725
self.active_person_handle = None
726+
self.drag_person = None
723727

724728
self.actions = Actions(dbstate, uistate, self.view.bookmarks)
725729
self.actions.connect('rebuild-graph', self.view.build_tree)
@@ -874,6 +878,22 @@ def __init__(self, view, dbstate, uistate):
874878
# used for popup menu, prevent destroy menu as local variable
875879
self.menu = None
876880

881+
# setup drag and drop
882+
drag_widget = self.get_widget()
883+
drag_widget.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [],
884+
Gdk.DragAction.COPY)
885+
drag_widget.connect("drag_data_get", self.cb_drag_data_get)
886+
drag_widget.connect("drag_begin", self.cb_drag_begin)
887+
drag_widget.connect("drag_end", self.cb_drag_end)
888+
889+
tglist = Gtk.TargetList.new([])
890+
tglist.add(DdTargets.PERSON_LINK.atom_drag_type,
891+
DdTargets.PERSON_LINK.target_flags,
892+
DdTargets.PERSON_LINK.app_id)
893+
# allow drag to a text document, info on drag_get will be 0L !
894+
tglist.add_text_targets(0)
895+
drag_widget.drag_source_set_target_list(tglist)
896+
877897
def add_popover(self, widget, container):
878898
"""
879899
Add popover for button.
@@ -1182,6 +1202,7 @@ def populate(self, active_person):
11821202
# set the busy cursor, so the user knows that we are working
11831203
self.uistate.set_busy_cursor(True)
11841204

1205+
self._in_drag = False
11851206
self.clear()
11861207
self.active_person_handle = active_person
11871208

@@ -1316,6 +1337,7 @@ def button_release(self, item, target, event):
13161337
"""
13171338
Exit from scroll mode when button release.
13181339
"""
1340+
self._in_drag = False
13191341
button = event.get_button()[1]
13201342
if((button == 1 or button == 2) and
13211343
event.type == getattr(Gdk.EventType, "BUTTON_RELEASE")):
@@ -1344,6 +1366,35 @@ def motion_notify_event(self, item, target, event):
13441366
(event.y_root - self._last_y) * scale_coef)
13451367
self.vadjustment.set_value(new_y)
13461368
return True
1369+
1370+
if self._in_drag and (event.type == Gdk.EventType.MOTION_NOTIFY):
1371+
# start drag when cursor moved more then 5
1372+
# to separate it from simple click
1373+
if ((abs(self._last_x - event.x) > 5)
1374+
or (abs(self._last_x - event.x) > 5)):
1375+
self.uistate.set_busy_cursor(False)
1376+
# Remove all single click events
1377+
for click_item in self.click_events:
1378+
if not click_item.is_destroyed():
1379+
GLib.source_remove(click_item.get_id())
1380+
self.click_events.clear()
1381+
1382+
# translate to drag_widget coords
1383+
drag_widget = self.get_widget()
1384+
scale_coef = self.canvas.get_scale()
1385+
bounds = self.canvas.get_root_item().get_bounds()
1386+
height_canvas = bounds.y2 - bounds.y1
1387+
x = self._last_x * scale_coef - self.hadjustment.get_value()
1388+
y = ((height_canvas + self._last_y) * scale_coef -
1389+
self.vadjustment.get_value())
1390+
1391+
drag_widget.drag_begin_with_coordinates(
1392+
drag_widget.drag_source_get_target_list(),
1393+
Gdk.DragAction.COPY,
1394+
Gdk.ModifierType.BUTTON1_MASK,
1395+
event,
1396+
x, y)
1397+
return True
13471398
return False
13481399

13491400
def set_zoom(self, value):
@@ -1387,6 +1438,7 @@ def select_node(self, item, target, event):
13871438

13881439
if button == 1 and node_class == 'node': # left mouse
13891440
self.uistate.set_busy_cursor(True)
1441+
self.drag_person = self.dbstate.db.get_person_from_handle(handle)
13901442
if handle == self.active_person_handle:
13911443
# Find a parent of the active person so that they can become
13921444
# the active person, if no parents then leave as the current
@@ -1397,7 +1449,6 @@ def select_node(self, item, target, event):
13971449
else:
13981450
# unset busy cursor as we don't change active person
13991451
self.uistate.set_busy_cursor(False)
1400-
return True
14011452

14021453
# redraw the graph based on the selected person
14031454
# schedule after because double click can occur
@@ -1407,6 +1458,11 @@ def select_node(self, item, target, event):
14071458
context = GLib.main_context_default()
14081459
self.click_events.append(context.find_source_by_id(click_event_id))
14091460

1461+
# go to drag mode, applyed on motion event
1462+
self._in_drag = True
1463+
self._last_x = event.x
1464+
self._last_y = event.y
1465+
14101466
elif button == 3 and node_class: # right mouse
14111467
if node_class == 'node':
14121468
self.menu = PopupMenu(self, 'person', handle)
@@ -1422,6 +1478,30 @@ def select_node(self, item, target, event):
14221478

14231479
return True
14241480

1481+
def cb_drag_begin(self, widget, data):
1482+
"""Set up some inital conditions for drag. Set up icon."""
1483+
self._in_drag = True
1484+
widget.drag_source_set_icon_name('gramps-person')
1485+
1486+
def cb_drag_end(self, widget, data):
1487+
"""Set up some inital conditions for drag. Set up icon."""
1488+
self._in_drag = False
1489+
1490+
def cb_drag_data_get(self, widget, context, sel_data, info, time):
1491+
"""
1492+
Returned parameters after drag.
1493+
Specified for 'person-link', for others return text info about person.
1494+
"""
1495+
tgs = [x.name() for x in context.list_targets()]
1496+
if info == DdTargets.PERSON_LINK.app_id:
1497+
data = (DdTargets.PERSON_LINK.drag_type,
1498+
id(self), self.drag_person.handle, 0)
1499+
sel_data.set(sel_data.get_target(), 8, pickle.dumps(data))
1500+
elif ('TEXT' in tgs or 'text/plain' in tgs) and info == 0:
1501+
format_helper = FormattingHelper(self.dbstate)
1502+
sel_data.set_text(
1503+
format_helper.format_person(self.drag_person, 11),-1)
1504+
14251505
def find_a_parent(self, handle):
14261506
"""
14271507
Locate a parent from the first family that the selected person is a

0 commit comments

Comments
 (0)