- Revision
- 15947
- Author
- jeffrey
- Date
- 2007-11-28 17:37:51 -0800 (Wed, 28 Nov 2007)
Log Message
[APP] Add all-day canvas
Modified Paths
Diff
Modified: branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar.py (15946 => 15947)
--- branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar.py 2007-11-29 01:21:06 UTC (rev 15946) +++ branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar.py 2007-11-29 01:37:51 UTC (rev 15947) @@ -37,7 +37,7 @@ TIME_BOTTOM_MARGIN = 2 -# arbitrary base size to use as a reference point for calculating calendar sizes +# arbitrary base size to use as a reference point for calculating calendar sizes BASE_WIDTH = 701.0 BASE_HEIGHT = 721.0 @@ -84,7 +84,7 @@ """ if other.start == self.start: if other.duration == self.duration: - return cmp(self.id, other.id) + return cmp(self.id, getattr(other, 'id', '')) else: return cmp(other.duration, self.duration) else: @@ -126,10 +126,15 @@ bisect.insort(level, event) return self.levels.append([event]) + + @property + def duration(self): + return self.end - self.start class EventsForDay(object): - __slots__ = ("start", "event_list", "clusters") + __slots__ = ("start", "event_list", "clusters", "duration") def __init__(self, start, event_list=None): + self.duration = timedelta(1) self.start = start if event_list is None: event_list = [] @@ -172,9 +177,14 @@ @property def end(self): - return self.start + timedelta(1) + return self.start + self.duration +class EventsForWeek(EventsForDay): + def __init__(self, start, event_list=None): + super(EventsForWeek, self).__init__(start, event_list) + self.duration = timedelta(7) + class Calendar(wx.ScrolledWindow): def __init__(self, time_formatter=None, *arguments, **keywords): super(Calendar, self).__init__ (*arguments, **keywords) @@ -272,8 +282,8 @@ width, height = self.GetSize() self.matrix = gc.GetTransform() - # create a matrix to transform rectangles with. The Mac default transform is a y-flip, - # so explicitly set the initial matrix + # create a matrix to transform rectangles with. The Mac default + # transform is a y-flip, so explicitly set the initial matrix self.matrix.Set(1,0,0,1,0,0) self.matrix.Translate(MARGIN_LEFT, 0) self.matrix.Scale((width - MARGIN_LEFT - self.margin_right)/BASE_WIDTH, @@ -286,32 +296,40 @@ max_depth = len(cluster.levels) for depth, level in enumerate(cluster.levels): for event in level: - self.DrawLozenge(gc, event, day, depth, max_depth) + args = event, day, depth, max_depth + rect, truncate, draw_text = self.LozengePosition(*args) + self.DrawLozenge(gc, event, rect, truncate, draw_text) - def DrawLozenge(self, gc, event, range, depth, max_depth): + def LozengePosition(self, event, range, depth, max_depth): + """Return (rect, truncate, draw_text).""" start = max(range.start, event.start) hour_start = start.hour + start.minute/60.0 end = min(range.end, event.end) duration = end - start - hours = duration.seconds / 3600.0 + hours = duration.seconds / 3600.0 + 24.0*duration.days column = (start - self.range_start).days - - rect = wx.Rect2D(BASE_DAY_WIDTH*column, BASE_HOUR_HEIGHT*hour_start, - BASE_DAY_WIDTH, BASE_HOUR_HEIGHT*hours) + + if self.range_mode == 'week': + x = BASE_DAY_WIDTH*column + width = BASE_DAY_WIDTH + elif self.range_mode == 'day': + x = 0 + width = BASE_DAY_WIDTH*7 + y = BASE_HOUR_HEIGHT*hour_start + rect = wx.Rect2D(x, y, width, BASE_HOUR_HEIGHT*hours) if max_depth > 1: if self.range_mode == 'day': - rect.Inset(depth*BASE_DAY_WIDTH/max_depth, 0, 0, 0) - rect.width = BASE_DAY_WIDTH/max_depth + rect.Inset(depth*width/max_depth, 0, 0, 0) + rect.width = width/max_depth else: - rect.Inset(depth/(max_depth - 1)*BASE_DAY_WIDTH*0.2, 0, 0, 0) - rect.width = BASE_DAY_WIDTH * 0.8 + rect.Inset(depth/(max_depth - 1)*width*0.2, 0, 0, 0) + rect.width = width * 0.8 rect = TransformRect(self.matrix, rect) rect.Inset(HALO_WIDTH, HALO_WIDTH) - line_height = self.title_font_height - min_height = 2*TEXT_Y_MARGIN + line_height + min_height = 2*TEXT_Y_MARGIN + self.title_font_height if rect.height < min_height: rect.SetBottom(min(self.hour_height * 24 - 1, rect.y + min_height)) @@ -324,16 +342,19 @@ truncate |= wx.TOP if event.end > end: truncate |= wx.BOTTOM - - if event.start < start: + + return rect, truncate, event.start >= start or column == 0 + + def DrawLozenge(self, gc, event, rect, truncate, draw_text): + # Room for how many swatches (min 2)? Draw swatches + if not draw_text: # continuation lozenge, just draw a small-radius lozenge DrawFilledLozenge(gc, rect, SMALL_RADIUS, truncate = truncate) - - # draw one line? Draw time? Draw title on first line? - # Room for how many swatches (min 2)? - - if event.start >= start: - radius = SMALL_RADIUS if rect.height != min_height else min_height/2 + else: + line_height = self.title_font_height + min_height = 2*TEXT_Y_MARGIN + line_height + + radius = SMALL_RADIUS if rect.height > min_height else rect.height/2 DrawFilledLozenge(gc, rect, radius, truncate = truncate) time_height = (TEXT_Y_MARGIN + TIME_BOTTOM_MARGIN + @@ -350,7 +371,7 @@ DrawClippedText(gc, event.title, rect.x, text_top, rect.width) else: rect.Inset(0, TEXT_Y_MARGIN, 0, 0) - time_text = self.time_formatter(start) + time_text = self.time_formatter(event.start) gc.SetFont(self.time_font, DefaultColors.text) gc.DrawText(time_text, rect.x, rect.y) width, height = gc.GetTextExtent(time_text) @@ -363,13 +384,11 @@ def DrawBackground(self, gc): gc.SetPen(wx.Pen(MINOR_LINE_COLOR)) - path = self.GetDayLinePath(gc) - path.AddPath(self.GetHalfHourLinePath(gc)) - gc.StrokePath(path) - + gc.StrokePath(self.GetHalfHourLinePath(gc)) + if self.range_mode == 'week': + gc.StrokePath(self.GetDayLinePath(gc)) gc.SetPen(wx.Pen(MAJOR_LINE_COLOR)) - path = (self.GetHourLinePath(gc)) - gc.StrokePath(path) + gc.StrokePath(self.GetHourLinePath(gc)) self.DrawLegend(gc) @@ -469,14 +488,20 @@ def SetRange(self, start): self.range_start = start - self.days = [EventsForDay(start + timedelta(day)) - for day in xrange(self.GetDays())] + self.Reset() - def SetRangeMode(self, range_mode): + def SetRangeMode(self, range_mode, start=None): self.range_mode = range_mode - if self.range_start is not None: - self.SetRange(self, self.range_start) + if start is None: + start = self.range_start + if start is not None: + self.SetRange(start) + 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())] + def ChangeEvent(self, id, **kw): """ Create an event for id or adjust an existing one, adjust ordering of @@ -501,6 +526,66 @@ """Remove an event from the events displayed.""" pass +class AllDayCalendar(Calendar): + def DrawBackground(self, gc): + gc.SetPen(wx.Pen(MINOR_LINE_COLOR)) + gc.StrokePath(self.GetDayLinePath(gc)) + + def GetDayLinePath(self, gc): + """Get path associated with day lines.""" + vertical = gc.CreatePath() + day_list = BASE_DAY_X_LIST + if self.range_mode == 'day': + day_list = BASE_DAY_X_LIST[0::7] + for x in day_list: + vertical.MoveToPoint(x, 0) + vertical.AddLineToPoint(x, BOTTOM_EDGE) + vertical.Transform(self.matrix) + return vertical + + def OnSize(self, event): + self.Refresh(False) + + def Reset(self): + """Empty out events.""" + if self.range_mode == 'day': + self.days = [EventsForDay(self.range_start)] + else: + self.days = [EventsForWeek(self.range_start)] + + def LozengePosition(self, event, range, depth, max_depth): + """Return (rect, truncate, draw_text).""" + start = max(range.start, event.start) + end = min(range.end, event.end) + column = (start - self.range_start).days + duration = max(1, (end - start).days) + height = 2*TEXT_Y_MARGIN + self.title_font_height + 2*HALO_WIDTH + + if self.range_mode == 'week': + x = BASE_DAY_WIDTH*column + width = BASE_DAY_WIDTH*duration + elif self.range_mode == 'day': + x = 0 + width = BASE_DAY_WIDTH*7 + rect = wx.Rect2D(x, 0, width, 0) + rect = TransformRect(self.matrix, rect) + rect.y = height*depth + rect.height = height + rect.Inset(HALO_WIDTH, HALO_WIDTH) + + if column == 0 and depth == 0: + # don't let the halo overlap the legend border by too much + rect.Inset(.5, 0, 0, 0) + + truncate = 0 + if event.start < start: + truncate |= wx.LEFT + if event.end > end: + truncate |= wx.RIGHT + + return rect, truncate, True + + ############## Utility functions ############################################### def GetGradientBrush(gc, offset, width, gradient_left, gradient_right): return gc.CreateLinearGradientBrush(offset, 0, offset + width, 0, @@ -620,9 +705,13 @@ def Demo(): wxApp = wx.PySimpleApp() - frame = wx.Frame(None, title="Calendar Mockup", size=wx.Size(200, 450)) + frame = wx.Frame(None, title="Calendar Mockup", size=wx.Size(600, 450)) + + splitterStyle = wx.SP_LIVE_UPDATE | wx.NO_BORDER | wx.SP_3DSASH + splitter = wx.SplitterWindow(frame, style=splitterStyle) + frame.CenterOnParent() - calendar = Calendar(parent=frame, time_formatter=None) + calendar = Calendar(parent=splitter, time_formatter=None) calendar.SetRange(datetime(2007,11,25)) calendar.ChangeEvent(id="foo0", title=lorem, start=datetime(2007,11,25,10), @@ -651,7 +740,29 @@ calendar.ChangeEvent(id="foo8", title=lorem, start=datetime(2007,11,25,23,45), duration=timedelta(hours=1)) + + allday = AllDayCalendar(parent=splitter, time_formatter=None) + allday.SetRange(datetime(2007,11,25)) + 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)) + allday.ChangeEvent(id="bar3", title=lorem, + start=datetime(2007,11,27), + duration=timedelta(1)) + allday.ChangeEvent(id="bar4", title=lorem, + start=datetime(2007,11,28), + duration=timedelta(5)) + splitter.SplitHorizontally(allday, calendar) + splitter.SetMinimumPaneSize(20) + splitter.SashPosition = 20 + frame.Show() wxApp.MainLoop()
_______________________________________________ Commits mailing list [email protected] http://lists.osafoundation.org/mailman/listinfo/commits
