In this commit:
Update Ancestor report Updated Descendant report new Family Descendant report new docs for these reports are found at http://www.gramps-project.org/wiki/index.php?title=User:Ander882 included into trunk for hopeful inclusion into 3.3 svn: r16348
This commit is contained in:
		
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -36,11 +36,11 @@ plg.gramps_target_version = '3.3' | ||||
| plg.status = STABLE | ||||
| plg.fname = 'AncestorTree.py' | ||||
| plg.ptype = REPORT | ||||
| plg.authors = ["Donald N. Allingham"] | ||||
| plg.authors_email = ["don@gramps-project.org"] | ||||
| plg.authors = ["Craig J. Anderson"] | ||||
| plg.authors_email = ["ander882@gramps-project.org"] | ||||
| plg.category = CATEGORY_DRAW | ||||
| plg.reportclass = 'AncestorTree' | ||||
| plg.optionclass = 'AncestorTreeOptions' | ||||
| plg.reportclass = 'AncestorTree2' | ||||
| plg.optionclass = 'AncestorTree2Options' | ||||
| plg.report_modes = [REPORT_MODE_GUI, REPORT_MODE_BKI, REPORT_MODE_CLI] | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| @@ -80,11 +80,34 @@ plg.gramps_target_version = '3.3' | ||||
| plg.status = STABLE | ||||
| plg.fname = 'DescendTree.py' | ||||
| plg.ptype = REPORT | ||||
| plg.authors = ["Donald N. Allingham"] | ||||
| plg.authors_email = ["don@gramps-project.org"] | ||||
| plg.authors = ["Craig J. Anderson"] | ||||
| plg.authors_email = ["ander882@gramps-project.org"] | ||||
| plg.category = CATEGORY_DRAW | ||||
| plg.reportclass = 'DescendTree' | ||||
| plg.optionclass = 'DescendTreeOptions' | ||||
| plg.reportclass = 'Descend2Tree' | ||||
| plg.optionclass = 'Descend2TreeOptions' | ||||
| plg.report_modes = [REPORT_MODE_GUI, REPORT_MODE_BKI, REPORT_MODE_CLI] | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Family Descendant Tree | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
|  | ||||
| plg = newplugin() | ||||
| plg.id    = 'family_descend_chart' | ||||
| plg.name  = _("Family Descendant Tree") | ||||
| plg.description =  _("Produces a graphical descendant tree around a family") | ||||
| plg.version = '1.0' | ||||
| plg.status = STABLE | ||||
| plg.fname = 'DescendTree.py' | ||||
| plg.ptype = REPORT | ||||
| plg.category = CATEGORY_DRAW | ||||
| plg.gramps_target_version = '3.3' | ||||
| plg.authors = ["Craig J. Anderson"] | ||||
| plg.authors_email = ["ander882@gramps-project.org"] | ||||
| plg.require_active = True | ||||
| plg.reportclass = 'Descend2Tree' | ||||
| plg.optionclass = 'Descend2TreeOptions' | ||||
| plg.report_modes = [REPORT_MODE_GUI, REPORT_MODE_BKI, REPORT_MODE_CLI] | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
|   | ||||
| @@ -26,13 +26,14 @@ pkgdata_PYTHON = \ | ||||
| 	libholiday.py\ | ||||
| 	libmapservice.py\ | ||||
| 	libmixin.py\ | ||||
|     libnarrate.py\ | ||||
| 	libnarrate.py\ | ||||
| 	libodfbackend.py\ | ||||
| 	libpersonview.py\ | ||||
| 	libplaceview.py\ | ||||
| 	libplugins.gpr.py\ | ||||
| 	libsubstkeyword.py\ | ||||
| 	libtranslate.py | ||||
| 	libtranslate.py\ | ||||
| 	libtreebase.py | ||||
|  | ||||
| pkgpyexecdir = @pkgpyexecdir@/plugins/lib | ||||
| pkgpythondir = @pkgpythondir@/plugins/lib | ||||
|   | ||||
| @@ -302,4 +302,21 @@ fname = 'libsubstkeyword.py', | ||||
| authors = ["The Gramps project"], | ||||
| authors_email = ["http://gramps-project.org"], | ||||
| ) | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # libtreebase | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| register(GENERAL,  | ||||
| id    = 'libtreebase', | ||||
| name  = "Graphical report lib", | ||||
| description =  _("Provides the base needed for the ancestor and " + | ||||
|                  "descendant graphical reports.") , | ||||
| version = '1.0', | ||||
| gramps_target_version = '3.3', | ||||
| status = STABLE, | ||||
| fname = 'libtreebase.py', | ||||
| authors = ["The Gramps project"], | ||||
| authors_email = ["http://gramps-project.org"], | ||||
| ) | ||||
|  | ||||
|   | ||||
							
								
								
									
										874
									
								
								src/plugins/lib/libtreebase.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										874
									
								
								src/plugins/lib/libtreebase.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,874 @@ | ||||
| # | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2008-2010 Craig J. Anderson | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software | ||||
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
| # | ||||
|  | ||||
| # $Id: Descendant.py .... ander882 $ | ||||
|  | ||||
| """Reports/Graphical Reports/Tree_Base""" | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # python modules | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| from gen.ggettext import sgettext as _ | ||||
|  | ||||
| from gen.plug.report import utils as ReportUtils | ||||
|  | ||||
| from gen.display.name import displayer as name_displayer | ||||
|  | ||||
| from libsubstkeyword import SubstKeywords | ||||
|  | ||||
| PT2CM = ReportUtils.pt2cm | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Class Calc_Lines | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| class CalcLines(object): | ||||
|     """ wrapper for libsubstkeyword and added functionality for | ||||
|     replacements. | ||||
|      | ||||
|     Receive:  Individual and family handle, and display format [string] | ||||
|     return: [Text] ready for a box. | ||||
|     """ | ||||
|     def __init__(self, dbase, repl): | ||||
|         self.database = dbase | ||||
|         self.display_repl = repl | ||||
|         #self.default_string = default_str | ||||
|  | ||||
|     def calc_lines(self, _indi_handle, _fams_handle, workinglines): | ||||
|         """ | ||||
|         In this pass we will: | ||||
|         1. make our text and do our replacements | ||||
|         2. remove any extra (unwanted) lines with the compres option | ||||
|         """ | ||||
|  | ||||
|         #################### | ||||
|         #1.1  Get our line information here | ||||
|         subst = SubstKeywords(self.database, _indi_handle, _fams_handle) | ||||
|         lines = subst.replace_and_clean(workinglines) | ||||
|  | ||||
|         #################### | ||||
|         #1.2  do our replacements | ||||
|         lns = [] | ||||
|         for line in lines: | ||||
|             for pair in self.display_repl: | ||||
|                 if pair.count("/") == 1: | ||||
|                     repl = pair.split("/", 1) | ||||
|                     line = line.replace(repl[0], repl[1]) | ||||
|             lns.append(line) | ||||
|          | ||||
|         return lns | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Class Canvas/Pages | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| class Page(object): | ||||
|     """ This class is a printable page. | ||||
|         Offsets from the canvas, Page numbers | ||||
|         boxes and lines | ||||
|         """ | ||||
|     def __init__(self, doc, canvas): | ||||
|         #parts from canvas | ||||
|         self.doc = doc | ||||
|         self.canvas = canvas | ||||
|          | ||||
|         #parts about the page | ||||
|         self.page_x_offset = 0 | ||||
|         self.page_y_offset = 0 | ||||
|         self.x_page_num = 0 | ||||
|         self.y_page_num = 0 | ||||
|         self.boxes = []   #All object must derive from BoxBase | ||||
|         self.lines = []   #All must derive form Linebase | ||||
|         self.note = None | ||||
|      | ||||
|     def is_blank(self): | ||||
|         """ Am I a blank page?  Notes and Titles are boxes too """ | ||||
|         return self.boxes == [] and self.lines == [] | ||||
|          | ||||
|     def add_box(self, box): | ||||
|         """ The box must derive from class Box_Base(Object): """ | ||||
|         self.boxes.append(box) | ||||
|         box.doc = self.doc | ||||
|         box.page = self | ||||
|      | ||||
|     def add_line(self, line): | ||||
|         """ Add a line onto this page """ | ||||
|         self.lines.append(line) | ||||
|          | ||||
|     def draw_border(self, line_name): | ||||
|         if self.y_page_num == 0: | ||||
|             self.doc.draw_line(line_name, 0, 0, | ||||
|                                self.doc.get_usable_width(), 0) | ||||
|         if self.x_page_num == 0: | ||||
|             self.doc.draw_line(line_name, 0, 0, 0, | ||||
|                                self.doc.get_usable_height()) | ||||
|         if self.y_page_num == self.canvas.y_pages-1: | ||||
|             self.doc.draw_line(line_name, 0, | ||||
|                                self.doc.get_usable_height(), | ||||
|                                self.doc.get_usable_width(), | ||||
|                                self.doc.get_usable_height()) | ||||
|         if self.x_page_num == self.canvas.x_pages-1: | ||||
|             self.doc.draw_line(line_name, self.doc.get_usable_width(), | ||||
|                                0, self.doc.get_usable_width(), | ||||
|                                self.doc.get_usable_height()) | ||||
|      | ||||
|     def display(self): | ||||
|         """ Display all boxes and lines that are on this page """ | ||||
|         for box in self.boxes: | ||||
|             box.display() | ||||
|         for line in self.lines: | ||||
|             line.display(self) | ||||
|  | ||||
|      | ||||
| class Canvas(Page): | ||||
|     """ The Canvas is two things. | ||||
|       The all in one canvas.  a canvas is a page of unlimited size | ||||
|       a group of pages.  each page is set is size and shows only a | ||||
|        part of what is on the entire canvas | ||||
|     """ | ||||
|     def __init__(self, doc): | ||||
|         Page.__init__(self, doc, self) | ||||
|         self.doc = doc | ||||
|         self.report_opts = None | ||||
|          | ||||
|         #How many pages are there in the report.  one more than real. | ||||
|         self.x_pages = 1 | ||||
|         self.y_pages = 1 | ||||
|         self.__pages = {(0, 0): self}  #set page 0,0 to me. | ||||
|         self.__fonts = {}  #keep a list of fonts so we don't have to lookup. | ||||
|         self.title = None | ||||
|         self.note = None | ||||
|          | ||||
|     def __new_page(self, x_page, y_page, x_offset, y_offset): | ||||
|         """ Make a new page.  This will only happen if we are  | ||||
|             paginating (making new pages to hold parts of the canvas) """ | ||||
|         if x_page >= self.x_pages: | ||||
|             self.x_pages = x_page + 1 | ||||
|         new_page = Page(self.doc, self) | ||||
|         new_page.x_page_num = x_page | ||||
|         new_page.y_page_num = y_page | ||||
|         new_page.page_x_offset = x_offset | ||||
|         new_page.page_y_offset = y_offset | ||||
|         self.__pages[x_page, y_page] = new_page | ||||
|         return new_page | ||||
|      | ||||
|     def sort_boxes_on_y_cm(self): | ||||
|         """ sorts the list of boxes on the canvas by .y_cm (top down) """ | ||||
|         self.boxes.sort( key=lambda box: box.y_cm) | ||||
|      | ||||
|     def add_title(self, title): | ||||
|         """ The title must derive from class TitleBox(BoxBase): """ | ||||
|         self.title = title | ||||
|      | ||||
|     def add_note(self, note): | ||||
|         """ The note must derive from class NoteBox(BoxBase, NoteType) """ | ||||
|         self.note = note | ||||
|         self.set_box_height_width(self.note) | ||||
|          | ||||
|     def __get_font(self, box): | ||||
|         """ returns the font used by a box.  makes a list of all seen fonts | ||||
|         to be faster.  If a new is found, run through the process to get it """ | ||||
|         if not self.__fonts.has_key(box.boxstr): | ||||
|             style_sheet = self.doc.get_style_sheet() | ||||
|             style_name = style_sheet.get_draw_style(box.boxstr) | ||||
|             style_name = style_name.get_paragraph_style() | ||||
|             self.__fonts[box.boxstr] = \ | ||||
|                 style_sheet.get_paragraph_style(style_name).get_font() | ||||
|          | ||||
|         return self.__fonts[box.boxstr] | ||||
|      | ||||
|     def get_report_height_width(self): | ||||
|         """ returns the (max width, max height) of the report | ||||
|         This does not take into account any shadows """ | ||||
|         max_width = 0 | ||||
|         max_height = 0 | ||||
|         for box in self.boxes: | ||||
|             tmp = box.x_cm + box.width + box.shadow | ||||
|             if tmp > max_width: | ||||
|                 max_width = tmp | ||||
|             tmp = box.y_cm + box.height + box.shadow | ||||
|             if tmp > max_height: | ||||
|                 max_height = tmp | ||||
|         return (max_width, max_height) | ||||
|      | ||||
|     def __scale_canvas(self, scale_amount): | ||||
|         """ scales everything up/down depending upon scale_amount """ | ||||
|         self.doc.report_opts.scale_everything(scale_amount) | ||||
|         self.title.scale(scale_amount) | ||||
|         if self.note is not None: | ||||
|             self.note.scale(scale_amount) | ||||
|         #scale down everyone!   | ||||
|         for box in self.boxes:   | ||||
|             box.scale(scale_amount) | ||||
|      | ||||
|     def set_box_height_width(self, box): | ||||
|         """ Sets the .width .height and .shadow of a box.  """ | ||||
|         if box.boxstr == "None": | ||||
|             box.height = box.width = 0 | ||||
|             return | ||||
|          | ||||
|         font = self.__get_font(box) | ||||
|         ##################### | ||||
|         #Get the width | ||||
|         for line in box.text: | ||||
|             width = self.doc.string_width(font, line)  | ||||
|             width = PT2CM(width) | ||||
|             if width > box.width: | ||||
|                 box.width = width | ||||
|          | ||||
|         ##################### | ||||
|         #Get the height | ||||
|         height = len(box.text) * font.get_size() * 1.5 | ||||
|         height += 1.0/2.0 * font.get_size() #funny number(s) based upon font. | ||||
|         box.height = PT2CM(height) | ||||
|          | ||||
|         style_sheet = self.doc.get_style_sheet() | ||||
|         style = style_sheet.get_draw_style(box.boxstr) | ||||
|         if style.get_shadow(): | ||||
|             box.shadow = style.get_shadow_space() | ||||
|  | ||||
|     def page_iter_gen(self, incblank): | ||||
|         """ generate the pages of the report.  do so in a left to right | ||||
|         up down approach.  incblank asks to include blank pages """ | ||||
|         blank = Page(self.doc, self) | ||||
|         for y_p in range(self.y_pages): | ||||
|             for x_p in range(self.x_pages): | ||||
|                 if self.__pages.has_key((x_p, y_p)): | ||||
|                     yield self.__pages[(x_p, y_p)] | ||||
|                 else: | ||||
|                     if incblank: | ||||
|                         blank.x_page_num = x_p | ||||
|                         blank.y_page_num = y_p | ||||
|                         yield blank   | ||||
|      | ||||
|     def __add_box_to_page(self, x_page, y_page, x_offset, y_offset, box): | ||||
|         """ adds a box to a page.  If the page is not there, make it first """ | ||||
|         if not self.__pages.has_key((x_page, y_page)): | ||||
|             #Add the new page into the dictionary | ||||
|             self.__new_page(x_page, y_page, x_offset, y_offset) | ||||
|          | ||||
|         #Add the box into the page | ||||
|         self.__pages[x_page, self.y_pages-1].add_box(box) | ||||
|      | ||||
|     def scale_report(self, one_page, scale_to_width, scale_to_height): | ||||
|         """ We have a report in its full size (on the canvas | ||||
|         and pages to print on.  scale one or both as needed/desired. | ||||
|          | ||||
|         - one_page, boolean.  Whether to make the page(or parts of) the size | ||||
|             of the report | ||||
|         - scale_to_width, boolean.  Scale the report width to the page size? | ||||
|         - scale_to_height, boolean.  Scale the report height to page size? | ||||
|         """ | ||||
|          | ||||
|         if scale_to_width or scale_to_height: | ||||
|             max_width, max_height = self.canvas.get_report_height_width() | ||||
|             max_width  += self.doc.report_opts.littleoffset | ||||
|             max_height += self.doc.report_opts.littleoffset | ||||
|          | ||||
|         """ | ||||
|         calc - Calculate the scale amount (if any).   | ||||
|           <1 everything is smaller to fit on the page | ||||
|           1 == no scaling | ||||
|           >1 make everything bigger to fill out the page | ||||
|         """ | ||||
|         scale = 1 | ||||
|         scaled_report_to = None | ||||
|  | ||||
|         ##################### | ||||
|         #scale the report option - width | ||||
|         if scale_to_width: | ||||
|             #Check the width of the title | ||||
|             title_width = self.title.width | ||||
|             title_width += self.doc.report_opts.littleoffset * 2 | ||||
|              | ||||
|             max_width = max(title_width, max_width) | ||||
|              | ||||
|             #This will be our base amount and | ||||
|             #then we will decrease only as needed from here. | ||||
|  | ||||
|             scale = self.doc.get_usable_width() / max_width | ||||
|             scaled_report_to = "width" | ||||
|  | ||||
|         ##################### | ||||
|         #scale the report option - height | ||||
|         if scale_to_height: | ||||
|             tmp = self.doc.get_usable_height() / max_height | ||||
|             if not scale_to_width or tmp < scale: | ||||
|                 scale = tmp | ||||
|                 scaled_report_to = "height" | ||||
|          | ||||
|         #Now I have the scale amount | ||||
|         if scale != 1:  #scale everything on the canvas | ||||
|             self.__scale_canvas(scale) | ||||
|          | ||||
|         ##################### | ||||
|         #Scale the page option | ||||
|         if one_page: | ||||
|  | ||||
|             #user wants PAGE to be the size of the report. | ||||
|             size = self.doc.paper.get_size() | ||||
|  | ||||
|             max_width, max_height = \ | ||||
|                 self.canvas.get_report_height_width() | ||||
|              | ||||
|             if scaled_report_to != "width": | ||||
|                 #calculate the width of the report | ||||
|                 max_width  += self.doc.report_opts.littleoffset | ||||
|                 max_width += self.doc.paper.get_left_margin()  | ||||
|                 max_width += self.doc.paper.get_right_margin()  | ||||
|                  | ||||
|                 #calculate the width of the title | ||||
|                 title_width = self.canvas.title.width | ||||
|                 title_width += self.doc.paper.get_left_margin() | ||||
|                 title_width += self.doc.paper.get_right_margin()  | ||||
|                 title_width += self.doc.report_opts.littleoffset | ||||
|                 max_width = max(title_width, max_width) | ||||
|                  | ||||
|                 size.set_width(max_width) | ||||
|          | ||||
|             if scaled_report_to != "height": | ||||
|                 #calculate the height of the report | ||||
|                 max_height += self.doc.paper.get_top_margin() | ||||
|                 max_height += self.doc.paper.get_bottom_margin() | ||||
|                 max_height += self.doc.report_opts.littleoffset | ||||
|                 size.set_height(max_height) | ||||
|              | ||||
|         return scale | ||||
|  | ||||
|      | ||||
|     def __paginate_x_offsets(self, colsperpage): | ||||
|         """ Go through the boxes and get the x page offsets """ | ||||
|         #fix soon.  should not use .level | ||||
|         liloffset = self.doc.report_opts.littleoffset | ||||
|         x_page_offsets = {0:0}  #change me to [] ??? | ||||
|         for box in self.boxes: | ||||
|             x_index = box.level[0] | ||||
|             x_page = x_index / colsperpage | ||||
|             if x_page not in x_page_offsets and x_index % colsperpage == 0: | ||||
|                 x_page_offsets[x_page] = box.x_cm - liloffset | ||||
|                 if x_page >= self.x_pages: | ||||
|                     self.x_pages = x_page+1 | ||||
|         return x_page_offsets | ||||
|      | ||||
|     def __paginate_y_pages(self, colsperpage, x_page_offsets): | ||||
|         """ Go through the boxes and put each one in a page | ||||
|         note that the self.boxes needs to be sorted by .y_cm """ | ||||
|         page_y_top = [0] | ||||
|         page_y_height = [self.doc.get_usable_height()] | ||||
|         liloffset = self.doc.report_opts.littleoffset | ||||
|          | ||||
|         for box in self.boxes: | ||||
|             #check to see if this box cross over to the next (y) page | ||||
|             height = box.y_cm + liloffset + box.height + box.shadow/2 | ||||
|              | ||||
|             if height > page_y_height[-1]: | ||||
|                 #we went off the end | ||||
|                 page_y_height.append(box.y_cm - liloffset + page_y_height[0]) | ||||
|                 page_y_top.append(box.y_cm - liloffset) | ||||
|                 self.y_pages = len(page_y_height) | ||||
|              | ||||
|             #Calculate my (x) page | ||||
|             #fix soon.  should not use .level | ||||
|             x_page = box.level[0] / colsperpage | ||||
|              | ||||
|             self.__add_box_to_page(x_page, self.y_pages-1, | ||||
|                                    x_page_offsets[x_page], | ||||
|                                    page_y_top[self.y_pages-1], | ||||
|                                    box) | ||||
|             #if not self.__pages.has_key((x_page, self.y_pages-1)): | ||||
|             #    #Add the new page into the dictionary | ||||
|             #    self.__new_page(x_page, self.y_pages-1, | ||||
|             #                    ) | ||||
|             # | ||||
|             ##Add the box into the page | ||||
|             #self.__pages[x_page, self.y_pages-1].add_box(box) | ||||
|         return page_y_top | ||||
|      | ||||
|     def __paginate_note(self, x_page_offsets, page_y_top): | ||||
|         """ Put the note on first.  it can be overwritten by other | ||||
|         boxes but it CAN NOT overwrite a box. """ | ||||
|         x_page, y_page = self.note.set_on_page(self) | ||||
|         if not self.__pages.has_key((x_page, y_page)): | ||||
|             #Add the new page into the dictionary | ||||
|             self.__new_page(x_page, y_page, | ||||
|                             x_page_offsets[x_page], page_y_top[y_page]) | ||||
|         #Add the box into the page | ||||
|         self.__pages[x_page, y_page].boxes.insert(0, self.note) | ||||
|         self.note.doc = self.doc | ||||
|         self.note.page = self | ||||
|      | ||||
|     def __paginate_lines(self, x_page_offsets, page_y_top): | ||||
|         """ Step three go through the lines and put each in page(s) """ | ||||
|         for line in self.lines: | ||||
|             pages = [] | ||||
|             #if type(line.start) == type([]): | ||||
|             pages = [] | ||||
|             end = line.start + line.end | ||||
|             #else: | ||||
|             #    end = [line.start] + line.end | ||||
|             #    pages = [] | ||||
|              | ||||
|             start_x_page = end[0].page.x_page_num | ||||
|             start_y_page = end[0].page.y_page_num | ||||
|             end_y_page = end[0].page.y_page_num | ||||
|                  | ||||
|             for box in end: | ||||
|                 x_page = box.page.x_page_num | ||||
|                 y_page = box.page.y_page_num | ||||
|                 if (x_page, y_page) not in pages: | ||||
|                     if not self.__pages.has_key((x_page, y_page)): | ||||
|                         #Add the new page into the dictionary | ||||
|                         self.__new_page(x_page, y_page, | ||||
|                                         x_page_offsets[x_page], | ||||
|                                         page_y_top[y_page]) | ||||
|                     self.__pages[x_page, y_page].add_line(line) | ||||
|                     pages.append((x_page, y_page)) | ||||
|                  | ||||
|                 if y_page < start_y_page: | ||||
|                     start_y_page = y_page | ||||
|                 if y_page > end_y_page: | ||||
|                     end_y_page = y_page | ||||
|                      | ||||
|             #if len(end) = 2 & end[0].y_page = 0 & end[1].y_page = 4 | ||||
|             #the line will not print on y_pages 1,2,3.  Fix that here. | ||||
|             x_page = start_x_page | ||||
|             for y_page in range(start_y_page, end_y_page+1): | ||||
|                 if (x_page, y_page) not in pages: | ||||
|                     if not self.__pages.has_key((x_page, y_page)): | ||||
|                         #Add the new page into the dictionary | ||||
|                         self.__new_page(x_page, y_page, | ||||
|                                         x_page_offsets[x_page], | ||||
|                                         page_y_top[y_page]) | ||||
|                     self.__pages[x_page, y_page].add_line(line) | ||||
|      | ||||
|     def __paginate_title(self, x_page_offsets): | ||||
|         #step four work with the title | ||||
|         if self.title.boxstr == "None": | ||||
|             return | ||||
|         #x_page_offsets[page] tells me the widths I can use | ||||
|         if len(x_page_offsets) > 1: | ||||
|             title_list = self.title.text.split(" ") | ||||
|             title_font = self.__get_font(self.title) | ||||
|             #space_width = PT2CM(self.doc.string_width(title_font," ")) | ||||
|              | ||||
|             list_title = [title_list.pop(0)] | ||||
|             while len(title_list): | ||||
|                 tmp = list_title[-1] + " " + title_list[0] | ||||
|                 if PT2CM(self.doc.string_width(title_font, tmp)) > \ | ||||
|                    x_page_offsets[1]: | ||||
|                     list_title.append("") | ||||
|                 if list_title[-1] != "":  | ||||
|                     list_title[-1] += " " | ||||
|                 list_title[-1] += title_list.pop(0) | ||||
|  | ||||
|             start_page = (len(x_page_offsets) - len(list_title)) / 2 | ||||
|             for tmp in range(start_page): | ||||
|                 list_title.insert(0, "") | ||||
|                 list_title.append("") | ||||
|             list_title.append("")  #one extra for security.  doesn't hurt. | ||||
|              | ||||
|             x_page = 0 | ||||
|             for title in list_title: | ||||
|                 if title == "": | ||||
|                     x_page += 1 | ||||
|                     continue | ||||
|                 if not self.__pages.has_key((x_page, 0)): | ||||
|                     #Add the new page into the dictionary | ||||
|                     self.__new_page(x_page, 0, x_page_offsets[1], 0) | ||||
|                  | ||||
|                 title_part = TitleBox(self.doc, self.title.boxstr) | ||||
|                 title_part.text = list_title[x_page] | ||||
|                 title_part.width = x_page_offsets[1] | ||||
|                  | ||||
|                 #Add the box into the page | ||||
|                 self.__pages[x_page, 0].add_box(title_part) | ||||
|                 x_page = x_page + 1 | ||||
|         else: | ||||
|             self.title.width = self.doc.get_usable_width() | ||||
|             self.__pages[0, 0].add_box(self.title) | ||||
|      | ||||
|     def __paginate(self, colsperpage): | ||||
|         """ take the boxes on the canvas and put them into separate pages. | ||||
|         The boxes need to be sorted by y_cm """ | ||||
|         liloffset = self.doc.report_opts.littleoffset | ||||
|         self.__pages = {} | ||||
|         x_page_offsets = self.__paginate_x_offsets(colsperpage) | ||||
|         page_y_top = self.__paginate_y_pages(colsperpage, x_page_offsets) | ||||
|          | ||||
|         if self.note is not None: | ||||
|             self.__paginate_note(x_page_offsets, page_y_top) | ||||
|         self.__paginate_lines(x_page_offsets, page_y_top) | ||||
|         self.__paginate_title(x_page_offsets) | ||||
|          | ||||
|  | ||||
|     def paginate(self, colsperpage, one_page_report): | ||||
|         """ self.boxes must be sorted by box.y_cm for this to work.  """ | ||||
|         if one_page_report: | ||||
|             #self.canvas.add_box(self.canvas.title) | ||||
|             title_part = TitleBox(self.doc, self.title.boxstr) | ||||
|             title_part.text = self.title.text | ||||
|             title_part.width = self.doc.get_usable_width() | ||||
|             self.add_box(title_part) | ||||
|              | ||||
|             if self.note is not None: | ||||
|                 self.note.set_on_page(self) | ||||
|                 self.boxes.insert(0, self.note) | ||||
|                 self.note.doc = self.doc | ||||
|                 self.note.page = self | ||||
|         else: | ||||
|             self.__paginate(colsperpage) | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Class Box_Base | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| class BoxBase(object): | ||||
|     """ boxes are always in/on a Page | ||||
|     Needed to print are: boxstr, text, x_cm, y_cm, width, height | ||||
|     """ | ||||
|     def __init__(self): | ||||
|         self.page = None | ||||
|          | ||||
|         #'None' will cause an error.  Sub-classes will init | ||||
|         self.boxstr = "None" | ||||
|         self.text = "" | ||||
|         self.level = (0,)  #which column/level am I in?  int zero based. | ||||
|         self.x_cm = 0.0 | ||||
|         self.y_cm = 0.0 | ||||
|         self.width = 0.0 | ||||
|         self.height = 0.0 | ||||
|         self.shadow = 0.0 | ||||
|          | ||||
|     def scale(self, scale_amount): | ||||
|         """ Scale the amounts """ | ||||
|         self.x_cm *= scale_amount | ||||
|         self.y_cm *= scale_amount | ||||
|         self.width *= scale_amount | ||||
|         self.height *= scale_amount | ||||
|         self.shadow *= scale_amount | ||||
|      | ||||
|     def display(self): | ||||
|         """ display the box accounting for page x, y offsets | ||||
|         Ignore any box with 'None' is boxstr """ | ||||
|         if self.boxstr != "None": | ||||
|             text = '\n'.join(self.text) | ||||
|             xbegin = self.x_cm - self.page.page_x_offset | ||||
|             ybegin = self.y_cm - self.page.page_y_offset | ||||
|              | ||||
|             self.page.doc.draw_box(self.boxstr, | ||||
|                     text, | ||||
|                     xbegin, ybegin,  | ||||
|                     self.width, self.height) | ||||
|  | ||||
| class TitleBox(BoxBase): | ||||
|     """ | ||||
|     Holds information about the Title that will print on a page | ||||
|     """ | ||||
|     def __init__(self, doc, boxstr): | ||||
|         """ initalize the title box """ | ||||
|         BoxBase.__init__(self) | ||||
|         self.doc = doc | ||||
|         self.boxstr = boxstr | ||||
|         if boxstr == "None": | ||||
|             return | ||||
|         self.cm_y = self.doc.report_opts.littleoffset | ||||
|          | ||||
|         style_sheet = self.doc.get_style_sheet() | ||||
|         style_name = style_sheet.get_draw_style(self.boxstr) | ||||
|         style_name = style_name.get_paragraph_style() | ||||
|         self.font = style_sheet.get_paragraph_style(style_name).get_font() | ||||
|  | ||||
|     def set_box_height_width(self): | ||||
|         if self.boxstr == "None": | ||||
|             return | ||||
|         #fix me. width should be the printable area | ||||
|         self.width = PT2CM(self.doc.string_width(self.font, self.text)) | ||||
|         self.height = PT2CM(self.font.get_size() * 1.2) | ||||
|  | ||||
|     def _get_names(self, persons): | ||||
|         """  A helper function that receives a list of persons and | ||||
|         returns their names in a list """ | ||||
|         tmp = [] | ||||
|         for person in persons: | ||||
|             tmp.append(name_displayer.display(person)) | ||||
|         return tmp | ||||
|  | ||||
|     def display(self): | ||||
|         """ display the title box.  """ | ||||
|         if self.page.y_page_num != 0 or self.boxstr == "None": | ||||
|             return | ||||
|         if self.text != "": | ||||
|             self.doc.center_text(self.boxstr, self.text, | ||||
|                              self.width/2, self.y_cm) | ||||
|      | ||||
| class PageNumberBox(BoxBase): | ||||
|     """ | ||||
|     Calculates information about the page numbers that will print on a page | ||||
|     do not put in a value for PageNumberBox.text.  this will be calculated for | ||||
|     each page """ | ||||
|      | ||||
|     def __init__(self, doc, boxstr): | ||||
|         """ initalize the page number box """ | ||||
|         BoxBase.__init__(self) | ||||
|         self.doc = doc | ||||
|         self.boxstr = boxstr | ||||
|      | ||||
|     def __calc_position(self, page): | ||||
|         """ calculate where I am to print on the page(s) """ | ||||
|         self.text = "(%d,%d)" | ||||
|  | ||||
|         style_sheet = self.doc.get_style_sheet() | ||||
|         style_name = style_sheet.get_draw_style(self.boxstr) | ||||
|         style_name = style_name.get_paragraph_style() | ||||
|         font = style_sheet.get_paragraph_style(style_name).get_font() | ||||
|          | ||||
|         #calcualate how much space is needed | ||||
|         if page.canvas.x_pages > 10: | ||||
|             tmp = "00" | ||||
|         else: | ||||
|             tmp = "0" | ||||
|         if page.canvas.y_pages > 10: | ||||
|             tmp += "00" | ||||
|         else: | ||||
|             tmp += "0" | ||||
|          | ||||
|         width = self.doc.string_width(font, '(,)'+tmp) | ||||
|         width = PT2CM(width) | ||||
|         self.width = width | ||||
|          | ||||
|         height = font.get_size() * 1.4 | ||||
|         height += 0.5/2.0 * font.get_size() #funny number(s) based upon font. | ||||
|         self.height = PT2CM(height) | ||||
|          | ||||
|         self.x_cm = self.doc.get_usable_width() - self.width | ||||
|         self.y_cm = self.doc.get_usable_height() - self.height | ||||
|  | ||||
|  | ||||
|     def display(self, page): | ||||
|         """ If this is the first time I am ran, get my position | ||||
|         then display the page number """ | ||||
|         if self.text == "": | ||||
|             self.__calc_position(page) | ||||
|              | ||||
|         self.doc.draw_text(self.boxstr, | ||||
|                    self.text % (page.x_page_num+1,page.y_page_num+1), | ||||
|                    self.x_cm, self.y_cm) | ||||
|  | ||||
| class NoteType(object): | ||||
|     """  Provide the different options (corners) to place the note """ | ||||
|      | ||||
|     TOPLEFT = 0 | ||||
|     TOPRIGHT = 1 | ||||
|     BOTTOMLEFT = 2 | ||||
|     BOTTOMRIGHT = 3 | ||||
|      | ||||
|     _DEFAULT = BOTTOMRIGHT | ||||
|      | ||||
|     _DATAMAP = [ | ||||
|         (TOPLEFT,     _("Top Left"),     "Top Left"), | ||||
|         (TOPRIGHT,    _("Top Right"),    "Top Right"), | ||||
|         (BOTTOMLEFT,  _("Bottom Left"),  "Bottom Left"), | ||||
|         (BOTTOMRIGHT, _("Bottom Right"), "Bottom Right"), | ||||
|         ] | ||||
|      | ||||
|     def __init__(self, value, exclude=None): | ||||
|         """ initalize GrampsType """ | ||||
|         self.value = value | ||||
|         self.exclude = exclude | ||||
|         #GrampsType.__init__(self, value) | ||||
|      | ||||
|     def note_locals(self, start=0): | ||||
|         """ generates an int of all the options """ | ||||
|         for tuple  in self._DATAMAP: | ||||
|             if tuple[0] != self.exclude: | ||||
|                 yield tuple[0], tuple[1] | ||||
|      | ||||
| class NoteBox(BoxBase, NoteType): | ||||
|     """ Box that will hold the note to display on the page """ | ||||
|      | ||||
|     def __init__(self, doc, boxstr, locale, exclude=None): | ||||
|         """ initalize the NoteBox """ | ||||
|         BoxBase.__init__(self) | ||||
|         NoteType.__init__(self, locale, exclude) | ||||
|         self.doc = doc | ||||
|         self.boxstr = boxstr | ||||
|  | ||||
|     def set_on_page(self, canvas): | ||||
|         """ set the x_cm and y_cm given | ||||
|         self.doc, leloffset, and title_height """ | ||||
|          | ||||
|         liloffset = self.doc.report_opts.littleoffset | ||||
|         #left or right side | ||||
|         if self.value == NoteType.BOTTOMLEFT or \ | ||||
|                            self.value == NoteType.TOPLEFT: | ||||
|             self.x_cm = liloffset | ||||
|         else: | ||||
|             self.x_cm = self.doc.get_usable_width() - self.width - liloffset | ||||
|         #top or bottom | ||||
|         if self.value == NoteType.TOPRIGHT or \ | ||||
|                            self.value == NoteType.TOPLEFT: | ||||
|             self.y_cm = canvas.title.height + liloffset*2 | ||||
|         else: | ||||
|             self.y_cm = self.doc.get_usable_height() - self.height - liloffset | ||||
|      | ||||
|         """ helper function for canvas.paginate(). | ||||
|         return the (x, y) page I want to print on """ | ||||
|         if self.value == NoteType.TOPLEFT: | ||||
|             return (0, 0) | ||||
|         elif self.value == NoteType.TOPRIGHT: | ||||
|             return (canvas.x_pages-1, 0) | ||||
|         elif self.value == NoteType.BOTTOMLEFT: | ||||
|             return (0, canvas.y_pages-1) | ||||
|         elif self.value == NoteType.BOTTOMRIGHT: | ||||
|             return (canvas.x_pages-1, canvas.y_pages-1) | ||||
|      | ||||
|     def display(self): | ||||
|         """ position the box and display """ | ||||
|         title = self.page.canvas.title | ||||
|         title_height = 0 | ||||
|         if title is not None: | ||||
|             title_height = title.height | ||||
|         text = '\n'.join(self.text) | ||||
|         self.doc.draw_box(self.boxstr, text, | ||||
|            self.x_cm, self.y_cm, | ||||
|            self.width, self.height) | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Class Line_base | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| class LineBase(object): | ||||
|     """ A simple line class. | ||||
|     self.start is the box that we are drawing a line from | ||||
|     self.end are the boxes that we are drawing lines to. | ||||
|     """ | ||||
|     def __init__(self, start): | ||||
|         self.linestr = "None" | ||||
|         self.start = [start] | ||||
|         self.end = [] | ||||
|      | ||||
|     def add_to(self, person): | ||||
|         """ add destination boxes to draw this line to """ | ||||
|         self.end.append(person) | ||||
|      | ||||
|     def display(self, page): | ||||
|         """ display the line.  left to right line.  one start, multiple end. | ||||
|         page will tell us what parts of the line we can print """ | ||||
|         if self.end == []: | ||||
|             return | ||||
|          | ||||
|         # y_cm and x_cm start points - take into account page offsets | ||||
|         #yme = self.start.y_cm + self.start.height/2 - page.page_y_offset | ||||
|         #if type(self.start) != type([]): | ||||
|         #    self.start = [self.start] | ||||
|         start = self.start[0] | ||||
|  | ||||
|         xbegin = start.x_cm + start.width - page.page_x_offset | ||||
|         # out 3/4 of the way and x_cm end point(s) | ||||
|         x34    = xbegin + (start.doc.report_opts.col_width * 3/4)  | ||||
|         xend = xbegin + start.doc.report_opts.col_width | ||||
|          | ||||
|         if x34 > 0:  # > 0 tell us we are printing on this page. | ||||
|             usable_height = start.doc.get_usable_height() | ||||
|             #1 - Line from start box out | ||||
|             for box in self.start: | ||||
|                 yme = box.y_cm + box.height/2 - page.page_y_offset | ||||
|                 if box.page.y_page_num == page.y_page_num: | ||||
|                     # and 0 < yme < usable_height and \ | ||||
|                     start.doc.draw_line(self.linestr, xbegin, yme, x34, yme) | ||||
|          | ||||
|             #2 - veritcal line | ||||
|             mid = [] | ||||
|             for box in self.start + self.end: | ||||
|                 tmp = box.y_cm + box.height/2 | ||||
|                 mid.append(tmp) | ||||
|             mid.sort() | ||||
|             mid = [mid[0]-page.page_y_offset, mid[-1]-page.page_y_offset] | ||||
|             if mid[0] < 0: | ||||
|                 mid[0] = 0 | ||||
|             if mid[1] > usable_height: | ||||
|                 mid[1] = usable_height | ||||
|             #draw the connecting vertical line. | ||||
|             start.doc.draw_line(self.linestr, x34, mid[0], x34, mid[1]) | ||||
|         else: | ||||
|             x34 = 0 | ||||
|          | ||||
|         #3 - horizontal line(s) | ||||
|         for box in self.end: | ||||
|             if box.page.y_page_num == page.y_page_num: | ||||
|                 yme = box.y_cm + box.height/2 - box.page.page_y_offset | ||||
|                 start.doc.draw_line(self.linestr, x34, yme, xend, yme) | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Class report_options | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| class ReportOptions(object): | ||||
|     """ | ||||
|     A simple class to hold various report information | ||||
|     Calculates | ||||
|       the gap between persons, | ||||
|       the column width, for lines, | ||||
|       the left hand spacing for spouses (Descendant report only) | ||||
|     """ | ||||
|      | ||||
|     def __init__(self, doc, normal_font): | ||||
|         """ initalize various report variables that are used """ | ||||
|         self.box_pgap = PT2CM(1.25*normal_font.get_size()) #gap between persons | ||||
|         self.box_mgap = self.box_pgap /2 #gap between marriage information | ||||
|         self.box_shadow = PT2CM(9)  #size of normal text | ||||
|         self.spouse_offset = PT2CM(doc.string_width(normal_font, "0")) | ||||
|          | ||||
|         self.col_width = PT2CM(doc.string_width(normal_font, "(000,0)")) | ||||
|         self.littleoffset = PT2CM(1) | ||||
|          | ||||
|         #Things that will get added later | ||||
|         self.max_box_width = 0 | ||||
|         self.max_box_height = 0 | ||||
|          | ||||
|         self.scale = 1 | ||||
|      | ||||
|     def scale_everything(self, amount): | ||||
|         """ Scale the amounts that are needed to generate a report """ | ||||
|         self.scale = amount | ||||
|  | ||||
|         self.col_width *= amount | ||||
|         self.littleoffset *= amount | ||||
|  | ||||
|         self.max_box_width *= amount  #box_width | ||||
|         self.spouse_offset *= amount | ||||
|         self.box_shadow *= amount | ||||
|  | ||||
| #===================================== | ||||
| #"And Jesus said unto them ... , "If ye have faith as a grain of mustard | ||||
| #seed, ye shall say unto this mountain, Remove hence to younder place; and | ||||
| #it shall remove; and nothing shall be impossible to you." | ||||
| #Romans 1:17 | ||||
		Reference in New Issue
	
	Block a user