40
69
self.set_border_width(0)
42
71
self.label = gtk.Label()
72
self.label.set_justify(gtk.JUSTIFY_CENTER)
74
self.label.connect('style-set', self.on_label_style_set)
75
self.connect('size_allocate', self.on_size_allocate)
43
77
self.add(self.label)
45
def set_text(self, text):
46
self.label.set_text(text)
79
self.activity, self.duration = None, None
82
def set_active(self, is_active):
83
self.set_property('active', is_active)
85
def set_text(self, activity, duration):
86
self.activity = activity
87
self.duration = duration
90
def reformat_label(self):
93
if self.use_two_line_format():
94
label = "%s\n%s" % (self.activity, self.duration)
96
label = "%s %s" % (self.activity, self.duration)
98
label = '<span gravity=\"south\">' + label + '</span>'
99
self.label.set_markup(label)
48
101
def get_pos(self):
49
102
return gtk.gdk.Window.get_origin(self.label.window)
51
def set_active(self, is_active):
52
self.set_property('active', is_active)
105
def use_two_line_format(self):
106
if not self.get_parent():
109
popup_dir = self.get_parent().get_orient()
111
orient_vertical = popup_dir in [gnomeapplet.ORIENT_LEFT] or \
112
popup_dir in [gnomeapplet.ORIENT_RIGHT]
116
context = self.label.get_pango_context()
117
metrics = context.get_metrics(self.label.style.font_desc,
118
pango.Context.get_language(context))
119
ascent = pango.FontMetrics.get_ascent(metrics)
120
descent = pango.FontMetrics.get_descent(metrics)
122
if orient_vertical == False:
123
thickness = self.style.ythickness;
125
thickness = self.style.xthickness;
127
focus_width = self.style_get_property("focus-line-width")
128
focus_pad = self.style_get_property("focus-padding")
130
required_size = 2 * ((pango.PIXELS(ascent + descent) ) + 2 * (focus_width + focus_pad + thickness))
133
available_size = self.get_allocation().width
135
available_size = self.get_allocation().height
137
return required_size <= available_size
139
def on_label_style_set(self, widget, something):
140
self.reformat_label()
143
def on_size_allocate(self, widget, allocation):
144
if not self.get_parent():
147
self.popup_dir = self.get_parent().get_orient()
149
orient_vertical = True
150
new_size = allocation.width
151
if self.popup_dir in [gnomeapplet.ORIENT_LEFT]:
153
elif self.popup_dir in [gnomeapplet.ORIENT_RIGHT]:
157
orient_vertical = False
158
new_size = allocation.height
160
if new_angle != self.label.get_angle():
161
self.label.set_angle(new_angle)
163
if new_size != self.prev_size:
164
self.reformat_label()
166
self.prev_size = new_size
55
169
class HamsterApplet(object):
170
def name_painter(self, column, cell, model, iter):
171
activity_name = model.get_value(iter, 1)
172
description = model.get_value(iter, 5)
174
text = """%s""" % activity_name
176
text+= """\n<span style="italic" size="small">%s</span>""" % (description)
178
cell.set_property('markup', text)
56
183
def __init__(self, applet):
57
184
self.config = GconfStore.get_instance()
59
186
self.applet = applet
60
187
self.applet.set_applet_flags (gnomeapplet.EXPAND_MINOR);
189
self.preferences_editor = None
190
self.applet.about = None
62
192
self.button = PanelButton()
64
194
# load window of activity switcher and todays view
65
195
self.glade = gtk.glade.XML(os.path.join(SHARED_DATA_DIR, "menu.glade"))
66
196
self.window = self.glade.get_widget('hamster-window')
68
# set up drop down menu
69
self.activity_list = self.glade.get_widget('activity-list')
70
self.activity_list.set_model(gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT))
71
self.activity_list.set_text_column(0)
197
self.window.set_keep_above(True)
73
# set up autocompletition for the drop-down menu
74
self.activities = gtk.ListStore(gobject.TYPE_STRING)
75
completion = gtk.EntryCompletion()
76
completion.set_model(self.activities)
77
completion.set_text_column(0)
78
completion.set_minimum_key_length(1)
79
self.activity_list.child.set_completion(completion)
81
201
# init today's tree
82
202
self.treeview = self.glade.get_widget('today')
83
203
self.treeview.set_tooltip_column(1)
85
205
self.treeview.append_column(gtk.TreeViewColumn("Time", gtk.CellRendererText(), text=2))
86
self.treeview.append_column(ExpanderColumn("Name", text = 1))
87
self.treeview.append_column(gtk.TreeViewColumn("", gtk.CellRendererText(), text=3))
207
nameColumn = gtk.TreeViewColumn(_("Name"))
208
nameColumn.set_expand(True)
209
nameCell = gtk.CellRendererText()
210
nameColumn.pack_start(nameCell, True)
211
nameCell.set_property("ellipsize", pango.ELLIPSIZE_END)
212
nameColumn.set_cell_data_func(nameCell, self.name_painter)
213
self.treeview.append_column(nameColumn)
89
216
edit_cell = gtk.CellRendererPixbuf()
90
217
edit_cell.set_property("stock_id", "gtk-edit")
91
218
self.edit_column = gtk.TreeViewColumn("", edit_cell)
92
219
self.treeview.append_column(self.edit_column)
223
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
224
name = dbus.service.BusName(HAMSTER_URI, dbus.SessionBus())
225
self.dbusController = HamsterDbusController(bus_name = name)
227
# let's also attach our listeners here
228
bus = dbus.SessionBus()
229
bus.add_signal_receiver(self.on_idle_changed,
230
dbus_interface="org.gnome.ScreenSaver",
231
signal_name="SessionIdleChanged")
232
except dbus.DBusException, e:
233
print "can't init dbus: %s" % e
95
235
# Load today's data, activities and set label
96
236
self.last_activity = None
97
237
self.today = datetime.date.today()
132
276
self.applet.set_background_widget(self.applet)
134
278
self.button.connect('toggled', self.on_toggle)
135
self.button.connect('size_allocate', self.on_applet_size_allocate)
137
280
self.button.connect('button_press_event', self.on_button_press)
138
281
self.glade.signal_autoconnect(self)
141
284
dispatcher.add_handler('keybinding_activated', self.on_keybinding_activated)
142
dispatcher.add_handler('gconf_timeout_changed', self.on_timeout_changed)
143
287
dispatcher.add_handler('gconf_timeout_enabled_changed', self.on_timeout_enabled_changed)
146
self.timeout_enabled = self.config.get_timeout_enabled()
288
self.on_timeout_enabled_changed(None, self.config.get_timeout_enabled())
290
# init nagging timeout
292
self.notify = Notifier('HamsterApplet', gtk.STOCK_DIALOG_QUESTION, self.button)
293
dispatcher.add_handler('gconf_notify_interval_changed', self.on_notify_interval_changed)
294
self.on_notify_interval_changed(None, self.config.get_notify_interval())
297
def on_idle_changed(self, state):
298
print "Idle state changed. Idle: ", state
299
# refresh when we are out of idle
300
# (like, instantly after computer has been turned on!
302
self.refresh_hamster()
304
def set_dropdown(self):
305
# set up drop down menu
306
self.activity_list = self.glade.get_widget('activity-list')
307
self.activity_list.set_model(gtk.ListStore(gobject.TYPE_STRING,
309
gobject.TYPE_STRING))
311
self.activity_list.clear()
312
activity_cell = gtk.CellRendererText()
313
self.activity_list.pack_start(activity_cell, True)
314
self.activity_list.add_attribute(activity_cell, 'text', 0)
315
category_cell = CategoryCell()
316
self.activity_list.pack_start(category_cell, False)
317
self.activity_list.add_attribute(category_cell, 'text', 1)
319
self.activity_list.set_property("text-column", 2)
322
# set up autocompletition
323
self.activities = gtk.ListStore(gobject.TYPE_STRING,
326
completion = gtk.EntryCompletion()
327
completion.set_model(self.activities)
329
activity_cell = gtk.CellRendererText()
330
completion.pack_start(activity_cell, True)
331
completion.add_attribute(activity_cell, 'text', 0)
332
completion.set_property("text-column", 2)
334
category_cell = CategoryCell()
335
completion.pack_start(category_cell, False)
336
completion.add_attribute(category_cell, 'text', 1)
339
def match_func(completion, key, iter):
340
model = completion.get_model()
341
text = model.get_value(iter, 2)
342
if text and text.startswith(key):
346
completion.set_match_func(match_func)
347
completion.set_minimum_key_length(1)
348
completion.set_inline_completion(True)
350
self.activity_list.child.set_completion(completion)
148
353
def on_today_release_event(self, tree, event):
149
354
pointer = event.window.get_pointer() # x, y, flags
193
403
delta = datetime.datetime.now() - self.last_activity['start_time']
194
404
duration = delta.seconds / 60
195
405
label = "%s %s" % (self.last_activity['name'], format_duration(duration))
406
self.button.set_text(self.last_activity['name'], format_duration(duration))
197
408
self.glade.get_widget('current_activity').set_text(self.last_activity['name'])
198
409
self.glade.get_widget('stop_tracking').set_sensitive(1);
200
411
label = "%s" % _(u"No activity")
412
self.button.set_text(label, None)
201
413
self.glade.get_widget('stop_tracking').set_sensitive(0);
202
self.button.set_text(label)
416
# Hamster DBusController current activity updating
417
self.dbusController.update_activity(label)
419
def check_user(self):
420
delta = datetime.datetime.now() - self.last_activity['start_time']
421
duration = delta.seconds / 60
423
if duration and duration % self.notify_interval == 0:
425
msg = _(u"Are you still working on <b>%s</b>?") % self.last_activity['name']
426
self.notify.msg(msg, self.switch_cb, self.stop_cb)
428
def switch_cb(self, n, action):
429
self.__show_toggle(None, not self.button.get_active())
431
def stop_cb(self, n, action):
432
self.on_stop_tracking(None)
204
435
def load_day(self):
205
436
"""sets up today's tree and fills it with records
206
437
returns information about last activity"""
210
441
if len(day.facts) == 0:
211
442
self.last_activity = None
212
443
self.glade.get_widget("todays_scroll").hide()
213
self.glade.get_widget("no_facts_today").show()
445
self.glade.get_widget("fact_totals").set_text(_("No records today"))
215
447
self.last_activity = day.facts[len(day.facts) - 1]
216
448
self.glade.get_widget("todays_scroll").show()
217
self.glade.get_widget("no_facts_today").hide()
451
for total in day.totals:
452
total_string += _("%(category)s: %(duration)s, ") % ({'category': total,
453
'duration': format_duration(day.totals[total])})
455
total_string = total_string.rstrip(", ") # trailing slash
456
self.glade.get_widget("fact_totals").set_text(total_string)
220
459
def refresh_menu(self):
221
#first populate the autocomplete - contains all entries
460
#first populate the autocomplete - contains all entries in lowercase
222
461
self.activities.clear()
223
all_activities = storage.get_activities()
462
all_activities = storage.get_autocomplete_activities()
224
463
for activity in all_activities:
225
self.activities.append([activity['name']])
464
activity_category = "%s@%s" % (activity['name'], activity['category'])
465
self.activities.append([activity['name'],
466
activity['category'],
228
470
#now populate the menu - contains only categorized entries
361
620
stats_viewer = StatsViewer()
362
621
stats_viewer.show()
623
def show_overview(self, menu_item, verb):
624
return self.on_overview(menu_item)
364
626
def on_custom_fact(self, menu_item):
365
627
from hamster.add_custom_fact import CustomFactController
366
628
custom_fact = CustomFactController()
367
629
custom_fact.show()
369
631
def on_about (self, component, verb):
370
from hamster.about import show_about
371
show_about(self.applet)
632
if self.applet.about:
633
self.applet.about.present()
635
from hamster.about import show_about
636
show_about(self.applet)
373
638
def show_preferences(self, menu_item, verb):
374
639
from hamster.preferences import PreferencesEditor
376
641
dispatcher.dispatch('panel_visible', False)
377
activities_editor = PreferencesEditor()
378
activities_editor.show()
643
if self.preferences_editor and self.preferences_editor.window:
644
self.preferences_editor.window.present()
646
self.preferences_editor = PreferencesEditor()
647
self.preferences_editor.show()
381
650
def after_activity_update(self, widget, renames):
387
656
if date.date() == datetime.date.today():
389
658
self.update_label()
391
662
"""global shortcuts"""
392
663
def on_keybinding_activated(self, event, data):
393
664
self.__show_toggle(None, not self.button.get_active())
395
def on_timeout_changed(self, event, new_timeout):
396
self.timeout = new_timeout
398
666
def on_timeout_enabled_changed(self, event, enabled):
399
667
# if enabled, set to value, otherwise set to zero, which means disable
400
668
self.timeout_enabled = enabled
402
def on_applet_size_allocate(self, widget, event):
403
self.popup_dir = self.applet.get_orient()
405
if self.popup_dir in [gnomeapplet.ORIENT_LEFT]:
407
elif self.popup_dir in [gnomeapplet.ORIENT_RIGHT]:
670
def on_notify_interval_changed(self, event, new_interval):
671
if PYNOTIFY and 0 < new_interval < 121:
672
self.notify_interval = new_interval
412
if new_angle != self.button.label.get_angle():
413
self.button.label.set_angle(new_angle)
674
self.notify_interval = None