# Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007-2009 Douglas S. Blank # Copyright (C) 2009 Gary Burton # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # $Id$ #------------------------------------------------------------------------ # # Python modules # #------------------------------------------------------------------------ import cgi import locale #------------------------------------------------------------------------ # # GRAMPS modules # #------------------------------------------------------------------------ from gen.plug import Gramplet from gen.ggettext import sgettext as _ from gen.ggettext import ngettext from gen.display.name import displayer as name_displayer import DateHandler import gen from gen.utils import get_birth_or_fallback, get_death_or_fallback #------------------------------------------------------------------------ # # Gramplet class # #------------------------------------------------------------------------ class PedigreeGramplet(Gramplet): def init(self): self.set_text(_("No Family Tree loaded.")) self.set_tooltip(_("Move mouse over links for options")) self.set_use_markup(True) self.max_generations = 100 self.show_dates = 1 self.box_mode = "UTF" def build_options(self): from gen.plug.menu import NumberOption, BooleanOption, EnumeratedListOption self.add_option(NumberOption(_("Max generations"), self.max_generations, 1, 100)) self.add_option(BooleanOption(_("Show dates"), bool(self.show_dates))) elist = EnumeratedListOption(_("Line type"), self.box_mode) elist.add_item("UTF", "UTF") elist.add_item("ASCII", "ASCII") self.add_option(elist) def save_options(self): self.max_generations = int(self.get_option(_("Max generations")).get_value()) self.show_dates = int(self.get_option(_("Show dates")).get_value()) self.box_mode = self.get_option(_("Line type")).get_value() def on_load(self): if len(self.gui.data) == 3: self.max_generations = int(self.gui.data[0]) self.show_dates = int(self.gui.data[1]) self.box_mode = self.gui.data[2] # "ASCII" or "UTF" def save_update_options(self, widget=None): self.max_generations = int(self.get_option(_("Max generations")).get_value()) self.show_dates = int(self.get_option(_("Show dates")).get_value()) self.box_mode = self.get_option(_("Line type")).get_value() self.gui.data = [self.max_generations, self.show_dates, self.box_mode] self.update() def db_changed(self): """ If a person or family changes, the ancestors of active person might have changed. """ self.dbstate.db.connect('person-add', self.update) self.dbstate.db.connect('person-delete', self.update) self.dbstate.db.connect('family-add', self.update) self.dbstate.db.connect('family-delete', self.update) self.dbstate.db.connect('person-rebuild', self.update) self.dbstate.db.connect('family-rebuild', self.update) def active_changed(self, handle): self.update() def get_boxes(self, generation, what): retval = u"" if self.box_mode == "UTF": space = u" " elif self.box_mode == "ASCII": space = u" " space_len = len(space) + 2 for i in range(generation+1): if self._boxes[i]: retval += space + u"|" else: retval += space + u" " if retval[-1] == u' ': if what == 'sf': retval = retval[:-space_len] + u"/" elif what == 'sm': retval = retval[:-space_len] + u"\\" elif retval.endswith(u"|" + space + u"|"): retval = retval[:-space_len] + u"+" if self.box_mode == "UTF": retval += u"-" retval = retval.replace(u"\\", u"\u2514") retval = retval.replace(u"-", u"\u2500") retval = retval.replace(u"|", u"\u2502") retval = retval.replace(u"/", u"\u250c") elif self.box_mode == "ASCII": retval += u"--" return retval def set_box(self, pos, value): self._boxes[pos] = value def process_person(self, handle, generation, what): if generation > self.max_generations: return person = self.dbstate.db.get_person_from_handle(handle) family_list = person.get_parent_family_handle_list() if what == "f": if len(family_list) > 0: family = self.dbstate.db.get_family_from_handle(family_list[0]) father = family.get_father_handle() mother = family.get_mother_handle() if father: self.process_person(father, generation + 1, "f") self.set_box(generation, 1) self.process_person(father, generation + 1, "sf") self.process_person(father, generation + 1, "m") elif mother: self.set_box(generation, 1) elif what[0] == "s": boxes = self.get_boxes(generation, what) if what[-1] == 'f': if self.box_mode == "UTF": boxes = boxes.replace("+", u"\u250c") else: boxes = boxes.replace("+", u"/") else: if self.box_mode == "UTF": boxes = boxes.replace("+", u"\u2514") else: boxes = boxes.replace("+", u"\\") self.append_text(boxes) self.link(name_displayer.display_name(person.get_primary_name()), 'Person', person.handle, tooltip=_("Click to make active\n") + \ _("Right-click to edit")) if self.show_dates: self.append_text(" ") self.render_text(self.info_string(person)) self.append_text("\n") if generation not in self._generations: self._generations[generation] = [] self._generations[generation].append(handle) elif what == "a": if self.box_mode == "UTF": self.append_text(u"o" + (u"\u2500" * 3)) elif self.box_mode == "ASCII": self.append_text(u"o---") self.append_text("%s " % name_displayer.display_name(person.get_primary_name())) if self.show_dates: self.render_text(self.info_string(person)) self.append_text("\n") if generation not in self._generations: self._generations[generation] = [] self._generations[generation].append(handle) elif what == "m": if len(family_list) > 0: family = self.dbstate.db.get_family_from_handle(family_list[0]) mother = family.get_mother_handle() if mother: self.process_person(mother, generation + 1, "f") self.process_person(mother, generation + 1, "sm") self.set_box(generation, 0) self.process_person(mother, generation + 1, "m") self.set_box(generation, 0) # regardless, turn off line if on def info_string(self, person): birth = get_birth_or_fallback(self.dbstate.db, person) if birth and birth.get_type() != gen.lib.EventType.BIRTH: sdate = DateHandler.get_date(birth) if sdate: bdate = "%s" % cgi.escape(sdate) else: bdate = "" elif birth: bdate = cgi.escape(DateHandler.get_date(birth)) else: bdate = "" death = get_death_or_fallback(self.dbstate.db, person) if death and death.get_type() != gen.lib.EventType.DEATH: sdate = DateHandler.get_date(death) if sdate: ddate = "%s" % cgi.escape(sdate) else: ddate = "" elif death: ddate = cgi.escape(DateHandler.get_date(death)) else: ddate = "" if bdate and ddate: value = _("(b. %(birthdate)s, d. %(deathdate)s)") % { 'birthdate' : bdate, 'deathdate' : ddate } elif bdate: value = _("(b. %s)") % (bdate) elif ddate: value = _("(d. %s)") % (ddate) else: value = "" return value def main(self): # return false finishes """ Generator which will be run in the background. """ self._boxes = [0] * (self.max_generations + 1) self._generations = {} self.gui.buffer.set_text("") active_handle = self.get_active('Person') active_person = self.dbstate.db.get_person_from_handle(active_handle) #no wrap in Gramplet self.no_wrap() if active_person is None: return self.process_person(active_person.handle, 1, "f") # father self.process_person(active_person.handle, 0, "a") # active #FIXME: should be 1? self.process_person(active_person.handle, 1, "m") # mother gens = sorted(self._generations) self.append_text(_("\nBreakdown by generation:\n")) all = [active_person.handle] percent_sign = _("percent sign or text string|%") for g in gens: yield True count = len(self._generations[g]) handles = self._generations[g] self.append_text(" ") if g == 0: self.link(_("Generation 1"), 'PersonList', handles, tooltip=_("Double-click to see people in generation")) percent = locale.format( '%.2f', 100) + percent_sign self.append_text(_(" has 1 of 1 individual (%(percent)s complete)\n") % {'percent': percent}) else: all.extend(handles) self.link(_("Generation %d") % g, 'PersonList', handles, tooltip=_("Double-click to see people in generation %d") % g) percent = locale.format('%.2f', float(count)/2**(g-1) * 100) + percent_sign self.append_text(ngettext( " has %(count_person)d of %(max_count_person)d individuals (%(percent)s complete)\n", " has %(count_person)d of %(max_count_person)d individuals (%(percent)s complete)\n", 2**(g-1)) % {'count_person': count, 'max_count_person': 2**(g-1), 'percent': percent}) self.link(_("All generations"), 'PersonList', all, tooltip=_("Double-click to see all generations")) self.append_text(ngettext( " have %d individual\n", " have %d individuals\n", len(all)) % len(all)) # Set to a fixed font if self.box_mode == "UTF": start, end = self.gui.buffer.get_bounds() self.gui.buffer.apply_tag_by_name("fixed", start, end) self.append_text("", scroll_to="begin")