3
3
import pickle
4
4
from gi .repository import Gtk , Gdk
5
5
6
+ from gramps .gen .db import DbTxn
6
7
from gramps .gen .display .name import displayer
7
- from gramps .gui .ddtargets import DdTargets
8
8
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
9
12
10
13
from gramps .gen .const import GRAMPS_LOCALE as glocale
11
14
try :
12
15
_trans = glocale .get_addon_translator (__file__ )
13
16
except ValueError :
14
17
_trans = glocale .translation
15
18
_ = _trans .gettext
19
+ ngettext = glocale .translation .ngettext
16
20
17
21
18
22
class DragAndDrop ():
19
23
"""
20
24
Add Drag-n-Drop feature to GraphView addon.
21
25
"""
22
26
23
- def __init__ (self , canvas , dbstate ):
27
+ def __init__ (self , canvas , dbstate , uistate , h_adj , v_adj ):
24
28
self .drag_person = None
25
29
self .drag_family = None
26
30
31
+ self .h_adj = h_adj
32
+ self .v_adj = v_adj
27
33
self .dbstate = dbstate
34
+ self .uistate = uistate
28
35
self .canvas = canvas
29
36
self .canvas .connect ("drag_data_get" , self .drag_data_get )
30
37
self .canvas .connect ("drag_begin" , self .begin )
31
38
self .canvas .connect ("drag_end" , self .stop )
32
39
33
40
self .enable_dnd (True )
34
41
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
+
35
60
def enable_dnd (self , state ):
36
61
"""
37
62
Enable or disable drag-n-drop for canvas widget.
@@ -55,6 +80,8 @@ def begin(self, widget, context):
55
80
if DdTargets .FAMILY_LINK .drag_type in tgs :
56
81
Gtk .drag_set_icon_name (context , 'gramps-family' , 0 , 0 )
57
82
83
+ self .item_cache .clear ()
84
+
58
85
def stop (self , * args ):
59
86
"""
60
87
Called when drag is end.
@@ -126,3 +153,92 @@ def drag_data_get(self, widget, context, sel_data, info, time):
126
153
mother = '...'
127
154
sel_data .set_text (
128
155
_ ('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 )
0 commit comments