diff --git a/src/DateEdit.py b/src/DateEdit.py index d05bd861d..c6a289c23 100644 --- a/src/DateEdit.py +++ b/src/DateEdit.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2002-2006 Donald N. Allingham +# Copyright (C) 2009 Douglas S. Blank # # 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 @@ -248,10 +249,22 @@ class DateEditorDialog(ManagedWindow.ManagedWindow): self.start_year.set_sensitive(0) self.calendar_box.set_sensitive(0) self.quality_box.set_sensitive(0) + self.dual_dated.set_sensitive(0) + self.new_year.set_sensitive(0) self.text_entry = self.top.get_widget('date_text_entry') self.text_entry.set_text(self.date.get_text()) - + + self.dual_dated = self.top.get_widget('dualdated') + if self.date.get_slash(): + self.dual_dated.set_active(1) + self.calendar_box.set_sensitive(0) + self.calendar_box.set_active(Date.CAL_JULIAN) + self.dual_dated.connect('toggled', self.switch_dual_dated) + + self.new_year = self.top.get_widget('newyear') + self.new_year.set_active(self.date.get_new_year()) + # The dialog is modal -- since dates don't have names, we don't # want to have several open dialogs, since then the user will # loose track of which is which. Much like opening files. @@ -270,14 +283,15 @@ class DateEditorDialog(ManagedWindow.ManagedWindow): else: if response == gtk.RESPONSE_OK: (the_quality, the_modifier, the_calendar, - the_value, the_text) = self.build_date_from_ui() + the_value, the_text, the_newyear) = self.build_date_from_ui() self.return_date = Date(self.date) self.return_date.set( quality=the_quality, modifier=the_modifier, calendar=the_calendar, value=the_value, - text=the_text) + text=the_text, + newyear=the_newyear) self.close() break @@ -313,19 +327,20 @@ class DateEditorDialog(ManagedWindow.ManagedWindow): self.start_day.get_value_as_int(), self.start_month_box.get_active(), self.start_year.get_value_as_int(), - False, + self.dual_dated.get_active(), self.stop_day.get_value_as_int(), self.stop_month_box.get_active(), self.stop_year.get_value_as_int(), - False) + self.dual_dated.get_active()) else: value = ( self.start_day.get_value_as_int(), self.start_month_box.get_active(), self.start_year.get_value_as_int(), - False) + self.dual_dated.get_active()) calendar = self.calendar_box.get_active() - return (quality, modifier, calendar, value, text) + newyear = self.new_year.get_active() + return (quality, modifier, calendar, value, text, newyear) def switch_type(self, obj): """ @@ -352,6 +367,20 @@ class DateEditorDialog(ManagedWindow.ManagedWindow): self.start_year.set_sensitive(date_sensitivity) self.calendar_box.set_sensitive(date_sensitivity) self.quality_box.set_sensitive(date_sensitivity) + self.dual_dated.set_sensitive(date_sensitivity) + self.new_year.set_sensitive(date_sensitivity) + + def switch_dual_dated(self, obj): + """ + Changed whether this is a dual dated year, or not. + Dual dated years are represented in the Julian calendar + so that the day/months don't changed in the Text representation. + """ + if self.dual_dated.get_active(): + self.calendar_box.set_active(Date.CAL_JULIAN) + self.calendar_box.set_sensitive(0) + else: + self.calendar_box.set_sensitive(1) def switch_calendar(self, obj): """ @@ -362,14 +391,15 @@ class DateEditorDialog(ManagedWindow.ManagedWindow): old_cal = self.date.get_calendar() new_cal = self.calendar_box.get_active() - (the_quality, the_modifier, the_calendar, the_value, the_text) = \ - self.build_date_from_ui() + (the_quality, the_modifier, the_calendar, + the_value, the_text, the_newyear) = self.build_date_from_ui() self.date.set( quality=the_quality, modifier=the_modifier, calendar=old_cal, value=the_value, - text=the_text) + text=the_text, + newyear=the_newyear) if not self.date.is_empty(): self.date.convert_calendar(new_cal) diff --git a/src/DateHandler/_DateDisplay.py b/src/DateHandler/_DateDisplay.py index 0150ef430..a47a66416 100644 --- a/src/DateHandler/_DateDisplay.py +++ b/src/DateHandler/_DateDisplay.py @@ -91,9 +91,11 @@ class DateDisplay: formats = ("YYYY-MM-DD (ISO)", ) calendar = ( - "", " (Julian)", " (Hebrew)", " (French Republican)", - " (Persian)", " (Islamic)" + "", "Julian", "Hebrew", "French Republican", + "Persian", "Islamic" ) + + newyear = ("", "Mar1", "Mar25", "Sep1") _mod_str = ("", "before ", "after ", "about ", "", "", "") @@ -133,6 +135,22 @@ class DateDisplay: else: return self.display(date) + def format_extras(self, cal, newyear): + """ + Formats the extra items (calendar, newyear) for a date. + """ + scal = self.calendar[cal] + snewyear = self.newyear[newyear] + retval = "" + for item in [scal, snewyear]: + if item: + if retval: + retval += "," + retval += item + if retval: + return " (%s)" % retval + return "" + def display(self, date): """ Return a text string representing the date. @@ -141,6 +159,7 @@ class DateDisplay: cal = date.get_calendar() qual = date.get_quality() start = date.get_start_date() + newyear = date.get_new_year() qual_str = self._qual_str[qual] @@ -151,11 +170,12 @@ class DateDisplay: elif mod == Date.MOD_SPAN or mod == Date.MOD_RANGE: d1 = self.display_iso(start) d2 = self.display_iso(date.get_stop_date()) - return "%s %s - %s%s" % (qual_str, d1, d2, self.calendar[cal]) + scal = self.format_extras(cal, newyear) + return "%s %s - %s%s" % (qual_str, d1, d2, scal) else: text = self.display_iso(start) - return "%s%s%s%s" % (qual_str, self._mod_str[mod], text, - self.calendar[cal]) + scal = self.format_extras(cal, newyear) + return "%s%s%s%s" % (qual_str, self._mod_str[mod], text, scal) def _slash_year(self, val, slash): if val < 0: @@ -328,6 +348,7 @@ class DateDisplayEn(DateDisplay): cal = date.get_calendar() qual = date.get_quality() start = date.get_start_date() + newyear = date.get_new_year() qual_str = self._qual_str[qual] @@ -338,13 +359,14 @@ class DateDisplayEn(DateDisplay): elif mod == Date.MOD_SPAN: d1 = self.display_cal[cal](start) d2 = self.display_cal[cal](date.get_stop_date()) - return "%sfrom %s to %s%s" % (qual_str, d1, d2, self.calendar[cal]) + scal = self.format_extras(cal, newyear) + return "%sfrom %s to %s%s" % (qual_str, d1, d2, scal) elif mod == Date.MOD_RANGE: d1 = self.display_cal[cal](start) d2 = self.display_cal[cal](date.get_stop_date()) - return "%sbetween %s and %s%s" % (qual_str, d1, d2, - self.calendar[cal]) + scal = self.format_extras(cal, newyear) + return "%sbetween %s and %s%s" % (qual_str, d1, d2, scal) else: text = self.display_cal[date.get_calendar()](start) - return "%s%s%s%s" % (qual_str, self._mod_str[mod], - text, self.calendar[cal]) + scal = self.format_extras(cal, newyear) + return "%s%s%s%s" % (qual_str, self._mod_str[mod], text, scal) diff --git a/src/DateHandler/_DateParser.py b/src/DateHandler/_DateParser.py index 04970ce34..99f32906d 100644 --- a/src/DateHandler/_DateParser.py +++ b/src/DateHandler/_DateParser.py @@ -173,6 +173,12 @@ class DateParser: 'persian' : Date.CAL_PERSIAN, 'p' : Date.CAL_PERSIAN, } + + newyear_to_int = { + "mar1": Date.NEWYEAR_MAR1, + "mar25": Date.NEWYEAR_MAR25, + "sep1" : Date.NEWYEAR_SEP1, + } quality_to_int = { 'estimated' : Date.QUAL_ESTIMATED, @@ -240,6 +246,7 @@ class DateParser: self._pmon_str = self.re_longest_first(self.persian_to_int.keys()) self._imon_str = self.re_longest_first(self.islamic_to_int.keys()) self._cal_str = self.re_longest_first(self.calendar_to_int.keys()) + self._ny_str = self.re_longest_first(self.newyear_to_int.keys()) # bce, calendar type and quality may be either at the end or at # the beginning of the given date string, therefore they will @@ -248,6 +255,11 @@ class DateParser: self._cal = re.compile("(.*)\s+\(%s\)( ?.*)" % self._cal_str, re.IGNORECASE) + self._calny = re.compile("(.*)\s+\(%s,%s\)( ?.*)" % (self._cal_str, + self._ny_str), + re.IGNORECASE) + self._ny = re.compile("(.*)\s+\(%s\)( ?.*)" % self._ny_str, + re.IGNORECASE) self._qual = re.compile("(.* ?)%s\s+(.+)" % self._qual_str, re.IGNORECASE) @@ -448,6 +460,31 @@ class DateParser: text = match.group(1) + match.group(3) return (text, cal) + def match_calendar_newyear(self, text, cal, newyear): + """ + Try parsing calendar and newyear code. + + Return newyear index and the text with calendar removed. + """ + match = self._calny.match(text) + if match: + cal = self.calendar_to_int[match.group(2).lower()] + newyear = self.newyear_to_int[match.group(3).lower()] + text = match.group(1) + match.group(4) + return (text, cal, newyear) + + def match_newyear(self, text, newyear): + """ + Try parsing calendar and newyear code. + + Return newyear index and the text with calendar removed. + """ + match = self._ny.match(text) + if match: + newyear = self.newyear_to_int[match.group(2).lower()] + text = match.group(1) + match.group(3) + return (text, newyear) + def match_quality(self, text, qual): """ Try matching quality. @@ -460,7 +497,7 @@ class DateParser: text = match.group(1) + match.group(3) return (text, qual) - def match_span(self, text, cal, qual, date): + def match_span(self, text, cal, ny, qual, date): """ Try matching span date. @@ -479,11 +516,11 @@ class DateParser: if bc2: stop = self.invert_year(stop) - date.set(qual, Date.MOD_SPAN, cal, start + stop) + date.set(qual, Date.MOD_SPAN, cal, start + stop, newyear=ny) return 1 return 0 - def match_range(self, text, cal, qual, date): + def match_range(self, text, cal, ny, qual, date): """ Try matching range date. @@ -502,7 +539,7 @@ class DateParser: if bc2: stop = self.invert_year(stop) - date.set(qual, Date.MOD_RANGE, cal, start + stop) + date.set(qual, Date.MOD_RANGE, cal, start + stop, newyear=ny) return 1 return 0 @@ -523,7 +560,7 @@ class DateParser: bc = True return (text, bc) - def match_modifier(self, text, cal, qual, bc, date): + def match_modifier(self, text, cal, ny, qual, bc, date): """ Try matching date with modifier. @@ -539,9 +576,9 @@ class DateParser: date.set_modifier(Date.MOD_TEXTONLY) date.set_text_value(text) elif bc: - date.set(qual, mod, cal, self.invert_year(start)) + date.set(qual, mod, cal, self.invert_year(start), newyear=ny) else: - date.set(qual, mod, cal, start) + date.set(qual, mod, cal, start, newyear=ny) return True # modifiers after the date if self.modifier_after_to_int: @@ -552,9 +589,9 @@ class DateParser: mod = self.modifier_after_to_int.get(grps[1].lower(), Date.MOD_NONE) if bc: - date.set(qual, mod, cal, self.invert_year(start)) + date.set(qual, mod, cal, self.invert_year(start), newyear=ny) else: - date.set(qual, mod, cal, start) + date.set(qual, mod, cal, start, newyear=ny) return True match = self._abt2.match(text) if match: @@ -562,9 +599,9 @@ class DateParser: start = self._parse_subdate(grps[0]) mod = Date.MOD_ABOUT if bc: - date.set(qual, mod, cal, self.invert_year(start)) + date.set(qual, mod, cal, self.invert_year(start), newyear=ny) else: - date.set(qual, mod, cal, start) + date.set(qual, mod, cal, start, newyear=ny) return True return False @@ -576,17 +613,20 @@ class DateParser: date.set_text_value(text) qual = Date.QUAL_NONE cal = Date.CAL_GREGORIAN + newyear = Date.NEWYEAR_JAN1 + (text, cal, newyear) = self.match_calendar_newyear(text, cal, newyear) (text, cal) = self.match_calendar(text, cal) + (text, newyear) = self.match_newyear(text, newyear) (text, qual) = self.match_quality(text, qual) - if self.match_span(text, cal, qual, date): + if self.match_span(text, cal, newyear, qual, date): return - if self.match_range(text, cal, qual, date): + if self.match_range(text, cal, newyear, qual, date): return (text, bc) = self.match_bce(text) - if self.match_modifier(text, cal, qual, bc, date): + if self.match_modifier(text, cal, newyear, qual, bc, date): return try: @@ -599,13 +639,9 @@ class DateParser: return if bc: - date.set(qual, Date.MOD_NONE, cal, self.invert_year(subdate)) + date.set(qual, Date.MOD_NONE, cal, self.invert_year(subdate), newyear=newyear) else: - date.set(qual, Date.MOD_NONE, cal, subdate) - - if date.get_slash(): - date.set_calendar(Date.CAL_JULIAN) - date.recalc_sort_value() # needed after the calendar change + date.set(qual, Date.MOD_NONE, cal, subdate, newyear=newyear) def invert_year(self, subdate): return (subdate[0], subdate[1], -subdate[2], subdate[3]) diff --git a/src/gen/lib/date.py b/src/gen/lib/date.py index 16334c8a5..9d6fae82b 100644 --- a/src/gen/lib/date.py +++ b/src/gen/lib/date.py @@ -259,6 +259,9 @@ class Span: def is_valid(self): return self.valid + def tuple(self): + return self._diff(self.date1, self.date2) + def __getitem__(self, pos): # Depricated! return self._diff(self.date1, self.date2)[pos] @@ -664,6 +667,8 @@ class Date: if isinstance(source, tuple): if calendar is None: self.calendar = Date.CAL_GREGORIAN + elif isinstance(calendar, int): + self.calendar = calendar else: self.calendar = self.lookup_calendar(calendar) if modifier is None: @@ -1165,6 +1170,18 @@ class Date: """ return self._get_low_item(Date._POS_YR) + def get_new_year(self): + """ + Return the new year code associated with the date. + """ + return self.newyear + + def set_new_year(self, value): + """ + Set the new year code associated with the date. + """ + self.newyear = value + def set_yr_mon_day(self, year, month, day): """ Set the year, month, and day values. @@ -1335,7 +1352,8 @@ class Date: """ return self.text - def set(self, quality, modifier, calendar, value, text=None): + def set(self, quality, modifier, calendar, value, text=None, + newyear=0): """ Set the date to the specified value. @@ -1379,14 +1397,38 @@ class Date: self.modifier = modifier self.calendar = calendar self.dateval = value + self.set_new_year(newyear) year = max(value[Date._POS_YR], 1) month = max(value[Date._POS_MON], 1) day = max(value[Date._POS_DAY], 1) + if year == month == 0 and day == 0: self.sortval = 0 else: func = Date._calendar_convert[calendar] self.sortval = func(year, month, day) + + if self.get_slash() and self.get_calendar() != Date.CAL_JULIAN: + self.set_calendar(Date.CAL_JULIAN) + self.recalc_sort_value() + + ny = self.get_new_year() + if ny: # new year offset? + if ny == Date.NEWYEAR_MAR1: + split = (3, 1) + elif ny == Date.NEWYEAR_MAR25: + split = (3, 25) + elif ny == Date.NEWYEAR_SEP1: + split = (9, 1) + if (self.get_month(), self.get_day()) >= split: + d1 = Date(self.get_year(), 1, 1, calendar=self.calendar).sortval + d2 = Date(self.get_year(), split[0], split[1], calendar=self.calendar).sortval + self.sortval -= (d2 - d1) + else: + d1 = Date(self.get_year(), 12, 31, calendar=self.calendar).sortval + d2 = Date(self.get_year(), split[0], split[1], calendar=self.calendar).sortval + self.sortval += (d1 - d2) + 1 + if text: self.text = text @@ -1551,10 +1593,16 @@ class Date: def get_slash(self): """ - Return true if the date is a slash-date. + Return true if the date is a slash-date (dual dated). """ return self._get_low_item_valid(Date._POS_SL) + def set_slash(self, value): + """ + Set to 1 if the date is a slash-date (dual dated). + """ + self.dateval[Date._POS_SL] = value + def Today(): """ Returns a Date object set to the current date. diff --git a/src/glade/gramps.glade b/src/glade/gramps.glade index 382b57a76..5f4e8cba7 100644 --- a/src/glade/gramps.glade +++ b/src/glade/gramps.glade @@ -9354,6 +9354,108 @@ + + + True + False + 0 + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 15 + False + False + + + + + + True + Old Style/New Style + True + Dua_l dated + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + Ne_w year begins: + True + False + GTK_JUSTIFY_RIGHT + False + False + 1 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + True + True + + + + + + True + January 1 +March 1 +March 25 +September 1 + + False + True + + + + 0 + True + True + + + + + 6 + True + True + + + True @@ -9813,8 +9915,8 @@ 6 - True - True + False + False @@ -9870,9 +9972,9 @@ - 6 - False - False + 0 + True + True