~rosco2/ubuntu/wily/gramps/bug-1492304

« back to all changes in this revision

Viewing changes to gramps/plugins/graph/gvfamilylines.py

  • Committer: Package Import Robot
  • Author(s): Ross Gammon
  • Date: 2015-08-11 23:03:11 UTC
  • mfrom: (1.4.3)
  • mto: This revision was merged to the branch mainline in revision 58.
  • Revision ID: package-import@ubuntu.com-20150811230311-acjr8gcfe8isx7ij
* New upstream release
* Drop patches applied upstream or cherry-picked from there
* Add version constraints for gtk and pygobject
* Add goocanvas dependency - available soon
* Drop webkit dpendency as HTML view has been removed
* Force removal of upstream packages when installing Debian one
  (LP: #1464845)
* Drop fixperm override as permissions fixed upstream
* Fix spelling error in changelog
* Switch to nose for unit tests
* Add build dependencies for the nose tests
* Update copyright file
* Add uversionmangle to watch file to deal with alpha/beta versions
* Add manual test cases
* Drop FAQ URL from upstream metadata - changes every release
* Add patch to fix transparent windows in Ubuntu.
  Thanks to Lance Orner (LP: #1451259)

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
# Copyright (C) 2009-2010  Gary Burton 
7
7
# Contribution 2009 by     Bob Ham <rah@bash.sh>
8
8
# Copyright (C) 2010       Jakim Friant
9
 
# Copyright (C) 2011-2013  Paul Franklin
 
9
# Copyright (C) 2011-2014  Paul Franklin
10
10
#
11
11
# This program is free software; you can redistribute it and/or modify
12
12
# it under the terms of the GNU General Public License as published by
24
24
#
25
25
 
26
26
"""
27
 
Family Lines, a GraphViz-based plugin for Gramps.
 
27
Family Lines, a Graphviz-based plugin for Gramps.
28
28
"""
29
29
 
30
30
#------------------------------------------------------------------------
32
32
# python modules
33
33
#
34
34
#------------------------------------------------------------------------
35
 
from __future__ import unicode_literals
36
35
from functools import partial
37
36
 
38
37
#------------------------------------------------------------------------
79
78
#   class FamilyLinesOptions(MenuReportOptions)
80
79
#       - this class is created when the report dialog comes up
81
80
#       - all configuration controls for the report are created here
82
 
#       - see src/ReportBase/_ReportOptions.py for more information
83
81
#
84
82
#   class FamilyLinesReport(Report)
85
83
#       - this class is created only after the user clicks on "OK"
86
84
#       - the actual report generation is done by this class
87
 
#       - see src/ReportBase/_Report.py for more information
88
 
#
89
 
# Likely to be of additional interest is register_report() at the
90
 
# very bottom of this file.
91
85
#
92
86
#------------------------------------------------------------------------
93
87
 
94
88
class FamilyLinesOptions(MenuReportOptions):
95
89
    """
96
90
    Defines all of the controls necessary
97
 
    to configure the FamilyLines reports.
 
91
    to configure the FamilyLines report.
98
92
    """
99
93
    def __init__(self, name, dbase):
100
94
        self.limit_parents = None
107
101
 
108
102
    def add_menu_options(self, menu):
109
103
 
110
 
        # --------------------------------
111
 
        category_name = _('People of Interest')
 
104
        # ---------------------
 
105
        category_name = _('Report Options')
112
106
        add_option = partial(menu.add_option, category_name)
113
 
        # --------------------------------
114
 
 
115
 
        person_list = PersonListOption(_('People of interest'))
116
 
        person_list.set_help(_('People of interest are used as a starting '
117
 
                               'point when determining "family lines".'))
118
 
        add_option('gidlist', person_list)
 
107
        # ---------------------
119
108
 
120
109
        stdoptions.add_name_format_option(menu, category_name)
121
110
 
122
 
        followpar = BooleanOption(
123
 
                           _('Follow parents to determine family lines'), True)
 
111
        stdoptions.add_private_data_option(menu, category_name, default=False)
 
112
        
 
113
        followpar = BooleanOption(_('Follow parents to determine '
 
114
                                    '"family lines"'), True)
124
115
        followpar.set_help(_('Parents and their ancestors will be '
125
116
                             'considered when determining "family lines".'))
126
117
        add_option('followpar', followpar)
131
122
                               'determining "family lines".'))
132
123
        add_option('followchild', followchild)
133
124
 
134
 
        remove_extra_people = BooleanOption(
135
 
                             _('Try to remove extra people and families'), True)
 
125
        remove_extra_people = BooleanOption(_('Try to remove extra '
 
126
                                              'people and families'), True)
136
127
        remove_extra_people.set_help(_('People and families not directly '
137
128
                                       'related to people of interest will '
138
129
                                       'be removed when determining '
139
130
                                       '"family lines".'))
140
131
        add_option('removeextra', remove_extra_people)
141
132
 
 
133
        use_roundedcorners = BooleanOption(_('Use rounded corners'), False)
 
134
        use_roundedcorners.set_help(_('Use rounded corners to differentiate '
 
135
                                      'between women and men.'))
 
136
        add_option("useroundedcorners", use_roundedcorners)
 
137
 
 
138
        color = EnumeratedListOption(_("Graph coloring"), "filled")
 
139
        for i in range(len(_COLORS)):
 
140
            color.add_item(_COLORS[i]["value"], _COLORS[i]["name"])
 
141
        color.set_help(_("Males will be shown with blue, females "
 
142
                         "with red, unless otherwise set above for filled. "
 
143
                         "If the sex of an individual "
 
144
                         "is unknown it will be shown with gray."))
 
145
        add_option("color", color)
 
146
 
142
147
        stdoptions.add_localization_option(menu, category_name)
143
148
 
144
 
        # ----------------------------
145
 
        add_option = partial(menu.add_option, _('Family Colors'))
146
 
        # ----------------------------
147
 
 
148
 
        surname_color = SurnameColorOption(_('Family colors'))
149
 
        surname_color.set_help(_('Colors to use for various family lines.'))
150
 
        add_option('surnamecolors', surname_color)
151
 
 
152
 
        # -------------------------
153
 
        add_option = partial(menu.add_option, _('Individuals'))
154
 
        # -------------------------
155
 
 
156
 
        color_males = ColorOption(_('Males'), '#e0e0ff')
157
 
        color_males.set_help(_('The color to use to display men.'))
158
 
        add_option('colormales', color_males)
159
 
 
160
 
        color_females = ColorOption(_('Females'), '#ffe0e0')
161
 
        color_females.set_help(_('The color to use to display women.'))
162
 
        add_option('colorfemales', color_females)
163
 
 
164
 
        color_unknown = ColorOption(_('Unknown'), '#e0e0e0')
165
 
        color_unknown.set_help(_('The color to use '
166
 
                                 'when the gender is unknown.'))
167
 
        add_option('colorunknown', color_unknown)
168
 
 
169
 
        color_family = ColorOption(_('Families'), '#ffffe0')
170
 
        color_family.set_help(_('The color to use to display families.'))
171
 
        add_option('colorfamilies', color_family)
 
149
        # --------------------------------
 
150
        add_option = partial(menu.add_option, _('People of Interest'))
 
151
        # --------------------------------
 
152
 
 
153
        person_list = PersonListOption(_('People of interest'))
 
154
        person_list.set_help(_('People of interest are used as a starting '
 
155
                               'point when determining "family lines".'))
 
156
        add_option('gidlist', person_list)
172
157
 
173
158
        self.limit_parents = BooleanOption(_('Limit the number of ancestors'), 
174
159
                                           False)
196
181
        add_option('maxchildren', self.max_children)
197
182
 
198
183
        # --------------------
199
 
        add_option = partial(menu.add_option, _('Images'))
 
184
        add_option = partial(menu.add_option, _('Include'))
200
185
        # --------------------
201
186
 
202
 
        self.include_images = BooleanOption(_('Include '
203
 
                                              'thumbnail images of people'),
204
 
                                            True)
205
 
        self.include_images.set_help(_('Whether to '
206
 
                                       'include thumbnail images of people.'))
207
 
        add_option('incimages', self.include_images)
208
 
        self.include_images.connect('value-changed', self.images_changed)
209
 
 
210
 
        self.image_location = EnumeratedListOption(_('Thumbnail location'), 0)
211
 
        self.image_location.add_item(0, _('Above the name'))
212
 
        self.image_location.add_item(1, _('Beside the name'))
213
 
        self.image_location.set_help(_('Where the thumbnail image '
214
 
                                       'should appear relative to the name'))
215
 
        add_option('imageonside', self.image_location)
216
 
 
217
 
        # ---------------------
218
 
        add_option = partial(menu.add_option, _('Options'))
219
 
        # ---------------------
220
 
 
221
 
        color = EnumeratedListOption(_("Graph coloring"), "filled")
222
 
        for i in range(len(_COLORS)):
223
 
            color.add_item(_COLORS[i]["value"], _COLORS[i]["name"])
224
 
        color.set_help(_("Males will be shown with blue, females "
225
 
                         "with red, unless otherwise set above for filled. "
226
 
                         "If the sex of an individual "
227
 
                         "is unknown it will be shown with gray."))
228
 
        add_option("color", color)
229
 
 
230
 
        use_roundedcorners = BooleanOption(_('Use rounded corners'), False)
231
 
        use_roundedcorners.set_help(_('Use rounded corners to differentiate '
232
 
                                      'between women and men.'))
233
 
        add_option("useroundedcorners", use_roundedcorners)
 
187
        include_id = EnumeratedListOption(_('Include Gramps ID'), 0)
 
188
        include_id.add_item(0, _('Do not include'))
 
189
        include_id.add_item(1, _('Share an existing line'))
 
190
        include_id.add_item(2, _('On a line of its own'))
 
191
        include_id.set_help(_("Whether (and where) to include Gramps IDs"))
 
192
        add_option("incid", include_id)
234
193
 
235
194
        self.include_dates = BooleanOption(_('Include dates'), True)
236
195
        self.include_dates.set_help(_('Whether to include dates for people '
249
208
                                  'and families.'))
250
209
        add_option('incplaces', include_places)
251
210
 
252
 
        include_num_children = BooleanOption(
253
 
                                      _('Include the number of children'), True)
 
211
        include_num_children = BooleanOption(_('Include the number of '
 
212
                                               'children'), True)
254
213
        include_num_children.set_help(_('Whether to include the number of '
255
214
                                        'children for families with more '
256
215
                                        'than 1 child.'))
257
216
        add_option('incchildcnt', include_num_children)
258
217
 
259
 
        include_private = BooleanOption(_('Include private records'), False)
260
 
        include_private.set_help(_('Whether to include names, dates, and '
261
 
                                   'families that are marked as private.'))
262
 
        add_option('incprivate', include_private)
263
 
        
 
218
        self.include_images = BooleanOption(_('Include '
 
219
                                              'thumbnail images of people'),
 
220
                                            True)
 
221
        self.include_images.set_help(_('Whether to '
 
222
                                       'include thumbnail images of people.'))
 
223
        add_option('incimages', self.include_images)
 
224
        self.include_images.connect('value-changed', self.images_changed)
 
225
 
 
226
        self.image_location = EnumeratedListOption(_('Thumbnail location'), 0)
 
227
        self.image_location.add_item(0, _('Above the name'))
 
228
        self.image_location.add_item(1, _('Beside the name'))
 
229
        self.image_location.set_help(_('Where the thumbnail image '
 
230
                                       'should appear relative to the name'))
 
231
        add_option('imageonside', self.image_location)
 
232
 
 
233
        # ----------------------------
 
234
        add_option = partial(menu.add_option, _('Family Colors'))
 
235
        # ----------------------------
 
236
 
 
237
        surname_color = SurnameColorOption(_('Family colors'))
 
238
        surname_color.set_help(_('Colors to use for various family lines.'))
 
239
        add_option('surnamecolors', surname_color)
 
240
 
 
241
        # -------------------------
 
242
        add_option = partial(menu.add_option, _('Individuals'))
 
243
        # -------------------------
 
244
 
 
245
        color_males = ColorOption(_('Males'), '#e0e0ff')
 
246
        color_males.set_help(_('The color to use to display men.'))
 
247
        add_option('colormales', color_males)
 
248
 
 
249
        color_females = ColorOption(_('Females'), '#ffe0e0')
 
250
        color_females.set_help(_('The color to use to display women.'))
 
251
        add_option('colorfemales', color_females)
 
252
 
 
253
        color_unknown = ColorOption(_('Unknown'), '#e0e0e0')
 
254
        color_unknown.set_help(_('The color to use '
 
255
                                 'when the gender is unknown.'))
 
256
        add_option('colorunknown', color_unknown)
 
257
 
 
258
        color_family = ColorOption(_('Families'), '#ffffe0')
 
259
        color_family.set_help(_('The color to use to display families.'))
 
260
        add_option('colorfamilies', color_family)
 
261
 
264
262
        self.limit_changed()
265
263
        self.images_changed()
266
264
 
298
296
        
299
297
        The arguments are:
300
298
 
301
 
        database    - the GRAMPS database instance
302
 
        options     - instance of the FamilyLinesOptions class for this report
303
 
        user        - a gen.user.User() instance
 
299
        database     - the GRAMPS database instance
 
300
        options      - instance of the FamilyLinesOptions class for this report
 
301
        user         - a gen.user.User() instance
 
302
        name_format  - Preferred format to display names
 
303
        incl_private - Whether to include private data
 
304
        incid        - Whether to include IDs.
304
305
        """
305
306
        Report.__init__(self, database, options, user)
306
307
 
 
308
        menu = options.menu
 
309
        get_option_by_name = menu.get_option_by_name
 
310
        get_value = lambda name: get_option_by_name(name).get_value()
 
311
        
 
312
        stdoptions.run_private_data_option(self, menu)
 
313
        self._db = self.database
 
314
 
307
315
        # initialize several convenient variables
308
 
        self._db = database
309
316
        self._people = set() # handle of people we need in the report
310
317
        self._families = set() # handle of families we need in the report
311
318
        self._deleted_people = 0
312
319
        self._deleted_families = 0
313
320
        self._user = user
314
321
        
315
 
        menu = options.menu
316
 
        get_option_by_name = menu.get_option_by_name
317
 
        get_value = lambda name: get_option_by_name(name).get_value()
318
 
        
319
322
        self._followpar = get_value('followpar')
320
323
        self._followchild = get_value('followchild')
321
324
        self._removeextra = get_value('removeextra')
336
339
        self._just_years = get_value('justyears')
337
340
        self._incplaces = get_value('incplaces')
338
341
        self._incchildcount = get_value('incchildcnt')
339
 
        self._incprivate = get_value('incprivate')
 
342
        self.includeid = get_value('incid')
340
343
 
341
344
        # the gidlist is annoying for us to use since we always have to convert
342
345
        # the GIDs to either Person or to handles, so we may as well convert the
354
357
        lang = menu.get_option_by_name('trans').get_value()
355
358
        self._locale = self.set_locale(lang)
356
359
 
357
 
        name_format = menu.get_option_by_name("name_format").get_value()
358
 
        if name_format != 0:
359
 
            self._name_display.set_default_format(name_format)
 
360
        stdoptions.run_name_format_option(self, menu)
360
361
 
361
362
        # convert the 'surnamecolors' string to a dictionary of names and colors
362
363
        self._surnamecolors = {}
439
440
            handle = ancestorsNotYetProcessed.pop()
440
441
 
441
442
            # One of 2 things can happen here:
442
 
            #   1) we've already know about this person and he/she is already 
 
443
            #   1) we already know about this person and he/she is already 
443
444
            #      in our list
444
445
            #   2) this is someone new, and we need to remember him/her
445
446
            #
454
455
 
455
456
                person = self._db.get_person_from_handle(handle)
456
457
 
457
 
                # if this is a private record, and we're not
458
 
                # including private records, then go back to the
459
 
                # top of the while loop to get the next person
460
 
                if person.private and not self._incprivate:
461
 
                    continue
462
 
 
463
458
                # remember this person!
464
459
                self._people.add(handle)
465
460
 
469
464
                # to link spouses together
470
465
                for family_handle in person.get_family_handle_list():
471
466
                    family = self._db.get_family_from_handle(family_handle)
 
467
                    if not family:
 
468
                        continue
472
469
                    spouse_handle = ReportUtils.find_spouse(person, family)
473
470
                    if spouse_handle:
474
471
                        if (spouse_handle in self._people or
489
486
                for family_handle in person.get_parent_family_handle_list():
490
487
                    family = self._db.get_family_from_handle(family_handle)
491
488
 
492
 
                    if not family.private or self._incprivate:
493
 
                        father = self._db.get_person_from_handle(
494
 
                                                 family.get_father_handle())
495
 
                        mother = self._db.get_person_from_handle(
496
 
                                                 family.get_mother_handle())
497
 
                        if father:
498
 
                            if not father.private or self._incprivate:
499
 
                                ancestorsNotYetProcessed.add(
500
 
                                                 family.get_father_handle())
501
 
                                self._families.add(family_handle)
502
 
                        if mother:
503
 
                            if not mother.private or self._incprivate:
504
 
                                ancestorsNotYetProcessed.add(
505
 
                                                 family.get_mother_handle())
506
 
                                self._families.add(family_handle)
 
489
                    father = self._db.get_person_from_handle(
 
490
                                             family.get_father_handle())
 
491
                    mother = self._db.get_person_from_handle(
 
492
                                             family.get_mother_handle())
 
493
                    if father:
 
494
                        ancestorsNotYetProcessed.add(
 
495
                                         family.get_father_handle())
 
496
                        self._families.add(family_handle)
 
497
                    if mother:
 
498
                        ancestorsNotYetProcessed.add(
 
499
                                         family.get_mother_handle())
 
500
                        self._families.add(family_handle)
507
501
 
508
502
    def removeUninterestingParents(self):
509
503
        # start with all the people we've already identified
512
506
        while len(unprocessed_parents) > 0:
513
507
            handle = unprocessed_parents.pop()
514
508
            person = self._db.get_person_from_handle(handle)
 
509
            if not person:
 
510
                continue
515
511
 
516
512
            # There are a few things we're going to need,
517
513
            # so look it all up right now; such as:
670
666
 
671
667
                person = self._db.get_person_from_handle(handle)
672
668
 
673
 
                # if this is a private record, and we're not
674
 
                # including private records, then go back to the
675
 
                # top of the while loop to get the next person
676
 
                if person.private and not self._incprivate:
677
 
                    continue
678
 
 
679
669
                # remember this person!
680
670
                childrenToInclude.add(handle)
681
671
 
694
684
                # iterate through this person's families
695
685
                for family_handle in person.get_family_handle_list():
696
686
                    family = self._db.get_family_from_handle(family_handle)
697
 
                    if (family.private and self._incprivate) or not family.private:
698
 
 
699
 
                        # queue up any children from this person's family
700
 
                        for childRef in family.get_child_ref_list():
701
 
                            child = self._db.get_person_from_handle(childRef.ref)
702
 
                            if (child.private and self._incprivate) or not child.private:
703
 
                                childrenNotYetProcessed.add(child.get_handle())
704
 
                                self._families.add(family_handle)
705
 
 
706
 
                        # include the spouse from this person's family
707
 
                        spouse_handle = ReportUtils.find_spouse(person, family)
708
 
                        if spouse_handle:
709
 
                            spouse = self._db.get_person_from_handle(spouse_handle)
710
 
                            if (spouse.private and self._incprivate) or not spouse.private:
711
 
                                childrenToInclude.add(spouse_handle)
712
 
                                self._families.add(family_handle)
 
687
 
 
688
                    # queue up any children from this person's family
 
689
                    for childRef in family.get_child_ref_list():
 
690
                        child = self._db.get_person_from_handle(childRef.ref)
 
691
                        childrenNotYetProcessed.add(child.get_handle())
 
692
                        self._families.add(family_handle)
 
693
 
 
694
                    # include the spouse from this person's family
 
695
                    spouse_handle = ReportUtils.find_spouse(person, family)
 
696
                    if spouse_handle:
 
697
                        spouse = self._db.get_person_from_handle(spouse_handle)
 
698
                        childrenToInclude.add(spouse_handle)
 
699
                        self._families.add(family_handle)
713
700
 
714
701
        # we now merge our temp set "childrenToInclude" into our master set
715
702
        self._people.update(childrenToInclude)
716
703
 
717
 
 
718
704
    def writePeople(self):
719
705
 
720
706
        self.doc.add_comment('')
726
712
            bUseHtmlOutput = True
727
713
 
728
714
        # loop through all the people we need to output
729
 
        for handle in self._people:
 
715
        for handle in sorted(self._people): # enable a diff
730
716
            person = self._db.get_person_from_handle(handle)
731
717
            name = self._name_display.display(person)
 
718
            p_id = person.get_gramps_id()
732
719
 
733
720
            # figure out what colour to use
734
721
            gender = person.get_gender()
754
741
            # output the birth or fallback event
755
742
            birthStr = None
756
743
            if bth_event and self._incdates:
757
 
                if not bth_event.private or self._incprivate:
758
 
                    date = bth_event.get_date_object()
759
 
                    if self._just_years and date.get_year_valid():
760
 
                        birthStr = '%i' % date.get_year()
761
 
                    else:
762
 
                        birthStr = self._get_date(date)
 
744
                date = bth_event.get_date_object()
 
745
                if self._just_years and date.get_year_valid():
 
746
                    birthStr = '%i' % date.get_year()
 
747
                else:
 
748
                    birthStr = self._get_date(date)
763
749
 
764
750
            # get birth place (one of:  city, state, or country) we can use
765
751
            birthplace = None
766
752
            if bth_event and self._incplaces:
767
 
                if not bth_event.private or self._incprivate:
768
 
                    place = self._db.get_place_from_handle(bth_event.get_place_handle())
769
 
                    if place:
770
 
                        location = get_main_location(self._db, place)
771
 
                        if location.get(PlaceType.CITY):
772
 
                            birthplace = location.get(PlaceType.CITY)
773
 
                        elif location.get(PlaceType.STATE):
774
 
                            birthplace = location.get(PlaceType.STATE)
775
 
                        elif location.get(PlaceType.COUNTRY):
776
 
                            birthplace = location.get(PlaceType.COUNTRY)
 
753
                place = self._db.get_place_from_handle(
 
754
                                             bth_event.get_place_handle())
 
755
                if place:
 
756
                    location = get_main_location(self._db, place)
 
757
                    if location.get(PlaceType.CITY):
 
758
                        birthplace = location.get(PlaceType.CITY)
 
759
                    elif location.get(PlaceType.STATE):
 
760
                        birthplace = location.get(PlaceType.STATE)
 
761
                    elif location.get(PlaceType.COUNTRY):
 
762
                        birthplace = location.get(PlaceType.COUNTRY)
777
763
 
778
764
            # see if we have a deceased date we can use
779
765
            deathStr = None
780
766
            if dth_event and self._incdates:
781
 
                if not dth_event.private or self._incprivate:
782
 
                    date = dth_event.get_date_object()
783
 
                    if self._just_years and date.get_year_valid():
784
 
                        deathStr = '%i' % date.get_year()
785
 
                    else:
786
 
                        deathStr = self._get_date(date)
 
767
                date = dth_event.get_date_object()
 
768
                if self._just_years and date.get_year_valid():
 
769
                    deathStr = '%i' % date.get_year()
 
770
                else:
 
771
                    deathStr = self._get_date(date)
787
772
 
788
773
            # get death place (one of:  city, state, or country) we can use
789
774
            deathplace = None
790
775
            if dth_event and self._incplaces:
791
 
                if not dth_event.private or self._incprivate:
792
 
                    place = self._db.get_place_from_handle(dth_event.get_place_handle())
793
 
                    if place:
794
 
                        location = get_main_location(self._db, place)
795
 
                        if location.get(PlaceType.CITY):
796
 
                            deathplace = location.get(PlaceType.CITY)
797
 
                        elif location.get(PlaceType.STATE):
798
 
                            deathplace = location.get(PlaceType.STATE)
799
 
                        elif location.get(PlaceType.COUNTRY):
800
 
                            deathplace = location.get(PlaceType.COUNTRY)
 
776
                place = self._db.get_place_from_handle(
 
777
                                             dth_event.get_place_handle())
 
778
                if place:
 
779
                    location = get_main_location(self._db, place)
 
780
                    if location.get(PlaceType.CITY):
 
781
                        deathplace = location.get(PlaceType.CITY)
 
782
                    elif location.get(PlaceType.STATE):
 
783
                        deathplace = location.get(PlaceType.STATE)
 
784
                    elif location.get(PlaceType.COUNTRY):
 
785
                        deathplace = location.get(PlaceType.COUNTRY)
801
786
 
802
787
            # see if we have an image to use for this person
803
788
            imagePath = None
832
817
 
833
818
            # at the very least, the label must have the person's name
834
819
            label += name
 
820
            if self.includeid == 1: # same line
 
821
                label += " (%s)" % p_id
 
822
            elif self.includeid == 2: # own line
 
823
                label += "%s(%s)" % (lineDelimiter, p_id)
835
824
 
836
825
            if birthStr or deathStr:
837
826
                label += '%s(' % lineDelimiter
877
866
                border = ""
878
867
 
879
868
            # we're done -- add the node
880
 
            self.doc.add_node(person.get_gramps_id(),
 
869
            self.doc.add_node(p_id,
881
870
                 label=label,
882
871
                 shape=shape,
883
872
                 color=border,
891
880
        ngettext = self._locale.translation.ngettext # to see "nearby" comments
892
881
 
893
882
        # loop through all the families we need to output
894
 
        for family_handle in self._families:
 
883
        for family_handle in sorted(self._families): # enable a diff
895
884
            family = self._db.get_family_from_handle(family_handle)
896
885
            fgid = family.get_gramps_id()
897
886
 
905
894
                    (event_ref.get_role() == EventRoleType.FAMILY or 
906
895
                    event_ref.get_role() == EventRoleType.PRIMARY ):
907
896
                        # get the wedding date
908
 
                        if (event.private and self._incprivate) or not event.private:
909
 
                            if self._incdates:
910
 
                                date = event.get_date_object()
911
 
                                if self._just_years and date.get_year_valid():
912
 
                                    weddingDate = '%i' % date.get_year()
913
 
                                else:
914
 
                                    weddingDate = self._get_date(date)
915
 
                            # get the wedding location
916
 
                            if self._incplaces:
917
 
                                place = self._db.get_place_from_handle(event.get_place_handle())
918
 
                                if place:
919
 
                                    location = get_main_location(self._db, place)
920
 
                                    if location.get(PlaceType.CITY):
921
 
                                        weddingPlace = location.get(PlaceType.CITY)
922
 
                                    elif location.get(PlaceType.STATE):
923
 
                                        weddingPlace = location.get(PlaceType.STATE)
924
 
                                    elif location.get(PlaceType.COUNTRY):
925
 
                                        weddingPlace = location.get(PlaceType.COUNTRY)
 
897
                        if self._incdates:
 
898
                            date = event.get_date_object()
 
899
                            if self._just_years and date.get_year_valid():
 
900
                                weddingDate = '%i' % date.get_year()
 
901
                            else:
 
902
                                weddingDate = self._get_date(date)
 
903
                        # get the wedding location
 
904
                        if self._incplaces:
 
905
                            place = self._db.get_place_from_handle(
 
906
                                                   event.get_place_handle())
 
907
                            if place:
 
908
                                location = get_main_location(self._db, place)
 
909
                                if location.get(PlaceType.CITY):
 
910
                                    weddingPlace = location.get(PlaceType.CITY)
 
911
                                elif location.get(PlaceType.STATE):
 
912
                                    weddingPlace = location.get(PlaceType.STATE)
 
913
                                elif location.get(PlaceType.COUNTRY):
 
914
                                    weddingPlace = location.get(PlaceType.COUNTRY)
926
915
                        break
927
916
 
928
917
            # figure out the number of children (if any)
936
925
                                          ).format(number_of=child_count)
937
926
 
938
927
            label = ''
 
928
            fgid_already = False
939
929
            if weddingDate:
940
930
                if label != '':
941
931
                    label += '\\n'
942
932
                label += '%s' % weddingDate
 
933
                if self.includeid == 1 and not fgid_already: # same line
 
934
                    label += " (%s)" % fgid
 
935
                    fgid_already = True
943
936
            if weddingPlace:
944
937
                if label != '':
945
938
                    label += '\\n'
946
939
                label += '%s' % weddingPlace
 
940
                if self.includeid == 1 and not fgid_already: # same line
 
941
                    label += " (%s)" % fgid
 
942
                    fgid_already = True
 
943
            if self.includeid == 1 and not label:
 
944
                label = "(%s)" % fgid
 
945
                fgid_already = True
 
946
            elif self.includeid == 2 and not label: # own line
 
947
                label = "(%s)" % fgid
 
948
                fgid_already = True
 
949
            elif self.includeid == 2 and label and not fgid_already:
 
950
                label += "\\n(%s)" % fgid
 
951
                fgid_already = True
947
952
            if childrenStr:
948
953
                if label != '':
949
954
                    label += '\\n'
950
955
                label += '%s' % childrenStr
 
956
                if self.includeid == 1 and not fgid_already: # same line
 
957
                    label += " (%s)" % fgid
 
958
                    fgid_already = True
951
959
 
952
960
            shape   = "ellipse"
953
961
            style   = "solid"