Skip to content

Commit 30d0ab3

Browse files
committed
add drag and drop function
1 parent 3a7b74d commit 30d0ab3

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
@@ -44,6 +44,7 @@
4444
from html import escape
4545
import gi
4646
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, Pango
47+
import pickle
4748

4849
#-------------------------------------------------------------------------
4950
#
@@ -67,6 +68,7 @@
6768
from gramps.gen.utils.libformatting import FormattingHelper
6869
from gramps.gen.utils.thumbnails import get_thumbnail_path
6970

71+
from gramps.gui.ddtargets import DdTargets
7072
from gramps.gui.dialog import OptionDialog, ErrorDialog, QuestionDialog2
7173
from gramps.gui.display import display_url
7274
from gramps.gui.editors import EditPerson, EditFamily, EditTagList
@@ -799,11 +801,13 @@ def __init__(self, view, dbstate, uistate):
799801
self._last_x = 0
800802
self._last_y = 0
801803
self._in_move = False
804+
self._in_drag = False
802805
self.view = view
803806
self.dbstate = dbstate
804807
self.uistate = uistate
805808
self.parser = None
806809
self.active_person_handle = None
810+
self.drag_person = None
807811

808812
self.actions = Actions(dbstate, uistate, self.view.bookmarks)
809813
self.actions.connect('rebuild-graph', self.view.build_tree)
@@ -960,6 +964,22 @@ def __init__(self, view, dbstate, uistate):
960964
self.retest_font = True # flag indicates need to resize font
961965
self.bold_size = self.norm_size = 0 # font sizes to send to dot
962966

967+
# setup drag and drop
968+
drag_widget = self.get_widget()
969+
drag_widget.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [],
970+
Gdk.DragAction.COPY)
971+
drag_widget.connect("drag_data_get", self.cb_drag_data_get)
972+
drag_widget.connect("drag_begin", self.cb_drag_begin)
973+
drag_widget.connect("drag_end", self.cb_drag_end)
974+
975+
tglist = Gtk.TargetList.new([])
976+
tglist.add(DdTargets.PERSON_LINK.atom_drag_type,
977+
DdTargets.PERSON_LINK.target_flags,
978+
DdTargets.PERSON_LINK.app_id)
979+
# allow drag to a text document, info on drag_get will be 0L !
980+
tglist.add_text_targets(0)
981+
drag_widget.drag_source_set_target_list(tglist)
982+
963983
def add_popover(self, widget, container):
964984
"""
965985
Add popover for button.
@@ -1268,6 +1288,7 @@ def populate(self, active_person):
12681288
# set the busy cursor, so the user knows that we are working
12691289
self.uistate.set_busy_cursor(True)
12701290

1291+
self._in_drag = False
12711292
self.clear()
12721293
self.active_person_handle = active_person
12731294

@@ -1407,6 +1428,7 @@ def button_release(self, item, target, event):
14071428
"""
14081429
Exit from scroll mode when button release.
14091430
"""
1431+
self._in_drag = False
14101432
button = event.get_button()[1]
14111433
if((button == 1 or button == 2) and
14121434
event.type == getattr(Gdk.EventType, "BUTTON_RELEASE")):
@@ -1435,6 +1457,35 @@ def motion_notify_event(self, _item, _target, event):
14351457
(event.y_root - self._last_y) * scale_coef)
14361458
self.vadjustment.set_value(new_y)
14371459
return True
1460+
1461+
if self._in_drag and (event.type == Gdk.EventType.MOTION_NOTIFY):
1462+
# start drag when cursor moved more then 5
1463+
# to separate it from simple click
1464+
if ((abs(self._last_x - event.x) > 5)
1465+
or (abs(self._last_x - event.x) > 5)):
1466+
self.uistate.set_busy_cursor(False)
1467+
# Remove all single click events
1468+
for click_item in self.click_events:
1469+
if not click_item.is_destroyed():
1470+
GLib.source_remove(click_item.get_id())
1471+
self.click_events.clear()
1472+
1473+
# translate to drag_widget coords
1474+
drag_widget = self.get_widget()
1475+
scale_coef = self.canvas.get_scale()
1476+
bounds = self.canvas.get_root_item().get_bounds()
1477+
height_canvas = bounds.y2 - bounds.y1
1478+
x = self._last_x * scale_coef - self.hadjustment.get_value()
1479+
y = ((height_canvas + self._last_y) * scale_coef -
1480+
self.vadjustment.get_value())
1481+
1482+
drag_widget.drag_begin_with_coordinates(
1483+
drag_widget.drag_source_get_target_list(),
1484+
Gdk.DragAction.COPY,
1485+
Gdk.ModifierType.BUTTON1_MASK,
1486+
event,
1487+
x, y)
1488+
return True
14381489
return False
14391490

14401491
def set_zoom(self, value):
@@ -1478,6 +1529,7 @@ def select_node(self, item, target, event):
14781529

14791530
if button == 1 and node_class == 'node': # left mouse
14801531
self.uistate.set_busy_cursor(True)
1532+
self.drag_person = self.dbstate.db.get_person_from_handle(handle)
14811533
if handle == self.active_person_handle:
14821534
# Find a parent of the active person so that they can become
14831535
# the active person, if no parents then leave as the current
@@ -1488,7 +1540,6 @@ def select_node(self, item, target, event):
14881540
else:
14891541
# unset busy cursor as we don't change active person
14901542
self.uistate.set_busy_cursor(False)
1491-
return True
14921543

14931544
# redraw the graph based on the selected person
14941545
# schedule after because double click can occur
@@ -1498,6 +1549,11 @@ def select_node(self, item, target, event):
14981549
context = GLib.main_context_default()
14991550
self.click_events.append(context.find_source_by_id(click_event_id))
15001551

1552+
# go to drag mode, applyed on motion event
1553+
self._in_drag = True
1554+
self._last_x = event.x
1555+
self._last_y = event.y
1556+
15011557
elif button == 3 and node_class: # right mouse
15021558
if node_class == 'node':
15031559
self.menu = PopupMenu(self, 'person', handle)
@@ -1513,6 +1569,30 @@ def select_node(self, item, target, event):
15131569

15141570
return True
15151571

1572+
def cb_drag_begin(self, widget, data):
1573+
"""Set up some inital conditions for drag. Set up icon."""
1574+
self._in_drag = True
1575+
widget.drag_source_set_icon_name('gramps-person')
1576+
1577+
def cb_drag_end(self, widget, data):
1578+
"""Set up some inital conditions for drag. Set up icon."""
1579+
self._in_drag = False
1580+
1581+
def cb_drag_data_get(self, widget, context, sel_data, info, time):
1582+
"""
1583+
Returned parameters after drag.
1584+
Specified for 'person-link', for others return text info about person.
1585+
"""
1586+
tgs = [x.name() for x in context.list_targets()]
1587+
if info == DdTargets.PERSON_LINK.app_id:
1588+
data = (DdTargets.PERSON_LINK.drag_type,
1589+
id(self), self.drag_person.handle, 0)
1590+
sel_data.set(sel_data.get_target(), 8, pickle.dumps(data))
1591+
elif ('TEXT' in tgs or 'text/plain' in tgs) and info == 0:
1592+
format_helper = FormattingHelper(self.dbstate)
1593+
sel_data.set_text(
1594+
format_helper.format_person(self.drag_person, 11),-1)
1595+
15161596
def find_a_parent(self, handle):
15171597
"""
15181598
Locate a parent from the first family that the selected person is a

0 commit comments

Comments
 (0)