1# 2# Gramps - a GTK+/GNOME based genealogy program 3# 4# Copyright (C) 2000-2007 Donald N. Allingham 5# Copyright (C) 2007-2012 Brian G. Matherly 6# Copyright (C) 2010 Jakim Friant 7# Copyright (C) 2009-2010 Craig J. Anderson 8# Copyright (C) 2014 Paul Franklin 9# 10# This program is free software; you can redistribute it and/or modify 11# it under the terms of the GNU General Public License as published by 12# the Free Software Foundation; either version 2 of the License, or 13# (at your option) any later version. 14# 15# This program is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License for more details. 19# 20# You should have received a copy of the GNU General Public License 21# along with this program; if not, write to the Free Software 22# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23# 24 25""" 26Reports/Graphical Reports/Familial Tree 27Reports/Graphical Reports/Personal Tree 28""" 29 30#------------------------------------------------------------------------ 31# 32# Gramps modules 33# 34#------------------------------------------------------------------------ 35from gramps.gen.const import GRAMPS_LOCALE as glocale 36_ = glocale.translation.sgettext 37from gramps.gen.errors import ReportError 38from gramps.gen.plug.menu import (TextOption, NumberOption, BooleanOption, 39 EnumeratedListOption, StringOption, 40 PersonOption, FamilyOption) 41from gramps.gen.plug.report import Report, MenuReportOptions, stdoptions 42from gramps.gen.plug.report import utils 43from gramps.gen.plug.docgen import (FontStyle, ParagraphStyle, GraphicsStyle, 44 FONT_SANS_SERIF, PARA_ALIGN_CENTER) 45from gramps.plugins.lib.libtreebase import * 46from gramps.gen.proxy import CacheProxyDb 47from gramps.gen.display.name import displayer as _nd 48from gramps.gen.utils.db import family_name 49 50PT2CM = utils.pt2cm 51 52#------------------------------------------------------------------------ 53# 54# Constants 55# 56#------------------------------------------------------------------------ 57_BORN = _("birth abbreviation|b."), 58_DIED = _("death abbreviation|d."), 59_MARR = _("marriage abbreviation|m."), 60 61_RPT_NAME = 'descend_chart' 62 63#------------------------------------------------------------------------ 64# 65# Box classes 66# 67#------------------------------------------------------------------------ 68class DescendantBoxBase(BoxBase): 69 """ 70 Base for all descendant boxes. 71 Set the boxstr and some new attributes that are needed 72 """ 73 74 def __init__(self, boxstr): 75 BoxBase.__init__(self) 76 self.boxstr = boxstr 77 self.linked_box = None 78 self.father = None 79 80 def calc_text(self, database, person, family): 81 """ A single place to calculate box text """ 82 83 gui = GuiConnect() 84 calc = gui.calc_lines(database) 85 self.text = calc.calc_lines(person, family, 86 gui.working_lines(self)) 87 88class PersonBox(DescendantBoxBase): 89 """ 90 Calculates information about the box that will print on a page 91 """ 92 93 def __init__(self, level, boldable=0): 94 DescendantBoxBase.__init__(self, "CG2-box") 95 self.level = level 96 97 def set_bold(self): 98 """ update me to a bolded box """ 99 self.boxstr = "CG2b-box" 100 101class FamilyBox(DescendantBoxBase): 102 """ 103 Calculates information about the box that will print on a page 104 """ 105 106 def __init__(self, level): 107 DescendantBoxBase.__init__(self, "CG2-fam-box") 108 self.level = level 109 110class PlaceHolderBox(BoxBase): 111 """ 112 I am a box that does not print. I am used to make sure information 113 does not run over areas that we don't want information (boxes) 114 """ 115 116 def __init__(self, level): 117 BoxBase.__init__(self) 118 self.boxstr = "None" 119 self.level = level 120 self.line_to = None 121 self.linked_box = None 122 123 def calc_text(self, database, person, family): 124 """ move along. Nothing to see here """ 125 return 126 127 128#------------------------------------------------------------------------ 129# 130# Titles Class(es) 131# 132#------------------------------------------------------------------------ 133class DescendantTitleBase(TitleBox): 134 def __init__(self, dbase, doc, locale, name_displayer, 135 boxstr="CG2-Title-box"): 136 self._nd = name_displayer 137 TitleBox.__init__(self, doc, boxstr) 138 self.database = dbase 139 self._ = locale.translation.sgettext 140 141 def descendant_print(self, person_list, person_list2=[]): 142 """ calculate the Descendant title 143 Person_list will always be passed 144 If in the Family reports and there are two families, person_list2 145 will be used. 146 """ 147 148 if len(person_list) == len(person_list2) == 1: 149 person_list = person_list + person_list2 150 person_list2 = [] 151 152 names = self._get_names(person_list, self._nd) 153 154 if person_list2: 155 names2 = self._get_names(person_list2, self._nd) 156 if len(names) + len(names2) == 3: 157 if len(names) == 1: 158 title = self._("Descendant Chart for %(person)s and " 159 "%(father1)s, %(mother1)s") % { 160 'person': names[0], 161 'father1': names2[0], 162 'mother1': names2[1], 163 } 164 else: # Should be 2 items in names list 165 title = self._("Descendant Chart for %(person)s, " 166 "%(father1)s and %(mother1)s") % { 167 'father1': names[0], 168 'mother1': names[1], 169 'person': names2[0], 170 } 171 else: # Should be 2 items in both names and names2 lists 172 title = self._("Descendant Chart for %(father1)s, %(father2)s " 173 "and %(mother1)s, %(mother2)s") % { 174 'father1': names[0], 175 'mother1': names[1], 176 'father2': names2[0], 177 'mother2': names2[1], 178 } 179 else: # No person_list2: Just one family 180 if len(names) == 1: 181 title = self._( 182 "Descendant Chart for %(person)s") % {'person': names[0]} 183 else: # Should be two items in names list 184 title = self._("Descendant Chart for %(father)s and " 185 "%(mother)s") % { 186 'father': names[0], 187 'mother': names[1], 188 } 189 return title 190 191 def get_parents(self, family_id): 192 """ For a family_id, return the father and mother """ 193 194 family1 = self.database.get_family_from_gramps_id(family_id) 195 father_h = family1.get_father_handle() 196 mother_h = family1.get_mother_handle() 197 198 parents = [self.database.get_person_from_handle(handle) 199 for handle in [father_h, mother_h] if handle] 200 201 return parents 202 203class TitleNone(TitleNoDisplay): 204 """No Title class for the report """ 205 206 def __init__(self, dbase, doc, locale): 207 TitleNoDisplay.__init__(self, doc, "CG2-Title-box") 208 self._ = locale.translation.sgettext 209 210 def calc_title(self, persons): 211 """Calculate the title of the report""" 212 #we want no text, but need a text for the TOC in a book! 213 self.mark_text = self._('Descendant Graph') 214 self.text = '' 215 216class TitleDPY(DescendantTitleBase): 217 """Descendant (Person yes start with parents) Chart 218 Title class for the report """ 219 220 def __init__(self, dbase, doc, locale, name_displayer): 221 DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer) 222 223 def calc_title(self, person_id): 224 """Calculate the title of the report""" 225 226 center = self.database.get_person_from_gramps_id(person_id) 227 family2_h = center.get_main_parents_family_handle() 228 if family2_h: 229 family2 = self.database.get_family_from_handle(family2_h) 230 else: 231 family2 = None 232 233 person_list = None 234 if family2: 235 father2_h = family2.get_father_handle() 236 mother2_h = family2.get_mother_handle() 237 person_list = [self.database.get_person_from_handle(handle) 238 for handle in [father2_h, mother2_h] if handle] 239 240 if not person_list: 241 person_list = [center] 242 243 self.text = self.descendant_print(person_list) 244 self.set_box_height_width() 245 246class TitleDPN(DescendantTitleBase): 247 """Descendant (Person no start with parents) Chart 248 Title class for the report """ 249 250 def __init__(self, dbase, doc, locale, name_displayer): 251 DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer) 252 253 def calc_title(self, person_id): 254 """Calculate the title of the report""" 255 256 center = self.database.get_person_from_gramps_id(person_id) 257 258 title = self.descendant_print([center]) 259 self.text = title 260 self.set_box_height_width() 261 262class TitleDFY(DescendantTitleBase): 263 """Descendant (Family yes start with parents) Chart 264 Title class for the report """ 265 266 def __init__(self, dbase, doc, locale, name_displayer): 267 DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer) 268 269 def get_parent_list(self, person): 270 """ return a list of my parents. If none, return me """ 271 if not person: 272 return None 273 274 parent_list = None 275 family_h = person.get_main_parents_family_handle() 276 if family_h: 277 family = self.database.get_family_from_handle(family_h) 278 else: 279 family = None 280 if family: # family = fathers parents 281 father_h = family.get_father_handle() 282 mother_h = family.get_mother_handle() 283 parent_list = [self.database.get_person_from_handle(handle) 284 for handle in [father_h, mother_h] if handle] 285 286 return parent_list or [person] 287 288 def calc_title(self, family_id): 289 """Calculate the title of the report""" 290 291 my_parents = self.get_parents(family_id) 292 293 dad_parents = self.get_parent_list(my_parents[0]) 294 295 mom_parents = [] 296 if len(my_parents) > 1: 297 if not dad_parents: 298 dad_parents = self.get_parent_list(my_parents[1]) 299 else: 300 mom_parents = self.get_parent_list(my_parents[1]) 301 302 self.text = self.descendant_print(dad_parents, mom_parents) 303 self.set_box_height_width() 304 305class TitleDFN(DescendantTitleBase): 306 """Descendant (Family no start with parents) Chart 307 Title class for the report """ 308 309 def __init__(self, dbase, doc, locale, name_displayer): 310 DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer) 311 312 def calc_title(self, family_id): 313 """Calculate the title of the report""" 314 315 self.text = self.descendant_print(self.get_parents(family_id)) 316 self.set_box_height_width() 317 318class TitleF(DescendantTitleBase): 319 """Family Chart Title class for the report """ 320 321 def __init__(self, dbase, doc, locale, name_displayer): 322 DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer) 323 324 def calc_title(self, family_id): 325 """Calculate the title of the report""" 326 parents = self.get_parents(family_id) 327 328 names = self._get_names(parents, self._nd) 329 330 if len(parents) == 1: 331 title = self._( 332 "Family Chart for %(person)s") % {'person': names[0]} 333 elif len(parents) == 2: 334 title = self._( 335 "Family Chart for %(father1)s and %(mother1)s") % { 336 'father1': names[0], 'mother1': names[1]} 337 #else: 338 # title = str(tmp) + " " + str(len(tmp)) 339 self.text = title 340 self.set_box_height_width() 341 342class TitleC(DescendantTitleBase): 343 """Cousin Chart Title class for the report """ 344 345 def __init__(self, dbase, doc, locale, name_displayer): 346 DescendantTitleBase.__init__(self, dbase, doc, locale, name_displayer) 347 348 def calc_title(self, family_id): 349 """Calculate the title of the report""" 350 351 family = self.database.get_family_from_gramps_id(family_id) 352 353 kids = [self.database.get_person_from_handle(kid.ref) 354 for kid in family.get_child_ref_list()] 355 356 #ok we have the children. Make a title off of them 357 # translators: needed for Arabic, ignore otherwise 358 cousin_names = self._(', ').join(self._get_names(kids, self._nd)) 359 360 self.text = self._( 361 "Cousin Chart for %(names)s") % {'names' : cousin_names} 362 363 self.set_box_height_width() 364 365 366#------------------------------------------------------------------------ 367# 368# Class RecurseDown 369# 370#------------------------------------------------------------------------ 371class RecurseDown: 372 """ 373 The main recursive functions that will use add_person to make 374 the tree of people (Descendants) to be included within the report. 375 """ 376 def __init__(self, dbase, canvas): 377 self.database = dbase 378 self.canvas = canvas 379 380 self.families_seen = set() 381 self.cols = [] 382 self.__last_direct = [] 383 384 gui = GuiConnect() 385 self.do_parents = gui.get_val('show_parents') 386 self.max_generations = gui.get_val('maxgen') 387 self.max_spouses = gui.get_val('maxspouse') 388 self.inlc_marr = gui.get_val("inc_marr") 389 if not self.max_spouses: 390 self.inlc_marr = False 391 self.spouse_indent = gui.get_val('ind_spouse') 392 393 #is the option even available? 394 self.bold_direct = gui.get_val('bolddirect') 395 #can we bold direct descendants? 396 #bold_now will have only three values 397 #0 - no bolding 398 #1 - Only bold the first person 399 #2 - Bold all direct descendants 400 self.bold_now = 0 401 gui = None 402 403 def add_to_col(self, box): 404 """ 405 Add the box to a column on the canvas. we will do these things: 406 set the .linked_box attrib for the boxs in this col 407 get the height and width of this box and set it no the column 408 also we set the .x_cm to any s_level (indentation) here 409 we will calculate the real .x_cm later (with indentation) 410 """ 411 412 level = box.level[0] 413 #make the column list of people 414 while len(self.cols) <= level: 415 self.cols.append(None) 416 self.__last_direct.append(None) 417 418 if self.cols[level]: #if (not the first box in this column) 419 last_box = self.cols[level] 420 last_box.linked_box = box 421 422 #calculate the .y_cm for this box. 423 box.y_cm = last_box.y_cm 424 box.y_cm += last_box.height 425 if last_box.boxstr in ["CG2-box", "CG2b-box"]: 426 box.y_cm += self.canvas.report_opts.box_shadow 427 428 if box.boxstr in ["CG2-box", "CG2b-box"]: 429 box.y_cm += self.canvas.report_opts.box_pgap 430 else: 431 box.y_cm += self.canvas.report_opts.box_mgap 432 433 if box.level[1] == 0 and self.__last_direct[level]: 434 #ok, a new direct descendant. 435 #print level, box.father is not None, \ 436 # self.__last_direct[level].father is not None, box.text[0], \ 437 # self.__last_direct[level].text[0] 438 if box.father != self.__last_direct[level].father and \ 439 box.father != self.__last_direct[level]: 440 box.y_cm += self.canvas.report_opts.box_pgap 441 442 self.cols[level] = box 443 if box.level[1] == 0: 444 self.__last_direct[level] = box 445 446 if self.spouse_indent: 447 box.x_cm = self.canvas.report_opts.spouse_offset * box.level[1] 448 else: 449 box.x_cm = 0.0 450 451 self.canvas.set_box_height_width(box) 452 453 def add_person_box(self, level, indi_handle, fams_handle, father): 454 """ Makes a person box and add that person into the Canvas. """ 455 myself = PersonBox(level) 456 myself.father = father 457 458 if myself.level[1] == 0 and self.bold_direct and self.bold_now: 459 if self.bold_now == 1: 460 self.bold_now = 0 461 myself.set_bold() 462 463 if level[1] == 0 and father and myself.level[0] != father.level[0]: 464 #I am a child 465 if father.line_to: 466 line = father.line_to 467 else: 468 line = LineBase(father) 469 father.line_to = line 470 #self.canvas.add_line(line) 471 472 line.end.append(myself) 473 474 #calculate the text. 475 myself.calc_text(self.database, indi_handle, fams_handle) 476 477 if indi_handle: 478 myself.add_mark(self.database, 479 self.database.get_person_from_handle(indi_handle)) 480 481 self.add_to_col(myself) 482 483 self.canvas.add_box(myself) 484 485 return myself 486 487 def add_marriage_box(self, level, indi_handle, fams_handle, father): 488 """ Makes a marriage box and add that person into the Canvas. """ 489 myself = FamilyBox(level) 490 491 #if father is not None: 492 # myself.father = father 493 #calculate the text. 494 myself.calc_text(self.database, indi_handle, fams_handle) 495 496 self.add_to_col(myself) 497 498 self.canvas.add_box(myself) 499 500 return myself 501 502 def recurse(self, person_handle, x_level, s_level, father): 503 """traverse the ancestors recursively until 504 either the end of a line is found, 505 or until we reach the maximum number of generations 506 or we reach the max number of spouses 507 that we want to deal with""" 508 509 if not person_handle: 510 return 511 if x_level > self.max_generations: 512 return 513 if s_level > 0 and s_level == self.max_spouses: 514 return 515 if person_handle in self.families_seen: 516 return 517 518 myself = None 519 person = self.database.get_person_from_handle(person_handle) 520 family_handles = person.get_family_handle_list() 521 if s_level == 0: 522 val = family_handles[0] if family_handles else None 523 myself = self.add_person_box((x_level, s_level), 524 person_handle, val, father) 525 526 marr = None 527 spouse = None 528 529 if s_level == 1: 530 tmp_bold = self.bold_now 531 self.bold_now = 0 532 533 for family_handle in family_handles: 534 if family_handle not in self.families_seen: 535 self.families_seen.add(family_handle) 536 537 family = self.database.get_family_from_handle(family_handle) 538 539 #Marriage box if the option is there. 540 if self.inlc_marr and self.max_spouses > 0: 541 marr = self.add_marriage_box((x_level, s_level+1), 542 person_handle, family_handle, 543 father if s_level else myself) 544 545 spouse_handle = utils.find_spouse(person, family) 546 if (self.max_spouses > s_level and 547 spouse_handle not in self.families_seen): 548 def _spouse_box(who): 549 return self.add_person_box((x_level, s_level+1), 550 spouse_handle, 551 family_handle, who) 552 if s_level > 0: 553 spouse = _spouse_box(father) 554 elif self.inlc_marr: 555 spouse = _spouse_box(marr) 556 else: 557 spouse = _spouse_box(myself) 558 559 mykids = [kid.ref for kid in family.get_child_ref_list()] 560 561 def _child_recurse(who): 562 self.recurse(child_ref, x_level+1, 0, who) 563 for child_ref in mykids: 564 if self.inlc_marr and self.max_spouses > 0: 565 _child_recurse(marr) 566 elif spouse: 567 _child_recurse(spouse) 568 else: 569 _child_recurse(myself) 570 571 if self.max_spouses > s_level and \ 572 spouse_handle not in self.families_seen: 573 #spouse_handle = utils.find_spouse(person,family) 574 self.recurse(spouse_handle, x_level, s_level+1, spouse) 575 576 if s_level == 1: 577 self.bold_now = tmp_bold 578 579 def add_family(self, level, family, father2): 580 """ 581 Adds a family into the canvas. 582 only will be used for my direct grandparents, and my parents only. 583 """ 584 585 family_h = family.get_handle() 586 father_h = family.get_father_handle() 587 mother_h = family.get_mother_handle() 588 589 self.bold_now = 2 590 if father_h: 591 father_b = self.add_person_box( 592 (level, 0), father_h, family_h, father2) 593 else: 594 father_b = self.add_person_box( 595 (level, 0), None, None, father2) 596 retrn = [father_b] 597 598 if self.inlc_marr: 599 family_b = self.add_marriage_box( 600 (level, 1), father_h, family_h, father_b) 601 retrn.append(family_b) 602 self.families_seen.add(family_h) 603 604 if mother_h: 605 mother_b = self.add_person_box( 606 (level, 0), mother_h, family_h, father_b) 607 else: 608 mother_b = self.add_person_box( 609 (level, 0), None, None, father_b) 610 retrn.append(mother_b) 611 612 family_line = family_b if self.inlc_marr else father_b 613 for child_ref in family.get_child_ref_list(): 614 self.recurse(child_ref.ref, level+1, 0, family_line) 615 616 self.bold_now = 0 617 618 #Set up the lines for the family 619 if not family_line.line_to: 620 #no children. 621 family_line.line_to = LineBase(family_line) 622 if self.inlc_marr: 623 family_line.line_to.start.append(father_b) 624 family_line.line_to.start.append(mother_b) 625 626 return retrn 627 628 def has_children(self, person_handle): 629 """ 630 Quickly check to see if this person has children 631 still we want to respect the FamiliesSeen list 632 """ 633 634 if not person_handle or person_handle in self.families_seen: 635 return False 636 637 person = self.database.get_person_from_handle(person_handle) 638 639 for family_handle in person.get_family_handle_list(): 640 if family_handle not in self.families_seen: 641 642 family = self.database.get_family_from_handle(family_handle) 643 644 if family.get_child_ref_list(): 645 return True 646 return False 647 648 def recurse_if(self, person_handle, level): 649 """ 650 Quickly check to see if we want to continue recursion 651 still we want to respect the FamiliesSeen list 652 """ 653 654 person = self.database.get_person_from_handle(person_handle) 655 656 show = False 657 myfams = person.get_family_handle_list() 658 if len(myfams) > 1: #and self.max_spouses > 0 659 show = True 660 if not self.inlc_marr: 661 #if the condition is true, we only want to show 662 #this parent again IF s/he has other children 663 show = self.has_children(person_handle) 664 665 #if self.max_spouses == 0 and not self.has_children(person_handle): 666 # self.families_seen.add(person_handle) 667 # show = False 668 669 if show: 670 self.bold_now = 1 671 self.recurse(person_handle, level, 0, None) 672 673 674#------------------------------------------------------------------------ 675# 676# Class MakePersonTree (Personal Descendant Tree option) 677# 678#------------------------------------------------------------------------ 679class MakePersonTree(RecurseDown): 680 """ 681 The main procedure to use recursion to make the tree based off of a person. 682 order of people inserted into Persons is important. 683 makes sure that order is done correctly. 684 """ 685 def __init__(self, dbase, canvas): 686 RecurseDown.__init__(self, dbase, canvas) 687 self.max_generations -= 1 688 689 def start(self, person_id): 690 """follow the steps to make a tree off of a person""" 691 persons = [] 692 693 center1 = self.database.get_person_from_gramps_id(person_id) 694 if center1 is None: 695 raise ReportError(_("Person %s is not in the Database") % person_id) 696 center1_h = center1.get_handle() #could be mom too. 697 698 family2 = family2_h = None 699 if self.do_parents: 700 family2_h = center1.get_main_parents_family_handle() 701 if family2_h: 702 family2 = self.database.get_family_from_handle(family2_h) 703 704 mother2_h = father2_h = None 705 if family2: 706 father2_h = family2.get_father_handle() 707 mother2_h = family2.get_mother_handle() 708 709 710 ####################### 711 #don't do center person's parents family. 712 if family2_h: 713 self.families_seen.add(family2_h) 714 715 ####################### 716 #Center person's Fathers OTHER wives 717 ####################### 718 #update to only run if he HAD other wives! 719 if father2_h: 720 self.recurse_if(father2_h, 0) 721 722 ####################### 723 #Center persons parents only! 724 ####################### 725 #now it will ONLY be my fathers parents 726 if family2: 727 self.add_family(0, family2, None) 728 else: 729 self.bold_now = 2 730 self.recurse(center1_h, 0, 0, None) 731 self.bold_now = 0 732 733 ####################### 734 #Center person's mothers OTHER husbands 735 ####################### 736 #update to only run if she HAD other husbands! 737 if mother2_h: 738 self.recurse_if(mother2_h, 0) 739 740 return persons 741 742#------------------------------------------------------------------------ 743# 744# Class MakeFamilyTree (Familial Descendant Tree option) 745# 746#------------------------------------------------------------------------ 747class MakeFamilyTree(RecurseDown): 748 """ 749 The main procedure to use recursion to make the tree based off of a family. 750 order of people inserted into Persons is important. 751 makes sure that order is done correctly. 752 """ 753 754 def __init__(self, dbase, canvas): 755 RecurseDown.__init__(self, dbase, canvas) 756 757 def start(self, family_id): 758 """follow the steps to make a tree off of a family""" 759 ## (my) referes to the children of family_id 760 # Step 1 print out my fathers, fathers, 761 # other wives families first (if needed) 762 family1 = self.database.get_family_from_gramps_id(family_id) 763 if family1 is None: 764 raise ReportError(_("Family %s is not in the Database") % family_id) 765 family1_h = family1.get_handle() 766 767 ####################### 768 #Initial setup of variables 769 ####################### 770 father1_h = family1.get_father_handle() 771 mother1_h = family1.get_mother_handle() 772 773 father1 = mother1 = family2 = family2_h = None 774 if father1_h: 775 father1 = self.database.get_person_from_handle(father1_h) 776 if self.do_parents: #b3 - remove grandparents? 777 family2_h = father1.get_main_parents_family_handle() 778 if family2_h: 779 family2 = self.database.get_family_from_handle(family2_h) 780 if mother1_h: 781 mother1 = self.database.get_person_from_handle(mother1_h) 782 783 mother2_h = father2_h = father2 = mother2 = None 784 if family2: #family2 = fathers parents 785 mother2_h = family2.get_mother_handle() 786 if mother2_h: 787 mother2 = self.database.get_person_from_handle(mother2_h) 788 father2_h = family2.get_father_handle() 789 if father2_h: 790 father2 = self.database.get_person_from_handle(father2_h) 791 792 #Helper variables. Assigned in one section, used in another. 793 father2_id = family2_id = None 794 mother1_id = None 795 796 ####################### 797 #don't do my fathers parents family. will be done later 798 if family2_h: 799 self.families_seen.add(family2_h) 800 801 ####################### 802 #my father mothers OTHER husbands 803 ####################### 804 #update to only run if she HAD other husbands! 805 if mother2_h: 806 self.recurse_if(mother2_h, 0) 807 808 ####################### 809 #father Fathers OTHER wives 810 ####################### 811 #update to only run if he HAD other wives! 812 if father2_h: 813 self.recurse_if(father2_h, 0) 814 815 ####################### 816 #don't do my parents family in recurse. will be done later 817 self.families_seen.add(family1_h) 818 ##If dad has no other children from other marriages. remove him 819 if self.max_spouses == 0 and not self.has_children(father1_h): 820 self.families_seen.add(father1_h) 821 822 ####################### 823 #my fathers parents! 824 ####################### 825 #now it will ONLY be my fathers parents 826 #will print dads parents. dad's other wifes will also print 827 if family2: 828 myfams = father1.get_family_handle_list() 829 show = False 830 if len(myfams) > 1: 831 show = True 832 if not self.inlc_marr and self.max_spouses == 0: 833 #if the condition is true, we only want to show 834 #this parent again IF s/he has children 835 show = self.has_children(father1_h) 836 if not show: 837 self.families_seen.add(father1_h) 838 839 family2_l = self.add_family(0, family2, None) 840 841 elif father1: 842 ####################### 843 #my father other wives (if all of the above does nothing) 844 #if my father does not have parents (he is the highest) 845 ####################### 846 #do his OTHER wives first. 847 self.recurse_if(father1_h, 1) 848 849 850 ####################### 851 #my father, marriage info, mother, siblings, me 852 ####################### 853 if family2: 854 #We need to add dad to the family 855 family2_line = family2_l[1] if self.inlc_marr else family2_l[0] 856 else: 857 family2_line = None 858 859 family1_l = self.add_family(1, family1, family2_line) 860 mother1_b = family1_l[-1] #Mom's Box 861 862 #make sure there is at least one child in this family. 863 #if not put in a placeholder 864 family1_line = family1_l[1] if self.inlc_marr else family1_l[0] 865 if family1_line.line_to.end == []: 866 box = PlaceHolderBox((mother1_b.level[0]+1, 0)) 867 box.father = family1_l[0] 868 self.add_to_col(box) 869 family1_line.line_to.end = [box] 870 871 ####################### 872 ####################### 873 #Lower half 874 #This will be quite like the first half. 875 #Just on the mothers side... 876 #Mom has already been printed with the family 877 ####################### 878 ####################### 879 880 ####################### 881 #Initial setup of variables 882 ####################### 883 mother1_h = family1.get_mother_handle() 884 family2_h = mother1 = family2 = None 885 if mother1_h: 886 mother1 = self.database.get_person_from_handle(mother1_h) 887 if self.do_parents: #b3 - remove grandparents? 888 family2_h = mother1.get_main_parents_family_handle() 889 if family2_h: 890 family2 = self.database.get_family_from_handle(family2_h) 891 892 mother2_h = father2_h = mother2 = father2 = None 893 if family2: 894 mother2_h = family2.get_mother_handle() 895 if mother2_h: 896 mother2 = self.database.get_person_from_handle(mother2_h) 897 father2_h = family2.get_father_handle() 898 if father2_h: 899 father2 = self.database.get_person_from_handle(father2_h) 900 901 ####################### 902 #don't do my parents family. 903 self.families_seen = set([family1_h]) 904 ##If mom has no other children from other marriages. remove her 905 if self.max_spouses == 0 and not self.has_children(mother1_h): 906 self.families_seen.add(mother1_h) 907 908 if mother1_h: 909 myfams = mother1.get_family_handle_list() 910 if len(myfams) < 2: 911 #If mom didn't have any other families, don't even do her 912 #she is already here with dad and will be added later 913 self.families_seen.add(mother1_h) 914 915 ####################### 916 #my mother other spouses (if no parents) 917 ####################### 918 #if my mother does not have parents (she is the highest) 919 #Then do her OTHER spouses. 920 if not family2 and mother1: 921 self.recurse_if(mother1_h, 1) 922 923 ####################### 924 #my mothers parents! 925 ####################### 926 if family2: 927 family2_l = self.add_family(0, family2, None) 928 family2_line = family2_l[1] if self.inlc_marr else family2_l[0] 929 930 family2_line = family2_line.line_to 931 if family2_line.end != []: 932 family2_line.end.insert(0, mother1_b) 933 else: 934 family2_line.end = [mother1_b] 935 936 #fix me. Moms other siblings have been given an extra space 937 #Because Moms-father is not siblings-father right now. 938 939 mother1_b.father = family2_line 940 941 ####################### 942 #my mother mothers OTHER husbands 943 ####################### 944 #update to only run if she HAD other husbands! 945 if mother2_h: 946 self.recurse_if(mother2_h, 0) 947 948 ####################### 949 #mother Fathers OTHER wives 950 ####################### 951 #update to only run if he HAD other wives! 952 if father2_h: 953 self.recurse_if(father2_h, 0) 954 955 956#------------------------------------------------------------------------ 957# 958# Class MakeReport 959# 960#------------------------------------------------------------------------ 961class MakeReport: 962 """ 963 Make a report out of a list of people. 964 The list of people is already made. Use this information to find where 965 people will be placed on the canvas. 966 """ 967 968 def __init__(self, dbase, canvas, ind_spouse, compress_tree): 969 self.database = dbase 970 self.canvas = canvas 971 972 gui = GuiConnect() 973 self.do_parents = gui.get_val('show_parents') 974 self.inlc_marr = gui.get_val("inc_marr") 975 self.max_spouses = gui.get_val('maxspouse') 976 gui = None 977 978 self.ind_spouse = ind_spouse 979 self.compress_tree = compress_tree 980 self.cols = [[]] 981 #self.max_generations = 0 982 983 #already done in recurse, 984 #Some of this code needs to be moved up to RecurseDown.add_to_col() 985 def calc_box(self, box): 986 """ calculate the max_box_width and max_box_height for the report """ 987 width = box.x_cm + box.width 988 if width > self.canvas.report_opts.max_box_width: 989 self.canvas.report_opts.max_box_width = width 990 991 if box.height > self.canvas.report_opts.max_box_height: 992 self.canvas.report_opts.max_box_height = box.height 993 994 while len(self.cols) <= box.level[0]: 995 self.cols.append([]) 996 997 self.cols[box.level[0]].append(box) 998 999 #tmp = box.level[0] 1000 #if tmp > self.max_generations: 1001 # self.max_generations = tmp 1002 1003 def __move_col_from_here_down(self, box, amount): 1004 """Move me and everyone below me in this column only down""" 1005 while box: 1006 box.y_cm += amount 1007 box = box.linked_box 1008 1009 def __move_next_cols_from_here_down(self, box, amount): 1010 """Move me, everyone below me in this column, 1011 and all of our children (and childrens children) down.""" 1012 col = [box] 1013 while col: 1014 if len(col) == 1 and col[0].line_to: 1015 col.append(col[0].line_to.end[0]) 1016 1017 col[0].y_cm += amount 1018 1019 col[0] = col[0].linked_box 1020 if col[0] is None: 1021 col.pop(0) 1022 1023 def __next_family_group(self, box): 1024 """ a helper function. Assume box is at the start of a family block. 1025 get this family block. """ 1026 while box: 1027 left_group = [] 1028 line = None 1029 1030 #Form the parental (left) group. 1031 #am I a direct descendant? 1032 if box.level[1] == 0: 1033 #I am the father/mother. 1034 left_group.append(box) 1035 if box.line_to: 1036 line = box.line_to 1037 box = box.linked_box 1038 1039 if box and box.level[1] != 0 and self.inlc_marr: 1040 #add/start with the marriage box 1041 left_group.append(box) 1042 if box.line_to: 1043 line = box.line_to 1044 box = box.linked_box 1045 1046 if box and box.level[1] != 0 and self.max_spouses > 0: 1047 #add/start with the spousal box 1048 left_group.append(box) 1049 if box.line_to: 1050 line = box.line_to 1051 box = box.linked_box 1052 1053 if line: 1054 if len(line.start) > 1 and line.start[-1].level[1] == 0: 1055 #a dad and mom family from RecurseDown.add_family. add mom 1056 left_group.append(line.start[-1]) 1057 box = box.linked_box 1058 1059 #we now have everyone we want 1060 return left_group, line.end 1061 #else 1062 # no children, so no family. go again until we find one to return. 1063 1064 return None, None 1065 1066 def __reverse_family_group(self): 1067 """ go through the n-1 to 0 cols of boxes looking for families 1068 (parents with children) that may need to be moved. """ 1069 for x_col in range(len(self.cols)-1, -1, -1): 1070 box = self.cols[x_col][0] #The first person in this col 1071 while box: 1072 left_group, right_group = self.__next_family_group(box) 1073 if not left_group: 1074 box = None #we found the end of this col 1075 else: 1076 yield left_group, right_group 1077 box = left_group[-1].linked_box 1078 1079 def __calc_movements(self, left_group, right_group): 1080 """ for a family group, see if parents or children need to be 1081 moved down so everyone is to the right/left of each other. 1082 1083 return a right y_cm and a left y_cm. these points will be used 1084 to move parents/children down. 1085 """ 1086 left_up = left_group[0].y_cm 1087 right_up = right_group[0].y_cm 1088 1089 left_center = left_up 1090 right_center = right_up 1091 1092 if self.compress_tree: 1093 #calculate a new left and right move points 1094 for left_line in left_group: 1095 if left_line.line_to: 1096 break 1097 left_center = left_line.y_cm + (left_line.height /2) 1098 1099 left_down = left_group[-1].y_cm + left_group[-1].height 1100 right_down = right_group[-1].y_cm + right_group[-1].height 1101 1102 #Lazy. Move down either side only as much as we NEED to. 1103 if left_center < right_up: 1104 right_center = right_group[0].y_cm 1105 elif left_up == right_up: 1106 left_center = left_up #Lets keep it. top line. 1107 elif left_center > right_down: 1108 right_center = right_down 1109 else: 1110 right_center = left_center 1111 1112 return right_center, left_center 1113 1114 def Make_report(self): 1115 """ 1116 Everyone on the page is as far up as they can go. 1117 Move them down to where they belong. 1118 1119 We are going to go through everyone from right to left 1120 top to bottom moving everyone down as needed to make the report. 1121 """ 1122 seen_parents = False 1123 1124 for left_group, right_group in self.__reverse_family_group(): 1125 right_y_cm, left_y_cm = self.__calc_movements(left_group, 1126 right_group) 1127 1128 #1. Are my children too high? if so move then down! 1129 if right_y_cm < left_y_cm: 1130 #we have to push our kids (and their kids) down. 1131 #We also need to push down all the kids (under) 1132 #these kids (in their column) 1133 amt = (left_y_cm - right_y_cm) 1134 self.__move_next_cols_from_here_down(right_group[0], amt) 1135 1136 #2. Am I (and spouses) too high? if so move us down! 1137 elif left_y_cm < right_y_cm: 1138 #Ok, I am too high. Move me down 1139 amt = (right_y_cm - left_y_cm) 1140 self.__move_col_from_here_down(left_group[0], amt) 1141 1142 #6. now check to see if we are working with dad and mom. 1143 #if so we need to move down marriage information 1144 #and mom! 1145 left_line = left_group[0].line_to 1146 if not left_line: 1147 left_line = left_group[1].line_to 1148 #left_line = left_line.start 1149 1150 if len(left_line.start) > 1 and not seen_parents: 1151 #only do Dad and Mom. len(left_line) > 1 1152 seen_parents = True 1153 1154 mom_cm = left_group[-1].y_cm + left_group[-1].height/2 1155 last_child_cm = right_group[-1].y_cm 1156 if not self.compress_tree: 1157 last_child_cm += right_group[-1].height/2 1158 move_amt = last_child_cm - mom_cm 1159 1160 #if the moms height is less than the last childs height 1161 #The 0.2 is to see if this is even worth it. 1162 if move_amt > 0.2: 1163 #our children take up more space than us parents. 1164 #so space mom out! 1165 self.__move_col_from_here_down(left_group[-1], move_amt) 1166 1167 #move marriage info 1168 if self.inlc_marr: 1169 left_group[1].y_cm += move_amt/2 1170 1171 if left_line.end[0].boxstr == 'None': 1172 left_line.end = [] 1173 1174 def start(self): 1175 """Make the report""" 1176 #for person in self.persons.depth_first_gen(): 1177 for box in self.canvas.boxes: 1178 self.calc_box(box) 1179 #At this point we know everything we need to make the report. 1180 #Width of each column of people - self.rept_opt.box_width 1181 #width of each column (or row) of lines - self.rept_opt.col_width 1182 1183 if not self.cols[0]: 1184 #We wanted to print parents of starting person/family but 1185 #there were none! 1186 #remove column 0 and move everyone back one level 1187 self.cols.pop(0) 1188 for box in self.canvas.boxes: 1189 box.level = (box.level[0] - 1, box.level[1]) 1190 1191 #go ahead and set it now. 1192 width = self.canvas.report_opts.max_box_width 1193 for box in self.canvas.boxes: 1194 box.width = width - box.x_cm 1195 box.x_cm += self.canvas.report_opts.littleoffset 1196 box.x_cm += (box.level[0] * 1197 (self.canvas.report_opts.col_width + 1198 self.canvas.report_opts.max_box_width)) 1199 1200 box.y_cm += self.canvas.report_opts.littleoffset 1201 box.y_cm += self.canvas.title.height 1202 1203 self.Make_report() 1204 1205 1206class GuiConnect: 1207 """ This is a BORG object. There is ONLY one. 1208 This give some common routines that EVERYONE can use like 1209 get the value from a GUI variable 1210 """ 1211 1212 __shared_state = {} 1213 def __init__(self): #We are BORG! 1214 self.__dict__ = self.__shared_state 1215 1216 def set__opts(self, options, which, locale, name_displayer): 1217 self._opts = options 1218 self._which_report = which.split(",")[0] 1219 self._locale = locale 1220 self._nd = name_displayer 1221 1222 def get_val(self, val): 1223 """ Get a GUI value. """ 1224 value = self._opts.get_option_by_name(val) 1225 if value: 1226 return value.get_value() 1227 else: 1228 False 1229 1230 def Title_class(self, database, doc): 1231 Title_type = self.get_val('report_title') 1232 if Title_type == 0: #None 1233 return TitleNone(database, doc, self._locale) 1234 1235 if Title_type == 1: #Descendant Chart 1236 if self._which_report == _RPT_NAME: 1237 if self.get_val('show_parents'): 1238 return TitleDPY(database, doc, self._locale, self._nd) 1239 else: 1240 return TitleDPN(database, doc, self._locale, self._nd) 1241 else: 1242 if self.get_val('show_parents'): 1243 return TitleDFY(database, doc, self._locale, self._nd) 1244 else: 1245 return TitleDFN(database, doc, self._locale, self._nd) 1246 1247 if Title_type == 2: 1248 return TitleF(database, doc, self._locale, self._nd) 1249 else: #Title_type == 3 1250 return TitleC(database, doc, self._locale, self._nd) 1251 1252 def Make_Tree(self, database, canvas): 1253 if self._which_report == _RPT_NAME: 1254 return MakePersonTree(database, canvas) 1255 else: 1256 return MakeFamilyTree(database, canvas) 1257 1258 def calc_lines(self, database): 1259 #calculate the printed lines for each box 1260 display_repl = self.get_val("replace_list") 1261 #str = "" 1262 #if self.get_val('miss_val'): 1263 # str = "_____" 1264 return CalcLines(database, display_repl, self._locale, self._nd) 1265 1266 def working_lines(self, box): 1267 display = self.get_val("descend_disp") 1268 #if self.get_val('diffspouse'): 1269 display_spou = self.get_val("spouse_disp") 1270 #else: 1271 # display_spou = display 1272 display_marr = [self.get_val("marr_disp")] 1273 1274 if box.boxstr == "CG2-fam-box": #((((( 1275 workinglines = display_marr 1276 elif box.level[1] > 0 or (box.level[0] == 0 and box.father): 1277 workinglines = display_spou 1278 else: 1279 workinglines = display 1280 return workinglines 1281 1282 1283#------------------------------------------------------------------------ 1284# 1285# DescendTree 1286# 1287#------------------------------------------------------------------------ 1288class DescendTree(Report): 1289 1290 def __init__(self, database, options, user): 1291 """ 1292 Create DescendTree object that produces the report. 1293 The arguments are: 1294 1295 database - the Gramps database instance 1296 options - instance of the Options class for this report 1297 user - a gen.user.User() instance 1298 1299 incl_private - Whether to include private data 1300 living_people - How to handle living people 1301 years_past_death - Consider as living this many years after death 1302 """ 1303 Report.__init__(self, database, options, user) 1304 1305 self.options = options 1306 1307 self.set_locale(options.menu.get_option_by_name('trans').get_value()) 1308 stdoptions.run_date_format_option(self, options.menu) 1309 stdoptions.run_private_data_option(self, options.menu) 1310 stdoptions.run_living_people_option(self, options.menu, self._locale) 1311 self.database = CacheProxyDb(self.database) 1312 stdoptions.run_name_format_option(self, options.menu) 1313 self._nd = self._name_display 1314 1315 def begin_report(self): 1316 """ make the report in its full size and pages to print on 1317 scale one or both as needed/desired. 1318 """ 1319 1320 database = self.database 1321 1322 self.Connect = GuiConnect() 1323 self.Connect.set__opts(self.options.menu, self.options.name, 1324 self._locale, self._nd) 1325 1326 style_sheet = self.doc.get_style_sheet() 1327 font_normal = style_sheet.get_paragraph_style("CG2-Normal").get_font() 1328 1329 #The canvas that we will put our report on and print off of 1330 self.canvas = Canvas(self.doc, 1331 ReportOptions(self.doc, font_normal, "CG2-line")) 1332 1333 self.canvas.report_opts.box_shadow *= \ 1334 self.Connect.get_val('shadowscale') 1335 self.canvas.report_opts.box_pgap *= self.Connect.get_val('box_Yscale') 1336 self.canvas.report_opts.box_mgap *= self.Connect.get_val('box_Yscale') 1337 1338 center_id = self.Connect.get_val('pid') 1339 1340 #make the tree 1341 tree = self.Connect.Make_Tree(database, self.canvas) 1342 tree.start(center_id) 1343 tree = None 1344 1345 #Title 1346 title = self.Connect.Title_class(database, self.doc) 1347 title.calc_title(center_id) 1348 self.canvas.add_title(title) 1349 1350 #make the report as big as it wants to be. 1351 ind_spouse = self.Connect.get_val("ind_spouse") 1352 compress_tree = self.Connect.get_val('compress_tree') 1353 report = MakeReport(database, self.canvas, ind_spouse, compress_tree) 1354 report.start() 1355 report = None 1356 1357 #note? 1358 if self.Connect.get_val("inc_note"): 1359 note_box = NoteBox(self.doc, "CG2-note-box", 1360 self.Connect.get_val("note_place")) 1361 subst = SubstKeywords(self.database, self._locale, self._nd, 1362 None, None) 1363 note_box.text = subst.replace_and_clean( 1364 self.Connect.get_val('note_disp')) 1365 self.canvas.add_note(note_box) 1366 1367 #Now we have the report in its full size. 1368 #Do we want to scale the report? 1369 one_page = self.Connect.get_val("resize_page") 1370 scale_report = self.Connect.get_val("scale_tree") 1371 1372 scale = self.canvas.scale_report(one_page, 1373 scale_report != 0, scale_report == 2) 1374 1375 if scale != 1 or self.Connect.get_val('shadowscale') != 1.0: 1376 self.scale_styles(scale) 1377 1378 def write_report(self): 1379 """ Canvas now has everyone ready to print. Get some misc stuff 1380 together and print. """ 1381 1382 one_page = self.Connect.get_val("resize_page") 1383 scale_report = self.Connect.get_val("scale_tree") 1384 1385 #Inlc_marr = self.Connect.get_val("inc_marr") 1386 inc_border = self.Connect.get_val('inc_border') 1387 incblank = self.Connect.get_val("inc_blank") 1388 prnnum = self.Connect.get_val("inc_pagenum") 1389 #ind_spouse = self.Connect.get_val("ind_spouse") 1390 lines = self.Connect.get_val('note_disp') 1391 1392 ##################### 1393 #Setup page information 1394 1395 colsperpage = self.doc.get_usable_width() 1396 colsperpage += self.canvas.report_opts.col_width 1397 tmp = self.canvas.report_opts.max_box_width 1398 tmp += self.canvas.report_opts.col_width 1399 colsperpage = int(colsperpage / tmp) 1400 colsperpage = colsperpage or 1 1401 1402 ##################### 1403 #Vars 1404 #p = self.doc.get_style_sheet().get_paragraph_style("CG2-Normal") 1405 #font = p.get_font() 1406 if prnnum: 1407 page_num_box = PageNumberBox(self.doc, 'CG2-box', self._locale) 1408 1409 ##################### 1410 #ok, everyone is now ready to print on the canvas. Paginate? 1411 self.canvas.sort_boxes_on_y_cm() 1412 self.canvas.paginate(colsperpage, one_page) 1413 1414 ##################### 1415 #Yeah!!! 1416 #lets finally make some pages!!! 1417 ##################### 1418 for page in self.canvas.page_iter_gen(incblank): 1419 1420 self.doc.start_page() 1421 1422 #do we need to print a border? 1423 if inc_border: 1424 page.draw_border('CG2-line') 1425 1426 #Do we need to print the page number? 1427 if prnnum: 1428 page_num_box.display(page) 1429 1430 page.display() 1431 1432 self.doc.end_page() 1433 1434 1435 def scale_styles(self, amount): 1436 """ 1437 Scale the styles for this report. This must be done in the constructor. 1438 """ 1439 style_sheet = self.doc.get_style_sheet() 1440 1441 graph_style = style_sheet.get_draw_style("CG2-fam-box") 1442 graph_style.set_shadow(graph_style.get_shadow(), 0) 1443 graph_style.set_line_width(graph_style.get_line_width() * amount) 1444 style_sheet.add_draw_style("CG2-fam-box", graph_style) 1445 1446 graph_style = style_sheet.get_draw_style("CG2-box") 1447 graph_style.set_shadow(graph_style.get_shadow(), 1448 self.canvas.report_opts.box_shadow * amount) 1449 graph_style.set_line_width(graph_style.get_line_width() * amount) 1450 style_sheet.add_draw_style("CG2-box", graph_style) 1451 1452 graph_style = style_sheet.get_draw_style("CG2b-box") 1453 graph_style.set_shadow(graph_style.get_shadow(), 1454 self.canvas.report_opts.box_shadow * amount) 1455 graph_style.set_line_width(graph_style.get_line_width() * amount) 1456 style_sheet.add_draw_style("CG2b-box", graph_style) 1457 1458 graph_style = style_sheet.get_draw_style("CG2-note-box") 1459 graph_style.set_shadow(graph_style.get_shadow(), 0) 1460 graph_style.set_line_width(graph_style.get_line_width() * amount) 1461 style_sheet.add_draw_style("CG2-note-box", graph_style) 1462 1463 para_style = style_sheet.get_paragraph_style("CG2-Title") 1464 font = para_style.get_font() 1465 font.set_size(font.get_size() * amount) 1466 para_style.set_font(font) 1467 style_sheet.add_paragraph_style("CG2-Title", para_style) 1468 1469 para_style = style_sheet.get_paragraph_style("CG2-Normal") 1470 font = para_style.get_font() 1471 font.set_size(font.get_size() * amount) 1472 para_style.set_font(font) 1473 style_sheet.add_paragraph_style("CG2-Normal", para_style) 1474 1475 para_style = style_sheet.get_paragraph_style("CG2-Bold") 1476 font = para_style.get_font() 1477 font.set_bold(True) 1478 font.set_size(font.get_size() * amount) 1479 para_style.set_font(font) 1480 style_sheet.add_paragraph_style("CG2-Bold", para_style) 1481 1482 para_style = style_sheet.get_paragraph_style("CG2-Note") 1483 font = para_style.get_font() 1484 font.set_size(font.get_size() * amount) 1485 para_style.set_font(font) 1486 style_sheet.add_paragraph_style("CG2-Note", para_style) 1487 1488 self.doc.set_style_sheet(style_sheet) 1489 1490 1491#------------------------------------------------------------------------ 1492# 1493# DescendTreeOptions 1494# 1495#------------------------------------------------------------------------ 1496class DescendTreeOptions(MenuReportOptions): 1497 1498 """ 1499 Defines options and provides handling interface. 1500 """ 1501 1502 def __init__(self, name, dbase): 1503 self.__pid = None 1504 self.__onepage = None 1505 self.__inc_title = None 1506 self.__title = None 1507 self.__blank = None 1508 self.scale = None 1509 self.__db = dbase 1510 self.name = name 1511 self.box_Y_sf = None 1512 self.box_shadow_sf = None 1513 MenuReportOptions.__init__(self, name, dbase) 1514 1515 def get_subject(self): 1516 """ Return a string that describes the subject of the report. """ 1517 gid = self.__pid.get_value() 1518 if self.name.split(",")[0] == _RPT_NAME: 1519 person = self.__db.get_person_from_gramps_id(gid) 1520 if person: 1521 return _nd.display(person) 1522 else: 1523 family = self.__db.get_family_from_gramps_id(gid) 1524 if family: 1525 return family_name(family, self.__db) 1526 return "" 1527 1528 def add_menu_options(self, menu): 1529 """ 1530 Add options to the menu for the descendant report. 1531 """ 1532 ################## 1533 category_name = _("Tree Options") 1534 1535 if self.name.split(",")[0] == _RPT_NAME: 1536 self.__pid = PersonOption(_("Report for")) 1537 self.__pid.set_help(_("The main person for the report")) 1538 menu.add_option(category_name, "pid", self.__pid) 1539 else: #if self.name == "familial_descend_tree": 1540 self.__pid = FamilyOption(_("Report for")) 1541 self.__pid.set_help(_("The main family for the report")) 1542 menu.add_option(category_name, "pid", self.__pid) 1543 1544 max_gen = NumberOption(_("Generations"), 10, 1, 50) 1545 max_gen.set_help(_("The number of generations to include in the tree")) 1546 menu.add_option(category_name, "maxgen", max_gen) 1547 1548 max_spouse = NumberOption(_("Level of Spouses"), 1, 0, 10) 1549 max_spouse.set_help(_("0=no Spouses, 1=include Spouses, 2=include " 1550 "Spouses of the spouse, etc")) 1551 menu.add_option(category_name, "maxspouse", max_spouse) 1552 1553 self.showparents = BooleanOption( 1554 _('Start with the parent(s) of the selected first'), 1555 False) 1556 self.showparents.set_help( 1557 _("Will show the parents, brother and sisters of the " 1558 "selected person.") 1559 ) 1560 menu.add_option(category_name, "show_parents", self.showparents) 1561 1562 compresst = BooleanOption(_('Compress tree'), False) 1563 compresst.set_help(_("Whether to move people up, where possible, " 1564 "resulting in a smaller tree")) 1565 menu.add_option(category_name, "compress_tree", compresst) 1566 1567 bold = BooleanOption(_('Bold direct descendants'), True) 1568 bold.set_help( 1569 _("Whether to bold those people that are direct " 1570 "(not step or half) descendants.") 1571 ) 1572 menu.add_option(category_name, "bolddirect", bold) 1573 1574 indspouce = BooleanOption(_('Indent Spouses'), True) 1575 indspouce.set_help(_("Whether to indent the spouses in the tree.")) 1576 menu.add_option(category_name, "ind_spouse", indspouce) 1577 1578 ################## 1579 category_name = _("Report Options") 1580 1581 self.title = EnumeratedListOption(_("Report Title"), 0) 1582 self.title.add_item(0, _("Do not include a title")) 1583 self.title.add_item(1, _("Descendant Chart for [selected person(s)]")) 1584 if self.name.split(",")[0] != _RPT_NAME: 1585 self.title.add_item(2, 1586 _("Family Chart for [names of chosen family]")) 1587 if self.showparents.get_value(): 1588 self.title.add_item(3, 1589 _("Cousin Chart for [names of children]")) 1590 self.title.set_help(_("Choose a title for the report")) 1591 menu.add_option(category_name, "report_title", self.title) 1592 self.showparents.connect('value-changed', self.__Title_enum) 1593 1594 border = BooleanOption(_('Include a border'), False) 1595 border.set_help(_("Whether to make a border around the report.")) 1596 menu.add_option(category_name, "inc_border", border) 1597 1598 prnnum = BooleanOption(_('Include Page Numbers'), False) 1599 prnnum.set_help(_("Whether to include page numbers on each page.")) 1600 menu.add_option(category_name, "inc_pagenum", prnnum) 1601 1602 self.scale = EnumeratedListOption(_("Scale tree to fit"), 0) 1603 self.scale.add_item(0, _("Do not scale tree")) 1604 self.scale.add_item(1, _("Scale tree to fit page width only")) 1605 self.scale.add_item(2, _("Scale tree to fit the size of the page")) 1606 self.scale.set_help( 1607 _("Whether to scale the tree to fit a specific paper size") 1608 ) 1609 menu.add_option(category_name, "scale_tree", self.scale) 1610 self.scale.connect('value-changed', self.__check_blank) 1611 1612 if "BKI" not in self.name.split(","): 1613 self.__onepage = BooleanOption( 1614 _("Resize Page to Fit Tree size\n" 1615 "\n" 1616 "Note: Overrides options in the 'Paper Option' tab" 1617 ), 1618 False) 1619 self.__onepage.set_help( 1620 _("Whether to resize the page to fit the size \n" 1621 "of the tree. Note: the page will have a \n" 1622 "non standard size.\n" 1623 "\n" 1624 "With this option selected, the following will happen:\n" 1625 "\n" 1626 "With the 'Do not scale tree' option the page\n" 1627 " is resized to the height/width of the tree\n" 1628 "\n" 1629 "With 'Scale tree to fit page width only' the height of\n" 1630 " the page is resized to the height of the tree\n" 1631 "\n" 1632 "With 'Scale tree to fit the size of the page' the page\n" 1633 " is resized to remove any gap in either height or width" 1634 )) 1635 menu.add_option(category_name, "resize_page", self.__onepage) 1636 self.__onepage.connect('value-changed', self.__check_blank) 1637 else: 1638 self.__onepage = None 1639 1640 self.__blank = BooleanOption(_('Include Blank Pages'), True) 1641 self.__blank.set_help(_("Whether to include pages that are blank.")) 1642 menu.add_option(category_name, "inc_blank", self.__blank) 1643 1644 ################## 1645 category_name = _("Report Options (2)") 1646 1647 stdoptions.add_name_format_option(menu, category_name) 1648 1649 stdoptions.add_private_data_option(menu, category_name) 1650 1651 stdoptions.add_living_people_option(menu, category_name) 1652 1653 locale_opt = stdoptions.add_localization_option(menu, category_name) 1654 1655 stdoptions.add_date_format_option(menu, category_name, locale_opt) 1656 1657 ################## 1658 category_name = _("Display") 1659 1660 disp = TextOption(_("Descendant\nDisplay Format"), 1661 ["$n", 1662 "%s $b" %_BORN, 1663 "-{%s $d}" %_DIED]) 1664 disp.set_help(_("Display format for a descendant.")) 1665 menu.add_option(category_name, "descend_disp", disp) 1666 1667 #bug 4767 1668 #diffspouse = BooleanOption( 1669 # _("Use separate display format for spouses"), 1670 # True) 1671 #diffspouse.set_help(_("Whether spouses can have a different format.")) 1672 #menu.add_option(category_name, "diffspouse", diffspouse) 1673 1674 sdisp = TextOption(_("Spousal\nDisplay Format"), 1675 ["$n", 1676 "%s $b" %_BORN, 1677 "-{%s $d}" %_DIED]) 1678 sdisp.set_help(_("Display format for a spouse.")) 1679 menu.add_option(category_name, "spouse_disp", sdisp) 1680 1681 self.incmarr = BooleanOption(_('Include Marriage box'), True) 1682 self.incmarr.set_help( 1683 _("Whether to include a separate marital box in the report")) 1684 menu.add_option(category_name, "inc_marr", self.incmarr) 1685 self.incmarr.connect('value-changed', self._incmarr_changed) 1686 1687 self.marrdisp = StringOption(_("Marriage\nDisplay Format"), 1688 "%s $m" % _MARR) 1689 self.marrdisp.set_help(_("Display format for the marital box.")) 1690 menu.add_option(category_name, "marr_disp", self.marrdisp) 1691 self._incmarr_changed() 1692 1693 ################## 1694 category_name = _("Advanced") 1695 1696 repldisp = TextOption( 1697 _("Replace Display Format:\n'Replace this'/' with this'"), 1698 []) 1699 repldisp.set_help(_("i.e.\nUnited States of America/U.S.A")) 1700 menu.add_option(category_name, "replace_list", repldisp) 1701 1702 self.usenote = BooleanOption(_('Include a note'), False) 1703 self.usenote.set_help(_("Whether to include a note on the report.")) 1704 menu.add_option(category_name, "inc_note", self.usenote) 1705 self.usenote.connect('value-changed', self._usenote_changed) 1706 1707 self.notedisp = TextOption(_("Note"), []) 1708 self.notedisp.set_help(_("Add a note\n\n" 1709 "$T inserts today's date")) 1710 menu.add_option(category_name, "note_disp", self.notedisp) 1711 1712 locales = NoteType(0) 1713 self.notelocal = EnumeratedListOption(_("Note Location"), 2) 1714 for num, text in locales.note_locals(): 1715 self.notelocal.add_item(num, text) 1716 self.notelocal.set_help(_("Where to place the note.")) 1717 menu.add_option(category_name, "note_place", self.notelocal) 1718 self._usenote_changed() 1719 1720 self.box_Y_sf = NumberOption(_("inter-box Y scale factor"), 1721 1.00, 0.10, 2.00, 0.01) 1722 self.box_Y_sf.set_help(_("Make the inter-box Y bigger or smaller")) 1723 menu.add_option(category_name, "box_Yscale", self.box_Y_sf) 1724 1725 self.box_shadow_sf = NumberOption(_("box shadow scale factor"), 1726 1.00, 0.00, 2.00, 0.01) # down to 0 1727 self.box_shadow_sf.set_help(_("Make the box shadow bigger or smaller")) 1728 menu.add_option(category_name, "shadowscale", self.box_shadow_sf) 1729 1730 def _incmarr_changed(self): 1731 """ 1732 If Marriage box is not enabled, disable Marriage Display Format box 1733 """ 1734 value = self.incmarr.get_value() 1735 self.marrdisp.set_available(value) 1736 1737 def _usenote_changed(self): 1738 """ 1739 If Note box is not enabled, disable Note Location box 1740 """ 1741 value = self.usenote.get_value() 1742 self.notelocal.set_available(value) 1743 1744 def __check_blank(self): 1745 """dis/enables the 'print blank pages' checkbox""" 1746 if self.__onepage: 1747 value = not self.__onepage.get_value() 1748 else: 1749 value = True 1750 off = value and (self.scale.get_value() != 2) 1751 self.__blank.set_available(off) 1752 1753 def __Title_enum(self): 1754 item_list = [ 1755 [0, _("Do not include a title")], 1756 [1, _("Descendant Chart for [selected person(s)]")], 1757 ] 1758 if self.name.split(",")[0] != _RPT_NAME: 1759 item_list.append( 1760 [2, _("Family Chart for [names of chosen family]")] 1761 ) 1762 if self.showparents.get_value(): 1763 item_list.append( 1764 [3, _("Cousin Chart for [names of children]")] 1765 ) 1766 self.title.set_items(item_list) 1767 1768 def make_default_style(self, default_style): 1769 """Make the default output style for the Descendant Tree.""" 1770 1771 ## Paragraph Styles: 1772 font = FontStyle() 1773 font.set_size(16) 1774 font.set_type_face(FONT_SANS_SERIF) 1775 para_style = ParagraphStyle() 1776 para_style.set_font(font) 1777 para_style.set_alignment(PARA_ALIGN_CENTER) 1778 para_style.set_description(_("The style used for the title.")) 1779 default_style.add_paragraph_style("CG2-Title", para_style) 1780 1781 font = FontStyle() 1782 font.set_size(9) 1783 font.set_type_face(FONT_SANS_SERIF) 1784 para_style = ParagraphStyle() 1785 para_style.set_font(font) 1786 para_style.set_description( 1787 _('The basic style used for the text display.')) 1788 default_style.add_paragraph_style("CG2-Normal", para_style) 1789 1790 #Set the size of the shadow based on the font size! Much better 1791 #will be set later too. 1792 box_shadow = PT2CM(font.get_size()) * .6 1793 1794 font.set_bold(True) 1795 para_style = ParagraphStyle() 1796 para_style.set_font(font) 1797 para_style.set_description( 1798 _('The bold style used for the text display.')) 1799 default_style.add_paragraph_style("CG2-Bold", para_style) 1800 1801 font = FontStyle() 1802 font.set_size(9) 1803 font.set_type_face(FONT_SANS_SERIF) 1804 para_style = ParagraphStyle() 1805 para_style.set_font(font) 1806 para_style.set_description( 1807 _('The basic style used for the note display.')) 1808 default_style.add_paragraph_style("CG2-Note", para_style) 1809 1810 # TODO this seems meaningless, as only the text is displayed 1811 graph_style = GraphicsStyle() 1812 graph_style.set_paragraph_style("CG2-Title") 1813 graph_style.set_color((0, 0, 0)) 1814 graph_style.set_fill_color((255, 255, 255)) 1815 graph_style.set_line_width(0) 1816 graph_style.set_description(_("Cannot edit this reference")) 1817 default_style.add_draw_style("CG2-Title-box", graph_style) 1818 1819 ## Draw styles 1820 graph_style = GraphicsStyle() 1821 graph_style.set_paragraph_style("CG2-Normal") 1822 graph_style.set_fill_color((255, 255, 255)) 1823 default_style.add_draw_style("CG2-fam-box", graph_style) 1824 1825 graph_style = GraphicsStyle() 1826 graph_style.set_paragraph_style("CG2-Normal") 1827 graph_style.set_shadow(1, box_shadow) 1828 graph_style.set_fill_color((255, 255, 255)) 1829 default_style.add_draw_style("CG2-box", graph_style) 1830 1831 graph_style = GraphicsStyle() 1832 graph_style.set_paragraph_style("CG2-Bold") 1833 graph_style.set_shadow(1, box_shadow) 1834 graph_style.set_fill_color((255, 255, 255)) 1835 default_style.add_draw_style("CG2b-box", graph_style) 1836 1837 graph_style = GraphicsStyle() 1838 graph_style.set_paragraph_style("CG2-Note") 1839 graph_style.set_fill_color((255, 255, 255)) 1840 default_style.add_draw_style("CG2-note-box", graph_style) 1841 1842 graph_style = GraphicsStyle() 1843 default_style.add_draw_style("CG2-line", graph_style) 1844 1845#===================================== 1846#So do not fear, for I am with you; do not be dismayed, 1847#for I am your God. I will strengthen you and help you; 1848#I will uphold you with my righteous right hand. 1849#Isaiah 41:10 1850