class DayHeader(WeekViewBase): def __init__(self, info, history, *args, **kwargs): WeekViewBase.__init__(self, info, *args, **kwargs) self.scrolling = MouseCommandDispatcher( history, (DragCalendarHorizontal,)) self.scrolling.observe(self) self.set_size_request(600, int(30)) def paint(self, cr): cr.identity_matrix() self.clear_background(cr) self.draw_day_headers(cr) self.draw_top_left_corner(cr) cr.save() cr.rectangle(1, 0, self.width - 2, self.height / 2) cr.set_source(settings.gloss_gradient) m = cairo.Matrix() m.scale(1.0/self.width, 1.0/self.height) settings.gloss_gradient.set_matrix(m) cr.fill() cr.restore() def draw_day_header(self, cr, nth_day): x = self.scale * (self.get_week_pixel_offset() + nth_day * self.day_width) leftmost_day = int(self.date) date = self.get_date(leftmost_day + nth_day) weekday = date.weekday() if weekday > 4: bgcolor = settings.weekday_bg_color else: bgcolor = settings.weekend_bg_color area = shapes.Area(x, 0, self.day_width * self.scale, self.height) shapes.labeled_box( cr, area, date.strftime("%a\n%x"), bgcolor, settings.heading_outline_color, settings.text_color) def draw_day_headers(self, cr): cr.save() cr.rectangle(self.day_width * self.scale, 0, self.width - self.day_width * self.scale, self.height) cr.clip() for i in xrange(0, (self.days_visible()) + 1): self.draw_day_header(cr, i) cr.restore() def draw_top_left_corner(self, cr): area = shapes.Area(0, 0, self.day_width * self.scale, self.height) shapes.labeled_box( cr, area, datetime.date.fromordinal(int(self.date + 1)).strftime("%x"), settings.corner_bg_color, settings.heading_outline_color, settings.text_color)
class TimedEvents(WeekViewBase): __gtype_name__ = "WeekView" x = gobject.property(type=int, default=0) y = gobject.property(type=int, default=0) _y_scroll_offset = 0 height = scaled_property("height") y_scroll_offset = scaled_property("y_scroll_offset") handle_locations = None def __init__(self, info, undo, *args, **kwargs): WeekViewBase.__init__(self, info, *args, **kwargs) self.editing = False self.occurrences = {} self.selected = None self.font_desc = pango.FontDescription("Sans 8") self.cursor_showing = True self.ti = TextInput(self.text_changed_cb) self.ti.observe(self) gobject.timeout_add(500, self._blink_cursor) self.dispatcher = MouseCommandDispatcher( undo, drag_commands = (DragCalendarVertical, SetEventStart, SetEventEnd, MoveTimedEvent, SelectArea), click_commands = (SelectPoint,)) self.dispatcher.observe(self) def _blink_cursor(self): if not self.editing: return True self.cursor_showing = not self.cursor_showing self.queue_draw() return True def point_to_datetime(self, x, y, snap=True): x /= self.scale y /= self.scale hour = ((y + (- self.y_scroll_offset)) / self.hour_height) if snap: minute = quantize(hour % 1 * 60, 15) else: minute = hour % 1 * 60 date = int(self.date + (x - self.day_width) / self.day_width) ret = datetime.datetime.fromordinal(date) delta = datetime.timedelta(hours=int(hour), minutes=minute) return ret + delta def point_to_timedelta(self, x, y, snap=True): x /= self.scale y /= self.scale day = int(x / self.day_width) if y < 0: day += 1 minute = (day * 24 * 60) + quantize(int((y / self.hour_height) * 60), 15) return datetime.timedelta(minutes=minute) def date_visible(self, dt): return (self.date - 1 < dt.toordinal() < self.date + self.days_visible()) def datetime_to_point(self, dt): if not self.date_visible(dt): raise DateNotVisible(dt) return (self.date_to_x(dt), (dt.hour + dt.minute / 60.0) * self.hour_height + self.y_scroll_offset) def area_from_start_end(self, start, end): start = self.datetime_to_point(start) end = self.datetime_to_point(end) return shapes.Area(start[0], start[1], self.day_width, end[1] - start[1]) def point_to_occurrence(self, x, y): point = (x / self.scale, y / self.scale) for ordinal, (event, area, period) in self.occurrences.iteritems(): if area.contains_point(point): return event, ordinal, area return None def point_to_event(self, x, y): ret = self.point_to_occurrence(x, y) return ret[0] if ret else None def selection_handles(self, cr): area = self.occurrences[self.selected][1] radius = 10 top = area.above(2, radius) bottom = area.below(2, radius) shapes.upward_tab(cr, top) shapes.downward_tab(cr, bottom) cr.set_source(settings.handle_arrow_color) shapes.upward_triangle(cr, top.scale(0.1, 0.8)) shapes.downward_triangle(cr, bottom.scale(0.1, 0.8)) self.handle_locations = top, bottom def point_in_handle (self, x, y): if not self.handle_locations: return 0 top, bottom = self.handle_locations point = (x / self.scale, y / self.scale) if top.contains_point(point): return 1 if bottom.contains_point(point): return 2 return 0 def draw_grid(self, cr): cr.save() cr.rectangle(0, 0, self.width, self.height) cr.clip() cr.set_source(settings.grid_line_color) cr.set_line_width(settings.grid_line_width) cr.set_antialias(cairo.ANTIALIAS_NONE) for i in xrange(1, 25): cr.move_to(0, self.y_scroll_offset + i * self.hour_height) cr.line_to(self.width, self.y_scroll_offset + i * self.hour_height) cr.stroke() x = self.get_week_pixel_offset() max_height = min(self.hour_height * 24 + self.y_scroll_offset, self.height) for i in xrange(0, (self.days_visible()) + 1): # draw vertical lines x += self.day_width cr.move_to (x, 0) cr.line_to (x, max_height) cr.stroke() cr.restore() def draw_hour_header(self, cr, hour): area = shapes.Area(0, hour * self.hour_height + self.y_scroll_offset, self.day_width, self.hour_height) shapes.centered_text( cr, area, "%2d:00" % hour, settings.text_color) def draw_hour_headers(self, cr): cr.save() cr.rectangle(0, 0, self.day_width, self.height) cr.clip() for i in range(0, 24): self.draw_hour_header(cr, i) cr.restore() def draw_event(self, cr, event, period): try: area = self.area_from_start_end(period.start, period.end).shrink(3, 0) except DateNotVisible: return #shapes.filled_box(cr, area, settings.default_event_bg_color) shapes.rounded_rect(cr, area, 5) m = cairo.Matrix() m.scale(1.0/area.width, 1.0/area.height) m.translate(-area.x, -area.y) settings.default_event_bg_color.set_matrix(m) cr.set_source(settings.default_event_bg_color) cr.fill_preserve() #cr.set_line_width(1.0) cr.set_source(settings.default_event_outline_color) settings.default_event_outline_color.set_matrix(m) #cr.set_antialias(cairo.ANTIALIAS_SUBPIXEL) cr.stroke() if (self.selected and (event == self.selected[0]) and (period.ordinal == self.selected[1]) and (self.cursor_showing)): cursor_pos = self.ti.get_cursor_pos() else: cursor_pos = -1 lyt = shapes.left_aligned_text(cr, area.shrink(1, 3), event.description, settings.default_event_text_color, cursor_pos) self.occurrences[(event, period.ordinal)] = (event, area, period) def draw_events(self, cr): cr.save() cr.rectangle(self.day_width, 0, self.width - self.day_width, self.height) cr.clip() self.occurrences = {} for evt, period in self.model.timedOccurrences(*self.dates_visible()): if period.all_day: continue self.draw_event(cr, evt, period) cr.restore() def draw_marquee(self, cr): if not self.selection_recurrence: return s, e = self.dates_visible() for instance in self.selection_recurrence.timedOccurrences(s, e): try: area = self.area_from_start_end(instance.start, instance.end).shrink(2, 0) except DateNotVisible: return shapes.filled_box(cr, area, settings.marquee_fill_color) cr.set_source(settings.marquee_text_color) text = instance.start.strftime ("%X") shapes.text_above(cr, text, area.x, area.y - 2, area.width) text = instance.end.strftime ("%X") shapes.text_below(cr, text, area.x, area.y2 + 2, area.width) duration = instance.duration m = int (duration.seconds / 60) % 60 h = int (duration.seconds / 60 / 60) text = "%dh %dm" % (h, m) shapes.centered_text(cr, area, text, settings.text_color) def paint(self, cr): cr.identity_matrix() cr.scale(self.scale, self.scale) self.clear_background(cr) self.draw_hour_headers(cr) self.draw_grid(cr) self.draw_events(cr) cr.save() cr.rectangle(self.day_width, 0, self.width - self.day_width, self.height) cr.clip() self.draw_marquee(cr) if (self.selected) and (self.selected in self.occurrences): self.selection_handles (cr) cr.restore() self.draw_comfort_lines(cr) def select_area(self, x, y, width, height, quantize=True): try: start = self.point_to_datetime (x, y, quantize) end = self.point_to_datetime(x + width, y + height, quantize) except DateNotVisible: return start_date = datetime.date(start.year, start.month, start.day) end_date = datetime.date(end.year, end.month, end.day) start_time = datetime.time(start.hour, start.minute) end_time = datetime.time(end.hour, end.minute) if (end_date - start_date).days < 4: self.info.selection_recurrence = recurrence.Period( recurrence.DateSet(*recurrence.dateRange(start_date, end_date)), start_time, end_time) else: self.info.selection_recurrence = recurrence.Period( recurrence.Until(recurrence.Daily(start_date, 1), end_date), start_time, end_time) def select_point(self, x, y): occurrence = self.point_to_occurrence(x, y) if occurrence: self.select_occurrence(occurrence[1]) else: self.select_occurrence(None) def get_occurence_event(self, occurrence): try: return self.occurrences[occurrence][0] except KeyError: return None def select_occurrence(self, occurrence): self.info.selected = occurrence if occurrence: self.configure_editor(self.get_occurence_event(occurrence)) self.queue_draw() def configure_editor(self, event): if event: self.grab_focus() self.ti.set_text(event.description) self.editing = True else: self.editing = False def text_changed_cb(self): if not self.editing: return self.get_occurence_event(self.selected).description = self.ti.get_text() self.queue_draw()