1# Gramps - a GTK+/GNOME based genealogy program
2#
3# Copyright (C) 2011 Nick Hall
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18#
19
20#-------------------------------------------------------------------------
21#
22# Gtk modules
23#
24#-------------------------------------------------------------------------
25from gi.repository import Gtk
26
27#-------------------------------------------------------------------------
28#
29# Gramps modules
30#
31#-------------------------------------------------------------------------
32from gramps.gui.editors import EditEvent
33from gramps.gui.listmodel import ListModel, NOSORT
34from gramps.gen.plug import Gramplet
35from gramps.gen.lib import (EventType, Date)
36from gramps.gen.plug.report.utils import find_spouse
37from gramps.gui.dbguielement import DbGUIElement
38from gramps.gen.display.place import displayer as place_displayer
39from gramps.gen.datehandler import get_date
40from gramps.gen.utils.db import (get_participant_from_event,
41                                 get_birth_or_fallback,
42                                 get_marriage_or_fallback)
43from gramps.gen.errors import WindowActiveError
44from gramps.gen.config import config
45from gramps.gen.const import GRAMPS_LOCALE as glocale
46_ = glocale.translation.gettext
47
48age_precision = config.get('preferences.age-display-precision')
49
50class Events(Gramplet, DbGUIElement):
51
52    def __init__(self, gui, nav_group=0):
53        Gramplet.__init__(self, gui, nav_group)
54        DbGUIElement.__init__(self, self.dbstate.db)
55
56    """
57    Displays the events for a person or family.
58    """
59    def init(self):
60        self.gui.WIDGET = self.build_gui()
61        self.gui.get_container_widget().remove(self.gui.textview)
62        self.gui.get_container_widget().add(self.gui.WIDGET)
63        self.gui.WIDGET.show()
64
65    def _connect_db_signals(self):
66        """
67        called on init of DbGUIElement, connect to db as required.
68        """
69        self.callman.register_callbacks({'event-update': self.changed})
70        self.callman.connect_all(keys=['event'])
71
72    def changed(self, handle):
73        """
74        Called when a registered event is updated.
75        """
76        self.update()
77
78    def build_gui(self):
79        """
80        Build the GUI interface.
81        """
82        tip = _('Double-click on a row to edit the selected event.')
83        self.set_tooltip(tip)
84        top = Gtk.TreeView()
85        titles = [('', NOSORT, 50,),
86                  (_('Type'), 1, 100),
87                  (_('Description'), 2, 150),
88                  (_('Date'), 4, 100),
89                  ('', NOSORT, 50),
90                  (_('Age'), 6, 100),
91                  ('', NOSORT, 50),
92                  (_('Place'), 7, 400),
93                  (_('Main Participants'), 8, 200),
94                  (_('Role'), 9, 100)]
95        self.model = ListModel(top, titles, event_func=self.edit_event)
96        return top
97
98    def add_event_ref(self, event_ref, spouse=None):
99        """
100        Add an event to the model.
101        """
102        self.callman.register_handles({'event': [event_ref.ref]})
103        event = self.dbstate.db.get_event_from_handle(event_ref.ref)
104        event_date = get_date(event)
105        event_sort = '%012d' % event.get_date_object().get_sort_value()
106        person_age = self.column_age(event)
107        person_age_sort = self.column_sort_age(event)
108        place = place_displayer.display_event(self.dbstate.db, event)
109
110        participants = get_participant_from_event(self.dbstate.db,
111                                                  event_ref.ref)
112
113        self.model.add((event.get_handle(),
114                        str(event.get_type()),
115                        event.get_description(),
116                        event_date,
117                        event_sort,
118                        person_age,
119                        person_age_sort,
120                        place,
121                        participants,
122                        str(event_ref.get_role())))
123
124    def column_age(self, event):
125        """
126        Returns a string representation of age in years.  Change
127        precision=2 for "year, month", or precision=3 for "year,
128        month, days"
129        """
130        date = event.get_date_object()
131        start_date = self.cached_start_date
132        if date and start_date:
133            if (date == start_date and date.modifier == Date.MOD_NONE
134                    and not (event.get_type().is_death_fallback() or
135                             event.get_type() == EventType.DEATH)):
136                return ""
137            else:
138                return (date - start_date).format(precision=age_precision)
139        else:
140            return ""
141
142    def column_sort_age(self, event):
143        """
144        Returns a string version of number of days of age.
145        """
146        date = event.get_date_object()
147        start_date = self.cached_start_date
148        if date and start_date:
149            return "%09d" % int(date - start_date)
150        else:
151            return ""
152
153    def edit_event(self, treeview):
154        """
155        Edit the selected event.
156        """
157        model, iter_ = treeview.get_selection().get_selected()
158        if iter_:
159            handle = model.get_value(iter_, 0)
160            try:
161                event = self.dbstate.db.get_event_from_handle(handle)
162                EditEvent(self.dbstate, self.uistate, [], event)
163            except WindowActiveError:
164                pass
165
166class PersonEvents(Events):
167    """
168    Displays the events for a person.
169    """
170    def db_changed(self):
171        self.connect(self.dbstate.db, 'person-update', self.update)
172
173    def active_changed(self, handle):
174        self.update()
175
176    def update_has_data(self):
177        active_handle = self.get_active('Person')
178        active = None
179        if active_handle:
180            active = self.dbstate.db.get_person_from_handle(active_handle)
181        self.set_has_data(self.get_has_data(active))
182
183    def get_has_data(self, active_person):
184        """
185        Return True if the gramplet has data, else return False.
186        """
187        if active_person:
188            if active_person.get_event_ref_list():
189                return True
190            for family_handle in active_person.get_family_handle_list():
191                family = self.dbstate.db.get_family_from_handle(family_handle)
192                if family:
193                    for event_ref in family.get_event_ref_list():
194                        return True
195        return False
196
197    def main(self): # return false finishes
198        active_handle = self.get_active('Person')
199
200        self.model.clear()
201        self.callman.unregister_all()
202        if active_handle:
203            self.display_person(active_handle)
204        else:
205            self.set_has_data(False)
206
207    def display_person(self, active_handle):
208        """
209        Display the events for the active person.
210        """
211        active_person = self.dbstate.db.get_person_from_handle(active_handle)
212        if active_person:
213            self.cached_start_date = self.get_start_date()
214            for event_ref in active_person.get_event_ref_list():
215                self.add_event_ref(event_ref)
216            for family_handle in active_person.get_family_handle_list():
217                family = self.dbstate.db.get_family_from_handle(family_handle)
218                self.display_family(family, active_person)
219        else:
220            self.cached_start_date = None
221        self.set_has_data(self.model.count > 0)
222
223    def display_family(self, family, active_person):
224        """
225        Display the events for the given family.
226        """
227        spouse_handle = find_spouse(active_person, family)
228        if spouse_handle:
229            spouse = self.dbstate.db.get_person_from_handle(spouse_handle)
230        else:
231            spouse = None
232        if family:
233            for event_ref in family.get_event_ref_list():
234                self.add_event_ref(event_ref, spouse)
235
236    def get_start_date(self):
237        """
238        Get the start date for a person, usually a birth date, or
239        something close to birth.
240        """
241        active_handle = self.get_active('Person')
242        active = self.dbstate.db.get_person_from_handle(active_handle)
243        event = get_birth_or_fallback(self.dbstate.db, active)
244        return event.get_date_object() if event else None
245
246class FamilyEvents(Events):
247    """
248    Displays the events for a family.
249    """
250    def db_changed(self):
251        self.connect(self.dbstate.db, 'family-update', self.update)
252        self.connect_signal('Family', self.update)
253
254    def update_has_data(self):
255        active_handle = self.get_active('Family')
256        active = None
257        if active_handle:
258            active = self.dbstate.db.get_family_from_handle(active_handle)
259        self.set_has_data(self.get_has_data(active))
260
261    def get_has_data(self, active_family):
262        """
263        Return True if the gramplet has data, else return False.
264        """
265        if active_family:
266            for event_ref in active_family.get_event_ref_list():
267                return True
268        return False
269
270    def main(self): # return false finishes
271        active_handle = self.get_active('Family')
272
273        self.model.clear()
274        self.callman.unregister_all()
275        if active_handle:
276            self.display_family(active_handle)
277        else:
278            self.set_has_data(False)
279
280    def display_family(self, active_handle):
281        """
282        Display the events for the active family.
283        """
284        active_family = self.dbstate.db.get_family_from_handle(active_handle)
285        self.cached_start_date = self.get_start_date()
286        for event_ref in active_family.get_event_ref_list():
287            self.add_event_ref(event_ref)
288        self.set_has_data(self.model.count > 0)
289
290    def get_start_date(self):
291        """
292        Get the start date for a family, usually a marriage date, or
293        something close to marriage.
294        """
295        active_handle = self.get_active('Family')
296        active = self.dbstate.db.get_family_from_handle(active_handle)
297        event = get_marriage_or_fallback(self.dbstate.db, active)
298        return event.get_date_object() if event else None
299
300