[hamster-applet] fixed bug 607108. due to all kinds of little things in treeview (no col and rowspan) had to go for "
- From: Toms Baugis <tbaugis src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [hamster-applet] fixed bug 607108. due to all kinds of little things in treeview (no col and rowspan) had to go for "
- Date: Sat, 16 Jan 2010 04:46:24 +0000 (UTC)
commit 137bb8e79078ab2a317d1dc8db1adb943086b936
Author: Toms Bauģis <toms baugis gmail com>
Date: Sat Jan 16 04:39:45 2010 +0000
fixed bug 607108. due to all kinds of little things in treeview (no col and rowspan) had to go for "tile style single cell - do your own layout and drawing" for the fact tree
Signed-off-by: Toms Bauģis <toms baugis gmail com>
data/applet.ui | 1 +
data/overview.ui | 1 +
hamster/applet.py | 5 +
hamster/overview.py | 7 +-
hamster/widgets/facttree.py | 381 ++++++++++++++++++++++++++++++++++--------
hamster/widgets/tags.py | 136 +---------------
6 files changed, 326 insertions(+), 205 deletions(-)
---
diff --git a/data/applet.ui b/data/applet.ui
index 0f69ac0..4ff525b 100644
--- a/data/applet.ui
+++ b/data/applet.ui
@@ -14,6 +14,7 @@
<property name="skip_pager_hint">True</property>
<property name="decorated">False</property>
<signal name="key_press_event" handler="on_windows_keys"/>
+ <signal name="configure_event" handler="on_window_configure_event"/>
<child>
<object class="GtkEventBox" id="hamster-box">
<property name="visible">True</property>
diff --git a/data/overview.ui b/data/overview.ui
index 8d995f6..22c8b3f 100644
--- a/data/overview.ui
+++ b/data/overview.ui
@@ -7,6 +7,7 @@
<property name="title" translatable="yes">Overview - Hamster</property>
<property name="default_width">800</property>
<property name="default_height">600</property>
+ <signal name="configure_event" handler="on_tabs_window_configure_event"/>
<signal name="delete_event" handler="on_tabs_window_deleted"/>
<child>
<object class="GtkVBox" id="vbox1">
diff --git a/hamster/applet.py b/hamster/applet.py
index 7f694a1..5e19047 100755
--- a/hamster/applet.py
+++ b/hamster/applet.py
@@ -617,6 +617,11 @@ class HamsterApplet(object):
self.last_activity = None
runtime.dispatcher.dispatch('panel_visible', False)
+ def on_window_configure_event(self, window, event):
+ # this is required so that the rows would grow on resize
+ self.treeview.fix_row_heights()
+
+
def show(self):
self.window.show_all()
diff --git a/hamster/overview.py b/hamster/overview.py
index d8e72f2..4091d88 100644
--- a/hamster/overview.py
+++ b/hamster/overview.py
@@ -251,6 +251,11 @@ class Overview(object):
self.search()
+ def on_tabs_window_configure_event(self, window, event):
+ # this is required so that the rows would grow on resize
+ self.fact_tree.fix_row_heights()
+
+
def on_start_date_entered(self, input):
self.start_date = input.get_date().date()
self.view_date = self.start_date
@@ -332,7 +337,7 @@ class Overview(object):
def on_edit_clicked(self, button):
fact = self.fact_tree.get_selected_fact()
- if not fact or isinstance(fact, date):
+ if not fact or isinstance(fact, dt.date):
return
dialogs.edit.show(fact_id = fact["id"])
diff --git a/hamster/widgets/facttree.py b/hamster/widgets/facttree.py
index 516dd1f..20510d8 100644
--- a/hamster/widgets/facttree.py
+++ b/hamster/widgets/facttree.py
@@ -22,54 +22,23 @@ import datetime as dt
from .hamster import stuff
from .hamster.stuff import format_duration, format_activity
-from tags import TagCellRenderer
+from tags import Tag
import pango
def parent_painter(column, cell, model, iter):
fact = model.get_value(iter, 0)
- parent_info = model.get_value(iter, 2)
+ parent_info = model.get_value(iter, 1)
if fact is None:
- if model.get_path(iter) == (0,): # first row
- text = '<span weight="heavy">%s</span>' % parent_info["label"]
- else:
- text = '<span weight="heavy" rise="-20000">%s</span>' % parent_info["label"]
-
- cell.set_property('markup', text)
-
+ parent_info["first"] = model.get_path(iter) == (0,) # first row
+ cell.set_property('data', parent_info)
else:
- if fact["end_time"]:
- fact_time = "%s - %s " % (fact["start_time"].strftime("%H:%M"),
- fact["end_time"].strftime("%H:%M"))
- else:
- fact_time = fact["start_time"].strftime("%H:%M ")
-
-
- activity_name = stuff.escape_pango("%s %s" % (fact_time, fact["name"]))
- description = stuff.escape_pango(fact["description"])
- category = stuff.escape_pango(fact["category"])
-
- markup = stuff.format_activity(activity_name,
- category,
- description,
- pad_description = True)
- cell.set_property('markup', markup)
-
-def duration_painter(column, cell, model, iter):
- cell.set_property('xalign', 1)
-
-
- text = model.get_value(iter, 1)
- if model.get_value(iter, 0) is None:
- if model.get_path(iter) == (0,):
- text = '<span weight="heavy">%s</span>' % text
- else:
- text = '<span weight="heavy" rise="-20000">%s</span>' % text
- cell.set_property('markup', text)
+ cell.set_property('data', fact)
def action_painter(column, cell, model, iter):
cell.set_property('xalign', 1)
+ cell.set_property('yalign', 0)
if model.get_value(iter, 0) is None:
cell.set_property("stock_id", "")
@@ -90,32 +59,15 @@ class FactTree(gtk.TreeView):
self.set_show_expanders(False)
# fact (None for parent), duration, parent data (if any)
- self.store_model = gtk.TreeStore(gobject.TYPE_PYOBJECT, str, gobject.TYPE_PYOBJECT)
+ self.store_model = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)
self.set_model(self.store_model)
- # start time - end time, name, category
- nameColumn = gtk.TreeViewColumn()
- nameCell = gtk.CellRendererText()
- #nameCell.set_property("ellipsize", pango.ELLIPSIZE_END)
- nameColumn.pack_start(nameCell, True)
- nameColumn.set_cell_data_func(nameCell, parent_painter)
- self.append_column(nameColumn)
-
- # tags
- tag_cell = TagCellRenderer()
- tag_cell.set_font_size(8);
- tagColumn = gtk.TreeViewColumn("", tag_cell, data=0)
- tagColumn.set_expand(True)
- self.append_column(tagColumn)
-
-
- # duration
- timeColumn = gtk.TreeViewColumn()
- timeCell = gtk.CellRendererText()
- timeColumn.pack_end(timeCell, True)
- timeColumn.set_cell_data_func(timeCell, duration_painter)
- self.append_column(timeColumn)
+ fact_cell = FactCellRenderer()
+ fact_column = gtk.TreeViewColumn("", fact_cell, data=0)
+ fact_column.set_cell_data_func(fact_cell, parent_painter)
+ fact_column.set_expand(True)
+ self.append_column(fact_column)
edit_cell = gtk.CellRendererPixbuf()
edit_cell.set_property("mode", gtk.CELL_RENDERER_MODE_ACTIVATABLE)
@@ -126,35 +78,43 @@ class FactTree(gtk.TreeView):
self.connect("row-activated", self._on_row_activated)
self.connect("button-release-event", self._on_button_release_event)
self.connect("key-press-event", self._on_key_pressed)
+ self.connect("configure-event", lambda *args: self.columns_autosize())
+
self.show()
+ self.longest_activity_category = 0 # we will need this for the cell renderer
+
+
self.stored_selection = None
+ self.box = None
+
+
+ def fix_row_heights(self):
+ alloc = self.get_allocation()
+ if alloc != self.box:
+ self.box = alloc
+ self.columns_autosize()
+
def clear(self):
self.store_model.clear()
+ self.longest_activity_category = 0
def add_fact(self, fact, parent = None):
- duration = stuff.duration_minutes(fact["delta"]) / 60
+ self.longest_activity_category = max(self.longest_activity_category,
+ len(fact["name"]) + len(fact["category"]))
- if fact["end_time"]:
- fact_time = "%s - %s " % (fact["start_time"].strftime("%H:%M"),
- fact["end_time"].strftime("%H:%M"))
- else:
- fact_time = fact["start_time"].strftime("%H:%M ")
-
- self.store_model.append(parent, [fact,
- stuff.format_duration(fact["delta"]),
- None])
+ self.store_model.append(parent, [fact, None])
def add_group(self, group_label, group_date, facts):
total = sum([stuff.duration_minutes(fact["delta"]) for fact in facts])
# adds group of facts with the given label
group_row = self.store_model.append(None, [None,
- stuff.format_duration(total),
dict(date = group_date,
- label = group_label)])
+ label = group_label,
+ duration = total)])
for fact in facts:
self.add_fact(fact, group_row)
@@ -181,7 +141,7 @@ class FactTree(gtk.TreeView):
selection = self.get_selection()
(model, iter) = selection.get_selected()
if iter:
- return model[iter][0] or model[iter][2]["date"]
+ return model[iter][0] or model[iter][1]["date"]
else:
return None
@@ -212,3 +172,278 @@ class FactTree(gtk.TreeView):
return True
return False
+
+
+
+class FactCellRenderer(gtk.GenericCellRenderer):
+ """ We need all kinds of wrapping and spanning and the treeview just does
+ not cut it"""
+
+ __gproperties__ = {
+ "data": (gobject.TYPE_PYOBJECT, "Data", "Data", gobject.PARAM_READWRITE),
+ }
+
+ def __init__(self):
+ gtk.GenericCellRenderer.__init__(self)
+ self.height = 0
+ self.data = None
+
+ default_font = gtk.Style().font_desc.to_string()
+ self.label_font = pango.FontDescription(default_font)
+ self.label_font_size = 10
+
+ self.selected_color = gtk.Style().text[gtk.STATE_SELECTED]
+ self.normal_color = gtk.Style().text[gtk.STATE_NORMAL]
+
+ self.tag_font = pango.FontDescription(default_font)
+ self.tag_font.set_size(pango.SCALE * 8)
+
+ self.layout = None
+
+ self.col_padding = 10
+ self.row_padding = 4
+
+ def do_set_property (self, pspec, value):
+ setattr(self, pspec.name, value)
+
+ def do_get_property(self, pspec):
+ return getattr (self, pspec.name)
+
+
+ def set_text(self, text):
+ # sets text and returns width and height of the layout
+ self.layout.set_text(text)
+ w, h = self.layout.get_pixel_size()
+ return w, h
+
+ def on_render (self, window, widget, background_area, cell_area, expose_area, flags):
+ if not self.data:
+ return
+
+ """
+ ASCII Art
+ --------------+--------------------------------------------+-------+---+
+ 13:12 - 17:18 | Some activity - category, tag, tag, tag, | 14:44 | E |
+ | tag, tag, some description in grey italics | | |
+ --------------+--------------------------------------------+-------+---+
+ """
+
+
+ context = window.cairo_create()
+ if not self.layout:
+ self.layout = context.create_layout()
+ self.layout.set_font_description(self.label_font)
+
+
+ if "id" in self.data:
+ fact, parent = self.data, None
+ else:
+ parent, fact = self.data, None
+
+
+
+
+ x, y, width, height = cell_area
+ context.translate(x, y)
+
+ current_fact = widget.get_selected_fact()
+
+ if parent:
+ text_color = self.normal_color
+ # if we are selected, change font color appropriately
+ if current_fact and isinstance(current_fact, dt.date) \
+ and current_fact == parent["date"]:
+ text_color = self.selected_color
+
+ self.set_color(context, text_color)
+
+ self.layout.set_markup("<b>%s</b>" % parent["label"])
+ if self.data["first"]:
+ y = 5
+ else:
+ y = 20
+
+ context.move_to(5, y)
+ context.show_layout(self.layout)
+
+ self.layout.set_markup("<b>%s</b>" % stuff.format_duration(parent["duration"]))
+ label_w, label_h = self.layout.get_pixel_size()
+
+ context.move_to(width - label_w, y)
+ context.show_layout(self.layout)
+
+ else:
+ text_color = self.normal_color
+ selected = False
+ # if we are selected, change font color appropriately
+ if current_fact and isinstance(current_fact, dt.date) == False \
+ and current_fact["id"] == fact["id"]:
+ text_color = self.selected_color
+ selected = True
+
+
+ """ start time and end time at beginning of column """
+ interval = fact["start_time"].strftime("%H:%M")
+ if fact["end_time"]:
+ interval = "%s - %s" % (interval, fact["end_time"].strftime("%H:%M"))
+
+ self.set_color(context, text_color)
+
+ self.layout.set_markup(interval)
+ context.move_to(self.col_padding, 2)
+ context.show_layout(self.layout)
+
+ """ duration at the end """
+ self.layout.set_markup(stuff.format_duration(fact["delta"]))
+ duration_w, duration_h = self.layout.get_pixel_size()
+ context.move_to(width - duration_w, 2)
+ context.show_layout(self.layout)
+
+ """ activity, category, tags, description in middle """
+ # we want our columns look aligned, so we will do fixed offset from
+ # both sides, in letter length
+ self.layout.set_markup("8")
+ letter_w, letter_h = self.layout.get_pixel_size()
+
+ box_x = letter_w * 14
+ box_w = width - letter_w * 15 - max(letter_w * 10, duration_w)
+
+ context.translate(box_x, 2)
+
+ context.move_to(0,0)
+ self.layout.set_markup(fact["name"])
+ label_w, label_h = self.layout.get_pixel_size()
+ self.set_color(context, text_color)
+ context.show_layout(self.layout)
+
+ context.move_to(label_w, 0)
+
+ if not selected:
+ self.set_color(context, widget.get_style().text[gtk.STATE_INSENSITIVE])
+ self.layout.set_markup(" - %s" % fact["category"])
+ label_w, label_h = self.layout.get_pixel_size()
+ context.show_layout(self.layout)
+
+ act_cat_offset = (widget.longest_activity_category + 4) * letter_w
+ context.move_to(act_cat_offset, 0)
+ context.set_source_rgb(0,0,0)
+
+ self.layout.set_font_description(self.tag_font)
+ cur_x, cur_y = act_cat_offset, 0
+ for i, tag in enumerate(fact["tags"]):
+ tag_w, tag_h = Tag.tag_size(tag, self.layout)
+
+ if i > 0 and cur_x + tag_w >= box_w:
+ cur_x = act_cat_offset
+ cur_y += tag_h + 4
+
+ Tag(context, self.layout, True, tag, None,
+ gtk.gdk.Rectangle(cur_x, cur_y, box_w - cur_x, height - cur_y))
+
+ cur_x += tag_w + 4
+
+ self.layout.set_font_description(self.label_font)
+
+ if fact["description"]:
+ self.layout.set_markup("<small><i>%s</i></small>" % fact["description"])
+ label_w, label_h = self.layout.get_pixel_size()
+
+ x, y = cur_x, cur_y + 4
+ if cur_x + label_w > box_w:
+ x = 0
+ y = label_h + 4
+
+ context.move_to(x, y)
+ self.layout.set_width((box_w - x) * pango.SCALE)
+ context.show_layout(self.layout)
+ self.layout.set_width(-1)
+
+ self.layout.set_font_description(self.label_font)
+
+
+ def set_color(self, context, color):
+ context.set_source_rgba(*self.color_to_cairo_rgba(color))
+
+ def color_to_cairo_rgba(self, c, a=1):
+ return c.red/65535.0, c.green/65535.0, c.blue/65535.0, a
+
+
+ def get_fact_size(self, widget):
+ #all we care for, is height
+ if not self.data or "id" not in self.data:
+ return None
+ fact = self.data
+
+ pixmap = gtk.gdk.Pixmap(None, 10, 10, 24)
+ context = pixmap.cairo_create()
+
+ layout = context.create_layout()
+ layout.set_font_description(self.label_font)
+
+
+ x, y, width, height = widget.get_allocation()
+
+
+ """ duration at the end """
+ layout.set_markup(stuff.format_duration(fact["delta"]))
+ duration_w, duration_h = layout.get_pixel_size()
+
+ """ activity, category, tags, description in middle """
+ # we want our columns look aligned, so we will do fixed offset from
+ # both sides, in letter length
+ layout.set_markup("8")
+ letter_w, letter_h = layout.get_pixel_size()
+
+ box_x = letter_w * 14
+ box_w = width - letter_w * 15 - max(letter_w * 14, duration_w)
+
+ act_cat_offset = (widget.longest_activity_category + 4) * letter_w
+
+ required_height = letter_h
+
+ if fact["tags"]:
+ layout.set_font_description(self.tag_font)
+ cur_x, cur_y = act_cat_offset, 0
+ for i, tag in enumerate(fact["tags"]):
+ tag_w, tag_h = Tag.tag_size(tag, layout)
+
+ if i > 0 and cur_x + tag_w >= box_w:
+ cur_x = act_cat_offset
+ cur_y += tag_h + 4
+
+ cur_x += tag_w + 4
+
+ required_height = cur_y + tag_h + 4
+
+ layout.set_font_description(self.label_font)
+
+ if fact["description"]:
+ layout.set_markup("<small><i>%s</i></small>" % fact["description"])
+ label_w, label_h = layout.get_pixel_size()
+
+ x, y = cur_x, cur_y + 4
+ if cur_x + label_w > box_w:
+ x = 0
+ y = label_h + 4
+
+ layout.set_width((box_w - x) * pango.SCALE)
+ label_w, label_h = layout.get_pixel_size()
+
+
+ required_height = y + label_h
+
+ required_height = required_height + 6
+ return (0, 0, 0, required_height)
+
+
+ def on_get_size (self, widget, cell_area = None):
+ if "id" in self.data: # fact
+ return self.get_fact_size(widget)
+ else:
+ if self.data["first"]:
+ return (0, 0, 0, 25)
+ else:
+ return (0, 0, 0, 40)
+
+
+
diff --git a/hamster/widgets/tags.py b/hamster/widgets/tags.py
index 945b393..b404999 100644
--- a/hamster/widgets/tags.py
+++ b/hamster/widgets/tags.py
@@ -307,133 +307,8 @@ class TagBox(graphics.Area):
cur_x += w + 6 #some padding too, please
-# snatch from winedoors. Carl Lattimer is my hero as always
-# wish he could write tutorials
-class TagCellRenderer(gtk.GenericCellRenderer):
- __gproperties__ = {
- "data": (gobject.TYPE_PYOBJECT, "Data", "Data", gobject.PARAM_READWRITE),
- }
-
- def __init__(self):
- gtk.GenericCellRenderer.__init__(self)
- self.height = 0
- self.width = None
- self.data = None
-
- self._font = pango.FontDescription(gtk.Style().font_desc.to_string())
- self._font_size = 10
- self.layout = None
-
- def font_size(self):
- return self._font_size
-
- def set_font_size(self, val):
- self._font_size = val
- self._font.set_size(pango.SCALE * self._font_size)
-
-
- def do_set_property (self, pspec, value):
- setattr (self, pspec.name, value)
-
- def do_get_property (self, pspec):
- return getattr (self, pspec.name)
-
-
- def tag_size(self, label):
- text_w, text_h = self.set_text(label)
- w = text_w + 16 # padding (we have some diagonals to draw)
- h = text_h + 2
- return w, h
-
- def set_text(self, text):
- # sets text and returns width and height of the layout
- self.layout.set_text(text)
- w, h = self.layout.get_pixel_size()
- return w, h
-
- def on_render (self, window, widget, background_area, cell_area, expose_area, flags):
- if not self.data: return
-
- self.width = cell_area.width
-
- context = window.cairo_create()
-
- if isinstance(self.data, dict):
- tags = self.data["tags"]
- else:
- tags = self.data
-
- x, y, width, h = cell_area
-
- context.translate(x, y)
-
- if not self.layout:
- self.layout = context.create_layout()
- self.layout.set_font_description(self._font)
-
- cur_x, cur_y = 4, 2
- for tag in tags:
- w, h = self.tag_size(tag)
- if cur_x + w >= self.width - 5: #if we do not fit, we wrap
- cur_x = 5
- cur_y += h + 6
-
- Tag(context,
- self.layout,
- True,
- tag,
- None,
- gtk.gdk.Rectangle(cur_x, cur_y, self.width - cur_x, self.height - cur_y))
-
-
- cur_x += w + 6 #some padding too, please
-
- self.height = cur_y
-
- def on_get_size (self, widget, cell_area = None):
- # TODO - we are replicating rendering code - should reuse it instead
- if isinstance(self.data, dict):
- tags = self.data["tags"]
- else:
- tags = self.data
-
- if not self.width or not tags:
- height = 0
- min_width = 80
- else:
- pixmap = gtk.gdk.Pixmap(None, self.width, 500, 24)
- context = pixmap.cairo_create()
- self.layout = context.create_layout()
- default_font = pango.FontDescription(gtk.Style().font_desc.to_string())
- default_font.set_size(pango.SCALE * self.font_size())
- self.layout.set_font_description(default_font)
-
- #make sure we fit in
- min_width = 0
- for tag in tags:
- min_width = max(min_width, self.tag_size(tag)[0]) + 6
-
-
- cur_x, cur_y = 4, 2
- for tag in tags:
- w, h = self.tag_size(tag)
- if cur_x + w >= self.width - 5: #if we do not fit, we wrap
- cur_x = 5
- cur_y += h + 6
-
- cur_x += w + 6 #some padding too, please
-
- cur_y += h + 3
-
- self.height = cur_y # TODO - this should actually trigger whole tree redraw if heights do not match
-
- return (0, 0, min_width, self.height)
-
-
class Tag(object):
def __init__(self, context, layout, render_now = False, label = None, color = None, rect = None):
- self.font_size = 10
-
if not context:
render_now = False
else:
@@ -469,22 +344,21 @@ class Tag(object):
w, h = self.layout.get_pixel_size()
return w, h
- def tag_size(self, label):
- text_w, text_h = self.set_text(label)
+ @staticmethod
+ def tag_size(label, layout):
+ layout.set_text(label)
+ text_w, text_h = layout.get_pixel_size()
w = text_w + 16 # padding (we have some diagonals to draw)
h = text_h + 2
return w, h
def draw_tag(self):
self.context.set_line_width(1)
- self.context.set_antialias(cairo.ANTIALIAS_NONE)
-
- self.context.set_antialias(cairo.ANTIALIAS_DEFAULT)
label, x, y, color = self.label, self.x, self.y, self.color
if x - round(x) != 0.5: x += 0.5
if y - round(y) != 0.5: y += 0.5
- w, h = self.tag_size(label)
+ w, h = self.tag_size(label, self.layout)
corner = h / 3
self.context.move_to(x, y + corner)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]