- Revision
- 15973
- Author
- jeffrey
- Date
- 2007-11-29 17:29:49 -0800 (Thu, 29 Nov 2007)
Log Message
[APP] Use live data for column header and month text, handle range
changes and day_mode changes
changes and day_mode changes
Modified Paths
Diff
Modified: branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar.py (15972 => 15973)
--- branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar.py 2007-11-30 00:54:38 UTC (rev 15972) +++ branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar.py 2007-11-30 01:29:49 UTC (rev 15973) @@ -20,8 +20,15 @@ from drawing import DrawWrappedText, DrawClippedText, getFont from button import BitmapButton from wx.lib.stattext import GenStaticText +import wx.lib.newevent from wx import colheader +_ = lambda x : x + +from PyICU import DateFormatSymbols, GregorianCalendar +dateFormatSymbols = DateFormatSymbols() +gregorianCalendar = GregorianCalendar() + SWATCH_BUFFER = (2,2) TEXT_BUFFER = (2,2) OUTLINE_WIDTH = 1 @@ -56,6 +63,9 @@ MAJOR_LINE_COLOR = wx.Color(204, 204, 204) LEGEND_COLOR = wx.Color(128, 128, 128) +SetRangeEvent, EVT_SET_RANGE = wx.lib.newevent.NewEvent() +SetRangeModeEvent, EVT_SET_RANGE_MODE = wx.lib.newevent.NewEvent() + class InitializeSlots(object): __slots__ = () def __init__(self, **kw): @@ -108,15 +118,12 @@ self.levels = [] self.event_list = [] - def add(self, event): - if self.start is None or event.start < self.start: - self.start = event.start - if self.end is None or event.end > self.end: - self.end = event.end - - bisect.insort(self.event_list, event) - + def add_to_levels(self, event): for level in self.levels: + if event.overlaps(level[0]) and event < level[0]: + # got an out-of-order event + self.reset() + return for level_event in level: if event.overlaps(level_event): break @@ -125,6 +132,20 @@ return self.levels.append([event]) + def reset(self): + self.levels = [] + for event in self.event_list: + self.add_to_levels(event) + + def add(self, event): + if self.start is None or event.start < self.start: + self.start = event.start + if self.end is None or event.end > self.end: + self.end = event.end + + bisect.insort(self.event_list, event) + self.add_to_levels(event) + @property def duration(self): return self.end - self.start @@ -192,7 +213,8 @@ self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None) self.events = {} - self.days = [] + # EventsForDay or EventsForWeek associated with the current range + self.intervals = [] self.matrix = None self.hour_height = BASE_HOUR_HEIGHT @@ -289,12 +311,12 @@ self.DrawBackground(gc) - for day in self.days: - for cluster in day.clusters: + for interval in self.intervals: + for cluster in interval.clusters: max_depth = len(cluster.levels) for depth, level in enumerate(cluster.levels): for event in level: - args = event, day, depth, max_depth + args = event, interval, depth, max_depth rect, truncate, draw_text = self.LozengePosition(*args) self.DrawLozenge(gc, event, rect, truncate, draw_text) @@ -481,9 +503,17 @@ return 7 if self.range_mode == 'week' else 1 @property - def range_end(self): - return self.range_start + timedelta(self.GetDays()) + def start(self): + return self.range_start + @property + def duration(self): + return timedelta(self.GetDays()) + + @property + def end(self): + return self.start + self.duration + def SetRange(self, start): self.range_start = start self.Reset() @@ -495,10 +525,20 @@ if start is not None: self.SetRange(start) + def FillIntervals(self): + if self.range_start is not None: + for event in sorted(self.events.itervalues()): + if event.overlaps(self): + for interval in self.intervals: + if event.overlaps(interval): + interval.change(event) + def Reset(self): """Create empty EventsForDay/EventsForWeek for the current range.""" - self.days = [EventsForDay(self.range_start + timedelta(day)) - for day in xrange(self.GetDays())] + self.intervals = [EventsForDay(self.range_start + timedelta(day)) + for day in xrange(self.GetDays())] + self.FillIntervals() + self.Refresh(False) def ChangeEvent(self, id, **kw): """ @@ -512,12 +552,12 @@ for key, value in kw.iteritems(): setattr(event, key, value) - for day in self.days: - if event.overlaps(day): - day.change(event) + for interval in self.intervals: + if event.overlaps(interval): + interval.change(event) self.Refresh(False) - elif event in day.event_list: - day.remove(event) + elif event in interval.event_list: + interval.remove(event) self.Refresh(False) def RemoveEvent(self, id): @@ -547,9 +587,11 @@ def Reset(self): """Empty out events.""" if self.range_mode == 'day': - self.days = [EventsForDay(self.range_start)] + self.intervals = [EventsForDay(self.range_start)] else: - self.days = [EventsForWeek(self.range_start)] + self.intervals = [EventsForWeek(self.range_start)] + self.FillIntervals() + self.Refresh(False) def LozengePosition(self, event, range, depth, max_depth): """Return (rect, truncate, draw_text).""" @@ -584,6 +626,189 @@ return rect, truncate, True +class CalendarPanel(wx.Panel): + def __init__(self, parent, name): + super(CalendarPanel, self).__init__(parent, -1, style=wx.NO_BORDER) + self.Bind(wx.EVT_SIZE, self.OnSize) + + bg_color = self.GetBackgroundColour() + self.SetBackgroundColour(wx.WHITE) + + self.prevButton = BitmapButton(self, "CalBackArrow") + self.nextButton = BitmapButton(self, "CalForwardArrow") + + self.Bind(wx.EVT_BUTTON, self.OnPrev, self.prevButton) + self.Bind(wx.EVT_BUTTON, self.OnNext, self.nextButton) + + self.monthText = GenStaticText(self, -1, 'November - December 2007') + + self.monthText.SetFont(getFont(size=15, weight=wx.BOLD)) + self.monthText.SetForegroundColour(wx.Color(64, 64, 64)) + + self.setupColumnHeader() + + splitterStyle = wx.SP_LIVE_UPDATE | wx.NO_BORDER | wx.SP_3DSASH + splitter = wx.SplitterWindow(self, style=splitterStyle) + # on the Mac, the splitter would inherit the panel's white background + splitter.SetBackgroundColour(bg_color) + + self.calendar = Calendar(parent=splitter, time_formatter=None) + self.allday = AllDayCalendar(parent=splitter, time_formatter=None) + + splitter.SplitHorizontally(self.allday, self.calendar) + splitter.SetMinimumPaneSize(20) + splitter.SashPosition = 20 + + self.range_start = None + self.range_mode = 'week' + # ICU's week is offset from isoweekday by one + self.first_day_of_week = gregorianCalendar.getFirstDayOfWeek() - 1 + + self.SetRange(datetime(2007,11,25)) + + sizer = wx.GridBagSizer(0, 0) + sizer.Add(self.prevButton, (0, 1), flag=wx.ALIGN_CENTER) + sizer.Add(self.nextButton, (0, 3), flag=wx.ALIGN_CENTER) + sizer.Add((10,30), (0, 4)) + sizer.Add(self.monthText, (0, 5), flag=wx.ALIGN_CENTER) + sizer.Add(self.weekColumnHeader, (1, 0), (1,8), flag=wx.EXPAND) + sizer.Add(splitter, (2,0), (1,8), flag=wx.EXPAND) + sizer.AddGrowableRow(2) + sizer.AddGrowableCol(6) + sizer.SetEmptyCellSize((5,5)) + + self.SetSizerAndFit(sizer) + + def setupColumnHeader(self): + header = self.weekColumnHeader = colheader.ColumnHeader(self) + + self.Bind(colheader.EVT_COLUMNHEADER_SELCHANGED, + self.OnDayColumnSelect, header) + + # turn this off for now, because our sizing needs to be exact + header.SetAttribute(colheader.CH_ATTR_ProportionalResizing,False) + + labels = [_(u"Week"), "S", "M", "Tu", "W", "Th", "F", "S", ''] + for label in labels: + header.AppendItem(label, wx.ALIGN_CENTER, 0, bSortEnabled=False) + + # set up initial selection + header.SetAttribute(colheader.CH_ATTR_VisibleSelection, True) + header.SetSelectedItem(0) + + self.margin_right = wx.SystemSettings_GetMetric(wx.SYS_VSCROLL_X) + self.margin_left = MARGIN_LEFT + if wx.Platform == "__WXMSW__": + self.margin_left += 1 + elif wx.Platform == "__WXMAC__": + self.margin_right += 1 + self.ResizeColumnHeader() + + def ResizeColumnHeader(self): + header = self.weekColumnHeader + header.Freeze() + widths = [self.margin_left, self.margin_right] + inner_width = self.GetSize()[0] - int(sum(widths)) + widths[1:1] = [inner_width/7] * 7 + remainder = inner_width % 7 + # evenly distribute remainder + for i in range(0, 3 * remainder, 3): + widths[(i % 7) + 1] += 1 + for i, width in enumerate(widths): + header.SetItemSize(i, (width, header.GetItemSize(i)[1])) + header.Thaw() + + def StartOfRange(self, date, force_week=False): + """Return the beginning of date's week, if in week mode, or date.""" + if self.range_mode == 'week' or force_week: + delta = (date.isoweekday() - self.first_day_of_week) % 7 + return date - timedelta(delta) + else: + return date + + def SetRange(self, range_start): + week_start = self.StartOfRange(range_start) + self.range_start = week_start + self.calendar.SetRange(week_start) + self.allday.SetRange(week_start) + self.UpdateHeader() + wx.PostEvent(self, SetRangeEvent(range_start=week_start)) + + def SetRangeMode(self, range_mode, start=None): + self.range_mode = range_mode + if start is None: + start = self.range_start + if start is not None: + start = self.StartOfRange(start) + self.range_start = start + self.UpdateHeader() + self.calendar.SetRangeMode(range_mode, start) + self.allday.SetRangeMode(range_mode, start) + wx.PostEvent(self, SetRangeModeEvent(range_mode=range_mode, + range_start=start)) + + def OnNext(self, evt): + delta = timedelta(7) if self.range_mode == 'week' else timedelta(1) + self.SetRange(self.range_start + delta) + + def OnPrev(self, evt): + delta = timedelta(7) if self.range_mode == 'week' else timedelta(1) + self.SetRange(self.range_start - delta) + + def OnDayColumnSelect(self, event): + colIndex = self.weekColumnHeader.GetSelectedItem() + + # column 0, week button + if colIndex == 0: + self.SetRangeMode('week') + elif colIndex == 8: + pass + else: + first = self.StartOfRange(self.range_start, force_week=True) + self.SetRangeMode('day', first + timedelta(colIndex - 1)) + + def UpdateHeader(self): + """Update the column header and month text.""" + first = self.StartOfRange(self.range_start, force_week=True).date() + last = first + timedelta(6) + months = dateFormatSymbols.getMonths() + year = last.year + + data = "" - 1], currentYear=year) + if (first.month == last.month): + # L10N: The header text of a Calendar week view. + # Displays the month and year ie. October 2007 + monthText = _(u'%(currentMonth)s %(currentYear)d') % data + else: + data['currentMonth1'] = data['currentMonth'] + data['currentMonth2'] = months[last.month - 1] + # L10N: The header text of a Calendar week view that has + # displayable days in two months. ie. October - November 2007 + form = _(u'%(currentMonth1)s - %(currentMonth2)s %(currentYear)d') + monthText = form % data + + self.monthText.SetLabel(monthText) + + shortWeekdays = dateFormatSymbols.getShortWeekdays() + + self.weekColumnHeader.Freeze() + for delta in xrange(7): + dt = first + timedelta(delta) + name = u"%s %d" %(shortWeekdays[dt.isoweekday() % 7 + 1], dt.day) + self.weekColumnHeader.SetLabelText(delta + 1, name) + + if self.range_mode == 'week': + self.weekColumnHeader.SetSelectedItem(0) + else: + reldate = self.range_start.date() - first + self.weekColumnHeader.SetSelectedItem(reldate.days+1) + + self.weekColumnHeader.Thaw() + + def OnSize(self, event): + self.ResizeColumnHeader() + event.Skip() + ############## Utility functions ############################################### def GetGradientBrush(gc, offset, width, gradient_left, gradient_right): return gc.CreateLinearGradientBrush(offset, 0, offset + width, 0, @@ -701,90 +926,6 @@ lorem = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt uty laboreq et dolore magna aliqua. Ut enimy qad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' -class CalendarPanel(wx.Panel): - def __init__(self, parent, name): - super(CalendarPanel, self).__init__(parent, -1, style=wx.NO_BORDER) - self.Bind(wx.EVT_SIZE, self.OnSize) - - bg_color = self.GetBackgroundColour() - self.SetBackgroundColour(wx.WHITE) - - self.prevButton = BitmapButton(self, "CalBackArrow") - self.nextButton = BitmapButton(self, "CalForwardArrow") - self.monthText = GenStaticText(self, -1, 'November - December 2007') - - self.monthText.SetFont(getFont(size=15, weight=wx.BOLD)) - self.monthText.SetForegroundColour(wx.Color(64, 64, 64)) - self.setupHeader() - - splitterStyle = wx.SP_LIVE_UPDATE | wx.NO_BORDER | wx.SP_3DSASH - splitter = wx.SplitterWindow(self, style=splitterStyle) - # on the Mac, the splitter would inherit the panel's white background - splitter.SetBackgroundColour(bg_color) - - self.calendar = Calendar(parent=splitter, time_formatter=None) - self.allday = AllDayCalendar(parent=splitter, time_formatter=None) - - splitter.SplitHorizontally(self.allday, self.calendar) - splitter.SetMinimumPaneSize(20) - splitter.SashPosition = 20 - - self.calendar.SetRange(datetime(2007,11,25)) - self.allday.SetRange(datetime(2007,11,25)) - - sizer = wx.GridBagSizer(0, 0) - sizer.Add(self.prevButton, (0, 1), flag=wx.ALIGN_CENTER) - sizer.Add(self.nextButton, (0, 3), flag=wx.ALIGN_CENTER) - sizer.Add((10,30), (0, 4)) - sizer.Add(self.monthText, (0, 5), flag=wx.ALIGN_CENTER) - sizer.Add(self.weekColumnHeader, (1, 0), (1,8), flag=wx.EXPAND) - sizer.Add(splitter, (2,0), (1,8), flag=wx.EXPAND) - sizer.AddGrowableRow(2) - sizer.AddGrowableCol(6) - sizer.SetEmptyCellSize((5,5)) - - self.SetSizerAndFit(sizer) - - def setupHeader(self): - weekColumnHeader = self.weekColumnHeader = colheader.ColumnHeader(self) - - # turn this off for now, because our sizing needs to be exact - weekColumnHeader.SetAttribute(colheader.CH_ATTR_ProportionalResizing,False) - - headerLabels = ["Week", "S", "M", "Tu", "W", "Th", "F", "S", ''] - for header in headerLabels: - weekColumnHeader.AppendItem(header, wx.ALIGN_CENTER, 0, bSortEnabled=False) - - # set up initial selection - weekColumnHeader.SetAttribute(colheader.CH_ATTR_VisibleSelection, True) - self.weekColumnHeader.SetSelectedItem(0) - self.margin_right = wx.SystemSettings_GetMetric(wx.SYS_VSCROLL_X) - self.margin_left = MARGIN_LEFT - if wx.Platform == "__WXMSW__": - self.margin_left += 1 - elif wx.Platform == "__WXMAC__": - self.margin_right += 1 - self.ResizeHeader() - - def ResizeHeader(self): - self.weekColumnHeader.Freeze() - widths = [self.margin_left, self.margin_right] - inner_width = self.GetSize()[0] - int(sum(widths)) - widths[1:1] = [inner_width/7] * 7 - remainder = inner_width % 7 - # evenly distribute remainder - for i in range(0, 3 * remainder, 3): - widths[(i % 7) + 1] += 1 - for (i,width) in enumerate(widths): - extentPt = self.weekColumnHeader.GetItemSize(i) - extentPt.width = width - self.weekColumnHeader.SetItemSize(i, extentPt) - self.weekColumnHeader.Thaw() - - def OnSize(self, event): - self.ResizeHeader() - event.Skip() - def Demo(app=None): if app is None: wxApp = wx.PySimpleApp() @@ -823,12 +964,12 @@ start=datetime(2007,11,25,23,45), duration=timedelta(hours=1)) + allday.ChangeEvent(id="bar1", title=lorem, + start=datetime(2007,11,25), + duration=timedelta(2)) allday.ChangeEvent(id="bar0", title=lorem, start=datetime(2007,11,24), duration=timedelta(3)) - allday.ChangeEvent(id="bar1", title=lorem, - start=datetime(2007,11,25), - duration=timedelta(2)) allday.ChangeEvent(id="bar2", title=lorem, start=datetime(2007,11,27), duration=timedelta(1))
_______________________________________________ Commits mailing list [email protected] http://lists.osafoundation.org/mailman/listinfo/commits
