46
46
from collections import abc , deque
47
47
import gi
48
48
from gi .repository import Gtk , Gdk , GdkPixbuf , GLib , Pango
49
+ import pickle
49
50
50
51
#-------------------------------------------------------------------------
51
52
#
70
71
from gramps .gen .utils .libformatting import FormattingHelper
71
72
from gramps .gen .utils .thumbnails import get_thumbnail_path
72
73
74
+ from gramps .gui .ddtargets import DdTargets
73
75
from gramps .gui .dialog import (OptionDialog , ErrorDialog , QuestionDialog2 ,
74
76
WarningDialog )
75
77
from gramps .gui .display import display_url
@@ -981,11 +983,13 @@ def __init__(self, view, dbstate, uistate):
981
983
self ._last_x = 0
982
984
self ._last_y = 0
983
985
self ._in_move = False
986
+ self ._in_drag = False
984
987
self .view = view
985
988
self .dbstate = dbstate
986
989
self .uistate = uistate
987
990
self .parser = None
988
991
self .active_person_handle = None
992
+ self .drag_person = None
989
993
990
994
self .actions = Actions (dbstate , uistate , self .view .bookmarks )
991
995
self .actions .connect ('rebuild-graph' , self .view .build_tree )
@@ -1152,6 +1156,22 @@ def __init__(self, view, dbstate, uistate):
1152
1156
self .retest_font = True # flag indicates need to resize font
1153
1157
self .bold_size = self .norm_size = 0 # font sizes to send to dot
1154
1158
1159
+ # setup drag and drop
1160
+ drag_widget = self .get_widget ()
1161
+ drag_widget .drag_source_set (Gdk .ModifierType .BUTTON1_MASK , [],
1162
+ Gdk .DragAction .COPY )
1163
+ drag_widget .connect ("drag_data_get" , self .cb_drag_data_get )
1164
+ drag_widget .connect ("drag_begin" , self .cb_drag_begin )
1165
+ drag_widget .connect ("drag_end" , self .cb_drag_end )
1166
+
1167
+ tglist = Gtk .TargetList .new ([])
1168
+ tglist .add (DdTargets .PERSON_LINK .atom_drag_type ,
1169
+ DdTargets .PERSON_LINK .target_flags ,
1170
+ DdTargets .PERSON_LINK .app_id )
1171
+ # allow drag to a text document, info on drag_get will be 0L !
1172
+ tglist .add_text_targets (0 )
1173
+ drag_widget .drag_source_set_target_list (tglist )
1174
+
1155
1175
def add_popover (self , widget , container ):
1156
1176
"""
1157
1177
Add popover for button.
@@ -1470,6 +1490,7 @@ def populate(self, active_person):
1470
1490
if self .uistate .window .get_window ().is_visible ():
1471
1491
process_pending_events ()
1472
1492
1493
+ self ._in_drag = False
1473
1494
self .clear ()
1474
1495
self .active_person_handle = active_person
1475
1496
@@ -1620,6 +1641,7 @@ def button_release(self, item, target, event):
1620
1641
"""
1621
1642
Exit from scroll mode when button release.
1622
1643
"""
1644
+ self ._in_drag = False
1623
1645
button = event .get_button ()[1 ]
1624
1646
if ((button == 1 or button == 2 ) and
1625
1647
event .type == getattr (Gdk .EventType , "BUTTON_RELEASE" )):
@@ -1648,6 +1670,35 @@ def motion_notify_event(self, _item, _target, event):
1648
1670
(event .y_root - self ._last_y ) * scale_coef )
1649
1671
self .vadjustment .set_value (new_y )
1650
1672
return True
1673
+
1674
+ if self ._in_drag and (event .type == Gdk .EventType .MOTION_NOTIFY ):
1675
+ # start drag when cursor moved more then 5
1676
+ # to separate it from simple click
1677
+ if ((abs (self ._last_x - event .x ) > 5 )
1678
+ or (abs (self ._last_x - event .x ) > 5 )):
1679
+ self .uistate .set_busy_cursor (False )
1680
+ # Remove all single click events
1681
+ for click_item in self .click_events :
1682
+ if not click_item .is_destroyed ():
1683
+ GLib .source_remove (click_item .get_id ())
1684
+ self .click_events .clear ()
1685
+
1686
+ # translate to drag_widget coords
1687
+ drag_widget = self .get_widget ()
1688
+ scale_coef = self .canvas .get_scale ()
1689
+ bounds = self .canvas .get_root_item ().get_bounds ()
1690
+ height_canvas = bounds .y2 - bounds .y1
1691
+ x = self ._last_x * scale_coef - self .hadjustment .get_value ()
1692
+ y = ((height_canvas + self ._last_y ) * scale_coef -
1693
+ self .vadjustment .get_value ())
1694
+
1695
+ drag_widget .drag_begin_with_coordinates (
1696
+ drag_widget .drag_source_get_target_list (),
1697
+ Gdk .DragAction .COPY ,
1698
+ Gdk .ModifierType .BUTTON1_MASK ,
1699
+ event ,
1700
+ x , y )
1701
+ return True
1651
1702
return False
1652
1703
1653
1704
def set_zoom (self , value ):
@@ -1708,6 +1759,11 @@ def select_node(self, item, target, event):
1708
1759
context = GLib .main_context_default ()
1709
1760
self .click_events .append (context .find_source_by_id (click_event_id ))
1710
1761
1762
+ # go to drag mode, applyed on motion event
1763
+ self ._in_drag = True
1764
+ self ._last_x = event .x
1765
+ self ._last_y = event .y
1766
+
1711
1767
elif button == 3 and node_class : # right mouse
1712
1768
if node_class == 'node' :
1713
1769
self .menu = PopupMenu (self , 'person' , handle )
@@ -1723,6 +1779,30 @@ def select_node(self, item, target, event):
1723
1779
1724
1780
return True
1725
1781
1782
+ def cb_drag_begin (self , widget , data ):
1783
+ """Set up some inital conditions for drag. Set up icon."""
1784
+ self ._in_drag = True
1785
+ widget .drag_source_set_icon_name ('gramps-person' )
1786
+
1787
+ def cb_drag_end (self , widget , data ):
1788
+ """Set up some inital conditions for drag. Set up icon."""
1789
+ self ._in_drag = False
1790
+
1791
+ def cb_drag_data_get (self , widget , context , sel_data , info , time ):
1792
+ """
1793
+ Returned parameters after drag.
1794
+ Specified for 'person-link', for others return text info about person.
1795
+ """
1796
+ tgs = [x .name () for x in context .list_targets ()]
1797
+ if info == DdTargets .PERSON_LINK .app_id :
1798
+ data = (DdTargets .PERSON_LINK .drag_type ,
1799
+ id (self ), self .drag_person .handle , 0 )
1800
+ sel_data .set (sel_data .get_target (), 8 , pickle .dumps (data ))
1801
+ elif ('TEXT' in tgs or 'text/plain' in tgs ) and info == 0 :
1802
+ format_helper = FormattingHelper (self .dbstate )
1803
+ sel_data .set_text (
1804
+ format_helper .format_person (self .drag_person , 11 ),- 1 )
1805
+
1726
1806
def find_a_parent (self , handle ):
1727
1807
"""
1728
1808
Locate a parent from the first family that the selected person is a
0 commit comments