Skip to content

Commit da460ab

Browse files
committed
move drag-n-drop to separate class
1 parent 887d783 commit da460ab

File tree

2 files changed

+152
-105
lines changed

2 files changed

+152
-105
lines changed

GraphView/drag_n_drop.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env python3
2+
3+
import pickle
4+
from gi.repository import Gtk, Gdk
5+
6+
from gramps.gen.display.name import displayer
7+
from gramps.gui.ddtargets import DdTargets
8+
from gramps.gen.utils.libformatting import FormattingHelper
9+
10+
from gramps.gen.const import GRAMPS_LOCALE as glocale
11+
try:
12+
_trans = glocale.get_addon_translator(__file__)
13+
except ValueError:
14+
_trans = glocale.translation
15+
_ = _trans.gettext
16+
17+
18+
class DragAndDrop():
19+
"""
20+
Add Drag-n-Drop feature to GraphView addon.
21+
"""
22+
23+
def __init__(self, widget, dbstate):
24+
self.ready = False # True - when drag can be started
25+
self.do_drag = False # True - when drag is started
26+
27+
self.drag_person = None
28+
self.drag_family = None
29+
30+
self.dbstate = dbstate
31+
self.widget = widget
32+
self.widget.connect("drag_data_get", self.drag_data_get)
33+
self.widget.connect("drag_begin", self.begin)
34+
self.widget.connect("drag_end", self.stop)
35+
36+
def begin(self, widget, context):
37+
"""
38+
Called when drag is start.
39+
"""
40+
self.do_drag = True
41+
tgs = [x.name() for x in context.list_targets()]
42+
# set icon depending on person or family drag
43+
if DdTargets.PERSON_LINK.drag_type in tgs:
44+
Gtk.drag_set_icon_name(context, 'gramps-person', 0, 0)
45+
if DdTargets.FAMILY_LINK.drag_type in tgs:
46+
Gtk.drag_set_icon_name(context, 'gramps-family', 0, 0)
47+
48+
def stop(self, *args):
49+
"""
50+
Called when drag is end.
51+
"""
52+
self.ready = False
53+
self.do_drag = False
54+
self.drag_person = None
55+
self.drag_family = None
56+
57+
def is_ready(self):
58+
"""
59+
Check if we ready to drag.
60+
"""
61+
return self.ready and (not self.do_drag)
62+
63+
def set_ready(self, node_class, handle):
64+
"""
65+
Set ready to drag state.
66+
"""
67+
self.stop()
68+
if node_class == 'node':
69+
self.drag_person = self.dbstate.db.get_person_from_handle(handle)
70+
elif node_class == 'familynode':
71+
self.drag_family = self.dbstate.db.get_family_from_handle(handle)
72+
73+
if self.drag_person or self.drag_family:
74+
self.ready = True
75+
76+
def drag_data_get(self, widget, context, sel_data, info, time):
77+
"""
78+
Returned parameters after drag.
79+
Specified for 'person-link' and 'family-link',
80+
also to return text info about person or family.
81+
"""
82+
tgs = [x.name() for x in context.list_targets()]
83+
84+
if info == DdTargets.PERSON_LINK.app_id:
85+
data = (DdTargets.PERSON_LINK.drag_type,
86+
id(widget), self.drag_person.handle, 0)
87+
sel_data.set(sel_data.get_target(), 8, pickle.dumps(data))
88+
elif info == DdTargets.FAMILY_LINK.app_id:
89+
data = (DdTargets.FAMILY_LINK.drag_type,
90+
id(widget), self.drag_family.handle, 0)
91+
sel_data.set(sel_data.get_target(), 8, pickle.dumps(data))
92+
elif ('TEXT' in tgs or 'text/plain' in tgs):
93+
if info == 0:
94+
format_helper = FormattingHelper(self.dbstate)
95+
sel_data.set_text(
96+
format_helper.format_person(self.drag_person, 11), -1)
97+
if info == 1:
98+
f_handle = self.drag_family.get_father_handle()
99+
m_handle = self.drag_family.get_mother_handle()
100+
if f_handle:
101+
father = self.dbstate.db.get_person_from_handle(f_handle)
102+
father = displayer.display(father)
103+
else:
104+
father = '...'
105+
if m_handle:
106+
mother = self.dbstate.db.get_person_from_handle(m_handle)
107+
mother = displayer.display(mother)
108+
else:
109+
mother = '...'
110+
sel_data.set_text(
111+
_('Family of %s and %s') % (father, mother), -1)
112+
113+
def start_drag(self, pos_x, pos_y, event):
114+
"""
115+
Activate drag.
116+
"""
117+
# setup targets
118+
tglist = Gtk.TargetList.new([])
119+
if self.drag_person is not None:
120+
tglist.add(DdTargets.PERSON_LINK.atom_drag_type,
121+
DdTargets.PERSON_LINK.target_flags,
122+
DdTargets.PERSON_LINK.app_id,
123+
)
124+
# allow drag to a text document, info on drag_get will be 0
125+
tglist.add_text_targets(0)
126+
if self.drag_family is not None:
127+
tglist.add(DdTargets.FAMILY_LINK.atom_drag_type,
128+
DdTargets.FAMILY_LINK.target_flags,
129+
DdTargets.FAMILY_LINK.app_id,
130+
)
131+
# allow drag to a text document, info on drag_get will be 1
132+
tglist.add_text_targets(1)
133+
134+
# start drag
135+
self.widget.drag_begin_with_coordinates(
136+
tglist,
137+
Gdk.DragAction.COPY,
138+
1, # left mouse button = 1
139+
event,
140+
pos_x, pos_y)
141+
return True

GraphView/graphview.py

Lines changed: 11 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
from collections import abc, deque
4747
import gi
4848
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib, Pango
49-
import pickle
5049

5150
#-------------------------------------------------------------------------
5251
#
@@ -71,7 +70,6 @@
7170
from gramps.gen.utils.libformatting import FormattingHelper
7271
from gramps.gen.utils.thumbnails import get_thumbnail_path
7372

74-
from gramps.gui.ddtargets import DdTargets
7573
from gramps.gui.dialog import (OptionDialog, ErrorDialog, QuestionDialog2,
7674
WarningDialog)
7775
from gramps.gui.display import display_url
@@ -137,6 +135,7 @@
137135
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
138136
from search_widget import SearchWidget, Popover, ListBoxRow, get_person_tooltip
139137
from avatars import Avatars
138+
from drag_n_drop import DragAndDrop
140139

141140

142141
#-------------------------------------------------------------------------
@@ -983,13 +982,11 @@ def __init__(self, view, dbstate, uistate):
983982
self._last_x = 0
984983
self._last_y = 0
985984
self._in_move = False
986-
self._in_drag = False
987985
self.view = view
988986
self.dbstate = dbstate
989987
self.uistate = uistate
990988
self.parser = None
991989
self.active_person_handle = None
992-
self.drag_person = None
993990

994991
self.actions = Actions(dbstate, uistate, self.view.bookmarks)
995992
self.actions.connect('rebuild-graph', self.view.build_tree)
@@ -1157,10 +1154,7 @@ def __init__(self, view, dbstate, uistate):
11571154
self.bold_size = self.norm_size = 0 # font sizes to send to dot
11581155

11591156
# setup drag and drop
1160-
drag_widget = self.get_widget()
1161-
drag_widget.connect("drag_data_get", self.cb_drag_data_get)
1162-
drag_widget.connect("drag_begin", self.cb_drag_begin)
1163-
drag_widget.connect("drag_end", self.cb_drag_end)
1157+
self.dnd = DragAndDrop(self.get_widget(), self.dbstate)
11641158

11651159
def add_popover(self, widget, container):
11661160
"""
@@ -1175,7 +1169,7 @@ def add_popover(self, widget, container):
11751169
def build_spinner(self, icon, start, end, tooltip, conf_const):
11761170
"""
11771171
Build spinner with icon and pack it into box.
1178-
Chenges apply to config with delay.
1172+
Changes apply to config with delay.
11791173
"""
11801174
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
11811175
img = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.MENU)
@@ -1480,8 +1474,8 @@ def populate(self, active_person):
14801474
if self.uistate.window.get_window().is_visible():
14811475
process_pending_events()
14821476

1483-
self._in_drag = False # True - when drag can be started
1484-
self._do_drag = False # True - when drag is started
1477+
self.dnd.stop()
1478+
14851479
self.clear()
14861480
self.active_person_handle = active_person
14871481

@@ -1632,7 +1626,7 @@ def button_release(self, item, target, event):
16321626
"""
16331627
Exit from scroll mode when button release.
16341628
"""
1635-
self._in_drag = False
1629+
self.dnd.stop()
16361630
button = event.get_button()[1]
16371631
if((button == 1 or button == 2) and
16381632
event.type == getattr(Gdk.EventType, "BUTTON_RELEASE")):
@@ -1665,7 +1659,7 @@ def motion_notify_event(self, _item, _target, event):
16651659
if not (event.type == Gdk.EventType.MOTION_NOTIFY):
16661660
return False
16671661

1668-
if self._in_drag and (not self._do_drag):
1662+
if self.dnd.is_ready():
16691663
# start drag when cursor moved more then 5
16701664
# to separate it from simple click
16711665
if ((abs(self._last_x - event.x_root) > 5)
@@ -1682,32 +1676,8 @@ def motion_notify_event(self, _item, _target, event):
16821676
x = self._last_x * scale_coef - self.hadjustment.get_value()
16831677
y = self._last_y * scale_coef - self.vadjustment.get_value()
16841678

1685-
# setup targets
1686-
tglist = Gtk.TargetList.new([])
1687-
if self.drag_person is not None:
1688-
tglist.add(DdTargets.PERSON_LINK.atom_drag_type,
1689-
DdTargets.PERSON_LINK.target_flags,
1690-
DdTargets.PERSON_LINK.app_id,
1691-
)
1692-
# allow drag to a text document, info on drag_get will be 0
1693-
tglist.add_text_targets(0)
1694-
if self.drag_family is not None:
1695-
tglist.add(DdTargets.FAMILY_LINK.atom_drag_type,
1696-
DdTargets.FAMILY_LINK.target_flags,
1697-
DdTargets.FAMILY_LINK.app_id,
1698-
)
1699-
# allow drag to a text document, info on drag_get will be 1
1700-
tglist.add_text_targets(1)
1701-
1702-
# start drag
1703-
drag_widget = self.get_widget()
1704-
drag_widget.drag_begin_with_coordinates(
1705-
tglist,
1706-
Gdk.DragAction.COPY,
1707-
1, # left mouse button = 1
1708-
event,
1709-
x, y)
1710-
return True
1679+
self.dnd.start_drag(x, y, event)
1680+
17111681
return False
17121682

17131683
def set_zoom(self, value):
@@ -1750,16 +1720,8 @@ def select_node(self, item, target, event):
17501720
return False
17511721

17521722
if button == 1 and node_class: # left mouse
1753-
# set drag mode, it will be applyed on motion event
1754-
self.drag_person = None
1755-
self.drag_family = None
1756-
if node_class == 'node':
1757-
self.drag_person = self.dbstate.db.get_person_from_handle(
1758-
handle)
1759-
if node_class == 'familynode':
1760-
self.drag_family = self.dbstate.db.get_family_from_handle(
1761-
handle)
1762-
self._in_drag = True
1723+
# set drag mode, it will be applied on motion event
1724+
self.dnd.set_ready(node_class, handle)
17631725
self._last_x = event.x_root
17641726
self._last_y = event.y_root
17651727

@@ -1797,62 +1759,6 @@ def select_node(self, item, target, event):
17971759

17981760
return True
17991761

1800-
def cb_drag_begin(self, widget, context):
1801-
"""
1802-
Called on start drag.
1803-
"""
1804-
self._do_drag = True
1805-
tgs = [x.name() for x in context.list_targets()]
1806-
# set icon depending on person or family drag
1807-
if DdTargets.PERSON_LINK.drag_type in tgs:
1808-
Gtk.drag_set_icon_name(context, 'gramps-person', 0, 0)
1809-
if DdTargets.FAMILY_LINK.drag_type in tgs:
1810-
Gtk.drag_set_icon_name(context, 'gramps-family', 0, 0)
1811-
1812-
def cb_drag_end(self, widget, context):
1813-
"""
1814-
Called when drag is end.
1815-
"""
1816-
self._in_drag = False
1817-
self._do_drag = False
1818-
1819-
def cb_drag_data_get(self, widget, context, sel_data, info, time):
1820-
"""
1821-
Returned parameters after drag.
1822-
Specified for 'person-link' and 'family-link',
1823-
also to return text info about person or family.
1824-
"""
1825-
tgs = [x.name() for x in context.list_targets()]
1826-
1827-
if info == DdTargets.PERSON_LINK.app_id:
1828-
data = (DdTargets.PERSON_LINK.drag_type,
1829-
id(widget), self.drag_person.handle, 0)
1830-
sel_data.set(sel_data.get_target(), 8, pickle.dumps(data))
1831-
elif info == DdTargets.FAMILY_LINK.app_id:
1832-
data = (DdTargets.FAMILY_LINK.drag_type,
1833-
id(widget), self.drag_family.handle, 0)
1834-
sel_data.set(sel_data.get_target(), 8, pickle.dumps(data))
1835-
elif ('TEXT' in tgs or 'text/plain' in tgs):
1836-
if info == 0:
1837-
format_helper = FormattingHelper(self.dbstate)
1838-
sel_data.set_text(
1839-
format_helper.format_person(self.drag_person, 11),-1)
1840-
if info == 1:
1841-
f_handle = self.drag_family.get_father_handle()
1842-
m_handle = self.drag_family.get_mother_handle()
1843-
if f_handle:
1844-
father = self.dbstate.db.get_person_from_handle(f_handle)
1845-
father = displayer.display(father)
1846-
else:
1847-
father = '...'
1848-
if m_handle:
1849-
mother = self.dbstate.db.get_person_from_handle(m_handle)
1850-
mother = displayer.display(mother)
1851-
else:
1852-
mother = '...'
1853-
sel_data.set_text(
1854-
_('Family of %s and %s') % (father, mother), -1)
1855-
18561762
def find_a_parent(self, handle):
18571763
"""
18581764
Locate a parent from the first family that the selected person is a

0 commit comments

Comments
 (0)