1
# Gramps - a GTK+/GNOME based genealogy program
3
# Copyright (C) 2000-2007 Donald N. Allingham
4
# Copyright (C) 2008 Gary Burton
5
# Copyright (C) 2009-2010 Nick Hall
6
# Copyright (C) 2010 Benny Malengier
7
# Copyright (C) 2011 Tim G L Lyons
9
# This program is free software; you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
11
# the Free Software Foundation; either version 2 of the License, or
12
# (at your option) any later version.
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU General Public License for more details.
19
# You should have received a copy of the GNU General Public License
20
# along with this program; if not, write to the Free Software
21
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 021111307 USA
27
Provide the base for a list person view.
30
#-------------------------------------------------------------------------
34
#-------------------------------------------------------------------------
35
from gi.repository import Gtk
37
#-------------------------------------------------------------------------
41
#-------------------------------------------------------------------------
43
_LOG = logging.getLogger(".gui.personview")
45
#-------------------------------------------------------------------------
49
#-------------------------------------------------------------------------
50
from gramps.gen.lib import Person, Surname
51
from gramps.gen.db import DbTxn
52
from gramps.gui.views.listview import ListView, TEXT, MARKUP, ICON
53
from gramps.gen.utils.string import data_recover_msg
54
from gramps.gen.display.name import displayer as name_displayer
55
from gramps.gui.dialog import ErrorDialog, QuestionDialog
56
from gramps.gen.errors import WindowActiveError
57
from gramps.gui.views.bookmarks import PersonBookmarks
58
from gramps.gen.config import config
59
from gramps.gui.ddtargets import DdTargets
60
from gramps.gui.editors import EditPerson
61
from gramps.gui.filters.sidebar import PersonSidebarFilter
62
from gramps.gui.merge import MergePerson
63
from gramps.gen.plug import CATEGORY_QR_PERSON
65
#-------------------------------------------------------------------------
69
#-------------------------------------------------------------------------
70
from gramps.gen.const import GRAMPS_LOCALE as glocale
71
_ = glocale.translation.sgettext
73
#-------------------------------------------------------------------------
77
#-------------------------------------------------------------------------
78
class BasePersonView(ListView):
80
Base view for PersonView listviews ListView, a treeview
95
(_('Name'), TEXT, None),
96
(_('ID'), TEXT, None),
97
(_('Gender'), TEXT, None),
98
(_('Birth Date'), MARKUP, None),
99
(_('Birth Place'), MARKUP, None),
100
(_('Death Date'), MARKUP, None),
101
(_('Death Place'), MARKUP, None),
102
(_('Spouse'), TEXT, None),
103
(_('Private'), ICON, 'gramps-lock'),
104
(_('Tags'), TEXT, None),
105
(_('Last Changed'), TEXT, None),
107
# default setting with visible columns, order of the col, and their size
109
('columns.visible', [COL_NAME, COL_ID, COL_GEN, COL_BDAT, COL_DDAT]),
110
('columns.rank', [COL_NAME, COL_ID, COL_GEN, COL_BDAT, COL_BPLAC,
111
COL_DDAT, COL_DPLAC, COL_SPOUSE, COL_PRIV, COL_TAGS,
113
('columns.size', [250, 75, 75, 100, 175, 100, 175, 100, 40, 100, 100])
115
ADD_MSG = _("Add a new person")
116
EDIT_MSG = _("Edit the selected person")
117
DEL_MSG = _("Remove the selected person")
118
MERGE_MSG = _("Merge the selected persons")
119
FILTER_TYPE = "Person"
120
QR_CATEGORY = CATEGORY_QR_PERSON
122
def __init__(self, pdata, dbstate, uistate, title, model, nav_group=0):
124
Create the Person View
127
'person-add' : self.row_add,
128
'person-update' : self.row_update,
129
'person-delete' : self.row_delete,
130
'person-rebuild' : self.object_build,
131
'person-groupname-rebuild' : self.object_build,
132
'tag-update' : self.tag_updated,
133
'no-database': self.no_database,
137
self, title, pdata, dbstate, uistate,
139
PersonBookmarks, nav_group,
141
filter_class=PersonSidebarFilter)
143
self.func_list.update({
144
'<PRIMARY>J' : self.jump,
145
'<PRIMARY>BackSpace' : self.key_delete,
148
uistate.connect('nameformat-changed', self.build_tree)
150
self.additional_uis.append(self.additional_ui())
152
def navigation_type(self):
154
Return the navigation type of the view.
160
Specify the drag type for a single selection
162
return DdTargets.PERSON_LINK
164
def exact_search(self):
166
Returns a tuple indicating columns requiring an exact search
167
'female' contains the string 'male' so we need an exact search
169
return (BasePersonView.COL_GEN,)
173
Use the grampsperson stock icon
175
return 'gramps-person'
177
def additional_ui(self):
179
Defines the UI string for UIManager
182
<menubar name="MenuBar">
183
<menu action="FileMenu">
184
<placeholder name="LocalExport">
185
<menuitem action="ExportTab"/>
188
<menu action="BookMenu">
189
<placeholder name="AddEditBook">
190
<menuitem action="AddBook"/>
191
<menuitem action="EditBook"/>
194
<menu action="GoMenu">
195
<placeholder name="CommonGo">
196
<menuitem action="Back"/>
197
<menuitem action="Forward"/>
199
<menuitem action="HomePerson"/>
203
<menu action="EditMenu">
204
<placeholder name="CommonEdit">
205
<menuitem action="Add"/>
206
<menuitem action="Edit"/>
207
<menuitem action="Remove"/>
208
<menuitem action="Merge"/>
210
<menuitem action="SetActive"/>
211
<menuitem action="FilterEdit"/>
214
<toolbar name="ToolBar">
215
<placeholder name="CommonNavigation">
216
<toolitem action="Back"/>
217
<toolitem action="Forward"/>
218
<toolitem action="HomePerson"/>
220
<placeholder name="CommonEdit">
221
<toolitem action="Add"/>
222
<toolitem action="Edit"/>
223
<toolitem action="Remove"/>
224
<toolitem action="Merge"/>
228
<menuitem action="Back"/>
229
<menuitem action="Forward"/>
230
<menuitem action="HomePerson"/>
232
<menuitem action="Add"/>
233
<menuitem action="Edit"/>
234
<menuitem action="Remove"/>
235
<menuitem action="Merge"/>
237
<menu name="QuickReport" action="QuickReport"/>
238
<menu name="WebConnect" action="WebConnect"/>
242
def get_handle_from_gramps_id(self, gid):
244
Return the handle of the person having the given Gramps ID.
246
obj = self.dbstate.db.get_person_from_gramps_id(gid)
248
return obj.get_handle()
254
Add a new person to the database.
257
#the editor requires a surname
258
person.primary_name.add_surname(Surname())
259
person.primary_name.set_primary_surname(0)
262
EditPerson(self.dbstate, self.uistate, [], person)
263
except WindowActiveError:
268
Edit an existing person in the database.
270
for handle in self.selected_handles():
271
person = self.dbstate.db.get_person_from_handle(handle)
273
EditPerson(self.dbstate, self.uistate, [], person)
274
except WindowActiveError:
277
def remove(self, obj):
279
Remove a person from the database.
281
for sel in self.selected_handles():
282
person = self.dbstate.db.get_person_from_handle(sel)
283
self.active_person = person
284
name = name_displayer.display(person)
286
msg = _('Deleting the person will remove the person '
287
'from the database.')
288
msg = "%s %s" % (msg, data_recover_msg)
289
QuestionDialog(_('Delete %s?') % name,
292
self.delete_person_response)
294
def delete_person_response(self):
296
Deletes the person from the database.
298
# set the busy cursor, so the user knows that we are working
299
self.uistate.set_busy_cursor(True)
301
# create the transaction
302
with DbTxn('', self.dbstate.db) as trans:
304
# create name to save
305
person = self.active_person
306
active_name = _("Delete Person (%s)") % name_displayer.display(person)
308
# delete the person from the database
309
# Above will emit person-delete, which removes the person via
310
# callback to the model, so row delete is signaled
311
self.dbstate.db.delete_person_from_database(person, trans)
312
trans.set_description(active_name)
314
self.uistate.set_busy_cursor(False)
316
def define_actions(self):
318
Required define_actions function for PageView. Builds the action
319
group information required. We extend beyond the normal here,
320
since we want to have more than one action group for the PersonView.
321
Most PageViews really won't care about this.
323
Special action groups for Forward and Back are created to allow the
324
handling of navigation buttons. Forward and Back allow the user to
325
advance or retreat throughout the history, and we want to have these
326
be able to toggle these when you are at the end of the history or
327
at the beginning of the history.
330
ListView.define_actions(self)
332
self.all_action = Gtk.ActionGroup(self.title + "/PersonAll")
333
self.edit_action = Gtk.ActionGroup(self.title + "/PersonEdit")
335
self.all_action.add_actions([
336
('FilterEdit', None, _('Person Filter Editor'), None, None,
338
('Edit', Gtk.STOCK_EDIT, _("action|_Edit..."),
339
"<PRIMARY>Return", self.EDIT_MSG, self.edit),
340
('QuickReport', None, _("Quick View"), None, None, None),
341
('WebConnect', None, _("Web Connection"), None, None, None),
345
self.edit_action.add_actions(
347
('Add', Gtk.STOCK_ADD, _("_Add..."), "<PRIMARY>Insert",
348
self.ADD_MSG, self.add),
349
('Remove', Gtk.STOCK_REMOVE, _("_Remove"), "<PRIMARY>Delete",
350
self.DEL_MSG, self.remove),
351
('Merge', 'gramps-merge', _('_Merge...'), None,
352
self.MERGE_MSG, self.merge),
353
('ExportTab', None, _('Export View...'), None, None,
357
self._add_action_group(self.edit_action)
358
self._add_action_group(self.all_action)
360
def enable_action_group(self, obj):
362
Turns on the visibility of the View's action group.
364
ListView.enable_action_group(self, obj)
365
self.all_action.set_visible(True)
366
self.edit_action.set_visible(True)
367
self.edit_action.set_sensitive(not self.dbstate.db.readonly)
369
def disable_action_group(self):
371
Turns off the visibility of the View's action group.
373
ListView.disable_action_group(self)
375
self.all_action.set_visible(False)
376
self.edit_action.set_visible(False)
378
def set_active(self):
380
Called when the page is displayed.
382
ListView.set_active(self)
383
self.uistate.viewmanager.tags.tag_enable()
385
def set_inactive(self):
387
Called when the page is no longer displayed.
389
ListView.set_inactive(self)
390
self.uistate.viewmanager.tags.tag_disable()
392
def merge(self, obj):
394
Merge the selected people.
396
mlist = self.selected_handles()
400
_("Cannot merge people"),
401
_("Exactly two people must be selected to perform a merge. "
402
"A second person can be selected by holding down the "
403
"control key while clicking on the desired person."))
405
MergePerson(self.dbstate, self.uistate, mlist[0], mlist[1])
407
def tag_updated(self, handle_list):
409
Update tagged rows when a tag color changes.
412
for tag_handle in handle_list:
413
links = set([link[1] for link in
414
self.dbstate.db.find_backlink_handles(tag_handle,
415
include_classes='Person')])
416
all_links = all_links.union(links)
417
self.row_update(list(all_links))
419
def add_tag(self, transaction, person_handle, tag_handle):
421
Add the given tag to the given person.
423
person = self.dbstate.db.get_person_from_handle(person_handle)
424
person.add_tag(tag_handle)
425
self.dbstate.db.commit_person(person, transaction)
427
def get_default_gramplets(self):
429
Define the default gramplets for the sidebar and bottombar.
431
return (("Person Filter",),