~ubuntu-branches/ubuntu/trusty/gramps/trusty-proposed

« back to all changes in this revision

Viewing changes to gramps/plugins/lib/libpersonview.py

  • Committer: Package Import Robot
  • Author(s): Ross Gammon
  • Date: 2014-02-03 17:28:04 UTC
  • mfrom: (39.1.7 sid)
  • Revision ID: package-import@ubuntu.com-20140203172804-76y7nwxiw92zhlnj
Tags: 4.0.3+dfsg-1
* New upstream release (Closes: #720858)
* To-do notes improved and made persistent (Closes: #680692)
* Applied patch to setup.py to fix resource path problem
* Applied patch to disable the optional HTML View & prevent a crash
* Remove sourceless javascript files (Closes: #736436)
* Gramps uses Bat Mitzva internally (Closes: #502532)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Gramps - a GTK+/GNOME based genealogy program
 
2
#
 
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
 
8
#
 
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.
 
13
#
 
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.
 
18
#
 
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
 
22
#
 
23
 
 
24
# $Id$
 
25
 
 
26
"""
 
27
Provide the base for a list person view.
 
28
"""
 
29
 
 
30
#-------------------------------------------------------------------------
 
31
#
 
32
# GTK/Gnome modules
 
33
#
 
34
#-------------------------------------------------------------------------
 
35
from gi.repository import Gtk
 
36
 
 
37
#-------------------------------------------------------------------------
 
38
#
 
39
# set up logging
 
40
#
 
41
#-------------------------------------------------------------------------
 
42
import logging
 
43
_LOG = logging.getLogger(".gui.personview")
 
44
 
 
45
#-------------------------------------------------------------------------
 
46
#
 
47
# gramps modules
 
48
#
 
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
 
64
 
 
65
#-------------------------------------------------------------------------
 
66
#
 
67
# Python modules
 
68
#
 
69
#-------------------------------------------------------------------------
 
70
from gramps.gen.const import GRAMPS_LOCALE as glocale
 
71
_ = glocale.translation.sgettext
 
72
 
 
73
#-------------------------------------------------------------------------
 
74
#
 
75
# PersonView
 
76
#
 
77
#-------------------------------------------------------------------------
 
78
class BasePersonView(ListView):
 
79
    """
 
80
    Base view for PersonView listviews ListView, a treeview
 
81
    """
 
82
    COL_NAME = 0
 
83
    COL_ID = 1
 
84
    COL_GEN = 2
 
85
    COL_BDAT = 3
 
86
    COL_BPLAC = 4
 
87
    COL_DDAT = 5
 
88
    COL_DPLAC = 6
 
89
    COL_SPOUSE = 7
 
90
    COL_PRIV = 8
 
91
    COL_TAGS = 9
 
92
    COL_CHAN = 10
 
93
    # column definitions
 
94
    COLUMNS = [
 
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),
 
106
        ]
 
107
    # default setting with visible columns, order of the col, and their size
 
108
    CONFIGSETTINGS = (
 
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,
 
112
                           COL_CHAN]),
 
113
        ('columns.size', [250, 75, 75, 100, 175, 100, 175, 100, 40, 100, 100])
 
114
        )  
 
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
 
121
 
 
122
    def __init__(self, pdata, dbstate, uistate, title, model, nav_group=0):
 
123
        """
 
124
        Create the Person View
 
125
        """
 
126
        signal_map = {
 
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,
 
134
            }
 
135
 
 
136
        ListView.__init__(
 
137
            self, title, pdata, dbstate, uistate,
 
138
            model, signal_map,
 
139
            PersonBookmarks, nav_group,
 
140
            multiple=True,
 
141
            filter_class=PersonSidebarFilter)
 
142
            
 
143
        self.func_list.update({
 
144
            '<PRIMARY>J' : self.jump,
 
145
            '<PRIMARY>BackSpace' : self.key_delete,
 
146
            })
 
147
 
 
148
        uistate.connect('nameformat-changed', self.build_tree)
 
149
 
 
150
        self.additional_uis.append(self.additional_ui())
 
151
 
 
152
    def navigation_type(self):
 
153
        """
 
154
        Return the navigation type of the view.
 
155
        """
 
156
        return 'Person'
 
157
 
 
158
    def drag_info(self):
 
159
        """
 
160
        Specify the drag type for a single selection
 
161
        """
 
162
        return DdTargets.PERSON_LINK
 
163
        
 
164
    def exact_search(self):
 
165
        """
 
166
        Returns a tuple indicating columns requiring an exact search
 
167
        'female' contains the string 'male' so we need an exact search
 
168
        """
 
169
        return (BasePersonView.COL_GEN,)
 
170
 
 
171
    def get_stock(self):
 
172
        """
 
173
        Use the grampsperson stock icon
 
174
        """
 
175
        return 'gramps-person'
 
176
 
 
177
    def additional_ui(self):
 
178
        """
 
179
        Defines the UI string for UIManager
 
180
        """
 
181
        return '''<ui>
 
182
          <menubar name="MenuBar">
 
183
            <menu action="FileMenu">
 
184
              <placeholder name="LocalExport">
 
185
                <menuitem action="ExportTab"/>
 
186
              </placeholder>
 
187
            </menu>
 
188
            <menu action="BookMenu">
 
189
              <placeholder name="AddEditBook">
 
190
                <menuitem action="AddBook"/>
 
191
                <menuitem action="EditBook"/>
 
192
              </placeholder>
 
193
            </menu>
 
194
            <menu action="GoMenu">
 
195
              <placeholder name="CommonGo">
 
196
                <menuitem action="Back"/>
 
197
                <menuitem action="Forward"/>
 
198
                <separator/>
 
199
                <menuitem action="HomePerson"/>
 
200
                <separator/>
 
201
              </placeholder>
 
202
            </menu>
 
203
            <menu action="EditMenu">
 
204
              <placeholder name="CommonEdit">
 
205
                <menuitem action="Add"/>
 
206
                <menuitem action="Edit"/>
 
207
                <menuitem action="Remove"/>
 
208
                <menuitem action="Merge"/>
 
209
              </placeholder>
 
210
              <menuitem action="SetActive"/>
 
211
              <menuitem action="FilterEdit"/>
 
212
            </menu>
 
213
          </menubar>
 
214
          <toolbar name="ToolBar">
 
215
            <placeholder name="CommonNavigation">
 
216
              <toolitem action="Back"/>
 
217
              <toolitem action="Forward"/>  
 
218
              <toolitem action="HomePerson"/>
 
219
            </placeholder>
 
220
            <placeholder name="CommonEdit">
 
221
              <toolitem action="Add"/>
 
222
              <toolitem action="Edit"/>
 
223
              <toolitem action="Remove"/>
 
224
              <toolitem action="Merge"/>
 
225
            </placeholder>
 
226
          </toolbar>
 
227
          <popup name="Popup">
 
228
            <menuitem action="Back"/>
 
229
            <menuitem action="Forward"/>
 
230
            <menuitem action="HomePerson"/>
 
231
            <separator/>
 
232
            <menuitem action="Add"/>
 
233
            <menuitem action="Edit"/>
 
234
            <menuitem action="Remove"/>
 
235
            <menuitem action="Merge"/>
 
236
            <separator/>
 
237
            <menu name="QuickReport" action="QuickReport"/>
 
238
            <menu name="WebConnect" action="WebConnect"/>
 
239
          </popup>
 
240
        </ui>'''
 
241
 
 
242
    def get_handle_from_gramps_id(self, gid):
 
243
        """
 
244
        Return the handle of the person having the given Gramps ID. 
 
245
        """
 
246
        obj = self.dbstate.db.get_person_from_gramps_id(gid)
 
247
        if obj:
 
248
            return obj.get_handle()
 
249
        else:
 
250
            return None
 
251
 
 
252
    def add(self, obj):
 
253
        """
 
254
        Add a new person to the database.
 
255
        """
 
256
        person = Person()
 
257
        #the editor requires a surname
 
258
        person.primary_name.add_surname(Surname())
 
259
        person.primary_name.set_primary_surname(0)
 
260
        
 
261
        try:
 
262
            EditPerson(self.dbstate, self.uistate, [], person)
 
263
        except WindowActiveError:
 
264
            pass
 
265
 
 
266
    def edit(self, obj):
 
267
        """
 
268
        Edit an existing person in the database.
 
269
        """
 
270
        for handle in self.selected_handles():
 
271
            person = self.dbstate.db.get_person_from_handle(handle)
 
272
            try:
 
273
                EditPerson(self.dbstate, self.uistate, [], person)
 
274
            except WindowActiveError:
 
275
                pass
 
276
 
 
277
    def remove(self, obj):
 
278
        """
 
279
        Remove a person from the database.
 
280
        """
 
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) 
 
285
 
 
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, 
 
290
                                          msg, 
 
291
                                          _('_Delete Person'), 
 
292
                                          self.delete_person_response)
 
293
 
 
294
    def delete_person_response(self):
 
295
        """
 
296
        Deletes the person from the database.
 
297
        """
 
298
        # set the busy cursor, so the user knows that we are working
 
299
        self.uistate.set_busy_cursor(True)
 
300
 
 
301
        # create the transaction
 
302
        with DbTxn('', self.dbstate.db) as trans:
 
303
        
 
304
            # create name to save
 
305
            person = self.active_person
 
306
            active_name = _("Delete Person (%s)") % name_displayer.display(person)
 
307
 
 
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)
 
313
 
 
314
        self.uistate.set_busy_cursor(False)
 
315
 
 
316
    def define_actions(self):
 
317
        """
 
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.
 
322
 
 
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.
 
328
        """
 
329
 
 
330
        ListView.define_actions(self)
 
331
 
 
332
        self.all_action = Gtk.ActionGroup(self.title + "/PersonAll")
 
333
        self.edit_action = Gtk.ActionGroup(self.title + "/PersonEdit")
 
334
 
 
335
        self.all_action.add_actions([
 
336
                ('FilterEdit', None, _('Person Filter Editor'), None, None,
 
337
                self.filter_editor),
 
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), 
 
342
                ])
 
343
 
 
344
 
 
345
        self.edit_action.add_actions(
 
346
            [
 
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,
 
354
                 self.export), 
 
355
                ])
 
356
 
 
357
        self._add_action_group(self.edit_action)
 
358
        self._add_action_group(self.all_action)
 
359
 
 
360
    def enable_action_group(self, obj):
 
361
        """
 
362
        Turns on the visibility of the View's action group.
 
363
        """
 
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)
 
368
        
 
369
    def disable_action_group(self):
 
370
        """
 
371
        Turns off the visibility of the View's action group.
 
372
        """
 
373
        ListView.disable_action_group(self)
 
374
 
 
375
        self.all_action.set_visible(False)
 
376
        self.edit_action.set_visible(False)
 
377
 
 
378
    def set_active(self):
 
379
        """
 
380
        Called when the page is displayed.
 
381
        """
 
382
        ListView.set_active(self)
 
383
        self.uistate.viewmanager.tags.tag_enable()
 
384
 
 
385
    def set_inactive(self):
 
386
        """
 
387
        Called when the page is no longer displayed.
 
388
        """
 
389
        ListView.set_inactive(self)
 
390
        self.uistate.viewmanager.tags.tag_disable()
 
391
 
 
392
    def merge(self, obj):
 
393
        """
 
394
        Merge the selected people.
 
395
        """
 
396
        mlist = self.selected_handles()
 
397
 
 
398
        if len(mlist) != 2:
 
399
            ErrorDialog(
 
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."))
 
404
        else:
 
405
            MergePerson(self.dbstate, self.uistate, mlist[0], mlist[1])
 
406
 
 
407
    def tag_updated(self, handle_list):
 
408
        """
 
409
        Update tagged rows when a tag color changes.
 
410
        """
 
411
        all_links = set([])
 
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))
 
418
 
 
419
    def add_tag(self, transaction, person_handle, tag_handle):
 
420
        """
 
421
        Add the given tag to the given person.
 
422
        """
 
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)
 
426
        
 
427
    def get_default_gramplets(self):
 
428
        """
 
429
        Define the default gramplets for the sidebar and bottombar.
 
430
        """
 
431
        return (("Person Filter",),
 
432
                ("Person Details",
 
433
                 "Person Gallery",
 
434
                 "Person Events",
 
435
                 "Person Children",
 
436
                 "Person Citations",
 
437
                 "Person Notes",
 
438
                 "Person Attributes",
 
439
                 "Person Backlinks"))