[hamster-applet] how did i forget these?



commit 9239c37c622afd181e5ec1b938e51b876d234a14
Author: Toms Bauģis <toms baugis gmail com>
Date:   Thu Nov 19 18:11:32 2009 +0000

    how did i forget these?

 hamster/widgets/dayline.py             |  346 ++++++++++++++++++++++++++++++++
 hamster/widgets/reportchooserdialog.py |  167 +++++++++++++++
 hamster/widgets/timeline.py            |  174 ++++++++++++++++
 3 files changed, 687 insertions(+), 0 deletions(-)
---
diff --git a/hamster/widgets/dayline.py b/hamster/widgets/dayline.py
new file mode 100644
index 0000000..15b90ee
--- /dev/null
+++ b/hamster/widgets/dayline.py
@@ -0,0 +1,346 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2007-2009 Toms Bauģis <toms.baugis at gmail.com>
+
+# This file is part of Project Hamster.
+
+# Project Hamster 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.
+
+# Project Hamster 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 Project Hamster.  If not, see <http://www.gnu.org/licenses/>.
+
+import gtk
+import gobject
+
+from hamster import stuff
+from hamster import graphics
+
+import datetime as dt
+import colorsys
+
+
+class DayLine(graphics.Area):
+    def __init__(self):
+        graphics.Area.__init__(self)
+
+        self.set_events(gtk.gdk.EXPOSURE_MASK
+                                 | gtk.gdk.LEAVE_NOTIFY_MASK
+                                 | gtk.gdk.BUTTON_PRESS_MASK
+                                 | gtk.gdk.BUTTON_RELEASE_MASK
+                                 | gtk.gdk.POINTER_MOTION_MASK
+                                 | gtk.gdk.POINTER_MOTION_HINT_MASK)
+        self.connect("button_release_event", self.on_button_release)
+        self.connect("motion_notify_event", self.draw_cursor)
+        self.highlight_start, self.highlight_end = None, None
+        self.drag_start = None
+        self.move_type = ""
+        self.on_time_changed = None #override this with your func to get notified when user changes date
+        self.on_more_data = None #supplement with more data func that accepts single date
+        self.in_progress = False
+
+        self.range_start = None
+        self.in_motion = False
+        self.days = []
+        
+
+    def draw(self, day_facts, highlight = None):
+        """Draw chart with given data"""
+        self.facts = day_facts
+        if self.facts:
+            self.days.append(self.facts[0]["start_time"].date())
+        
+        start_time = highlight[0] - dt.timedelta(minutes = highlight[0].minute) - dt.timedelta(hours = 10)
+        
+        if self.range_start:
+            self.range_start.target(start_time)
+            self.scroll_to_range_start()
+        else:
+            self.range_start = graphics.Integrator(start_time, damping = 0.35, attraction = 0.5)
+
+        self.highlight = highlight
+        
+        self.show()
+        
+        self.redraw_canvas()
+
+
+    def on_button_release(self, area, event):
+        if not self.drag_start:
+            return
+        
+        self.drag_start, self.move_type = None, None
+
+        if event.state & gtk.gdk.BUTTON1_MASK:
+            self.__call_parent_time_changed()
+
+    def set_in_progress(self, in_progress):
+        self.in_progress = in_progress
+
+    def __call_parent_time_changed(self):
+        #now calculate back from pixels into minutes
+        start_time = self.highlight[0]
+        end_time = self.highlight[1]
+
+        if self.on_time_changed:
+            self.on_time_changed(start_time, end_time)
+    
+    def get_time(self, pixels):
+        minutes = self.get_value_at_pos(x = pixels)
+        return self.range_start.value + dt.timedelta(minutes = minutes) 
+    
+    def scroll_to_range_start(self):
+        if not self.in_motion:
+            self.in_motion = True
+            gobject.timeout_add(1000 / 30, self.animate_scale)
+        
+        
+    def animate_scale(self):
+        moving = self.range_start.update() > 5
+        
+        
+        # check if maybe we are approaching day boundaries and should ask for
+        # more data!
+        if self.on_more_data:
+            now = self.range_start.value
+            date_plus = (now + dt.timedelta(hours = 12 + 2*4 + 1)).date()
+            date_minus = (now - dt.timedelta(hours=1)).date()
+
+            if date_minus != now.date() and date_minus not in self.days:
+                self.facts += self.on_more_data(date_minus)
+                self.days.append(date_minus)
+            elif date_plus != now.date() and date_plus not in self.days:
+                self.facts += self.on_more_data(date_plus)
+                self.days.append(date_plus)
+        
+        
+        self.redraw_canvas()
+        if moving:
+            return True
+        else:
+            self.in_motion = False
+            return False
+
+
+        
+    def draw_cursor(self, area, event):
+        if event.is_hint:
+            x, y, state = event.window.get_pointer()
+        else:
+            x = event.x
+            y = event.y
+            state = event.state
+
+        mouse_down = state & gtk.gdk.BUTTON1_MASK
+            
+        #print x, self.highlight_start, self.highlight_end
+        if self.highlight_start != None:
+            start_drag = 10 > (self.highlight_start - x) > -1
+
+            end_drag = 10 > (x - self.highlight_end) > -1
+
+            if start_drag and end_drag:
+                start_drag = abs(x - self.highlight_start) < abs(x - self.highlight_end)
+
+            in_between = self.highlight_start <= x <= self.highlight_end
+            scale = True
+
+            if self.in_progress:
+                end_drag = False
+                in_between = False
+
+            if mouse_down and not self.drag_start:
+                self.drag_start = x
+                if start_drag:
+                    self.move_type = "start"
+                elif end_drag:
+                    self.move_type = "end"
+                elif in_between:
+                    self.move_type = "move"
+                    self.drag_start = x - self.highlight_start
+                elif scale:
+                    self.move_type = "scale_drag"
+                    self.drag_start_time = self.range_start.value
+
+            
+            if mouse_down and self.drag_start:
+                start, end = 0, 0
+                if self.move_type and self.move_type != "scale_drag":
+                    if self.move_type == "start":
+                        if 0 <= x <= self.width:
+                            start = x
+                            end = self.highlight_end
+                    elif self.move_type == "end":
+                        if 0 <= x <= self.width:
+                            start = self.highlight_start
+                            end = x
+                    elif self.move_type == "move":
+                        width = self.highlight_end - self.highlight_start
+                        start = x - self.drag_start
+                        start = max(0, min(start, self.width))
+                        
+                        end = start + width
+                        if end > self.width:
+                            end = self.width
+                            start = end - width
+    
+                    if end - start > 1:
+                        self.highlight = (self.get_time(start), self.get_time(end))
+                        self.redraw_canvas()
+
+                    self.__call_parent_time_changed()
+                else:
+                    self.range_start.target(self.drag_start_time +
+                                            dt.timedelta(minutes = self.get_value_at_pos(x = self.drag_start) - self.get_value_at_pos(x = x)))
+                    self.scroll_to_range_start()
+
+
+
+            if start_drag:
+                area.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_SIDE))
+            elif end_drag:
+                area.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE))
+            elif in_between:
+                area.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
+            else:
+                area.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW))
+                
+        
+    def _minutes_from_start(self, date):
+            delta = (date - self.range_start.value)
+            return delta.days * 24 * 60 + delta.seconds / 60
+            
+    def _render(self):
+        context = self.context
+        #TODO - use system colors and fonts
+ 
+        context.set_line_width(1)
+
+        #we will buffer 4 hours to both sides so partial labels also appear
+        range_end = self.range_start.value + dt.timedelta(hours = 12 + 2 * 4)        
+        self.graph_x = -self.width / 3 #so x moves one third out of screen
+        self.set_value_range(x_min = 0, x_max = 12 * 60)
+
+        minutes = self._minutes_from_start(range_end)
+
+
+
+        graph_y = 4
+        graph_height = self.height - 10
+        graph_y2 = graph_y + graph_height
+
+        
+        # graph area
+        self.fill_area(0, graph_y - 1, self.width, graph_height, (1,1,1))
+    
+        #bars
+        for fact in self.facts:
+            start_minutes = self._minutes_from_start(fact["start_time"])
+            
+            if fact["end_time"]:
+                end_minutes = self._minutes_from_start(fact["end_time"])
+            else:
+                if fact["start_time"].date() > dt.date.today() - dt.timedelta(days=1):
+                    end_minutes = self._minutes_from_start(dt.datetime.now())
+                else:
+                    end_minutes = start_minutes
+            
+            if self.get_pixel(end_minutes) > 0 and \
+                self.get_pixel(start_minutes) < self.width:
+                    context.set_source_rgba(0.86, 0.86, 0.86, 0.5)
+
+                    context.rectangle(round(self.get_pixel(start_minutes)),
+                                      graph_y,
+                                      round(self.get_pixel(end_minutes) - self.get_pixel(start_minutes)),
+                                      graph_height - 1)
+                    context.fill()
+                    context.stroke()
+
+                    context.set_source_rgba(0.86, 0.86, 0.86, 1)
+                    self.move_to(start_minutes, graph_y)
+                    self.line_to(start_minutes, graph_y2)
+                    self.move_to(end_minutes, graph_y)
+                    self.line_to(end_minutes, graph_y2)
+                    context.stroke()
+
+        
+        
+        #time scale
+        context.set_source_rgb(0, 0, 0)
+        self.layout.set_width(-1)
+        for i in range(minutes):
+            label_time = (self.range_start.value + dt.timedelta(minutes=i))
+            
+            if label_time.minute == 0:
+                context.set_source_rgb(0.8, 0.8, 0.8)
+                self.move_to(i, graph_y2 - 15)
+                self.line_to(i, graph_y2)
+                context.stroke()
+            elif label_time.minute % 15 == 0:
+                context.set_source_rgb(0.8, 0.8, 0.8)
+                self.move_to(i, graph_y2 - 5)
+                self.line_to(i, graph_y2)
+                context.stroke()
+                
+                
+                
+            if label_time.minute == 0 and label_time.hour % 2 == 0:
+                if label_time.hour == 0:
+                    context.set_source_rgb(0.8, 0.8, 0.8)
+                    self.move_to(i, graph_y)
+                    self.line_to(i, graph_y2)
+                    label_minutes = label_time.strftime("%b %d")
+                else:
+                    label_minutes = label_time.strftime("%H<small><sup>%M</sup></small>")
+
+                context.set_source_rgb(0.4, 0.4, 0.4)
+                self.layout.set_markup(label_minutes)
+                label_w, label_h = self.layout.get_pixel_size()
+                
+                context.move_to(self.get_pixel(i) + 2, graph_y2 - label_h - 8)                
+
+                context.show_layout(self.layout)
+        context.stroke()
+        
+        #highlight rectangle
+        if self.highlight:
+            self.highlight_start = self.get_pixel(self._minutes_from_start(self.highlight[0]))
+            self.highlight_end = self.get_pixel(self._minutes_from_start(self.highlight[1]))
+
+        #TODO - make a proper range check here
+        if self.highlight_end > 0 and self.highlight_start < self.width:
+            rgb = colorsys.hls_to_rgb(.6, .7, .5)
+
+
+            self.fill_area(self.highlight_start, graph_y,
+                           self.highlight_end - self.highlight_start, graph_height,
+                           (rgb[0], rgb[1], rgb[2], 0.5))
+            context.stroke()
+
+            context.set_source_rgb(*rgb)
+            self.context.move_to(self.highlight_start, graph_y)
+            self.context.line_to(self.highlight_start, graph_y + graph_height)
+            self.context.move_to(self.highlight_end, graph_y)
+            self.context.line_to(self.highlight_end, graph_y + graph_height)
+            context.stroke()
+
+        #and now put a frame around the whole thing
+        context.set_source_rgb(0.7, 0.7, 0.7)
+        context.rectangle(0, graph_y-1, self.width - 1, graph_height)
+        context.stroke()
+        
+        if self.move_type == "move" and (self.highlight_start == 0 or self.highlight_end == self.width):
+            if self.highlight_start == 0:
+                self.range_start.target(self.range_start.value - dt.timedelta(minutes=30))
+            if self.highlight_end == self.width:
+                self.range_start.target(self.range_start.value + dt.timedelta(minutes=30))
+            self.scroll_to_range_start()
+
+
diff --git a/hamster/widgets/reportchooserdialog.py b/hamster/widgets/reportchooserdialog.py
new file mode 100644
index 0000000..c27e29c
--- /dev/null
+++ b/hamster/widgets/reportchooserdialog.py
@@ -0,0 +1,167 @@
+# - coding: utf-8 -
+
+# Copyright (C) 2009 Toms Bauģis <toms.baugis at gmail.com>
+
+# This file is part of Project Hamster.
+
+# Project Hamster 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.
+
+# Project Hamster 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 Project Hamster.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import pygtk
+pygtk.require('2.0')
+
+import os
+import gtk, gobject
+
+class ReportChooserDialog(gtk.Dialog):
+    __gsignals__ = {
+        # format, path, start_date, end_date
+        'report-chosen': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+                          (gobject.TYPE_STRING, gobject.TYPE_STRING,
+                           gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
+                           gobject.TYPE_PYOBJECT)),
+        'report-chooser-closed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
+    }
+    def __init__(self):
+        gtk.Dialog.__init__(self)
+        ui = stuff.load_ui_file("stats.ui")
+        self.dialog = ui.get_object('save_report_dialog')
+
+        self.dialog.set_action(gtk.FILE_CHOOSER_ACTION_SAVE)
+        self.dialog.set_current_folder(os.path.expanduser("~"))
+
+        self.filters = {}
+
+        filter = gtk.FileFilter()
+        filter.set_name(_("HTML Report"))
+        filter.add_mime_type("text/html")
+        filter.add_pattern("*.html")
+        filter.add_pattern("*.htm")
+        self.filters[filter] = "html"
+        self.dialog.add_filter(filter)
+
+        filter = gtk.FileFilter()
+        filter.set_name(_("Tab-Separated Values (TSV)"))
+        filter.add_mime_type("text/plain")
+        filter.add_pattern("*.tsv")
+        filter.add_pattern("*.txt")
+        self.filters[filter] = "tsv"
+        self.dialog.add_filter(filter)
+
+        filter = gtk.FileFilter()
+        filter.set_name(_("XML"))
+        filter.add_mime_type("text/xml")
+        filter.add_pattern("*.xml")
+        self.filters[filter] = "xml"
+        self.dialog.add_filter(filter)
+
+        filter = gtk.FileFilter()
+        filter.set_name(_("iCal"))
+        filter.add_mime_type("text/calendar")
+        filter.add_pattern("*.ics")
+        self.filters[filter] = "ical"
+        self.dialog.add_filter(filter)
+
+        filter = gtk.FileFilter()
+        filter.set_name("All files")
+        filter.add_pattern("*")
+        self.dialog.add_filter(filter)
+        
+        self.start_date = widgets.DateInput()
+        ui.get_object('from_date_box').add(self.start_date)
+        self.end_date = widgets.DateInput()
+        ui.get_object('to_date_box').add(self.end_date)
+
+        self.category_box = ui.get_object('category_box')
+
+        ui.get_object('save_button').connect("clicked", self.on_save_button_clicked)
+        ui.get_object('cancel_button').connect("clicked", self.on_cancel_button_clicked)
+        
+
+    def show(self, start_date, end_date):
+        #set suggested name to something readable, replace backslashes with dots
+        #so the name is valid in linux
+        filename = "Time track %s - %s." % (start_date.strftime("%x").replace("/", "."),
+                                           end_date.strftime("%x").replace("/", "."))
+        self.dialog.set_current_name(filename)
+        
+        self.start_date.set_date(start_date)
+        self.end_date.set_date(end_date)
+        
+        #add unsorted category
+        button_all = gtk.CheckButton(C_("categories", "All").encode("utf-8"))
+        button_all.value = None
+        button_all.set_active(True)
+        
+        def on_category_all_clicked(checkbox):
+            active = checkbox.get_active()
+            for checkbox in self.category_box.get_children():
+                checkbox.set_active(active)
+        
+        button_all.connect("clicked", on_category_all_clicked)
+        self.category_box.attach(button_all, 0, 1, 0, 1)
+
+        categories = runtime.storage.get_category_list()
+        col, row = 0, 0
+        for category in categories:
+            col +=1
+            if col % 4 == 0:
+                col = 0
+                row +=1
+
+            button = gtk.CheckButton(category['name'].encode("utf-8"))
+            button.value = category['id']
+            button.set_active(True)
+            self.category_box.attach(button, col, col+1, row, row+1)
+
+        
+
+        response = self.dialog.show_all()
+
+    def present(self):
+        self.dialog.present()
+
+    def on_save_button_clicked(self, widget):
+        path, format = None,  None
+
+        format = "html"
+        if self.dialog.get_filter() in self.filters:
+            format = self.filters[self.dialog.get_filter()]
+        path = self.dialog.get_filename()
+        
+        # append correct extension if it is missing
+        # TODO - proper way would be to change extension on filter change
+        # only pointer in web is http://www.mail-archive.com/pygtk daa com au/msg08740.html
+        if path.endswith(".%s" % format) == False:
+            path = "%s.%s" % (path.rstrip("."), format)
+        
+        categories = []
+        for button in self.category_box.get_children():
+            if button.get_active():
+                categories.append(button.value)
+        
+        if None in categories:
+            categories = None # nothing is everything
+        
+        # format, path, start_date, end_date
+        self.emit("report-chosen", format, path,
+                           self.start_date.get_date().date(),
+                           self.end_date.get_date().date(),
+                           categories)
+        self.dialog.destroy()
+        
+
+    def on_cancel_button_clicked(self, widget):
+        self.emit("report-chooser-closed")
+        self.dialog.destroy()
diff --git a/hamster/widgets/timeline.py b/hamster/widgets/timeline.py
new file mode 100644
index 0000000..c3fc508
--- /dev/null
+++ b/hamster/widgets/timeline.py
@@ -0,0 +1,174 @@
+# - coding: utf-8 -
+
+# Copyright (C) 2009 Toms Bauģis <toms.baugis at gmail.com>
+
+# This file is part of Project Hamster.
+
+# Project Hamster 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.
+
+# Project Hamster 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 Project Hamster.  If not, see <http://www.gnu.org/licenses/>.
+
+from hamster import graphics
+import datetime as dt
+
+class TimeLine(graphics.Area):
+    MODE_YEAR = 0
+    MODE_MONTH = 1
+    MODE_WEEK = 1
+    MODE_DAY = 3
+    def __init__(self):
+        graphics.Area.__init__(self)
+        self.start_date, self.end_date = None, None
+        self.draw_mode = None
+        self.max_hours = None
+        
+    
+    def draw(self, facts):
+        import itertools
+        self.facts = {}
+        for date, date_facts in itertools.groupby(facts, lambda x: x["start_time"].date()):
+            date_facts = list(date_facts)
+            self.facts[date] = date_facts
+            self.max_hours = max(self.max_hours,
+                                 sum([fact["delta"].seconds / 60 / float(60) +
+                               fact["delta"].days * 24 for fact in date_facts]))
+        
+        start_date = facts[0]["start_time"].date()
+        end_date = facts[-1]["start_time"].date()
+
+        self.draw_mode = self.MODE_YEAR
+        self.start_date = start_date.replace(month=1, day=1)
+        self.end_date = end_date.replace(month=12, day=31)
+        
+
+        """
+        #TODO - for now we have only the year mode        
+        if start_date.year != end_date.year or start_date.month != end_date.month:
+            self.draw_mode = self.MODE_YEAR
+            self.start_date = start_date.replace(month=1, day=1)
+            self.end_date = end_date.replace(month=12, day=31)
+        elif start_date.strftime("%W") != end_date.strftime("%W"):
+            self.draw_mode = self.MODE_MONTH
+            self.start_date = start_date.replace(day=1)
+            self.end_date = end_date.replace(date =
+                                    calendar.monthrange(self.end_date.year,
+                                                        self.end_date.month)[1])
+        elif start_date != end_date:
+            self.draw_mode = self.MODE_WEEK
+        else:
+            self.draw_mode = self.MODE_DAY
+        """
+        
+        self.redraw_canvas()
+        
+        
+    def _render(self):
+        import calendar
+        
+        if self.draw_mode != self.MODE_YEAR:
+            return
+
+        self.fill_area(0, 0, self.width, self.height, (0.975,0.975,0.975))
+        self.set_color((100,100,100))
+
+        self.set_value_range(x_min = 1, x_max = (self.end_date - self.start_date).days)        
+        month_label_fits = True
+        for month in range(1, 13):
+            self.layout.set_text(calendar.month_abbr[month])
+            label_w, label_h = self.layout.get_pixel_size()
+            if label_w * 2 > self.x_factor * 30:
+                month_label_fits = False
+                break
+        
+        
+        ticker_date = self.start_date
+        
+        year_pos = 0
+        
+        for year in range(self.start_date.year, self.end_date.year + 1):
+            #due to how things lay over, we are putting labels on backwards, so that they don't overlap
+            
+            self.context.set_line_width(1)
+            for month in range(1, 13):
+                for day in range(1, calendar.monthrange(year, month)[1] + 1):
+                    ticker_pos = year_pos + ticker_date.timetuple().tm_yday
+                    
+                    #if ticker_date.weekday() in [0, 6]:
+                    #    self.fill_area(ticker_pos * self.x_factor + 1, 20, self.x_factor, self.height - 20, (240, 240, 240))
+                    #    self.context.stroke()
+                        
+    
+                    if self.x_factor > 5:
+                        self.move_to(ticker_pos, self.height - 20)
+                        self.line_to(ticker_pos, self.height)
+                   
+                        self.layout.set_text(ticker_date.strftime("%d"))
+                        label_w, label_h = self.layout.get_pixel_size()
+                        
+                        if label_w < self.x_factor / 1.2: #if label fits
+                            self.context.move_to(self.get_pixel(ticker_pos) + 2,
+                                                 self.height - 20)
+                            self.context.show_layout(self.layout)
+                    
+                        self.context.stroke()
+                        
+                    #now facts
+                    facts_today = self.facts.get(ticker_date, [])
+                    if facts_today:
+                        total_length = dt.timedelta()
+                        for fact in facts_today:
+                            total_length += fact["delta"]
+                        total_length = total_length.seconds / 60 / 60.0 + total_length.days * 24
+                        total_length = total_length / float(self.max_hours) * self.height - 16
+
+                        self.fill_area(round(ticker_pos * self.x_factor),
+                                       round(self.height - total_length),
+                                       round(self.x_factor),
+                                       round(total_length),
+                                       (190,190,190))
+
+
+                        
+
+                    ticker_date += dt.timedelta(1)
+                
+            
+                
+                if month_label_fits:
+                    #roll back a little
+                    month_pos = ticker_pos - calendar.monthrange(year, month)[1] + 1
+
+                    self.move_to(month_pos, 0)
+                    #self.line_to(month_pos, 20)
+                    
+                    self.layout.set_text(dt.date(year, month, 1).strftime("%b"))
+    
+                    self.move_to(month_pos, 0)
+                    self.context.show_layout(self.layout)
+
+
+    
+            
+    
+            self.layout.set_text("%d" % year)
+            label_w, label_h = self.layout.get_pixel_size()
+                        
+            self.move_to(year_pos + 2 / self.x_factor, month_label_fits * label_h * 1.2)
+    
+            self.context.show_layout(self.layout)
+            
+            self.context.stroke()
+
+            year_pos = ticker_pos #save current state for next year
+
+
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]