File webcal.py.diff of Package gramps

From: Richard Bos <rbos@opensuse.org>
Date: 2016-01-13 19:28:22 +0200
Subject: provide nicer webcal experience
References: https://github.com/gramps-project/gramps/commits/master/gramps/plugins/webreport/webcal.py
Upstream: submitted

--- gramps-4.2.2/gramps/plugins/webreport/webcal.py	2016-01-06 17:44:13.000000000 +0100
+++ gramps-4.2.2.new/gramps/plugins/webreport/webcal.py	2016-01-09 09:38:15.392065001 +0100
@@ -8,13 +8,14 @@
 # Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
 # Copyright (C) 2008      Jason Simanek
 # Copyright (C) 2010      Jakim Friant
+# Copyright (C) 2015      Serge Noiraud
 #
 # 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, 
+# 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.
@@ -43,7 +44,7 @@
 log = logging.getLogger(".WebPage")
 
 #------------------------------------------------------------------------
-# GRAMPS module
+# Gramps module
 #------------------------------------------------------------------------
 from gramps.gen.const import GRAMPS_LOCALE as glocale
 _ = glocale.translation.sgettext
@@ -93,6 +94,15 @@
 PLUGMAN = GuiPluginManager.get_instance()
 CSS = PLUGMAN.process_plugin_data('WEBSTUFF')
 
+def _escape(string):
+    """ replace character in text that html shows correctly
+    special characters: & < and >
+    """
+    string = string.replace('&', '&amp;') # must be the first
+    string = string.replace('<', '&lt;')
+    string = string.replace('>', '&gt;')
+    return string
+
 #------------------------------------------------------------------------
 #
 # WebCalReport
@@ -139,7 +149,7 @@
         self.home_link = mgobn('home_link')
 
         self.month_notes = [mgobn('note_' + month)
-            for month in ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 
+            for month in ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul',
                 'aug', 'sep', 'oct', 'nov', 'dec']]
 
         self.encoding = mgobn('encoding')
@@ -172,13 +182,13 @@
 
     def get_note_format(self, note):
         """
-        will get the note from the database, and will return either the 
-        styled text or plain note 
+        will get the note from the database, and will return either the
+        styled text or plain note
         """
 
         # retrieve the body of the note
         note_text = note.get()
- 
+
         # styled notes
         htmlnotetext = self.styled_note(note.get_styledtext(),
                                                                note.get_format())
@@ -259,14 +269,16 @@
         config.set('paths.website-directory',
                    os.path.dirname(self.html_dir) + os.sep)
 
-    def add_day_item(self, text, year, month, day, event):
+    def add_day_item(self, text, year, month, day, event, age_at_death, dead_event_date):
         """
         adds birthdays, anniversaries, and holidays to their perspective lists
 
         text -- line to be added
-        year, month, day -- date to add the text to 
+        year, month, day -- date to add the text to
 
         event -- one of 'BirthDay', 'Anniversary', or 'Holiday'
+        age_at_death -- The age in text. ie : 68 years, 6 months
+        dead_event_date -- The date of the event used to calculate the age_at_death
         """
 
         # This may happen for certain "about" dates.
@@ -289,7 +301,7 @@
         else:
             event_date = Date.EMPTY            # Incomplete date....
 
-        day_list.append((text, event, event_date))
+        day_list.append((text, event, event_date, age_at_death, dead_event_date))
         month_dict[day] = day_list
 
         # determine which dictionary to add it to???
@@ -311,9 +323,9 @@
             holiday_table.load_holidays(year, country)
             for month in range(1, 13):
                 for day in range(1, 32):
-                    holiday_names = holiday_table.get_holidays(month, day) 
+                    holiday_names = holiday_table.get_holidays(month, day)
                     for holiday_name in holiday_names:
-                        self.add_day_item(holiday_name, year, month, day, 'Holiday')
+                        self.add_day_item(holiday_name, year, month, day, 'Holiday', None, None)
                     step()
 
     def copy_calendar_files(self):
@@ -418,7 +430,7 @@
         if body_id is not None:
             body.attr = "id = '%(idtag)s'" % { 'idtag' : body_id }
 
-        # GRAMPS favicon
+        # Gramps favicon
         fname1 = "/".join(subdirs + ["images", "favicon2.ico"])
 
         # _CALENDARSCREEN stylesheet
@@ -432,7 +444,7 @@
         # add horizontal menu if css == Blue or Visually because there is no menus?
         if CSS[self.css]["navigation"]:
             fname = "/".join(subdirs + ["css", "calendar-menus.css"])
-            links.extend( 
+            links.extend(
                 Html("link", href = fname, type = "text/css", media = "screen", rel = "stylesheet", indent = False)
             )
 
@@ -452,7 +464,7 @@
 
             # Created for ?
             msg = None
-            if self.author and self.email:  
+            if self.author and self.email:
                 msg = _('the "WebCal" will be the potential-email Subject|'
                         'Created for %(html_email_author_start)s'
                         'WebCal%(html_email_author_end)s') % {
@@ -466,7 +478,7 @@
             if msg:
                 header += Html("p", msg, id = "CreatorInfo")
 
-            body += header 
+            body += header
         return page, body
 
     def year_navigation(self, nr_up, currentsection):
@@ -496,7 +508,7 @@
                 # this will always need an extension added
                 full_month_name = date_displayer.long_months[self.today.get_month() ]
 
-                # Note. We use '/' here because it is a URL, not a OS dependent 
+                # Note. We use '/' here because it is a URL, not a OS dependent
                 # pathname.
                 url = '/'.join(subdirs + [full_month_name]) + self.ext
                 hyper = Html("a", str(cal_year), href = url, title = str(cal_year))
@@ -522,7 +534,7 @@
         nr_up = number of directories up to reach root directory
         year = year being created
         currentsection = month name being created for proper CSS styling
-        use_home = if creating a link to home 
+        use_home = if creating a link to home
             -- a link to root directory of website
         """
         navs = []
@@ -555,11 +567,11 @@
                     if not (url.startswith('http:') or url.startswith('/')):
                         add_subdirs = not any(url.endswith(ext)
                                                 for ext in _WEB_EXT)
-                         
+
                     # whether to add subdirs or not???
-                    if add_subdirs: 
+                    if add_subdirs:
                         subdirs = ['..'] * nr_up
-                        subdirs.append(str(year)) 
+                        subdirs.append(str(year))
                         url = '/'.join(subdirs + [url_fname])
 
                     if not _has_webpage_extension(url):
@@ -637,7 +649,7 @@
             # day_col is based on range(0 - 6)
             return previous_month_day
 
-        def __get_next_month_day(year, month, day_col):  
+        def __get_next_month_day(year, month, day_col):
 
             if month == 12:
                 nextmonth = calendar.monthcalendar((year + 1), 1)
@@ -650,7 +662,7 @@
             # day_col is based on range(0 - 6)
             return next_month_day
 
-        # Begin calendar head. We'll use the capitalized name, because here it 
+        # Begin calendar head. We'll use the capitalized name, because here it
         # seems appropriate for most countries.
         month_name = full_month_name.capitalize()
         th_txt = month_name
@@ -661,7 +673,7 @@
         # begin calendar table and table head
         with Html("table", class_ = "calendar", id = month_name, role = "Calendar-Grid") as table:
             thead = Html("thead")
-            table += thead 
+            table += thead
 
             if clickable:
                 name = th_txt + self.ext
@@ -669,15 +681,57 @@
                 linkable = Html("a", th_txt, href = url, name = url, title = th_txt)
             else:
                 linkable = th_txt
+                if not self.multiyear:
+                    self.end_year = self.start_year
+                if month > 1:
+                    full_month_name = date_displayer.long_months[month-1]
+                    url = full_month_name + self.ext
+                    prevm = Date(int(year), int(month-1), 0)
+                    my_title = Html("a", _escape("<"), href = url, title = date_displayer.display(prevm))
+                elif self.multiyear and year > self.start_year:
+                    full_month_name = date_displayer.long_months[12]
+                    url = full_month_name + self.ext
+                    dest = os.path.join("../", str(year-1), url)
+                    prevm = Date(int(year-1), 12, 0)
+                    my_title = Html("a", _escape("<"), href = dest, title = date_displayer.display(prevm))
+                else:
+                    full_month_name = date_displayer.long_months[12]
+                    url = full_month_name + self.ext
+                    dest = os.path.join("../", str(self.end_year), url)
+                    prevy = Date(self.end_year, 12, 0)
+                    my_title = Html("a", _escape("<"), href = dest, title = date_displayer.display(prevy))
+                my_title += Html("</a>&nbsp;")
+                if month < 12:
+                    full_month_name = date_displayer.long_months[month+1]
+                    url = full_month_name + self.ext
+                    nextd = Date(int(year), int(month+1), 0)
+                    my_title += Html("a", _escape(">"), href = url, title = date_displayer.display(nextd))
+                elif self.multiyear and year < self.end_year:
+                    full_month_name = date_displayer.long_months[1]
+                    url = full_month_name + self.ext
+                    dest = os.path.join("../", str(year+1), url)
+                    nextd = Date(int(year+1), 1, 0)
+                    my_title += Html("a", _escape(">"), href = dest, title = date_displayer.display(nextd))
+                else:
+                    full_month_name = date_displayer.long_months[1]
+                    url = full_month_name + self.ext
+                    dest = os.path.join("../", str(self.start_year), url)
+                    nexty = Date(self.start_year, 1, 0)
+                    my_title += Html("a", _escape(">"), href = dest, title = date_displayer.display(nexty))
+                my_title += Html("</a>")
+                trow = Html("tr") + (
+                    Html("th", my_title, class_ ='monthName', colspan=7, inline = True)
+                    )
+                thead += trow
             trow = Html("tr") + (
                 Html("th", linkable, class_ ='monthName', colspan=7, inline = True)
                 )
-            thead += trow    
+            thead += trow
 
             # Calendar weekday names header
             trow = Html("tr")
             thead += trow
-  
+
             for day_col in range(7):
                 dayclass = get_class_for_daycol(day_col)
                 dayname = get_name_for_daycol(day_col)
@@ -688,10 +742,10 @@
             tbody = Html("tbody")
             table += tbody
 
-            # get first of the month and month information 
+            # get first of the month and month information
             current_date, current_ord, monthinfo = get_first_day_of_month(year, month)
 
-            # begin calendar table rows, starting week0 
+            # begin calendar table rows, starting week0
             nweeks = len(monthinfo)
             for week_row in range(0, nweeks):
                 week = monthinfo[week_row]
@@ -704,7 +758,7 @@
                 for day_col in range(0, 7):
                     dayclass = get_class_for_daycol(day_col)
 
-                    # day number, can also be a zero -- a day before or after month 
+                    # day number, can also be a zero -- a day before or after month
                     day = week[day_col]
 
                     # start the beginning variable for <td>, table cell
@@ -718,7 +772,7 @@
 
                         # day in previous/ next month
                         specday = __get_previous_month_day(year, month, day_col) if week_row == 0 \
-                            else __get_next_month_day(year, month, day_col) 
+                            else __get_next_month_day(year, month, day_col)
 
                         specclass = "previous " if week_row == 0 else "next "
                         specclass += dayclass
@@ -728,7 +782,7 @@
                             Html("div", specday, class_ = "date", inline = True) )
 
                     # normal day number in current month
-                    else: 
+                    else:
                         thisday = datetime.date.fromordinal(current_ord)
 
                         # Something this month
@@ -740,10 +794,10 @@
                             event_date = Date(thisday.year, thisday.month, thisday.day)
 
                             # get events for this day
-                            day_list = get_day_list(event_date, holiday_list, bday_anniv_list) 
+                            day_list = get_day_list(event_date, holiday_list, bday_anniv_list)
 
                             # is there something this day?
-                            if day_list: 
+                            if day_list:
 
                                 hilightday = 'highlight ' + dayclass
                                 tcell = Html("td", id = tcell_id, class_ = hilightday)
@@ -751,17 +805,17 @@
                                 # Year at a Glance
                                 if cal == "yg":
 
-                                    # make one day pages and hyperlink 
-                                    if self.makeoneday: 
+                                    # make one day pages and hyperlink
+                                    if self.makeoneday:
 
-                                        # create yyyymmdd date string for 
+                                        # create yyyymmdd date string for
                                         # "One Day" calendar page filename
                                         fname_date = '%04d%02d%02d' % (year,month,day) + self.ext
 
                                         # create hyperlink to one_day()
                                         tcell += Html("a", datediv, href = fname_date, inline = True)
 
-                                        # only year_glance() needs this to create the one_day() pages 
+                                        # only year_glance() needs this to create the one_day() pages
                                         self.one_day(event_date, fname_date, day_list)
 
                                     # just year_glance(), but no one_day() pages
@@ -772,31 +826,31 @@
 
                                             # adds date division
                                             Html("div", day, class_ = "date", inline = True)
-                                            ) 
+                                            )
 
                                 # WebCal
                                 else:
 
                                     # add date to table cell
-                                    tcell += datediv 
+                                    tcell += datediv
 
                                     # list the events
                                     unordered = Html("ul")
                                     tcell += unordered
 
-                                    for nyears, date, text, event in day_list:
-                                        unordered += Html("li", text, inline = False 
+                                    for nyears, date, text, event, notused, notused in day_list:
+                                        unordered += Html("li", text, inline = False
                                             if event == 'Anniversary' else True)
 
                             # no events for this day
-                            else: 
+                            else:
 
-                                # create empty day with date 
+                                # create empty day with date
                                 tcell = Html("td", class_ = dayclass, inline = True) + (
 
                                     # adds date division
                                     Html("div", day, class_ = "date", inline = True)
-                                    ) 
+                                    )
 
                         # nothing for this month
                         else:
@@ -804,7 +858,7 @@
 
                                 # adds date division
                                 Html("div", day, class_ = "date", inline = True)
-                                ) 
+                                )
 
                     # attach table cell to table row
                     # close the day column
@@ -873,13 +927,13 @@
                     note = self.database.get_note_from_gramps_id(note)
                     note = self.get_note_format(note)
 
-                # table foot  section 
+                # table foot  section
                 cal_foot = Html("tfoot")
                 monthly_calendar += cal_foot
 
                 trow = Html("tr") + (
                     Html("td", note, colspan=7, inline = True)
-                    ) 
+                    )
                 cal_foot += trow
 
                 # create blank line for stylesheets
@@ -899,6 +953,28 @@
         year -- year being created
         """
 
+        self.event_list = []
+        prv = None
+        nxt = None
+        evdte = None
+        for month in sorted(self.calendar):
+            vals = sorted(self.calendar.get(month, {}))
+            if month == 0: # why ?
+                continue
+            for day in vals:
+                event_date = "%04d%02d%02d" % (year, month, day)
+                if evdte == None:
+                    evdte = event_date
+                elif nxt == None:
+                    nxt = event_date
+                    self.event_list.append((evdte, prv, nxt))
+                else:
+                    prv = evdte
+                    evdte = nxt
+                    nxt = event_date
+                    self.event_list.append((evdte, prv, nxt))
+        self.event_list.append((nxt, evdte, None))
+
         nr_up = 1                       # Number of directory levels up to get to root
 
         # generate progress pass for "Year At A Glance"
@@ -927,7 +1003,7 @@
                            'on a date will take you to a page that shows all the events for '
                            'that date, if there are any.\n'))
 
-            # page description 
+            # page description
             content = Html("div", class_ = "content", id = "YearGlance")
             body += content
 
@@ -937,7 +1013,7 @@
 
                 # build the calendar
                 monthly_calendar = self.calendar_build("yg", year, month, clickable=True)
-                content += monthly_calendar  
+                content += monthly_calendar
 
                 # increase progress bar
                 step()
@@ -987,15 +1063,41 @@
         currentsection = date_displayer.long_months[month]
         body += self.month_navigation(nr_up, year, currentsection, True)
 
-        # set date display as in user prevferences 
+        # set date display as in user prevferences
         content = Html("div", class_="content", id = "OneDay")
         body += content
-        content += Html("h3", date_displayer.display(event_date), inline = True)
+        evt = fname_date[:8]
+        found = (evt, None, None)
+        for event in self.event_list:
+            if event[0] == evt:
+                found = event
+                break
+        my_title = Html()
+        url = "#"
+        if found[1] is not None:
+            url = event[1] + self.ext
+            prevd = Date(int(event[1][:4]), int(event[1][4:6]), int(event[1][6:]))
+            my_title = Html("a", _escape("<"), href = url, title = date_displayer.display(prevd))
+        else:
+            my_title = Html('<em>&nbsp;&nbsp;</em>')
+        my_title += Html("</a>&nbsp;")
+        if found[2] is not None:
+            url = event[2] + self.ext
+            nextd = Date(int(event[2][:4]), int(event[2][4:6]), int(event[2][6:]))
+            my_title += Html("a", _escape(">"), href = url, title = date_displayer.display(nextd))
+        else:
+            my_title += Html('<b>&nbsp;&nbsp;</b>')
+        my_title += Html("</a>")
+        content += Html("h3", my_title, inline = True)
+        my_title = Html("span", " ")
+        my_title += date_displayer.display(event_date)
+        my_title += Html("span", " ")
+        content += Html("h3", my_title, inline = True)
 
         # list the events
         ordered = Html("ol")
-        content += ordered  
-        for nyears, date, text, event in day_list:
+        content += ordered
+        for nyears, date, text, event, age_at_death, dead_event_date in day_list:
             ordered += Html("li", text, inline = False if event == 'Anniversary' else True)
 
         # create blank line for stylesheets
@@ -1004,7 +1106,7 @@
         body += (fullclear, footer)
 
         # send calendar page to web output
-        # and close the file 
+        # and close the file
         self.XHTMLWriter(oneday, od)
 
     def build_url_fname_html(self, fname, subdir=None, prefix=None):
@@ -1016,7 +1118,7 @@
         If the subdirectory is given, then two extra levels of subdirectory are inserted
         between 'subdir' and the filename. The reason is to prevent directories with
         too many entries.
-        If 'prefix' is set, then is inserted in front of the result. 
+        If 'prefix' is set, then is inserted in front of the result.
 
         The extension is added to the filename as well.
 
@@ -1045,9 +1147,9 @@
         return subdirs
 
     def get_name(self, person, maiden_name = None):
-        """ 
-        Return person's name, unless maiden_name given, unless married_name 
-        listed. 
+        """
+        Return person's name, unless maiden_name given, unless married_name
+        listed.
 
         person -- person to get short name from
         maiden_name  -- either a woman's maiden name or man's surname
@@ -1083,7 +1185,7 @@
 
         people = db.iter_person_handles()
         with self._user.progress(_("Web Calendar Report"),
-                                  _('Applying Filter...'), 
+                                  _('Applying Filter...'),
                                   db.get_number_of_people()) as step:
             people = self.filter.apply(db, people, step)
 
@@ -1094,21 +1196,31 @@
 
                 family_list = person.get_family_handle_list()
                 birth_ref = person.get_birth_ref()
-                birth_date = Date.EMPTY
+                birth_date = Date()
                 if birth_ref:
                     birth_event = db.get_event_from_handle(birth_ref.ref)
                     birth_date = birth_event.get_date_object()
 
+                death_ref = person.get_death_ref()
+                person_death = Date()
+                age_at_death = None
+                if death_ref and birth_date:
+                    death_event = db.get_event_from_handle(death_ref.ref)
+                    death_date = death_event.get_date_object()
+                    person_death = death_date
+                    if birth_date != Date() and birth_date.is_valid() and death_date:
+                        age_at_death = death_date - birth_date
+
                 # determine birthday information???
-                if (self.birthday and birth_date is not Date.EMPTY and birth_date.is_valid()):
+                if (self.birthday and birth_date is not Date() and birth_date.is_valid()):
                     birth_date = gregorian(birth_date)
 
-                    year = birth_date.get_year() or this_year
+                    year = birth_date.get_year()
                     month = birth_date.get_month()
                     day = birth_date.get_day()
 
                     # date to figure if someone is still alive
-                    # current year of calendar, month nd day is their birth month and birth day 
+                    # current year of calendar, month nd day is their birth month and birth day
                     prob_alive_date = Date(this_year, month, day)
 
                     # add some things to handle maiden name:
@@ -1116,7 +1228,7 @@
                     if person.gender == Person.FEMALE:
 
                         # get husband's last name:
-                        if self.maiden_name in ['spouse_first', 'spouse_last']: 
+                        if self.maiden_name in ['spouse_first', 'spouse_last']:
                             if family_list:
                                 if self.maiden_name == 'spouse_first':
                                     fhandle = family_list[0]
@@ -1129,7 +1241,7 @@
                                     if father_handle:
                                         father = db.get_person_from_handle(father_handle)
                                         if father is not None:
-                                            father_surname = _get_regular_surname(person.gender, 
+                                            father_surname = _get_regular_surname(person.gender,
                                                 father.get_primary_name())
                     short_name = self.get_name(person, father_surname)
                     alive = probably_alive(person, db, prob_alive_date)
@@ -1137,12 +1249,15 @@
 
                         # add link to NarrativeWeb
                         if self.link_to_narweb:
-                            text = str(Html("a", short_name, 
-                                        href = self.build_url_fname_html(person.handle, "ppl", 
+                            text = str(Html("a", short_name,
+                                        href = self.build_url_fname_html(person.handle, "ppl",
                                                                        prefix = self.narweb_prefix)))
                         else:
                             text = short_name
-                        self.add_day_item(text, year, month, day, 'Birthday')
+                        if age_at_death == None:
+                            self.add_day_item(text, year, month, day, 'Birthday', age_at_death, birth_date)
+                        else:
+                            self.add_day_item(text, year, month, day, 'Birthday', age_at_death, person_death)
 
                 # add anniversary if requested
                 if self.anniv:
@@ -1159,12 +1274,26 @@
                             if spouse:
                                 spouse_name = self.get_name(spouse)
                                 short_name = self.get_name(person)
+                                death_ref = spouse.get_death_ref()
+                                spouse_death = Date()
+                                if death_ref:
+                                    death_event = db.get_event_from_handle(death_ref.ref)
+                                    death_date = death_event.get_date_object()
+                                    if death_date != Date() and death_date.is_valid():
+                                        spouse_death = death_date
+                            first_died = Date()
+                            if person_death == Date():
+                                first_died = spouse_death
+                            elif spouse_death != Date():
+                                first_died = person_death if spouse_death > person_death else spouse_death
+                            else:
+                                first_died = person_death
 
-                            # will return a marriage event or False if not married any longer 
+                            # will return a marriage event or False if not married any longer
                             marriage_event = get_marriage_event(db, fam)
                             if marriage_event:
                                 event_date = marriage_event.get_date_object()
-                                if event_date is not Date.EMPTY and event_date.is_valid():
+                                if event_date is not Date() and event_date.is_valid():
                                     event_date = gregorian(event_date)
                                     year = event_date.get_year()
                                     month = event_date.get_month()
@@ -1172,25 +1301,31 @@
 
                                     # date to figure if someone is still alive
                                     prob_alive_date = Date(this_year, month, day)
+                                    wedding_age = None
+                                    if first_died != Date():
+                                        wedding_age = first_died - event_date
 
                                     if self.link_to_narweb:
                                         spouse_name = str(Html("a", spouse_name,
-                                                      href = self.build_url_fname_html(spouse_handle, 'ppl', 
+                                                      href = self.build_url_fname_html(spouse_handle, 'ppl',
                                                       prefix = self.narweb_prefix)))
                                         short_name = str(Html("a", short_name,
-                                                          href = self.build_url_fname_html(person.handle, 'ppl', 
+                                                          href = self.build_url_fname_html(person.handle, 'ppl',
                                                            prefix = self.narweb_prefix)))
-                                    
+
                                     alive1 = probably_alive(person, db, prob_alive_date)
                                     alive2 = probably_alive(spouse, db, prob_alive_date)
+                                    if first_died == Date():
+                                        first_died = Date(0, 0, 0)
                                     if ((self.alive and alive1 and alive2) or not self.alive):
 
                                         text = _('%(spouse)s and %(person)s') % {
                                             'spouse' : spouse_name,
                                             'person' : short_name}
 
-                                        self.add_day_item(text, year, month, day, 'Anniversary')
-        
+                                        self.add_day_item(text, year, month, day, 'Anniversary',
+                                                          wedding_age, first_died)
+
     def write_footer(self, nr_up):
         """
         Writes the footer section of the pages
@@ -1223,7 +1358,7 @@
             else:
                 text = "&copy; %s %s" % (self.today.get_year(), self.author)
 
-            footer += Html("p", text, id = 'copyright') 
+            footer += Html("p", text, id = 'copyright')
 
         # return footer to its callers
         return footer
@@ -1243,7 +1378,7 @@
 
     def write_report(self):
         """
-        The short method that runs through each month and creates a page. 
+        The short method that runs through each month and creates a page.
         """
         # get data from database for birthdays/ anniversaries
         self.collect_data(self.start_year)
@@ -1269,7 +1404,7 @@
                 # create webcalendar() calendar pages
                 self.webcalendar(cal_year)
 
-                # create "Year At A Glance" and 
+                # create "Year At A Glance" and
                 # "One Day" calendar pages
                 if self.fullyear:
                     self.year_glance(cal_year)
@@ -1279,7 +1414,7 @@
             cal_year = self.start_year
 
             self.holidays = {}
-                
+
             # get the information, first from holidays:
             if self.country != 0:
                 self.__get_holidays(cal_year)
@@ -1287,7 +1422,7 @@
             # create webcalendar() calendar pages
             self.webcalendar(cal_year)
 
-            # create "Year At A Glance" and 
+            # create "Year At A Glance" and
             # "One Day" calendar pages
             if self.fullyear:
                 self.year_glance(cal_year)
@@ -1358,7 +1493,7 @@
             if val[0] == defaultnum:
                 default =  ind
                 break
-        name_format = EnumeratedListOption(_("Name format"), 
+        name_format = EnumeratedListOption(_("Name format"),
                             fmt_list[default][0])
         for num, name, fmt_str, act in fmt_list:
             name_format.add_item(num, name)
@@ -1383,11 +1518,11 @@
         cright.set_help( _("The copyright to be used for the web files"))
         menu.add_option(category_name, "cright", cright)
 
-        css_list = sorted([(CSS[key]["translation"], CSS[key]["id"]) 
+        css_list = sorted([(CSS[key]["translation"], CSS[key]["id"])
                             for key in list(CSS.keys())
                             if CSS[key]["user"]])
         css = EnumeratedListOption(_('StyleSheet'), css_list[0][1])
-        for css_item in css_list:                              
+        for css_item in css_list:
             css.add_item(css_item[1], css_item[0])
         css.set_help( _('The stylesheet to be used for the web pages'))
         menu.add_option(category_name, "css", css)
@@ -1404,7 +1539,7 @@
         self.__multiyear = BooleanOption(_('Create multiple year calendars'), False)
         self.__multiyear.set_help(_('Whether to create Multiple year calendars or not.'))
         menu.add_option(category_name, 'multiyear', self.__multiyear)
-        self.__multiyear.connect('value-changed', self.__multiyear_changed) 
+        self.__multiyear.connect('value-changed', self.__multiyear_changed)
 
         self.__start_year = NumberOption(_('Start Year for the Calendar(s)'), today.get_year(),
             1900, 3000)
@@ -1424,7 +1559,7 @@
         holiday_table = libholiday.HolidayTable()
         countries = holiday_table.get_countries()
         countries.sort()
-        if (len(countries) == 0 or 
+        if (len(countries) == 0 or
             (len(countries) > 0 and countries[0] != '')):
             countries.insert(0, '')
         count = 0
@@ -1439,7 +1574,7 @@
         start_dow = EnumeratedListOption(_("First day of week"), 1)
         for count in range(1, 8):
             start_dow.add_item(count,
-                               date_displayer.long_days[count].capitalize()) 
+                               date_displayer.long_days[count].capitalize())
         start_dow.set_help(_("Select the first day of the week for the calendar"))
         menu.add_option(category_name, "start_dow", start_dow)
 
@@ -1544,11 +1679,11 @@
         makeoneday = BooleanOption(_('Create one day event pages for'
                                      ' Year At A Glance calendar'), False)
         makeoneday.set_help(_('Whether to create one day pages or not'))
-        menu.add_option(category_name, 'makeoneday', makeoneday)  
+        menu.add_option(category_name, 'makeoneday', makeoneday)
 
         self.__links = BooleanOption(_('Link to Narrated Web Report'), False)
         self.__links.set_help(_('Whether to link data to web report or not'))
-        menu.add_option(category_name, 'link_to_narweb', self.__links)  
+        menu.add_option(category_name, 'link_to_narweb', self.__links)
         self.__links.connect('value-changed', self.__links_changed)
 
         dbname = self.__db.get_dbname()
@@ -1595,7 +1730,7 @@
 
     def __links_changed(self):
         """
-        Handle checkbox change. 
+        Handle checkbox change.
         """
         if self.__links.get_value():
             self.__prefix.set_available(True)
@@ -1612,7 +1747,7 @@
         surname = surname + ", " + suffix
     return surname
 
-# Simple utility list to convert Gramps day-of-week numbering 
+# Simple utility list to convert Gramps day-of-week numbering
 # to calendar.firstweekday numbering
 dow_gramps2iso = [ -1, calendar.SUNDAY, calendar.MONDAY, calendar.TUESDAY,
                    calendar.WEDNESDAY, calendar.THURSDAY, calendar.FRIDAY,
@@ -1644,8 +1779,8 @@
     # first day of the month
     current_date = datetime.date(year, month, 1)
 
-    # monthinfo is filled using standard Python library 
-    # calendar.monthcalendar. It fills a list of 7-day-lists. The first day 
+    # monthinfo is filled using standard Python library
+    # calendar.monthcalendar. It fills a list of 7-day-lists. The first day
     # of the 7-day-list is determined by calendar.firstweekday.
     monthinfo = calendar.monthcalendar(year, month)
 
@@ -1665,14 +1800,14 @@
     Will fill day_list and return it to its caller: calendar_build()
 
     holiday_list -- list of holidays for event_date
-    bday_anniv_list -- list of birthdays and anniversaries 
+    bday_anniv_list -- list of birthdays and anniversaries
         for event_date
 
-    event_date -- date for this day_list 
+    event_date -- date for this day_list
 
-    'day_list' - a combination of both dictionaries to be able 
-        to create one day nyears, date, text, event --- are 
-        necessary for figuring the age or years married for 
+    'day_list' - a combination of both dictionaries to be able
+        to create one day nyears, date, text, event --- are
+        necessary for figuring the age or years married for
         each day being created...
     """
 
@@ -1682,22 +1817,22 @@
     ##################################################################
     # birthday/ anniversary on this day
     # Date.EMPTY signifies an incomplete date for an event. See add_day_item()
-    bday_anniv_list = [(t, e, d) for t, e, d in bday_anniv_list
+    bday_anniv_list = [(t, e, d, n, x) for t, e, d, n, x in bday_anniv_list
                        if d != Date.EMPTY]
 
     # number of years have to be at least zero
-    bday_anniv_list = [(t, e, d) for t, e, d in bday_anniv_list
+    bday_anniv_list = [(t, e, d, n, x) for t, e, d, n, x in bday_anniv_list
                        if (event_date.get_year() - d.get_year()) >= 0]
 
     # a holiday
     # zero will force holidays to be first in list
     nyears = 0
 
-    for text, event, date in holiday_list:
-        day_list.append((nyears, date, text, event))
+    for text, event, date, notused, notused  in holiday_list:
+        day_list.append((nyears, date, text, event, notused, notused))
 
     # birthday and anniversary list
-    for text, event, date in bday_anniv_list:
+    for text, event, date, age_at_death, dead_event_date in bday_anniv_list:
 
         # number of years married, ex: 10
         nyears = (event_date.get_year() - date.get_year())
@@ -1709,12 +1844,18 @@
         # a birthday
         if event == 'Birthday':
 
-            txt_str = (text + ', <em>'
-               # TRANSLATORS: expands to smth like "12 years old",
-               # where "12 years" is already localized to your language
-            + (_('%s old') % str(age_str) 
-                if nyears else _('birth'))
-            + '</em>')
+            if age_at_death is not None:
+                death_symbol = "&#10014;" # latin cross for html code
+                mess = _("Died %(death_date)s.") % { 'death_date' : dead_event_date }
+                age = ", <font size='+1' ><b>%s</b></font> <em>%s (%s)" % (death_symbol, mess, age_at_death)
+            else:
+                # TRANSLATORS: expands to smth like "12 years old",
+                # where "12 years" is already localized to your language
+                age = ', <em>'
+                date_y = date.get_year()
+                age += _('%s old') % str(age_str) if date_y != 0 else \
+                                          _("Born %(birth_date)s.") % {'birth_date' : dead_event_date }
+            txt_str = (text + age + '</em>')
 
         # an anniversary
         elif event == "Anniversary":
@@ -1722,19 +1863,30 @@
             if nyears == 0:
                 txt_str = _('%(couple)s, <em>wedding</em>') % {
                             'couple' : text}
-            else: 
-                years_str = '<em>%s</em>' % nyears
-                # translators: leave all/any {...} untranslated
-                txt_str = ngettext("{couple}, {years} year anniversary",
-                                   "{couple}, {years} year anniversary",
-                                   nyears).format(couple=text, years=years_str)
+            else:
+                if age_at_death is not None:
+                    age = '%s %s' % ( _("Married"), age_at_death)
+                    txt_str = "%s, <em>%s" % (text, age)
+                    if isinstance(dead_event_date, Date) and dead_event_date.get_year() > 0:
+                        txt_str += " (" + _("Until") + " "
+                        txt_str += str(dead_event_date)
+                        txt_str += ")</em>"
+                    else:
+                        txt_str += "</em>"
+                else:
+                    age = '<em>%s' % nyears
+                    # translators: leave all/any {...} untranslated
+                    txt_str = ngettext("{couple}, {years} year anniversary",
+                                       "{couple}, {years} year anniversary",
+                                       nyears).format(couple=text, years=age)
+                    txt_str += "</em>"
             txt_str = Html('span', txt_str, class_ = "yearsmarried")
 
-        day_list.append((nyears, date, txt_str, event))
+        day_list.append((nyears, date, txt_str, event, age_at_death, dead_event_date))
 
     # sort them based on number of years
     # holidays will always be on top of event list
     day_list= sorted(day_list, key=lambda x: (isinstance(x[0], str), x[0]))
- 
+
     # return to its caller calendar_build()
     return day_list