Source code for timelinelib.calendar.bosparanian.timepicker.datepickercontroller

# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
#
# This file is part of Timeline.
#
# Timeline 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 3 of the License, or
# (at your option) any later version.
#
# Timeline is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Timeline.  If not, see <http://www.gnu.org/licenses/>.


from timelinelib.calendar.bosparanian.bosparanian import BosparanianDateTime
from timelinelib.calendar.bosparanian.dateformatter import BosparanianDateFormatter
from timelinelib.calendar.bosparanian.time import BosparanianDelta
from timelinelib.calendar.bosparanian.timetype.timetype import BosparanianTimeType


[docs]class BosparanianDatePickerController:
[docs] def __init__(self, date_picker, error_bg="pink", on_change=None): self.date_picker = date_picker self.error_bg = error_bg self.original_bg = self.date_picker.GetBackgroundColour() self.date_formatter = BosparanianDateFormatter() self.separator = self.date_formatter.separator() self.region_year, self.region_month, self.region_day = self.date_formatter.get_regions() self.region_siblings = ((self.region_year, self.region_month), (self.region_month, self.region_day)) self.preferred_day = None self.save_preferred_day = True self.last_selection = None self.on_change = on_change
[docs] def GetDate(self): try: (year, month, day) = self._parse_year_month_day() self._ensure_within_allowed_period((year, month, day)) return (year, month, day) except ValueError: raise ValueError("Invalid date.")
[docs] def SetDate(self, value): year, month, day = value date_string = self.date_formatter.format(year, month, day) self.date_picker.set_date_string(date_string) self._on_change()
def _on_change(self): if self._current_date_is_valid() and self.on_change is not None: self.on_change()
[docs] def on_set_focus(self): if self.last_selection: start, end = self.last_selection self.date_picker.SetSelection(start, end) else: self._select_region_if_possible(self.region_year) self.last_selection = self.date_picker.GetSelection()
[docs] def on_kill_focus(self): if self.last_selection: self.last_selection = self.date_picker.GetSelection()
[docs] def on_tab(self): for (left_region, right_region) in self.region_siblings: if self._insertion_point_in_region(left_region): self._select_region_if_possible(right_region) return False return True
[docs] def on_shift_tab(self): for (left_region, right_region) in self.region_siblings: if self._insertion_point_in_region(right_region): self._select_region_if_possible(left_region) return False return True
[docs] def on_text_changed(self): self._change_background_depending_on_date_validity() if self._current_date_is_valid(): current_date = self.GetDate() # To prevent saving of preferred day when year or month is changed # in on_up() and on_down()... # Save preferred day only when text is entered in the date text # control and not when up or down keys has been used. # When up and down keys are used, the preferred day is saved in # on_up() and on_down() only when day is changed. if self.save_preferred_day: self._save_preferred_day(current_date) self._on_change()
[docs] def on_up(self): max_year = BosparanianDateTime.from_time(BosparanianTimeType().get_max_time()).year def increment_year(date): year, month, day = date if year < max_year - 1: return self._set_valid_day(year + 1, month, day) return date def increment_month(date): year, month, day = date if month < 13: return self._set_valid_day(year, month + 1, day) elif year < max_year - 1: return self._set_valid_day(year + 1, 1, day) return date def increment_day(date): year, month, day = date time = BosparanianDateTime.from_ymd(year, month, day).to_time() if time < BosparanianTimeType().get_max_time() - BosparanianDelta.from_days(1): return BosparanianDateTime.from_time(time + BosparanianDelta.from_days(1)).to_date_tuple() return date if not self._current_date_is_valid(): return selection = self.date_picker.GetSelection() current_date = self.GetDate() if self._insertion_point_in_region(self.region_year): new_date = increment_year(current_date) elif self._insertion_point_in_region(self.region_month): new_date = increment_month(current_date) else: new_date = increment_day(current_date) self._save_preferred_day(new_date) if current_date != new_date: self._set_new_date_and_restore_selection(new_date, selection) self._on_change()
[docs] def on_down(self): def decrement_year(date): year, month, day = date if year > BosparanianDateTime.from_time(BosparanianTimeType().get_min_time()).year: return self._set_valid_day(year - 1, month, day) return date def decrement_month(date): year, month, day = date if month > 1: return self._set_valid_day(year, month - 1, day) elif year > BosparanianDateTime.from_time(BosparanianTimeType().get_min_time()).year: return self._set_valid_day(year - 1, 13, day) return date def decrement_day(date): year, month, day = date if day > 1: return self._set_valid_day(year, month, day - 1) elif month > 1: return self._set_valid_day(year, month - 1, 30) elif year > BosparanianDateTime.from_time(BosparanianTimeType().get_min_time()).year: return self._set_valid_day(year - 1, 13, 5) return date if not self._current_date_is_valid(): return selection = self.date_picker.GetSelection() current_date = self.GetDate() if self._insertion_point_in_region(self.region_year): new_date = decrement_year(current_date) elif self._insertion_point_in_region(self.region_month): new_date = decrement_month(current_date) else: year, month, day = current_date BosparanianDateTime.from_ymd(year, month, day) if BosparanianDateTime.from_ymd(year, month, day).to_time() == BosparanianTimeType().get_min_time(): return new_date = decrement_day(current_date) self._save_preferred_day(new_date) if current_date != new_date: self._set_new_date_and_restore_selection(new_date, selection) self._on_change()
def _change_background_depending_on_date_validity(self): if self._current_date_is_valid(): self.date_picker.SetBackgroundColour(self.original_bg) else: self.date_picker.SetBackgroundColour(self.error_bg) self.date_picker.SetFocus() self.date_picker.Refresh() def _parse_year_month_day(self): return self.date_formatter.parse(self.date_picker.get_date_string()) def _ensure_within_allowed_period(self, date): year, month, day = date time = BosparanianDateTime(year, month, day, 0, 0, 0).to_time() if (time >= BosparanianTimeType().get_max_time() or time < BosparanianTimeType().get_min_time()): raise ValueError() def _set_new_date_and_restore_selection(self, new_date, selection): def restore_selection(selection): self.date_picker.SetSelection(selection[0], selection[1]) self.save_preferred_day = False if self.preferred_day is not None: year, month, _ = new_date new_date = self._set_valid_day(year, month, self.preferred_day) self.SetDate(new_date) restore_selection(selection) self.save_preferred_day = True def _set_valid_day(self, new_year, new_month, new_day): done = False while not done: try: date = BosparanianDateTime.from_ymd(new_year, new_month, new_day) done = True except Exception: new_day -= 1 return date.to_date_tuple() def _save_preferred_day(self, date): _, _, day = date if day > 28: self.preferred_day = day else: self.preferred_day = None def _current_date_is_valid(self): try: self.GetDate() except ValueError: return False return True def _select_region_if_possible(self, region): region_range = self._get_region_range(region) if region_range: self.date_picker.SetSelection(region_range[0], region_range[-1]) def _insertion_point_in_region(self, n): region_range = self._get_region_range(n) if region_range: return self.date_picker.GetInsertionPoint() in region_range def _get_region_range(self, n): # Returns a range of valid cursor positions for a valid region year, # month or day. def region_is_not_valid(region): return region not in (self.region_year, self.region_month, self.region_day) def date_has_exactly_two_seperators(datestring): return len(datestring.split(self.separator)) == 3 def calculate_pos_range(region, datestring): pos_of_separator1 = datestring.find(self.separator) pos_of_separator2 = datestring.find(self.separator, pos_of_separator1 + 1) if region == self.region_year: return range(0, pos_of_separator1 + 1) elif region == self.region_month: return range(pos_of_separator1 + 1, pos_of_separator2 + 1) else: return range(pos_of_separator2 + 1, len(datestring) + 1) if region_is_not_valid(n): return None date = self.date_picker.get_date_string() if not date_has_exactly_two_seperators(date): return None pos_range = calculate_pos_range(n, date) return pos_range