Index based loops across the Gramps project are mapped to their Pythonic equalivant. Many of these improves radability, and a some of them improves performance by being lazy evaluated.
1851 lines
68 KiB
Python
1851 lines
68 KiB
Python
#
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
#
|
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
|
# Copyright (C) 2007-2012 Brian G. Matherly
|
|
# Copyright (C) 2010 Jakim Friant
|
|
# Copyright (C) 2009-2010 Craig J. Anderson
|
|
# Copyright (C) 2014 Paul Franklin
|
|
#
|
|
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
|
|
"""
|
|
Reports/Graphical Reports/Familial Tree
|
|
Reports/Graphical Reports/Personal Tree
|
|
"""
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Gramps modules
|
|
#
|
|
#------------------------------------------------------------------------
|
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
|
_ = glocale.translation.sgettext
|
|
from gramps.gen.errors import ReportError
|
|
from gramps.gen.plug.menu import (TextOption, NumberOption, BooleanOption,
|
|
EnumeratedListOption, StringOption,
|
|
PersonOption, FamilyOption)
|
|
from gramps.gen.plug.report import Report, MenuReportOptions, stdoptions
|
|
from gramps.gen.plug.report import utils
|
|
from gramps.gen.plug.docgen import (FontStyle, ParagraphStyle, GraphicsStyle,
|
|
FONT_SANS_SERIF, PARA_ALIGN_CENTER)
|
|
from gramps.plugins.lib.libtreebase import *
|
|
from gramps.gen.proxy import CacheProxyDb
|
|
from gramps.gen.display.name import displayer as _nd
|
|
from gramps.gen.utils.db import family_name
|
|
|
|
PT2CM = utils.pt2cm
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Constants
|
|
#
|
|
#------------------------------------------------------------------------
|
|
_BORN = _("b.", "birth abbreviation"),
|
|
_DIED = _("d.", "death abbreviation"),
|
|
_MARR = _("m.", "marriage abbreviation"),
|
|
|
|
_RPT_NAME = 'descend_chart'
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Box classes
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class DescendantBoxBase(BoxBase):
|
|
"""
|
|
Base for all descendant boxes.
|
|
Set the boxstr and some new attributes that are needed
|
|
"""
|
|
|
|
def __init__(self, boxstr):
|
|
BoxBase.__init__(self)
|
|
self.boxstr = boxstr
|
|
self.linked_box = None
|
|
self.father = None
|
|
|
|
def calc_text(self, database, person, family):
|
|
""" A single place to calculate box text """
|
|
|
|
gui = GuiConnect()
|
|
calc = gui.calc_lines(database)
|
|
self.text = calc.calc_lines(person, family,
|
|
gui.working_lines(self))
|
|
|
|
class PersonBox(DescendantBoxBase):
|
|
"""
|
|
Calculates information about the box that will print on a page
|
|
"""
|
|
|
|
def __init__(self, level, boldable=0):
|
|
DescendantBoxBase.__init__(self, "CG2-box")
|
|
self.level = level
|
|
|
|
def set_bold(self):
|
|
""" update me to a bolded box """
|
|
self.boxstr = "CG2b-box"
|
|
|
|
class FamilyBox(DescendantBoxBase):
|
|
"""
|
|
Calculates information about the box that will print on a page
|
|
"""
|
|
|
|
def __init__(self, level):
|
|
DescendantBoxBase.__init__(self, "CG2-fam-box")
|
|
self.level = level
|
|
|
|
class PlaceHolderBox(BoxBase):
|
|
"""
|
|
I am a box that does not print. I am used to make sure information
|
|
does not run over areas that we don't want information (boxes)
|
|
"""
|
|
|
|
def __init__(self, level):
|
|
BoxBase.__init__(self)
|
|
self.boxstr = "None"
|
|
self.level = level
|
|
self.line_to = None
|
|
self.linked_box = None
|
|
|
|
def calc_text(self, database, person, family):
|
|
""" move along. Nothing to see here """
|
|
return
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Titles Class(es)
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class DescendantTitleBase(TitleBox):
|
|
def __init__(self, dbase, doc, locale, name_displayer,
|
|
boxstr="CG2-Title-box"):
|
|
self._nd = name_displayer
|
|
TitleBox.__init__(self, doc, boxstr)
|
|
self.database = dbase
|
|
self._ = locale.translation.sgettext
|
|
|
|
def descendant_print(self, person_list, person_list2=[]):
|
|
""" calculate the Descendant title
|
|
Person_list will always be passed
|
|
If in the Family reports and there are two families, person_list2
|
|
will be used.
|
|
"""
|
|
|
|
if len(person_list) == len(person_list2) == 1:
|
|
person_list = person_list + person_list2
|
|
person_list2 = []
|
|
|
|
names = self._get_names(person_list, self._nd)
|
|
|
|
if person_list2:
|
|
names2 = self._get_names(person_list2, self._nd)
|
|
if len(names) + len(names2) == 3:
|
|
if len(names) == 1:
|
|
title = self._("Descendant Chart for %(person)s and "
|
|
"%(father1)s, %(mother1)s") % {
|
|
'person': names[0],
|
|
'father1': names2[0],
|
|
'mother1': names2[1],
|
|
}
|
|
else: # Should be 2 items in names list
|
|
title = self._("Descendant Chart for %(person)s, "
|
|
"%(father1)s and %(mother1)s") % {
|
|
'father1': names[0],
|
|
'mother1': names[1],
|
|
'person': names2[0],
|
|
}
|
|
else: # Should be 2 items in both names and names2 lists
|
|
title = self._("Descendant Chart for %(father1)s, %(father2)s "
|
|
"and %(mother1)s, %(mother2)s") % {
|
|
'father1': names[0],
|
|
'mother1': names[1],
|
|
'father2': names2[0],
|
|
'mother2': names2[1],
|
|
}
|
|
else: # No person_list2: Just one family
|
|
if len(names) == 1:
|
|
title = self._(
|
|
"Descendant Chart for %(person)s") % {'person': names[0]}
|
|
else: # Should be two items in names list
|
|
title = self._("Descendant Chart for %(father)s and "
|
|
"%(mother)s") % {
|
|
'father': names[0],
|
|
'mother': names[1],
|
|
}
|
|
return title
|
|
|
|
def get_parents(self, family_id):
|
|
""" For a family_id, return the father and mother """
|
|
|
|
family1 = self.database.get_family_from_gramps_id(family_id)
|
|
father_h = family1.get_father_handle()
|
|
mother_h = family1.get_mother_handle()
|
|
|
|
parents = [self.database.get_person_from_handle(handle)
|
|
for handle in [father_h, mother_h] if handle]
|
|
|
|
return parents
|
|
|
|
class TitleNone(TitleNoDisplay):
|
|
"""No Title class for the report """
|
|
|
|
def __init__(self, dbase, doc, locale):
|
|
TitleNoDisplay.__init__(self, doc, "CG2-Title-box")
|
|
self._ = locale.translation.sgettext
|
|
|
|
def calc_title(self, persons):
|
|
"""Calculate the title of the report"""
|
|
#we want no text, but need a text for the TOC in a book!
|
|
self.mark_text = self._('Descendant Graph')
|
|
self.text = ''
|
|
|
|
class TitleDPY(DescendantTitleBase):
|
|
"""Descendant (Person yes start with parents) Chart
|
|
Title class for the report """
|
|
|
|
def __init__(self, dbase, doc, locale, name_displayer):
|
|
DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer)
|
|
|
|
def calc_title(self, person_id):
|
|
"""Calculate the title of the report"""
|
|
|
|
center = self.database.get_person_from_gramps_id(person_id)
|
|
family2_h = center.get_main_parents_family_handle()
|
|
if family2_h:
|
|
family2 = self.database.get_family_from_handle(family2_h)
|
|
else:
|
|
family2 = None
|
|
|
|
person_list = None
|
|
if family2:
|
|
father2_h = family2.get_father_handle()
|
|
mother2_h = family2.get_mother_handle()
|
|
person_list = [self.database.get_person_from_handle(handle)
|
|
for handle in [father2_h, mother2_h] if handle]
|
|
|
|
if not person_list:
|
|
person_list = [center]
|
|
|
|
self.text = self.descendant_print(person_list)
|
|
self.set_box_height_width()
|
|
|
|
class TitleDPN(DescendantTitleBase):
|
|
"""Descendant (Person no start with parents) Chart
|
|
Title class for the report """
|
|
|
|
def __init__(self, dbase, doc, locale, name_displayer):
|
|
DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer)
|
|
|
|
def calc_title(self, person_id):
|
|
"""Calculate the title of the report"""
|
|
|
|
center = self.database.get_person_from_gramps_id(person_id)
|
|
|
|
title = self.descendant_print([center])
|
|
self.text = title
|
|
self.set_box_height_width()
|
|
|
|
class TitleDFY(DescendantTitleBase):
|
|
"""Descendant (Family yes start with parents) Chart
|
|
Title class for the report """
|
|
|
|
def __init__(self, dbase, doc, locale, name_displayer):
|
|
DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer)
|
|
|
|
def get_parent_list(self, person):
|
|
""" return a list of my parents. If none, return me """
|
|
if not person:
|
|
return None
|
|
|
|
parent_list = None
|
|
family_h = person.get_main_parents_family_handle()
|
|
if family_h:
|
|
family = self.database.get_family_from_handle(family_h)
|
|
else:
|
|
family = None
|
|
if family: # family = fathers parents
|
|
father_h = family.get_father_handle()
|
|
mother_h = family.get_mother_handle()
|
|
parent_list = [self.database.get_person_from_handle(handle)
|
|
for handle in [father_h, mother_h] if handle]
|
|
|
|
return parent_list or [person]
|
|
|
|
def calc_title(self, family_id):
|
|
"""Calculate the title of the report"""
|
|
|
|
my_parents = self.get_parents(family_id)
|
|
|
|
dad_parents = self.get_parent_list(my_parents[0])
|
|
|
|
mom_parents = []
|
|
if len(my_parents) > 1:
|
|
if not dad_parents:
|
|
dad_parents = self.get_parent_list(my_parents[1])
|
|
else:
|
|
mom_parents = self.get_parent_list(my_parents[1])
|
|
|
|
self.text = self.descendant_print(dad_parents, mom_parents)
|
|
self.set_box_height_width()
|
|
|
|
class TitleDFN(DescendantTitleBase):
|
|
"""Descendant (Family no start with parents) Chart
|
|
Title class for the report """
|
|
|
|
def __init__(self, dbase, doc, locale, name_displayer):
|
|
DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer)
|
|
|
|
def calc_title(self, family_id):
|
|
"""Calculate the title of the report"""
|
|
|
|
self.text = self.descendant_print(self.get_parents(family_id))
|
|
self.set_box_height_width()
|
|
|
|
class TitleF(DescendantTitleBase):
|
|
"""Family Chart Title class for the report """
|
|
|
|
def __init__(self, dbase, doc, locale, name_displayer):
|
|
DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer)
|
|
|
|
def calc_title(self, family_id):
|
|
"""Calculate the title of the report"""
|
|
parents = self.get_parents(family_id)
|
|
|
|
names = self._get_names(parents, self._nd)
|
|
|
|
if len(parents) == 1:
|
|
title = self._(
|
|
"Family Chart for %(person)s") % {'person': names[0]}
|
|
elif len(parents) == 2:
|
|
title = self._(
|
|
"Family Chart for %(father1)s and %(mother1)s") % {
|
|
'father1': names[0], 'mother1': names[1]}
|
|
#else:
|
|
# title = str(tmp) + " " + str(len(tmp))
|
|
self.text = title
|
|
self.set_box_height_width()
|
|
|
|
class TitleC(DescendantTitleBase):
|
|
"""Cousin Chart Title class for the report """
|
|
|
|
def __init__(self, dbase, doc, locale, name_displayer):
|
|
DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer)
|
|
|
|
def calc_title(self, family_id):
|
|
"""Calculate the title of the report"""
|
|
|
|
family = self.database.get_family_from_gramps_id(family_id)
|
|
|
|
kids = [self.database.get_person_from_handle(kid.ref)
|
|
for kid in family.get_child_ref_list()]
|
|
|
|
#ok we have the children. Make a title off of them
|
|
# Translators: needed for Arabic, ignore otherwise
|
|
cousin_names = self._(', ').join(self._get_names(kids, self._nd))
|
|
|
|
self.text = self._(
|
|
"Cousin Chart for %(names)s") % {'names' : cousin_names}
|
|
|
|
self.set_box_height_width()
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class RecurseDown
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class RecurseDown:
|
|
"""
|
|
The main recursive functions that will use add_person to make
|
|
the tree of people (Descendants) to be included within the report.
|
|
"""
|
|
def __init__(self, dbase, canvas):
|
|
self.database = dbase
|
|
self.canvas = canvas
|
|
|
|
self.families_seen = set()
|
|
self.cols = []
|
|
self.__last_direct = []
|
|
|
|
gui = GuiConnect()
|
|
self.do_parents = gui.get_val('show_parents')
|
|
self.max_generations = gui.get_val('maxgen')
|
|
self.max_spouses = gui.get_val('maxspouse')
|
|
self.inlc_marr = gui.get_val("inc_marr")
|
|
if not self.max_spouses:
|
|
self.inlc_marr = False
|
|
self.spouse_indent = gui.get_val('ind_spouse')
|
|
|
|
#is the option even available?
|
|
self.bold_direct = gui.get_val('bolddirect')
|
|
#can we bold direct descendants?
|
|
#bold_now will have only three values
|
|
#0 - no bolding
|
|
#1 - Only bold the first person
|
|
#2 - Bold all direct descendants
|
|
self.bold_now = 0
|
|
gui = None
|
|
|
|
def add_to_col(self, box):
|
|
"""
|
|
Add the box to a column on the canvas. we will do these things:
|
|
set the .linked_box attrib for the boxs in this col
|
|
get the height and width of this box and set it no the column
|
|
also we set the .x_cm to any s_level (indentation) here
|
|
we will calculate the real .x_cm later (with indentation)
|
|
"""
|
|
|
|
level = box.level[0]
|
|
#make the column list of people
|
|
while len(self.cols) <= level:
|
|
self.cols.append(None)
|
|
self.__last_direct.append(None)
|
|
|
|
if self.cols[level]: #if (not the first box in this column)
|
|
last_box = self.cols[level]
|
|
last_box.linked_box = box
|
|
|
|
#calculate the .y_cm for this box.
|
|
box.y_cm = last_box.y_cm
|
|
box.y_cm += last_box.height
|
|
if last_box.boxstr in ["CG2-box", "CG2b-box"]:
|
|
box.y_cm += self.canvas.report_opts.box_shadow
|
|
|
|
if box.boxstr in ["CG2-box", "CG2b-box"]:
|
|
box.y_cm += self.canvas.report_opts.box_pgap
|
|
else:
|
|
box.y_cm += self.canvas.report_opts.box_mgap
|
|
|
|
if box.level[1] == 0 and self.__last_direct[level]:
|
|
#ok, a new direct descendant.
|
|
#print level, box.father is not None, \
|
|
# self.__last_direct[level].father is not None, box.text[0], \
|
|
# self.__last_direct[level].text[0]
|
|
if box.father != self.__last_direct[level].father and \
|
|
box.father != self.__last_direct[level]:
|
|
box.y_cm += self.canvas.report_opts.box_pgap
|
|
|
|
self.cols[level] = box
|
|
if box.level[1] == 0:
|
|
self.__last_direct[level] = box
|
|
|
|
if self.spouse_indent:
|
|
box.x_cm = self.canvas.report_opts.spouse_offset * box.level[1]
|
|
else:
|
|
box.x_cm = 0.0
|
|
|
|
self.canvas.set_box_height_width(box)
|
|
|
|
def add_person_box(self, level, indi_handle, fams_handle, father):
|
|
""" Makes a person box and add that person into the Canvas. """
|
|
myself = PersonBox(level)
|
|
myself.father = father
|
|
|
|
if myself.level[1] == 0 and self.bold_direct and self.bold_now:
|
|
if self.bold_now == 1:
|
|
self.bold_now = 0
|
|
myself.set_bold()
|
|
|
|
if level[1] == 0 and father and myself.level[0] != father.level[0]:
|
|
#I am a child
|
|
if father.line_to:
|
|
line = father.line_to
|
|
else:
|
|
line = LineBase(father)
|
|
father.line_to = line
|
|
#self.canvas.add_line(line)
|
|
|
|
line.end.append(myself)
|
|
|
|
#calculate the text.
|
|
myself.calc_text(self.database, indi_handle, fams_handle)
|
|
|
|
if indi_handle:
|
|
myself.add_mark(self.database,
|
|
self.database.get_person_from_handle(indi_handle))
|
|
|
|
self.add_to_col(myself)
|
|
|
|
self.canvas.add_box(myself)
|
|
|
|
return myself
|
|
|
|
def add_marriage_box(self, level, indi_handle, fams_handle, father):
|
|
""" Makes a marriage box and add that person into the Canvas. """
|
|
myself = FamilyBox(level)
|
|
|
|
#if father is not None:
|
|
# myself.father = father
|
|
#calculate the text.
|
|
myself.calc_text(self.database, indi_handle, fams_handle)
|
|
|
|
self.add_to_col(myself)
|
|
|
|
self.canvas.add_box(myself)
|
|
|
|
return myself
|
|
|
|
def recurse(self, person_handle, x_level, s_level, father):
|
|
"""traverse the ancestors recursively until
|
|
either the end of a line is found,
|
|
or until we reach the maximum number of generations
|
|
or we reach the max number of spouses
|
|
that we want to deal with"""
|
|
|
|
if not person_handle:
|
|
return
|
|
if x_level > self.max_generations:
|
|
return
|
|
if s_level > 0 and s_level == self.max_spouses:
|
|
return
|
|
if person_handle in self.families_seen:
|
|
return
|
|
|
|
myself = None
|
|
person = self.database.get_person_from_handle(person_handle)
|
|
family_handles = person.get_family_handle_list()
|
|
if s_level == 0:
|
|
val = family_handles[0] if family_handles else None
|
|
myself = self.add_person_box((x_level, s_level),
|
|
person_handle, val, father)
|
|
|
|
marr = None
|
|
spouse = None
|
|
|
|
if s_level == 1:
|
|
tmp_bold = self.bold_now
|
|
self.bold_now = 0
|
|
|
|
for family_handle in family_handles:
|
|
if family_handle not in self.families_seen:
|
|
self.families_seen.add(family_handle)
|
|
|
|
family = self.database.get_family_from_handle(family_handle)
|
|
|
|
#Marriage box if the option is there.
|
|
if self.inlc_marr and self.max_spouses > 0:
|
|
marr = self.add_marriage_box((x_level, s_level+1),
|
|
person_handle, family_handle,
|
|
father if s_level else myself)
|
|
|
|
spouse_handle = utils.find_spouse(person, family)
|
|
if (self.max_spouses > s_level and
|
|
spouse_handle not in self.families_seen):
|
|
def _spouse_box(who):
|
|
return self.add_person_box((x_level, s_level+1),
|
|
spouse_handle,
|
|
family_handle, who)
|
|
if s_level > 0:
|
|
spouse = _spouse_box(father)
|
|
elif self.inlc_marr:
|
|
spouse = _spouse_box(marr)
|
|
else:
|
|
spouse = _spouse_box(myself)
|
|
|
|
mykids = [kid.ref for kid in family.get_child_ref_list()]
|
|
|
|
def _child_recurse(who):
|
|
self.recurse(child_ref, x_level+1, 0, who)
|
|
for child_ref in mykids:
|
|
if self.inlc_marr and self.max_spouses > 0:
|
|
_child_recurse(marr)
|
|
elif spouse:
|
|
_child_recurse(spouse)
|
|
else:
|
|
_child_recurse(myself)
|
|
|
|
if self.max_spouses > s_level and \
|
|
spouse_handle not in self.families_seen:
|
|
#spouse_handle = utils.find_spouse(person,family)
|
|
self.recurse(spouse_handle, x_level, s_level+1, spouse)
|
|
|
|
if s_level == 1:
|
|
self.bold_now = tmp_bold
|
|
|
|
def add_family(self, level, family, father2):
|
|
"""
|
|
Adds a family into the canvas.
|
|
only will be used for my direct grandparents, and my parents only.
|
|
"""
|
|
|
|
family_h = family.get_handle()
|
|
father_h = family.get_father_handle()
|
|
mother_h = family.get_mother_handle()
|
|
|
|
self.bold_now = 2
|
|
if father_h:
|
|
father_b = self.add_person_box(
|
|
(level, 0), father_h, family_h, father2)
|
|
else:
|
|
father_b = self.add_person_box(
|
|
(level, 0), None, None, father2)
|
|
retrn = [father_b]
|
|
|
|
if self.inlc_marr:
|
|
family_b = self.add_marriage_box(
|
|
(level, 1), father_h, family_h, father_b)
|
|
retrn.append(family_b)
|
|
self.families_seen.add(family_h)
|
|
|
|
if mother_h:
|
|
mother_b = self.add_person_box(
|
|
(level, 0), mother_h, family_h, father_b)
|
|
else:
|
|
mother_b = self.add_person_box(
|
|
(level, 0), None, None, father_b)
|
|
retrn.append(mother_b)
|
|
|
|
family_line = family_b if self.inlc_marr else father_b
|
|
for child_ref in family.get_child_ref_list():
|
|
self.recurse(child_ref.ref, level+1, 0, family_line)
|
|
|
|
self.bold_now = 0
|
|
|
|
#Set up the lines for the family
|
|
if not family_line.line_to:
|
|
#no children.
|
|
family_line.line_to = LineBase(family_line)
|
|
if self.inlc_marr:
|
|
family_line.line_to.start.append(father_b)
|
|
family_line.line_to.start.append(mother_b)
|
|
|
|
return retrn
|
|
|
|
def has_children(self, person_handle):
|
|
"""
|
|
Quickly check to see if this person has children
|
|
still we want to respect the FamiliesSeen list
|
|
"""
|
|
|
|
if not person_handle or person_handle in self.families_seen:
|
|
return False
|
|
|
|
person = self.database.get_person_from_handle(person_handle)
|
|
|
|
for family_handle in person.get_family_handle_list():
|
|
if family_handle not in self.families_seen:
|
|
|
|
family = self.database.get_family_from_handle(family_handle)
|
|
|
|
if family.get_child_ref_list():
|
|
return True
|
|
return False
|
|
|
|
def recurse_if(self, person_handle, level):
|
|
"""
|
|
Quickly check to see if we want to continue recursion
|
|
still we want to respect the FamiliesSeen list
|
|
"""
|
|
|
|
person = self.database.get_person_from_handle(person_handle)
|
|
|
|
show = False
|
|
myfams = person.get_family_handle_list()
|
|
if len(myfams) > 1: #and self.max_spouses > 0
|
|
show = True
|
|
if not self.inlc_marr:
|
|
#if the condition is true, we only want to show
|
|
#this parent again IF s/he has other children
|
|
show = self.has_children(person_handle)
|
|
|
|
#if self.max_spouses == 0 and not self.has_children(person_handle):
|
|
# self.families_seen.add(person_handle)
|
|
# show = False
|
|
|
|
if show:
|
|
self.bold_now = 1
|
|
self.recurse(person_handle, level, 0, None)
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class MakePersonTree (Personal Descendant Tree option)
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class MakePersonTree(RecurseDown):
|
|
"""
|
|
The main procedure to use recursion to make the tree based off of a person.
|
|
order of people inserted into Persons is important.
|
|
makes sure that order is done correctly.
|
|
"""
|
|
def __init__(self, dbase, canvas):
|
|
RecurseDown.__init__(self, dbase, canvas)
|
|
self.max_generations -= 1
|
|
|
|
def start(self, person_id):
|
|
"""follow the steps to make a tree off of a person"""
|
|
persons = []
|
|
|
|
center1 = self.database.get_person_from_gramps_id(person_id)
|
|
if center1 is None:
|
|
raise ReportError(_("Person %s is not in the Database") % person_id)
|
|
center1_h = center1.get_handle() #could be mom too.
|
|
|
|
family2 = family2_h = None
|
|
if self.do_parents:
|
|
family2_h = center1.get_main_parents_family_handle()
|
|
if family2_h:
|
|
family2 = self.database.get_family_from_handle(family2_h)
|
|
|
|
mother2_h = father2_h = None
|
|
if family2:
|
|
father2_h = family2.get_father_handle()
|
|
mother2_h = family2.get_mother_handle()
|
|
|
|
|
|
#######################
|
|
#don't do center person's parents family.
|
|
if family2_h:
|
|
self.families_seen.add(family2_h)
|
|
|
|
#######################
|
|
#Center person's Fathers OTHER wives
|
|
#######################
|
|
#update to only run if he HAD other wives!
|
|
if father2_h:
|
|
self.recurse_if(father2_h, 0)
|
|
|
|
#######################
|
|
#Center persons parents only!
|
|
#######################
|
|
#now it will ONLY be my fathers parents
|
|
if family2:
|
|
self.add_family(0, family2, None)
|
|
else:
|
|
self.bold_now = 2
|
|
self.recurse(center1_h, 0, 0, None)
|
|
self.bold_now = 0
|
|
|
|
#######################
|
|
#Center person's mothers OTHER husbands
|
|
#######################
|
|
#update to only run if she HAD other husbands!
|
|
if mother2_h:
|
|
self.recurse_if(mother2_h, 0)
|
|
|
|
return persons
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class MakeFamilyTree (Familial Descendant Tree option)
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class MakeFamilyTree(RecurseDown):
|
|
"""
|
|
The main procedure to use recursion to make the tree based off of a family.
|
|
order of people inserted into Persons is important.
|
|
makes sure that order is done correctly.
|
|
"""
|
|
|
|
def __init__(self, dbase, canvas):
|
|
RecurseDown.__init__(self, dbase, canvas)
|
|
|
|
def start(self, family_id):
|
|
"""follow the steps to make a tree off of a family"""
|
|
## (my) referes to the children of family_id
|
|
# Step 1 print out my fathers, fathers,
|
|
# other wives families first (if needed)
|
|
family1 = self.database.get_family_from_gramps_id(family_id)
|
|
if family1 is None:
|
|
raise ReportError(_("Family %s is not in the Database") % family_id)
|
|
family1_h = family1.get_handle()
|
|
|
|
#######################
|
|
#Initial setup of variables
|
|
#######################
|
|
father1_h = family1.get_father_handle()
|
|
mother1_h = family1.get_mother_handle()
|
|
|
|
father1 = mother1 = family2 = family2_h = None
|
|
if father1_h:
|
|
father1 = self.database.get_person_from_handle(father1_h)
|
|
if self.do_parents: #b3 - remove grandparents?
|
|
family2_h = father1.get_main_parents_family_handle()
|
|
if family2_h:
|
|
family2 = self.database.get_family_from_handle(family2_h)
|
|
if mother1_h:
|
|
mother1 = self.database.get_person_from_handle(mother1_h)
|
|
|
|
mother2_h = father2_h = father2 = mother2 = None
|
|
if family2: #family2 = fathers parents
|
|
mother2_h = family2.get_mother_handle()
|
|
if mother2_h:
|
|
mother2 = self.database.get_person_from_handle(mother2_h)
|
|
father2_h = family2.get_father_handle()
|
|
if father2_h:
|
|
father2 = self.database.get_person_from_handle(father2_h)
|
|
|
|
#Helper variables. Assigned in one section, used in another.
|
|
father2_id = family2_id = None
|
|
mother1_id = None
|
|
|
|
#######################
|
|
#don't do my fathers parents family. will be done later
|
|
if family2_h:
|
|
self.families_seen.add(family2_h)
|
|
|
|
#######################
|
|
#my father mothers OTHER husbands
|
|
#######################
|
|
#update to only run if she HAD other husbands!
|
|
if mother2_h:
|
|
self.recurse_if(mother2_h, 0)
|
|
|
|
#######################
|
|
#father Fathers OTHER wives
|
|
#######################
|
|
#update to only run if he HAD other wives!
|
|
if father2_h:
|
|
self.recurse_if(father2_h, 0)
|
|
|
|
#######################
|
|
#don't do my parents family in recurse. will be done later
|
|
self.families_seen.add(family1_h)
|
|
##If dad has no other children from other marriages. remove him
|
|
if self.max_spouses == 0 and not self.has_children(father1_h):
|
|
self.families_seen.add(father1_h)
|
|
|
|
#######################
|
|
#my fathers parents!
|
|
#######################
|
|
#now it will ONLY be my fathers parents
|
|
#will print dads parents. dad's other wifes will also print
|
|
if family2:
|
|
myfams = father1.get_family_handle_list()
|
|
show = False
|
|
if len(myfams) > 1:
|
|
show = True
|
|
if not self.inlc_marr and self.max_spouses == 0:
|
|
#if the condition is true, we only want to show
|
|
#this parent again IF s/he has children
|
|
show = self.has_children(father1_h)
|
|
if not show:
|
|
self.families_seen.add(father1_h)
|
|
|
|
family2_l = self.add_family(0, family2, None)
|
|
|
|
elif father1:
|
|
#######################
|
|
#my father other wives (if all of the above does nothing)
|
|
#if my father does not have parents (he is the highest)
|
|
#######################
|
|
#do his OTHER wives first.
|
|
self.recurse_if(father1_h, 1)
|
|
|
|
|
|
#######################
|
|
#my father, marriage info, mother, siblings, me
|
|
#######################
|
|
if family2:
|
|
#We need to add dad to the family
|
|
family2_line = family2_l[1] if self.inlc_marr else family2_l[0]
|
|
else:
|
|
family2_line = None
|
|
|
|
family1_l = self.add_family(1, family1, family2_line)
|
|
mother1_b = family1_l[-1] #Mom's Box
|
|
|
|
#make sure there is at least one child in this family.
|
|
#if not put in a placeholder
|
|
family1_line = family1_l[1] if self.inlc_marr else family1_l[0]
|
|
if family1_line.line_to.end == []:
|
|
box = PlaceHolderBox((mother1_b.level[0]+1, 0))
|
|
box.father = family1_l[0]
|
|
self.add_to_col(box)
|
|
family1_line.line_to.end = [box]
|
|
|
|
#######################
|
|
#######################
|
|
#Lower half
|
|
#This will be quite like the first half.
|
|
#Just on the mothers side...
|
|
#Mom has already been printed with the family
|
|
#######################
|
|
#######################
|
|
|
|
#######################
|
|
#Initial setup of variables
|
|
#######################
|
|
mother1_h = family1.get_mother_handle()
|
|
family2_h = mother1 = family2 = None
|
|
if mother1_h:
|
|
mother1 = self.database.get_person_from_handle(mother1_h)
|
|
if self.do_parents: #b3 - remove grandparents?
|
|
family2_h = mother1.get_main_parents_family_handle()
|
|
if family2_h:
|
|
family2 = self.database.get_family_from_handle(family2_h)
|
|
|
|
mother2_h = father2_h = mother2 = father2 = None
|
|
if family2:
|
|
mother2_h = family2.get_mother_handle()
|
|
if mother2_h:
|
|
mother2 = self.database.get_person_from_handle(mother2_h)
|
|
father2_h = family2.get_father_handle()
|
|
if father2_h:
|
|
father2 = self.database.get_person_from_handle(father2_h)
|
|
|
|
#######################
|
|
#don't do my parents family.
|
|
self.families_seen = set([family1_h])
|
|
##If mom has no other children from other marriages. remove her
|
|
if self.max_spouses == 0 and not self.has_children(mother1_h):
|
|
self.families_seen.add(mother1_h)
|
|
|
|
if mother1_h:
|
|
myfams = mother1.get_family_handle_list()
|
|
if len(myfams) < 2:
|
|
#If mom didn't have any other families, don't even do her
|
|
#she is already here with dad and will be added later
|
|
self.families_seen.add(mother1_h)
|
|
|
|
#######################
|
|
#my mother other spouses (if no parents)
|
|
#######################
|
|
#if my mother does not have parents (she is the highest)
|
|
#Then do her OTHER spouses.
|
|
if not family2 and mother1:
|
|
self.recurse_if(mother1_h, 1)
|
|
|
|
#######################
|
|
#my mothers parents!
|
|
#######################
|
|
if family2:
|
|
family2_l = self.add_family(0, family2, None)
|
|
family2_line = family2_l[1] if self.inlc_marr else family2_l[0]
|
|
|
|
family2_line = family2_line.line_to
|
|
if family2_line.end != []:
|
|
family2_line.end.insert(0, mother1_b)
|
|
else:
|
|
family2_line.end = [mother1_b]
|
|
|
|
#fix me. Moms other siblings have been given an extra space
|
|
#Because Moms-father is not siblings-father right now.
|
|
|
|
mother1_b.father = family2_line
|
|
|
|
#######################
|
|
#my mother mothers OTHER husbands
|
|
#######################
|
|
#update to only run if she HAD other husbands!
|
|
if mother2_h:
|
|
self.recurse_if(mother2_h, 0)
|
|
|
|
#######################
|
|
#mother Fathers OTHER wives
|
|
#######################
|
|
#update to only run if he HAD other wives!
|
|
if father2_h:
|
|
self.recurse_if(father2_h, 0)
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Class MakeReport
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class MakeReport:
|
|
"""
|
|
Make a report out of a list of people.
|
|
The list of people is already made. Use this information to find where
|
|
people will be placed on the canvas.
|
|
"""
|
|
|
|
def __init__(self, dbase, canvas, ind_spouse, compress_tree):
|
|
self.database = dbase
|
|
self.canvas = canvas
|
|
|
|
gui = GuiConnect()
|
|
self.do_parents = gui.get_val('show_parents')
|
|
self.inlc_marr = gui.get_val("inc_marr")
|
|
self.max_spouses = gui.get_val('maxspouse')
|
|
gui = None
|
|
|
|
self.ind_spouse = ind_spouse
|
|
self.compress_tree = compress_tree
|
|
self.cols = [[]]
|
|
#self.max_generations = 0
|
|
|
|
#already done in recurse,
|
|
#Some of this code needs to be moved up to RecurseDown.add_to_col()
|
|
def calc_box(self, box):
|
|
""" calculate the max_box_width and max_box_height for the report """
|
|
width = box.x_cm + box.width
|
|
if width > self.canvas.report_opts.max_box_width:
|
|
self.canvas.report_opts.max_box_width = width
|
|
|
|
if box.height > self.canvas.report_opts.max_box_height:
|
|
self.canvas.report_opts.max_box_height = box.height
|
|
|
|
while len(self.cols) <= box.level[0]:
|
|
self.cols.append([])
|
|
|
|
self.cols[box.level[0]].append(box)
|
|
|
|
#tmp = box.level[0]
|
|
#if tmp > self.max_generations:
|
|
# self.max_generations = tmp
|
|
|
|
def __move_col_from_here_down(self, box, amount):
|
|
"""Move me and everyone below me in this column only down"""
|
|
while box:
|
|
box.y_cm += amount
|
|
box = box.linked_box
|
|
|
|
def __move_next_cols_from_here_down(self, box, amount):
|
|
"""Move me, everyone below me in this column,
|
|
and all of our children (and childrens children) down."""
|
|
col = [box]
|
|
while col:
|
|
if len(col) == 1 and col[0].line_to:
|
|
col.append(col[0].line_to.end[0])
|
|
|
|
col[0].y_cm += amount
|
|
|
|
col[0] = col[0].linked_box
|
|
if col[0] is None:
|
|
col.pop(0)
|
|
|
|
def __next_family_group(self, box):
|
|
""" a helper function. Assume box is at the start of a family block.
|
|
get this family block. """
|
|
while box:
|
|
left_group = []
|
|
line = None
|
|
|
|
#Form the parental (left) group.
|
|
#am I a direct descendant?
|
|
if box.level[1] == 0:
|
|
#I am the father/mother.
|
|
left_group.append(box)
|
|
if box.line_to:
|
|
line = box.line_to
|
|
box = box.linked_box
|
|
|
|
if box and box.level[1] != 0 and self.inlc_marr:
|
|
#add/start with the marriage box
|
|
left_group.append(box)
|
|
if box.line_to:
|
|
line = box.line_to
|
|
box = box.linked_box
|
|
|
|
if box and box.level[1] != 0 and self.max_spouses > 0:
|
|
#add/start with the spousal box
|
|
left_group.append(box)
|
|
if box.line_to:
|
|
line = box.line_to
|
|
box = box.linked_box
|
|
|
|
if line:
|
|
if len(line.start) > 1 and line.start[-1].level[1] == 0:
|
|
#a dad and mom family from RecurseDown.add_family. add mom
|
|
left_group.append(line.start[-1])
|
|
box = box.linked_box
|
|
|
|
#we now have everyone we want
|
|
return left_group, line.end
|
|
#else
|
|
# no children, so no family. go again until we find one to return.
|
|
|
|
return None, None
|
|
|
|
def __reverse_family_group(self):
|
|
""" go through the n-1 to 0 cols of boxes looking for families
|
|
(parents with children) that may need to be moved. """
|
|
for col in reversed(self.cols):
|
|
box = col[0] #The first person in this col
|
|
while box:
|
|
left_group, right_group = self.__next_family_group(box)
|
|
if not left_group:
|
|
box = None #we found the end of this col
|
|
else:
|
|
yield left_group, right_group
|
|
box = left_group[-1].linked_box
|
|
|
|
def __calc_movements(self, left_group, right_group):
|
|
""" for a family group, see if parents or children need to be
|
|
moved down so everyone is to the right/left of each other.
|
|
|
|
return a right y_cm and a left y_cm. these points will be used
|
|
to move parents/children down.
|
|
"""
|
|
left_up = left_group[0].y_cm
|
|
right_up = right_group[0].y_cm
|
|
|
|
left_center = left_up
|
|
right_center = right_up
|
|
|
|
if self.compress_tree:
|
|
#calculate a new left and right move points
|
|
for left_line in left_group:
|
|
if left_line.line_to:
|
|
break
|
|
left_center = left_line.y_cm + (left_line.height /2)
|
|
|
|
left_down = left_group[-1].y_cm + left_group[-1].height
|
|
right_down = right_group[-1].y_cm + right_group[-1].height
|
|
|
|
#Lazy. Move down either side only as much as we NEED to.
|
|
if left_center < right_up:
|
|
right_center = right_group[0].y_cm
|
|
elif left_up == right_up:
|
|
left_center = left_up #Lets keep it. top line.
|
|
elif left_center > right_down:
|
|
right_center = right_down
|
|
else:
|
|
right_center = left_center
|
|
|
|
return right_center, left_center
|
|
|
|
def Make_report(self):
|
|
"""
|
|
Everyone on the page is as far up as they can go.
|
|
Move them down to where they belong.
|
|
|
|
We are going to go through everyone from right to left
|
|
top to bottom moving everyone down as needed to make the report.
|
|
"""
|
|
seen_parents = False
|
|
|
|
for left_group, right_group in self.__reverse_family_group():
|
|
right_y_cm, left_y_cm = self.__calc_movements(left_group,
|
|
right_group)
|
|
|
|
#1. Are my children too high? if so move then down!
|
|
if right_y_cm < left_y_cm:
|
|
#we have to push our kids (and their kids) down.
|
|
#We also need to push down all the kids (under)
|
|
#these kids (in their column)
|
|
amt = (left_y_cm - right_y_cm)
|
|
self.__move_next_cols_from_here_down(right_group[0], amt)
|
|
|
|
#2. Am I (and spouses) too high? if so move us down!
|
|
elif left_y_cm < right_y_cm:
|
|
#Ok, I am too high. Move me down
|
|
amt = (right_y_cm - left_y_cm)
|
|
self.__move_col_from_here_down(left_group[0], amt)
|
|
|
|
#6. now check to see if we are working with dad and mom.
|
|
#if so we need to move down marriage information
|
|
#and mom!
|
|
left_line = left_group[0].line_to
|
|
if not left_line:
|
|
left_line = left_group[1].line_to
|
|
#left_line = left_line.start
|
|
|
|
if len(left_line.start) > 1 and not seen_parents:
|
|
#only do Dad and Mom. len(left_line) > 1
|
|
seen_parents = True
|
|
|
|
mom_cm = left_group[-1].y_cm + left_group[-1].height/2
|
|
last_child_cm = right_group[-1].y_cm
|
|
if not self.compress_tree:
|
|
last_child_cm += right_group[-1].height/2
|
|
move_amt = last_child_cm - mom_cm
|
|
|
|
#if the moms height is less than the last childs height
|
|
#The 0.2 is to see if this is even worth it.
|
|
if move_amt > 0.2:
|
|
#our children take up more space than us parents.
|
|
#so space mom out!
|
|
self.__move_col_from_here_down(left_group[-1], move_amt)
|
|
|
|
#move marriage info
|
|
if self.inlc_marr:
|
|
left_group[1].y_cm += move_amt/2
|
|
|
|
if left_line.end[0].boxstr == 'None':
|
|
left_line.end = []
|
|
|
|
def start(self):
|
|
"""Make the report"""
|
|
#for person in self.persons.depth_first_gen():
|
|
for box in self.canvas.boxes:
|
|
self.calc_box(box)
|
|
#At this point we know everything we need to make the report.
|
|
#Width of each column of people - self.rept_opt.box_width
|
|
#width of each column (or row) of lines - self.rept_opt.col_width
|
|
|
|
if not self.cols[0]:
|
|
#We wanted to print parents of starting person/family but
|
|
#there were none!
|
|
#remove column 0 and move everyone back one level
|
|
self.cols.pop(0)
|
|
for box in self.canvas.boxes:
|
|
box.level = (box.level[0] - 1, box.level[1])
|
|
|
|
#go ahead and set it now.
|
|
width = self.canvas.report_opts.max_box_width
|
|
for box in self.canvas.boxes:
|
|
box.width = width - box.x_cm
|
|
box.x_cm += self.canvas.report_opts.littleoffset
|
|
box.x_cm += (box.level[0] *
|
|
(self.canvas.report_opts.col_width +
|
|
self.canvas.report_opts.max_box_width))
|
|
|
|
box.y_cm += self.canvas.report_opts.littleoffset
|
|
box.y_cm += self.canvas.title.height
|
|
|
|
self.Make_report()
|
|
|
|
|
|
class GuiConnect:
|
|
""" This is a BORG object. There is ONLY one.
|
|
This give some common routines that EVERYONE can use like
|
|
get the value from a GUI variable
|
|
"""
|
|
|
|
__shared_state = {}
|
|
def __init__(self): #We are BORG!
|
|
self.__dict__ = self.__shared_state
|
|
|
|
def set__opts(self, options, which, locale, name_displayer):
|
|
self._opts = options
|
|
self._which_report = which.split(",")[0]
|
|
self._locale = locale
|
|
self._nd = name_displayer
|
|
|
|
def get_val(self, val):
|
|
""" Get a GUI value. """
|
|
value = self._opts.get_option_by_name(val)
|
|
if value:
|
|
return value.get_value()
|
|
else:
|
|
False
|
|
|
|
def Title_class(self, database, doc):
|
|
Title_type = self.get_val('report_title')
|
|
if Title_type == 0: #None
|
|
return TitleNone(database, doc, self._locale)
|
|
|
|
if Title_type == 1: #Descendant Chart
|
|
if self._which_report == _RPT_NAME:
|
|
if self.get_val('show_parents'):
|
|
return TitleDPY(database, doc, self._locale, self._nd)
|
|
else:
|
|
return TitleDPN(database, doc, self._locale, self._nd)
|
|
else:
|
|
if self.get_val('show_parents'):
|
|
return TitleDFY(database, doc, self._locale, self._nd)
|
|
else:
|
|
return TitleDFN(database, doc, self._locale, self._nd)
|
|
|
|
if Title_type == 2:
|
|
return TitleF(database, doc, self._locale, self._nd)
|
|
else: #Title_type == 3
|
|
return TitleC(database, doc, self._locale, self._nd)
|
|
|
|
def Make_Tree(self, database, canvas):
|
|
if self._which_report == _RPT_NAME:
|
|
return MakePersonTree(database, canvas)
|
|
else:
|
|
return MakeFamilyTree(database, canvas)
|
|
|
|
def calc_lines(self, database):
|
|
#calculate the printed lines for each box
|
|
display_repl = self.get_val("replace_list")
|
|
#str = ""
|
|
#if self.get_val('miss_val'):
|
|
# str = "_____"
|
|
return CalcLines(database, display_repl, self._locale, self._nd)
|
|
|
|
def working_lines(self, box):
|
|
display = self.get_val("descend_disp")
|
|
#if self.get_val('diffspouse'):
|
|
display_spou = self.get_val("spouse_disp")
|
|
#else:
|
|
# display_spou = display
|
|
display_marr = [self.get_val("marr_disp")]
|
|
|
|
if box.boxstr == "CG2-fam-box": #(((((
|
|
workinglines = display_marr
|
|
elif box.level[1] > 0 or (box.level[0] == 0 and box.father):
|
|
workinglines = display_spou
|
|
else:
|
|
workinglines = display
|
|
return workinglines
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# DescendTree
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class DescendTree(Report):
|
|
|
|
def __init__(self, database, options, user):
|
|
"""
|
|
Create DescendTree object that produces the report.
|
|
The arguments are:
|
|
|
|
database - the Gramps database instance
|
|
options - instance of the Options class for this report
|
|
user - a gen.user.User() instance
|
|
|
|
incl_private - Whether to include private data
|
|
living_people - How to handle living people
|
|
years_past_death - Consider as living this many years after death
|
|
"""
|
|
Report.__init__(self, database, options, user)
|
|
|
|
self.options = options
|
|
|
|
self.set_locale(options.menu.get_option_by_name('trans').get_value())
|
|
stdoptions.run_date_format_option(self, options.menu)
|
|
stdoptions.run_private_data_option(self, options.menu)
|
|
stdoptions.run_living_people_option(self, options.menu, self._locale)
|
|
self.database = CacheProxyDb(self.database)
|
|
stdoptions.run_name_format_option(self, options.menu)
|
|
self._nd = self._name_display
|
|
|
|
def begin_report(self):
|
|
""" make the report in its full size and pages to print on
|
|
scale one or both as needed/desired.
|
|
"""
|
|
|
|
database = self.database
|
|
|
|
self.Connect = GuiConnect()
|
|
self.Connect.set__opts(self.options.menu, self.options.name,
|
|
self._locale, self._nd)
|
|
|
|
style_sheet = self.doc.get_style_sheet()
|
|
font_normal = style_sheet.get_paragraph_style("CG2-Normal").get_font()
|
|
|
|
#The canvas that we will put our report on and print off of
|
|
self.canvas = Canvas(self.doc,
|
|
ReportOptions(self.doc, font_normal, "CG2-line"))
|
|
|
|
self.canvas.report_opts.box_shadow *= \
|
|
self.Connect.get_val('shadowscale')
|
|
self.canvas.report_opts.box_pgap *= self.Connect.get_val('box_Yscale')
|
|
self.canvas.report_opts.box_mgap *= self.Connect.get_val('box_Yscale')
|
|
|
|
center_id = self.Connect.get_val('pid')
|
|
|
|
#make the tree
|
|
tree = self.Connect.Make_Tree(database, self.canvas)
|
|
tree.start(center_id)
|
|
tree = None
|
|
|
|
#Title
|
|
title = self.Connect.Title_class(database, self.doc)
|
|
title.calc_title(center_id)
|
|
self.canvas.add_title(title)
|
|
|
|
#make the report as big as it wants to be.
|
|
ind_spouse = self.Connect.get_val("ind_spouse")
|
|
compress_tree = self.Connect.get_val('compress_tree')
|
|
report = MakeReport(database, self.canvas, ind_spouse, compress_tree)
|
|
report.start()
|
|
report = None
|
|
|
|
#note?
|
|
if self.Connect.get_val("inc_note"):
|
|
note_box = NoteBox(self.doc, "CG2-note-box",
|
|
self.Connect.get_val("note_place"))
|
|
subst = SubstKeywords(self.database, self._locale, self._nd,
|
|
None, None)
|
|
note_box.text = subst.replace_and_clean(
|
|
self.Connect.get_val('note_disp'))
|
|
self.canvas.add_note(note_box)
|
|
|
|
#Now we have the report in its full size.
|
|
#Do we want to scale the report?
|
|
one_page = self.Connect.get_val("resize_page")
|
|
scale_report = self.Connect.get_val("scale_tree")
|
|
|
|
scale = self.canvas.scale_report(one_page,
|
|
scale_report != 0, scale_report == 2)
|
|
|
|
if scale != 1 or self.Connect.get_val('shadowscale') != 1.0:
|
|
self.scale_styles(scale)
|
|
|
|
def write_report(self):
|
|
""" Canvas now has everyone ready to print. Get some misc stuff
|
|
together and print. """
|
|
|
|
one_page = self.Connect.get_val("resize_page")
|
|
scale_report = self.Connect.get_val("scale_tree")
|
|
|
|
#Inlc_marr = self.Connect.get_val("inc_marr")
|
|
inc_border = self.Connect.get_val('inc_border')
|
|
incblank = self.Connect.get_val("inc_blank")
|
|
prnnum = self.Connect.get_val("inc_pagenum")
|
|
#ind_spouse = self.Connect.get_val("ind_spouse")
|
|
lines = self.Connect.get_val('note_disp')
|
|
|
|
#####################
|
|
#Setup page information
|
|
|
|
colsperpage = self.doc.get_usable_width()
|
|
colsperpage += self.canvas.report_opts.col_width
|
|
tmp = self.canvas.report_opts.max_box_width
|
|
tmp += self.canvas.report_opts.col_width
|
|
colsperpage = int(colsperpage / tmp)
|
|
colsperpage = colsperpage or 1
|
|
|
|
#####################
|
|
#Vars
|
|
#p = self.doc.get_style_sheet().get_paragraph_style("CG2-Normal")
|
|
#font = p.get_font()
|
|
if prnnum:
|
|
page_num_box = PageNumberBox(self.doc, 'CG2-box', self._locale)
|
|
|
|
#####################
|
|
#ok, everyone is now ready to print on the canvas. Paginate?
|
|
self.canvas.sort_boxes_on_y_cm()
|
|
self.canvas.paginate(colsperpage, one_page)
|
|
|
|
#####################
|
|
#Yeah!!!
|
|
#lets finally make some pages!!!
|
|
#####################
|
|
for page in self.canvas.page_iter_gen(incblank):
|
|
|
|
self.doc.start_page()
|
|
|
|
#do we need to print a border?
|
|
if inc_border:
|
|
page.draw_border('CG2-line')
|
|
|
|
#Do we need to print the page number?
|
|
if prnnum:
|
|
page_num_box.display(page)
|
|
|
|
page.display()
|
|
|
|
self.doc.end_page()
|
|
|
|
|
|
def scale_styles(self, amount):
|
|
"""
|
|
Scale the styles for this report. This must be done in the constructor.
|
|
"""
|
|
style_sheet = self.doc.get_style_sheet()
|
|
|
|
graph_style = style_sheet.get_draw_style("CG2-fam-box")
|
|
graph_style.set_shadow(graph_style.get_shadow(), 0)
|
|
graph_style.set_line_width(graph_style.get_line_width() * amount)
|
|
style_sheet.add_draw_style("CG2-fam-box", graph_style)
|
|
|
|
graph_style = style_sheet.get_draw_style("CG2-box")
|
|
graph_style.set_shadow(graph_style.get_shadow(),
|
|
self.canvas.report_opts.box_shadow * amount)
|
|
graph_style.set_line_width(graph_style.get_line_width() * amount)
|
|
style_sheet.add_draw_style("CG2-box", graph_style)
|
|
|
|
graph_style = style_sheet.get_draw_style("CG2b-box")
|
|
graph_style.set_shadow(graph_style.get_shadow(),
|
|
self.canvas.report_opts.box_shadow * amount)
|
|
graph_style.set_line_width(graph_style.get_line_width() * amount)
|
|
style_sheet.add_draw_style("CG2b-box", graph_style)
|
|
|
|
graph_style = style_sheet.get_draw_style("CG2-note-box")
|
|
graph_style.set_shadow(graph_style.get_shadow(), 0)
|
|
graph_style.set_line_width(graph_style.get_line_width() * amount)
|
|
style_sheet.add_draw_style("CG2-note-box", graph_style)
|
|
|
|
para_style = style_sheet.get_paragraph_style("CG2-Title")
|
|
font = para_style.get_font()
|
|
font.set_size(font.get_size() * amount)
|
|
para_style.set_font(font)
|
|
style_sheet.add_paragraph_style("CG2-Title", para_style)
|
|
|
|
para_style = style_sheet.get_paragraph_style("CG2-Normal")
|
|
font = para_style.get_font()
|
|
font.set_size(font.get_size() * amount)
|
|
para_style.set_font(font)
|
|
style_sheet.add_paragraph_style("CG2-Normal", para_style)
|
|
|
|
para_style = style_sheet.get_paragraph_style("CG2-Bold")
|
|
font = para_style.get_font()
|
|
font.set_bold(True)
|
|
font.set_size(font.get_size() * amount)
|
|
para_style.set_font(font)
|
|
style_sheet.add_paragraph_style("CG2-Bold", para_style)
|
|
|
|
para_style = style_sheet.get_paragraph_style("CG2-Note")
|
|
font = para_style.get_font()
|
|
font.set_size(font.get_size() * amount)
|
|
para_style.set_font(font)
|
|
style_sheet.add_paragraph_style("CG2-Note", para_style)
|
|
|
|
self.doc.set_style_sheet(style_sheet)
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# DescendTreeOptions
|
|
#
|
|
#------------------------------------------------------------------------
|
|
class DescendTreeOptions(MenuReportOptions):
|
|
|
|
"""
|
|
Defines options and provides handling interface.
|
|
"""
|
|
|
|
def __init__(self, name, dbase):
|
|
self.__pid = None
|
|
self.__onepage = None
|
|
self.__inc_title = None
|
|
self.__title = None
|
|
self.__blank = None
|
|
self.scale = None
|
|
self.__db = dbase
|
|
self.name = name
|
|
self.box_Y_sf = None
|
|
self.box_shadow_sf = None
|
|
MenuReportOptions.__init__(self, name, dbase)
|
|
|
|
def get_subject(self):
|
|
""" Return a string that describes the subject of the report. """
|
|
gid = self.__pid.get_value()
|
|
if self.name.split(",")[0] == _RPT_NAME:
|
|
person = self.__db.get_person_from_gramps_id(gid)
|
|
if person:
|
|
return _nd.display(person)
|
|
else:
|
|
family = self.__db.get_family_from_gramps_id(gid)
|
|
if family:
|
|
return family_name(family, self.__db)
|
|
return ""
|
|
|
|
def add_menu_options(self, menu):
|
|
"""
|
|
Add options to the menu for the descendant report.
|
|
"""
|
|
##################
|
|
category_name = _("Tree Options")
|
|
|
|
if self.name.split(",")[0] == _RPT_NAME:
|
|
self.__pid = PersonOption(_("Report for"))
|
|
self.__pid.set_help(_("The main person for the report"))
|
|
menu.add_option(category_name, "pid", self.__pid)
|
|
else: #if self.name == "familial_descend_tree":
|
|
self.__pid = FamilyOption(_("Report for"))
|
|
self.__pid.set_help(_("The main family for the report"))
|
|
menu.add_option(category_name, "pid", self.__pid)
|
|
|
|
max_gen = NumberOption(_("Generations"), 10, 1, 50)
|
|
max_gen.set_help(_("The number of generations to include in the tree"))
|
|
menu.add_option(category_name, "maxgen", max_gen)
|
|
|
|
max_spouse = NumberOption(_("Level of Spouses"), 1, 0, 10)
|
|
max_spouse.set_help(_("0=no Spouses, 1=include Spouses, 2=include "
|
|
"Spouses of the spouse, etc"))
|
|
menu.add_option(category_name, "maxspouse", max_spouse)
|
|
|
|
self.showparents = BooleanOption(
|
|
_('Start with the parent(s) of the selected first'),
|
|
False)
|
|
self.showparents.set_help(
|
|
_("Will show the parents, brother and sisters of the "
|
|
"selected person.")
|
|
)
|
|
menu.add_option(category_name, "show_parents", self.showparents)
|
|
|
|
compresst = BooleanOption(_('Compress tree'), False)
|
|
compresst.set_help(_("Whether to move people up, where possible, "
|
|
"resulting in a smaller tree"))
|
|
menu.add_option(category_name, "compress_tree", compresst)
|
|
|
|
bold = BooleanOption(_('Bold direct descendants'), True)
|
|
bold.set_help(
|
|
_("Whether to bold those people that are direct "
|
|
"(not step or half) descendants.")
|
|
)
|
|
menu.add_option(category_name, "bolddirect", bold)
|
|
|
|
indspouce = BooleanOption(_('Indent Spouses'), True)
|
|
indspouce.set_help(_("Whether to indent the spouses in the tree."))
|
|
menu.add_option(category_name, "ind_spouse", indspouce)
|
|
|
|
##################
|
|
category_name = _("Report Options")
|
|
|
|
self.title = EnumeratedListOption(_("Report Title"), 0)
|
|
self.title.add_item(0, _("Do not include a title"))
|
|
self.title.add_item(1, _("Descendant Chart for [selected person(s)]"))
|
|
if self.name.split(",")[0] != _RPT_NAME:
|
|
self.title.add_item(2,
|
|
_("Family Chart for [names of chosen family]"))
|
|
if self.showparents.get_value():
|
|
self.title.add_item(3,
|
|
_("Cousin Chart for [names of children]"))
|
|
self.title.set_help(_("Choose a title for the report"))
|
|
menu.add_option(category_name, "report_title", self.title)
|
|
self.showparents.connect('value-changed', self.__Title_enum)
|
|
|
|
border = BooleanOption(_('Include a border'), False)
|
|
border.set_help(_("Whether to make a border around the report."))
|
|
menu.add_option(category_name, "inc_border", border)
|
|
|
|
prnnum = BooleanOption(_('Include Page Numbers'), False)
|
|
prnnum.set_help(_("Whether to include page numbers on each page."))
|
|
menu.add_option(category_name, "inc_pagenum", prnnum)
|
|
|
|
self.scale = EnumeratedListOption(_("Scale tree to fit"), 0)
|
|
self.scale.add_item(0, _("Do not scale tree"))
|
|
self.scale.add_item(1, _("Scale tree to fit page width only"))
|
|
self.scale.add_item(2, _("Scale tree to fit the size of the page"))
|
|
self.scale.set_help(
|
|
_("Whether to scale the tree to fit a specific paper size")
|
|
)
|
|
menu.add_option(category_name, "scale_tree", self.scale)
|
|
self.scale.connect('value-changed', self.__check_blank)
|
|
|
|
if "BKI" not in self.name.split(","):
|
|
self.__onepage = BooleanOption(
|
|
_("Resize Page to Fit Tree size\n"
|
|
"\n"
|
|
"Note: Overrides options in the 'Paper Option' tab"
|
|
),
|
|
False)
|
|
self.__onepage.set_help(
|
|
_("Whether to resize the page to fit the size \n"
|
|
"of the tree. Note: the page will have a \n"
|
|
"non standard size.\n"
|
|
"\n"
|
|
"With this option selected, the following will happen:\n"
|
|
"\n"
|
|
"With the 'Do not scale tree' option the page\n"
|
|
" is resized to the height/width of the tree\n"
|
|
"\n"
|
|
"With 'Scale tree to fit page width only' the height of\n"
|
|
" the page is resized to the height of the tree\n"
|
|
"\n"
|
|
"With 'Scale tree to fit the size of the page' the page\n"
|
|
" is resized to remove any gap in either height or width"
|
|
))
|
|
menu.add_option(category_name, "resize_page", self.__onepage)
|
|
self.__onepage.connect('value-changed', self.__check_blank)
|
|
else:
|
|
self.__onepage = None
|
|
|
|
self.__blank = BooleanOption(_('Include Blank Pages'), True)
|
|
self.__blank.set_help(_("Whether to include pages that are blank."))
|
|
menu.add_option(category_name, "inc_blank", self.__blank)
|
|
|
|
##################
|
|
category_name = _("Report Options (2)")
|
|
|
|
stdoptions.add_name_format_option(menu, category_name)
|
|
|
|
stdoptions.add_private_data_option(menu, category_name)
|
|
|
|
stdoptions.add_living_people_option(menu, category_name)
|
|
|
|
locale_opt = stdoptions.add_localization_option(menu, category_name)
|
|
|
|
stdoptions.add_date_format_option(menu, category_name, locale_opt)
|
|
|
|
##################
|
|
category_name = _("Display")
|
|
|
|
disp = TextOption(_("Descendant\nDisplay Format"),
|
|
["$n",
|
|
"%s $b" %_BORN,
|
|
"-{%s $d}" %_DIED])
|
|
disp.set_help(_("Display format for a descendant."))
|
|
menu.add_option(category_name, "descend_disp", disp)
|
|
|
|
#bug 4767
|
|
#diffspouse = BooleanOption(
|
|
# _("Use separate display format for spouses"),
|
|
# True)
|
|
#diffspouse.set_help(_("Whether spouses can have a different format."))
|
|
#menu.add_option(category_name, "diffspouse", diffspouse)
|
|
|
|
sdisp = TextOption(_("Spousal\nDisplay Format"),
|
|
["$n",
|
|
"%s $b" %_BORN,
|
|
"-{%s $d}" %_DIED])
|
|
sdisp.set_help(_("Display format for a spouse."))
|
|
menu.add_option(category_name, "spouse_disp", sdisp)
|
|
|
|
self.incmarr = BooleanOption(_('Include Marriage box'), True)
|
|
self.incmarr.set_help(
|
|
_("Whether to include a separate marital box in the report"))
|
|
menu.add_option(category_name, "inc_marr", self.incmarr)
|
|
self.incmarr.connect('value-changed', self._incmarr_changed)
|
|
|
|
self.marrdisp = StringOption(_("Marriage\nDisplay Format"),
|
|
"%s $m" % _MARR)
|
|
self.marrdisp.set_help(_("Display format for the marital box."))
|
|
menu.add_option(category_name, "marr_disp", self.marrdisp)
|
|
self._incmarr_changed()
|
|
|
|
##################
|
|
category_name = _("Advanced")
|
|
|
|
repldisp = TextOption(
|
|
_("Replace Display Format:\n'Replace this'/' with this'"),
|
|
[])
|
|
repldisp.set_help(_("i.e.\nUnited States of America/U.S.A."))
|
|
menu.add_option(category_name, "replace_list", repldisp)
|
|
|
|
self.usenote = BooleanOption(_('Include a note'), False)
|
|
self.usenote.set_help(_("Whether to include a note on the report."))
|
|
menu.add_option(category_name, "inc_note", self.usenote)
|
|
self.usenote.connect('value-changed', self._usenote_changed)
|
|
|
|
self.notedisp = TextOption(_("Note"), [])
|
|
self.notedisp.set_help(_("Add a note\n\n"
|
|
"$T inserts today's date"))
|
|
menu.add_option(category_name, "note_disp", self.notedisp)
|
|
|
|
locales = NoteType(0)
|
|
self.notelocal = EnumeratedListOption(_("Note Location"), 2)
|
|
for num, text in locales.note_locals():
|
|
self.notelocal.add_item(num, text)
|
|
self.notelocal.set_help(_("Where to place the note."))
|
|
menu.add_option(category_name, "note_place", self.notelocal)
|
|
self._usenote_changed()
|
|
|
|
self.box_Y_sf = NumberOption(_("inter-box Y scale factor"),
|
|
1.00, 0.10, 2.00, 0.01)
|
|
self.box_Y_sf.set_help(_("Make the inter-box Y bigger or smaller"))
|
|
menu.add_option(category_name, "box_Yscale", self.box_Y_sf)
|
|
|
|
self.box_shadow_sf = NumberOption(_("box shadow scale factor"),
|
|
1.00, 0.00, 2.00, 0.01) # down to 0
|
|
self.box_shadow_sf.set_help(_("Make the box shadow bigger or smaller"))
|
|
menu.add_option(category_name, "shadowscale", self.box_shadow_sf)
|
|
|
|
def _incmarr_changed(self):
|
|
"""
|
|
If Marriage box is not enabled, disable Marriage Display Format box
|
|
"""
|
|
value = self.incmarr.get_value()
|
|
self.marrdisp.set_available(value)
|
|
|
|
def _usenote_changed(self):
|
|
"""
|
|
If Note box is not enabled, disable Note Location box
|
|
"""
|
|
value = self.usenote.get_value()
|
|
self.notelocal.set_available(value)
|
|
|
|
def __check_blank(self):
|
|
"""dis/enables the 'print blank pages' checkbox"""
|
|
if self.__onepage:
|
|
value = not self.__onepage.get_value()
|
|
else:
|
|
value = True
|
|
off = value and (self.scale.get_value() != 2)
|
|
self.__blank.set_available(off)
|
|
|
|
def __Title_enum(self):
|
|
item_list = [
|
|
[0, _("Do not include a title")],
|
|
[1, _("Descendant Chart for [selected person(s)]")],
|
|
]
|
|
if self.name.split(",")[0] != _RPT_NAME:
|
|
item_list.append(
|
|
[2, _("Family Chart for [names of chosen family]")]
|
|
)
|
|
if self.showparents.get_value():
|
|
item_list.append(
|
|
[3, _("Cousin Chart for [names of children]")]
|
|
)
|
|
self.title.set_items(item_list)
|
|
|
|
def make_default_style(self, default_style):
|
|
"""Make the default output style for the Descendant Tree."""
|
|
|
|
## Paragraph Styles:
|
|
font = FontStyle()
|
|
font.set_size(16)
|
|
font.set_type_face(FONT_SANS_SERIF)
|
|
para_style = ParagraphStyle()
|
|
para_style.set_font(font)
|
|
para_style.set_alignment(PARA_ALIGN_CENTER)
|
|
para_style.set_description(_("The style used for the title."))
|
|
default_style.add_paragraph_style("CG2-Title", para_style)
|
|
|
|
font = FontStyle()
|
|
font.set_size(9)
|
|
font.set_type_face(FONT_SANS_SERIF)
|
|
para_style = ParagraphStyle()
|
|
para_style.set_font(font)
|
|
para_style.set_description(
|
|
_('The basic style used for the text display.'))
|
|
default_style.add_paragraph_style("CG2-Normal", para_style)
|
|
|
|
#Set the size of the shadow based on the font size! Much better
|
|
#will be set later too.
|
|
box_shadow = PT2CM(font.get_size()) * .6
|
|
|
|
font.set_bold(True)
|
|
para_style = ParagraphStyle()
|
|
para_style.set_font(font)
|
|
para_style.set_description(
|
|
_('The bold style used for the text display.'))
|
|
default_style.add_paragraph_style("CG2-Bold", para_style)
|
|
|
|
font = FontStyle()
|
|
font.set_size(9)
|
|
font.set_type_face(FONT_SANS_SERIF)
|
|
para_style = ParagraphStyle()
|
|
para_style.set_font(font)
|
|
para_style.set_description(
|
|
_('The basic style used for the note display.'))
|
|
default_style.add_paragraph_style("CG2-Note", para_style)
|
|
|
|
# TODO this seems meaningless, as only the text is displayed
|
|
graph_style = GraphicsStyle()
|
|
graph_style.set_paragraph_style("CG2-Title")
|
|
graph_style.set_color((0, 0, 0))
|
|
graph_style.set_fill_color((255, 255, 255))
|
|
graph_style.set_line_width(0)
|
|
graph_style.set_description(_("Cannot edit this reference"))
|
|
default_style.add_draw_style("CG2-Title-box", graph_style)
|
|
|
|
## Draw styles
|
|
graph_style = GraphicsStyle()
|
|
graph_style.set_paragraph_style("CG2-Normal")
|
|
graph_style.set_fill_color((255, 255, 255))
|
|
graph_style.set_description(_("The style for the marriage box."))
|
|
default_style.add_draw_style("CG2-fam-box", graph_style)
|
|
|
|
graph_style = GraphicsStyle()
|
|
graph_style.set_paragraph_style("CG2-Normal")
|
|
graph_style.set_shadow(1, box_shadow)
|
|
graph_style.set_fill_color((255, 255, 255))
|
|
graph_style.set_description(_("The style for the spouse box."))
|
|
default_style.add_draw_style("CG2-box", graph_style)
|
|
|
|
graph_style = GraphicsStyle()
|
|
graph_style.set_paragraph_style("CG2-Bold")
|
|
graph_style.set_shadow(1, box_shadow)
|
|
graph_style.set_fill_color((255, 255, 255))
|
|
graph_style.set_description(
|
|
_("The style for the direct descendant box."))
|
|
default_style.add_draw_style("CG2b-box", graph_style)
|
|
|
|
graph_style = GraphicsStyle()
|
|
graph_style.set_paragraph_style("CG2-Note")
|
|
graph_style.set_fill_color((255, 255, 255))
|
|
graph_style.set_description(_("The style for the note box."))
|
|
default_style.add_draw_style("CG2-note-box", graph_style)
|
|
|
|
graph_style = GraphicsStyle()
|
|
graph_style.set_description(
|
|
_("The style for the connection lines and report border."))
|
|
default_style.add_draw_style("CG2-line", graph_style)
|