diff --git a/GooCalendar/Calendar.py b/GooCalendar/Calendar.py index 521a9d6fcb3de40b08df7d3b40609be5047866c0_R29vQ2FsZW5kYXIvQ2FsZW5kYXIucHk=..05eba381c931f1e670e11a8b5659da862f9b477a_R29vQ2FsZW5kYXIvQ2FsZW5kYXIucHk= 100644 --- a/GooCalendar/Calendar.py +++ b/GooCalendar/Calendar.py @@ -51,9 +51,9 @@ self.connect('size-allocate', self.on_size_allocate) self.connect('key-press-event', self.on_key_press_event) - # Initialize background and days and add them to canvas + # Initialize background, timeline and days and add them to canvas root = self.get_root_item() style = self.get_style() color = util.color_to_string(style.bg[gtk.STATE_PRELIGHT]) self.bg_rect = goocanvas.Rect(parent=root, x=0, y=0, \ stroke_color=color, fill_color=color) @@ -55,8 +55,10 @@ root = self.get_root_item() style = self.get_style() color = util.color_to_string(style.bg[gtk.STATE_PRELIGHT]) self.bg_rect = goocanvas.Rect(parent=root, x=0, y=0, \ stroke_color=color, fill_color=color) + self.timeline = TimelineItem(self) + root.add_child(self.timeline) self.days = [] while len(self.days) < 42: # 6 rows of 7 days box = DayItem(self) @@ -182,6 +184,7 @@ Draws the currently selected week. """ style = self.get_style() + pango_size = style.font_desc.get_size() text_color = util.color_to_string(style.fg[gtk.STATE_NORMAL]) border_color = util.color_to_string(style.mid[gtk.STATE_NORMAL]) body_color = util.color_to_string(style.light[gtk.STATE_ACTIVE]) @@ -189,8 +192,12 @@ style.mid[gtk.STATE_SELECTED]) today_body_color = 'ivory' x, y, w, h = self.get_bounds() - timeline_w = w / 12 - day_width = (w - timeline_w) / 7 + timeline_w = self.timeline.width + caption_size = max(len(day_name) for day_name in calendar.day_name) + caption_size += 3 # The needed space for the date before the day_name + day_width_min = caption_size * pango_size / pango.SCALE + day_width_max = (w - timeline_w) / 7 + day_width = max(day_width_min, day_width_max) day_height = h # Redraw all days. @@ -243,7 +250,12 @@ """ style = self.get_style() x1, y1, w, h = self.get_bounds() - day_width = w / 7 + pango_size = style.font_desc.get_size() + caption_size = max(len(day_name) for day_name in calendar.day_name) + caption_size += 3 # The needed space for the date before the day_name + day_width_min = caption_size * pango_size / pango.SCALE + day_width_max = w / 7 + day_width = max(day_width_min, day_width_max) day_height = (h - self.line_height) / 6 text_height = max(day_height / 12, 10) font_descr = style.font_desc.copy() @@ -247,7 +259,6 @@ day_height = (h - self.line_height) / 6 text_height = max(day_height / 12, 10) font_descr = style.font_desc.copy() - font_descr.set_absolute_size(text_height * pango.SCALE) text_color = util.color_to_string(style.fg[gtk.STATE_NORMAL]) inactive_text_color = util.color_to_string( style.fg[gtk.STATE_INSENSITIVE]) @@ -401,7 +412,10 @@ continue max_line_height = max(x.line_height for x in days) - max_y = max((free_line + 2) * max_line_height + 2, max_y) + all_day_events_height = (free_line + 2) * max_line_height + all_day_events_height += (free_line + 1) * 2 #2px margin per line + all_day_events_height += 1 # 1px padding-top + max_y = max(all_day_events_height, max_y) for day in days: day.lines[free_line] = 1 @@ -415,7 +429,6 @@ week_end = week_start + (7 - weekday) week = days[week_start:week_end] weeks.append(week) - #print "Week:", weekday, [d.date for d in week], start, end week_start = week_end for week in weeks: @@ -427,7 +440,9 @@ self.event_items.append(event_item) self.get_root_item().add_child(event_item) event_item.x = day.x - event_item.y = day.y + (free_line + 1) * day.line_height + 2 + event_item.y = day.y + (free_line + 1) * day.line_height + event_item.y += free_line * 2 # 2px of margin per line + event_item.y += 1 # 1px padding-top event_item.width = (day.width + 2) * len(week) event_item.height = day.line_height week_start = week[0].date @@ -457,10 +472,7 @@ text_color = util.color_to_string(style.fg[gtk.STATE_NORMAL]) border_color = util.color_to_string(style.mid[gtk.STATE_NORMAL]) body_color = util.color_to_string(style.light[gtk.STATE_ACTIVE]) - if self.timeline is None: - self.timeline = TimelineItem(self) - self.get_root_item().add_child(self.timeline) self.timeline.set_property('visibility', goocanvas.ITEM_VISIBLE) x, y, w, h = self.get_bounds() self.timeline.x = x self.timeline.y = max_y @@ -463,8 +475,7 @@ self.timeline.set_property('visibility', goocanvas.ITEM_VISIBLE) x, y, w, h = self.get_bounds() self.timeline.x = x self.timeline.y = max_y - self.timeline.width = w / 12 - 2 self.timeline.height = h - max_y - 2 self.timeline.line_color = body_color self.timeline.bg_color = border_color @@ -515,7 +526,7 @@ self.on_event_item_button_press_event) self.event_items.append(event_item) self.get_root_item().add_child(event_item) - minute_height = self.timeline.height / 24 / 60 + minute_height = self.timeline.line_height / 60 y_off1 = top_offset_mins * minute_height y_off2 = bottom_offset_mins * minute_height column_width = day.width / parallel @@ -523,7 +534,6 @@ event_item.y = max_y + y_off1 event_item.width = column_width - 4 event_item.height = y_off2 - event_item.font_size = day.font_size if event.start < event1_start and event.end > event1_end: event_item.type = 'mid' elif event.start < event1_start: diff --git a/GooCalendar/DayItem.py b/GooCalendar/DayItem.py index 521a9d6fcb3de40b08df7d3b40609be5047866c0_R29vQ2FsZW5kYXIvRGF5SXRlbS5weQ==..05eba381c931f1e670e11a8b5659da862f9b477a_R29vQ2FsZW5kYXIvRGF5SXRlbS5weQ== 100644 --- a/GooCalendar/DayItem.py +++ b/GooCalendar/DayItem.py @@ -4,9 +4,10 @@ import goocanvas import pango +import math class DayItem(goocanvas.Group): """ A canvas item representing a day. """ @@ -7,9 +8,10 @@ class DayItem(goocanvas.Group): """ A canvas item representing a day. """ + def __init__(self, cal, **kwargs): super(DayItem, self).__init__() @@ -24,8 +26,6 @@ self.date = kwargs.get('date') self.type = kwargs.get('type', 'month') self.show_indic = False - self.line_height = 0 - self.font_size = 0 self.lines = {} self.n_lines = 0 self.title_text_color = "" @@ -37,11 +37,12 @@ self.indic = goocanvas.Rect(parent=self) def update(self): - if self.type == 'month': - self.font_size = max(self.height / 12, 10) - else: - self.font_size = max(self.height / 65, 10) - text_padding = max(self.font_size / 2.5, 4) - self.line_height = self.font_size + 2 * text_padding + if not self.date: + return + + date_tuple = self.date.timetuple()[:3] + week_day = calendar.weekday(*date_tuple) + day_name = calendar.day_name[week_day] + caption = '%s %s' % (date_tuple[2], day_name) style = self.cal.get_style() font_descr = style.font_desc.copy() @@ -46,14 +47,12 @@ style = self.cal.get_style() font_descr = style.font_desc.copy() - font_descr.set_absolute_size(self.font_size * pango.SCALE) - self.font = font_descr.to_string() - if self.date: - date_tuple = self.date.timetuple()[:3] - week_day = calendar.weekday(*date_tuple) - day_name = calendar.day_name[week_day] - caption = '%s %s' % (date_tuple[2], day_name) - else: - caption = "" + font = font_descr.to_string() + pango_size = font_descr.get_size() + self.text.set_property('font', font) + self.text.set_property('text', caption) + logical_height = self.text.get_natural_extents()[1][3] + line_height = int(math.ceil(float(logical_height) / pango.SCALE)) + self.line_height = line_height # Draw the border. self.border.set_property('x', self.x) @@ -64,13 +63,11 @@ self.border.set_property('fill_color', self.border_color) # Draw the title text. - x = self.x + text_padding - self.text.set_property('x', x) - self.text.set_property('y', self.y + text_padding) - self.text.set_property('font', self.font) - self.text.set_property('text', caption) + padding_left = 2 + self.text.set_property('x', self.x + padding_left) + self.text.set_property('y', self.y) self.text.set_property('fill_color', self.title_text_color) # Print the "body" of the day. if self.full_border: box_x = self.x + 2 @@ -72,7 +69,7 @@ self.text.set_property('fill_color', self.title_text_color) # Print the "body" of the day. if self.full_border: box_x = self.x + 2 - box_y = self.y + self.line_height + box_y = self.y + line_height box_width = max(self.width - 4, 0) @@ -78,4 +75,4 @@ box_width = max(self.width - 4, 0) - box_height = max(self.height - self.line_height - 3, 0) + box_height = max(self.height - line_height - 3, 0) else: box_x = self.x + 1 @@ -80,4 +77,4 @@ else: box_x = self.x + 1 - box_y = self.y + self.line_height + box_y = self.y + line_height box_width = max(self.width - 2, 0) @@ -83,5 +80,5 @@ box_width = max(self.width - 2, 0) - box_height = max(self.height - self.line_height, 0) + box_height = max(self.height - line_height, 0) self.box.set_property('x', box_x) self.box.set_property('y', box_y) self.box.set_property('width', box_width) @@ -89,7 +86,7 @@ self.box.set_property('stroke_color', self.body_color) self.box.set_property('fill_color', self.body_color) - self.n_lines = int(box_height / self.line_height) + self.n_lines = int(box_height / line_height) # Show an indicator in the title, if requested. if not self.show_indic: @@ -98,11 +95,11 @@ self.indic.set_property('visibility', goocanvas.ITEM_VISIBLE) self.indic.set_property('x', - self.x + self.width - self.line_height / 1.5) - self.indic.set_property('y', self.y + self.line_height / 3) - self.indic.set_property('width', self.line_height / 3) - self.indic.set_property('height', self.line_height / 3) + self.x + self.width - line_height / 1.5) + self.indic.set_property('y', self.y + line_height / 3) + self.indic.set_property('width', line_height / 3) + self.indic.set_property('height', line_height / 3) self.indic.set_property('stroke_color', self.title_text_color) self.indic.set_property('fill_color', self.title_text_color) # Draw a triangle. @@ -105,12 +102,12 @@ self.indic.set_property('stroke_color', self.title_text_color) self.indic.set_property('fill_color', self.title_text_color) # Draw a triangle. - x1 = self.x + self.width - self.line_height / 1.5 - y1 = self.y + self.line_height / 3 - x2 = x1 + self.line_height / 6 - y2 = y1 + self.line_height / 3 - x3 = x1 + self.line_height / 3 + x1 = self.x + self.width - line_height / 1.5 + y1 = self.y + line_height / 3 + x2 = x1 + line_height / 6 + y2 = y1 + line_height / 3 + x3 = x1 + line_height / 3 y3 = y1 path = 'M%s,%s L%s,%s L%s,%s Z' % (x1, y1, x2, y2, x3, y3) self.indic.set_property('clip_path', path) diff --git a/GooCalendar/EventItem.py b/GooCalendar/EventItem.py index 521a9d6fcb3de40b08df7d3b40609be5047866c0_R29vQ2FsZW5kYXIvRXZlbnRJdGVtLnB5..05eba381c931f1e670e11a8b5659da862f9b477a_R29vQ2FsZW5kYXIvRXZlbnRJdGVtLnB5 100644 --- a/GooCalendar/EventItem.py +++ b/GooCalendar/EventItem.py @@ -8,6 +8,7 @@ """ A canvas item representing an event. """ + def __init__(self, cal, **kwargs): super(EventItem, self).__init__() @@ -18,7 +19,6 @@ self.height = kwargs.get('height') self.bg_color = kwargs.get('bg_color') self.text_color = kwargs.get('text_color') - self.font_size = kwargs.get('font_size') self.event = kwargs.get('event') self.type = kwargs.get('type', 'leftright') @@ -39,8 +39,7 @@ def update_event(self): style = self.cal.get_style() font_descr = style.font_desc.copy() - if self.font_size is not None: - font_descr.set_absolute_size(self.font_size * pango.SCALE) + self.width = max(self.width, 0) self.font = font_descr.to_string() caption = self.event.caption the_event_bg_color = self.event.bg_color @@ -83,11 +82,11 @@ self.box2.set_property('fill_color', the_event_bg_color) # Print the event name into the title box. - self.text.set_property('x', self.x + 6) - self.text.set_property('y', self.y + 2) - self.text.set_property('font', self.font) - self.text.set_property('text', caption) + self.text.set_property('x', self.x + 2) + self.text.set_property('y', self.y) + self.text.set_property('font', self.font) + self.text.set_property('text', caption) self.text.set_property('fill_color', the_event_text_color) # Clip the text. x2, y2 = self.x + self.width, self.y + self.height, @@ -90,11 +89,9 @@ self.text.set_property('fill_color', the_event_text_color) # Clip the text. x2, y2 = self.x + self.width, self.y + self.height, - path = 'M%s,%s L%s,%s L%s,%s L%s,%s Z' % (self.x, self.y, - self.x, y2, - x2, y2, - x2, self.y) - self.text.set_property('clip_path', path) + path = 'M%s,%s L%s,%s L%s,%s L%s,%s Z' % (self.x, self.y, self.x, y2, + x2, y2, x2, self.y) + self.text.set_property('clip_path', path) def update_all_day_event(self): @@ -99,6 +96,5 @@ def update_all_day_event(self): - text_padding = max(self.height / 4.5, 4) - text_height = max(self.height - 2 * text_padding, 10) + self.width = max(self.width, 0) style = self.cal.get_style() font_descr = style.font_desc.copy() @@ -103,6 +99,5 @@ style = self.cal.get_style() font_descr = style.font_desc.copy() - font_descr.set_absolute_size(text_height * pango.SCALE) self.font = font_descr.to_string() caption = self.event.caption the_event_bg_color = self.event.bg_color @@ -106,6 +101,10 @@ self.font = font_descr.to_string() caption = self.event.caption the_event_bg_color = self.event.bg_color + self.text.set_property('font', self.font) + self.text.set_property('text', caption) + logical_height = self.text.get_natural_extents()[1][3] + self.height = logical_height / pango.SCALE # Choose text color. if self.event.text_color is None: @@ -131,7 +130,7 @@ self.box.set_property('x', self.x) self.box.set_property('y', self.y) self.box.set_property('width', self.width) - self.box.set_property('height', self.height - 4) + self.box.set_property('height', self.height) self.box.set_property('radius_x', radius) self.box.set_property('radius_y', radius) self.box.set_property('stroke_color', the_event_bg_color) @@ -140,8 +139,8 @@ # Box 2 hides the rounded corners of box1. self.box2.set_property('y', self.y) self.box2.set_property('width', radius) - self.box2.set_property('height', self.height - 4) + self.box2.set_property('height', self.height) self.box2.set_property('stroke_color', the_event_bg_color) self.box2.set_property('fill_color', the_event_bg_color) # Print the event name into the title box. @@ -144,14 +143,12 @@ self.box2.set_property('stroke_color', the_event_bg_color) self.box2.set_property('fill_color', the_event_bg_color) # Print the event name into the title box. - self.text.set_property('x', self.x + 6) - self.text.set_property('y', self.y + 2) - self.text.set_property('font', self.font) - self.text.set_property('text', caption) + self.text.set_property('x', self.x + 2) + self.text.set_property('y', self.y) self.text.set_property('fill_color', the_event_text_color) # Clip the text. x2, y2 = self.x + self.width, self.y + self.height, path = 'M%s,%s L%s,%s L%s,%s L%s,%s Z' % ( self.x, self.y, self.x, y2, x2, y2, x2, self.y) @@ -152,7 +149,7 @@ self.text.set_property('fill_color', the_event_text_color) # Clip the text. x2, y2 = self.x + self.width, self.y + self.height, path = 'M%s,%s L%s,%s L%s,%s L%s,%s Z' % ( self.x, self.y, self.x, y2, x2, y2, x2, self.y) - self.text.set_property('clip_path', path) + self.text.set_property('clip_path', path) diff --git a/GooCalendar/TimelineItem.py b/GooCalendar/TimelineItem.py index 521a9d6fcb3de40b08df7d3b40609be5047866c0_R29vQ2FsZW5kYXIvVGltZWxpbmVJdGVtLnB5..05eba381c931f1e670e11a8b5659da862f9b477a_R29vQ2FsZW5kYXIvVGltZWxpbmVJdGVtLnB5 100644 --- a/GooCalendar/TimelineItem.py +++ b/GooCalendar/TimelineItem.py @@ -2,9 +2,10 @@ #this repository contains the full copyright notices and license terms. import goocanvas import pango +import math class TimelineItem(goocanvas.Group): """ A canvas item representing a timeline. """ @@ -5,12 +6,13 @@ class TimelineItem(goocanvas.Group): """ A canvas item representing a timeline. """ + def __init__(self, cal, **kwargs): super(TimelineItem, self).__init__() self.cal = cal self.x = kwargs.get('x') self.y = kwargs.get('y') @@ -11,11 +13,9 @@ def __init__(self, cal, **kwargs): super(TimelineItem, self).__init__() self.cal = cal self.x = kwargs.get('x') self.y = kwargs.get('y') - self.width = kwargs.get('width') - self.height = kwargs.get('height') self.line_color = kwargs.get('line_color') self.bg_color = kwargs.get('bg_color') self.text_color = kwargs.get('text_color') @@ -23,11 +23,12 @@ # Create canvas items. self.timeline_rect = {} self.timeline_text = {} - for n in range(0, 24): - caption = '%s:00' % n + for n in range(24): + hour = str(n).zfill(2) + caption = hour + ':00' self.timeline_rect[n] = goocanvas.Rect(parent=self) self.timeline_text[n] = goocanvas.Text(parent=self, text=caption) if self.x is not None: self.update() @@ -28,13 +29,28 @@ self.timeline_rect[n] = goocanvas.Rect(parent=self) self.timeline_text[n] = goocanvas.Text(parent=self, text=caption) if self.x is not None: self.update() - def update(self): - text_padding = max(self.width / 20, 4) - text_width = self.width - 2 * text_padding - text_height = text_width / 5 - line_height = self.height / 24 + @property + def width(self): + style = self.cal.get_style() + font = style.font_desc + + # Find the maximal padding and width of texts + ink_padding_left = 0 + ink_max_width = 0 + for n in range(24): + self.timeline_text[n].set_property('font', font) + natural_extents = self.timeline_text[n].get_natural_extents() + ink_rect = natural_extents[0] + ink_padding_left = max(ink_padding_left, ink_rect[0]) + ink_max_width = max(ink_max_width, ink_rect[2]) + self._width = int(math.ceil(float(ink_padding_left + ink_max_width) + / pango.SCALE)) + return self._width + + @property + def line_height(self): style = self.cal.get_style() font_descr = style.font_desc.copy() @@ -39,6 +55,27 @@ style = self.cal.get_style() font_descr = style.font_desc.copy() - font_descr.set_absolute_size(text_height * pango.SCALE) - font = font_descr.to_string() - + pango_size = font_descr.get_size() + logical_height = 0 + ink_padding_top = 0 + for n in range(24): + natural_extents = self.timeline_text[n].get_natural_extents() + logical_rect = natural_extents[1] + logical_height = max(logical_height, logical_rect[3]) + ink_rect = natural_extents[0] + ink_padding_top = max(ink_padding_top, ink_rect[0]) + line_height = int(math.ceil(float(logical_height) / pango.SCALE)) + + # If we have more vertical space, use it and center the text + self.padding_top = 0 + if line_height < self.height / 24: + line_height = self.height / 24 + pango_size = self.cal.get_style().font_desc.get_size() + padding_top = (line_height - pango_size / pango.SCALE) / 2 + padding_top -= int(math.ceil(float(ink_padding_top) / pango.SCALE)) + self.padding_top = padding_top + return line_height + + def update(self): + line_height = self.line_height + # Draw the timeline. @@ -44,5 +81,5 @@ # Draw the timeline. - for n in range(0, 24): + for n in range(24): rect = self.timeline_rect[n] text = self.timeline_text[n] y = self.y + n * line_height @@ -54,7 +91,6 @@ rect.set_property('stroke_color', self.line_color) rect.set_property('fill_color', self.bg_color) - text.set_property('x', self.x + text_padding) - text.set_property('y', y + text_padding) - text.set_property('font', font) + text.set_property('x', self.x) + text.set_property('y', y + self.padding_top) text.set_property('fill_color', self.text_color)