Skip to content

Commit aa3748b

Browse files
committed
add drop support
1 parent a55a20c commit aa3748b

File tree

2 files changed

+120
-3
lines changed

2 files changed

+120
-3
lines changed

GraphView/drag_n_drop.py

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,60 @@
33
import pickle
44
from gi.repository import Gtk, Gdk
55

6+
from gramps.gen.db import DbTxn
67
from gramps.gen.display.name import displayer
7-
from gramps.gui.ddtargets import DdTargets
88
from gramps.gen.utils.libformatting import FormattingHelper
9+
from gramps.gen.lib import ChildRef
10+
from gramps.gui.ddtargets import DdTargets
11+
from gramps.gui.dialog import QuestionDialog2
912

1013
from gramps.gen.const import GRAMPS_LOCALE as glocale
1114
try:
1215
_trans = glocale.get_addon_translator(__file__)
1316
except ValueError:
1417
_trans = glocale.translation
1518
_ = _trans.gettext
19+
ngettext = glocale.translation.ngettext
1620

1721

1822
class DragAndDrop():
1923
"""
2024
Add Drag-n-Drop feature to GraphView addon.
2125
"""
2226

23-
def __init__(self, canvas, dbstate):
27+
def __init__(self, canvas, dbstate, uistate, h_adj, v_adj):
2428
self.drag_person = None
2529
self.drag_family = None
2630

31+
self.h_adj = h_adj
32+
self.v_adj = v_adj
2733
self.dbstate = dbstate
34+
self.uistate = uistate
2835
self.canvas = canvas
2936
self.canvas.connect("drag_data_get", self.drag_data_get)
3037
self.canvas.connect("drag_begin", self.begin)
3138
self.canvas.connect("drag_end", self.stop)
3239

3340
self.enable_dnd(True)
3441

42+
# add drop support
43+
self.canvas.drag_dest_set(
44+
Gtk.DestDefaults.ALL,
45+
[],
46+
Gdk.DragAction.COPY
47+
)
48+
tglist = Gtk.TargetList.new([])
49+
tglist.add(DdTargets.PERSON_LINK.atom_drag_type,
50+
DdTargets.PERSON_LINK.target_flags,
51+
DdTargets.PERSON_LINK.app_id,
52+
)
53+
# TODO: add other targets. For now only person drop supported.
54+
self.canvas.drag_dest_set_target_list(tglist)
55+
self.canvas.connect("drag-motion", self.drag_motion)
56+
self.canvas.connect("drag-data-received", self.drag_data_receive)
57+
58+
self.item_cache = {}
59+
3560
def enable_dnd(self, state):
3661
"""
3762
Enable or disable drag-n-drop for canvas widget.
@@ -55,6 +80,8 @@ def begin(self, widget, context):
5580
if DdTargets.FAMILY_LINK.drag_type in tgs:
5681
Gtk.drag_set_icon_name(context, 'gramps-family', 0, 0)
5782

83+
self.item_cache.clear()
84+
5885
def stop(self, *args):
5986
"""
6087
Called when drag is end.
@@ -126,3 +153,92 @@ def drag_data_get(self, widget, context, sel_data, info, time):
126153
mother = '...'
127154
sel_data.set_text(
128155
_('Family of %s and %s') % (father, mother), -1)
156+
157+
def drag_motion(self, widget, context, x, y, time):
158+
"""
159+
Monitor drag motion. And check if we can receive the data.
160+
Disable drop if we are not at person or family node.
161+
"""
162+
if self.get_item_at_pos(x, y) is None:
163+
# disable drop
164+
Gdk.drag_status(context, 0, time)
165+
166+
def drag_data_receive(self, widget, context, x, y, data, info, time):
167+
"""
168+
Handle drop event.
169+
"""
170+
receiver = self.get_item_at_pos(x, y)
171+
172+
# unpickle data and get dropped person's handles
173+
out_data = []
174+
p_data = pickle.loads(data.get_data())
175+
if isinstance(p_data[0], bytes):
176+
for d in p_data:
177+
tmp = pickle.loads(d)
178+
if tmp[0] == 'person-link':
179+
out_data.append(tmp[2])
180+
elif p_data[0] == 'person-link':
181+
out_data.append(p_data[2])
182+
183+
# if person is dropped to family node then add them to this family
184+
if receiver[0] == 'familynode' and out_data:
185+
title = ngettext("Add child to family?",
186+
"Add children to family?",
187+
len(out_data))
188+
quest = ngettext("Do you want to add child to the family?",
189+
"Do you want to add children to the family?",
190+
len(out_data))
191+
dialog = QuestionDialog2(title, quest, _("Yes"), _("No"),
192+
self.uistate.window)
193+
if dialog.run():
194+
for person_handle in out_data:
195+
self.__add_child_to_family(person_handle, receiver[1])
196+
197+
def get_item_at_pos(self, x, y):
198+
"""
199+
Get GooCanvas item at cursor position.
200+
Return: (node_class, person/family object) or None.
201+
"""
202+
scale_coef = self.canvas.get_scale()
203+
x_pos = (x + self.h_adj.get_value()) / scale_coef
204+
y_pos = (y + self.v_adj.get_value()) / scale_coef
205+
206+
item = self.canvas.get_item_at(x_pos, y_pos, True)
207+
obj = self.item_cache.get(item)
208+
if obj is not None:
209+
return obj
210+
try:
211+
# data stored in GooCanvasGroup which is parent of the item
212+
parent = item.get_parent()
213+
handle = parent.title
214+
node_class = parent.description
215+
216+
if node_class == 'node' and handle:
217+
obj = (node_class,
218+
self.dbstate.db.get_person_from_handle(handle))
219+
elif node_class == 'familynode' and handle:
220+
obj = (node_class,
221+
self.dbstate.db.get_family_from_handle(handle))
222+
else:
223+
return None
224+
self.item_cache[item] = obj
225+
return obj
226+
except:
227+
pass
228+
return None
229+
230+
def __add_child_to_family(self, person_handle, family):
231+
"""
232+
Write data to db.
233+
"""
234+
person = self.dbstate.db.get_person_from_handle(person_handle)
235+
ref = ChildRef()
236+
ref.ref = person_handle
237+
family.add_child_ref(ref)
238+
person.add_parent_family_handle(family.get_handle())
239+
240+
with DbTxn(_("Add Child to Family"), self.dbstate.db) as trans:
241+
# default relationship is used
242+
self.dbstate.db.commit_person(person, trans)
243+
# add child to family
244+
self.dbstate.db.commit_family(family, trans)

GraphView/graphview.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,8 @@ def __init__(self, view, dbstate, uistate):
11541154
self.bold_size = self.norm_size = 0 # font sizes to send to dot
11551155

11561156
# setup drag and drop
1157-
self.dnd = DragAndDrop(self.canvas, self.dbstate)
1157+
self.dnd = DragAndDrop(self.canvas, self.dbstate, self.uistate,
1158+
self.hadjustment, self.vadjustment)
11581159
self.canvas.connect("drag-begin", self.del_click_events)
11591160

11601161
def add_popover(self, widget, container):

0 commit comments

Comments
 (0)