192
190
[("about", self.on_about),
193
191
("overview", self.show_overview),
194
192
("preferences", self.show_preferences),
195
("help", self.on_more_info_button_clicked),
193
("help", self.on_help_clicked),
198
196
# load window of activity switcher and todays view
199
197
self._gui = stuff.load_ui_file("applet.ui")
200
198
self.window = self._gui.get_object('hamster-window')
201
199
# on close don't destroy the popup, just hide it instead
202
self.window.connect("delete_event", lambda *args: self.__show_toggle(None, False))
200
self.window.connect("delete_event", lambda *args: self.__show_toggle(False))
201
self.window.connect("window-state-event", self.on_window_state_changed)
204
203
self.new_name = widgets.ActivityEntry()
205
204
self.new_name.connect("value-entered", self.on_switch_activity_clicked)
206
self.new_name.set_property("secondary-icon-name", "help")
207
self.new_name.connect("icon-press", self.on_more_info_button_clicked)
206
209
widgets.add_hint(self.new_name, _("Activity"))
207
210
self.get_widget("new_name_box").add(self.new_name)
208
211
self.new_name.connect("changed", self.on_activity_text_changed)
227
230
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
228
name = dbus.service.BusName(HAMSTER_URI, dbus.SessionBus())
229
self.dbusController = HamsterDbusController(bus_name = name)
231
231
# Set up connection to the screensaver
232
self.dbusIdleListener = idle.DbusIdleListener(runtime.dispatcher)
233
runtime.dispatcher.add_handler('active_changed', self.on_idle_changed)
232
self.dbusIdleListener = idle.DbusIdleListener()
233
self.dbusIdleListener.connect('idle-changed', self.on_idle_changed)
235
235
except dbus.DBusException, e:
236
236
logging.error("Can't init dbus: %s" % e)
241
241
self.notify_interval = conf.get("notify_interval")
242
242
self.workspace_tracking = conf.get("workspace_tracking")
244
self.hotkey = conf.get("keybinding")
247
runtime.dispatcher.add_handler('conf_changed', self.on_conf_changed)
244
conf.connect('conf-changed', self.on_conf_changed)
249
246
# Load today's data, activities and set label
250
247
self.last_activity = None
252
249
self.update_label()
254
# Hamster DBusController current fact initialising
257
252
# refresh hamster every 60 seconds to update duration
258
253
gobject.timeout_add_seconds(60, self.refresh_hamster)
260
runtime.dispatcher.add_handler('panel_visible', self.__show_toggle)
261
runtime.dispatcher.add_handler('activity_updated', self.after_activity_update)
262
runtime.dispatcher.add_handler('day_updated', self.after_fact_update)
254
runtime.storage.connect('activities-changed', self.after_activity_update)
255
runtime.storage.connect('facts-changed', self.after_fact_update)
256
runtime.storage.connect('toggle-called', self.on_toggle_called)
264
258
self.screen = None
265
259
if self.workspace_tracking:
345
330
self.notification.show()
348
def edit_cb(self, n, action):
349
dialogs.edit.show(self.applet, activity_id = self.last_activity['id'])
351
def switch_cb(self, n, action):
352
self.__show_toggle(None, not self.button.get_active())
355
333
def load_day(self):
356
334
"""sets up today's tree and fills it with records
357
335
returns information about last activity"""
359
337
facts = runtime.storage.get_todays_facts()
361
self.treeview.detach_model()
362
self.treeview.clear()
364
339
if facts and facts[-1]["end_time"] == None:
365
340
self.last_activity = facts[-1]
367
342
self.last_activity = None
370
self._gui.get_object("today_box").set_size_request(-1, 360)
371
self._gui.get_object("today_box").set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
373
self._gui.get_object("today_box").set_size_request(-1, -1)
374
self._gui.get_object("today_box").set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
378
duration = 24 * 60 * fact["delta"].days + fact["delta"].seconds / 60
379
by_category[fact['category']] = \
380
by_category.setdefault(fact['category'], 0) + duration
381
self.treeview.add_fact(fact)
383
self.treeview.attach_model()
386
self._gui.get_object("today_box").hide()
387
self._gui.get_object("fact_totals").set_text(_("No records today"))
389
self._gui.get_object("today_box").show()
392
for category in by_category:
393
# listing of today's categories and time spent in them
394
duration = "%.1f" % (by_category[category] / 60.0)
395
total_strings.append(_("%(category)s: %(duration)s") % \
396
({'category': category,
397
#duration in main drop-down per category in hours
398
'duration': _("%sh") % duration
401
total_string = ", ".join(total_strings)
402
self._gui.get_object("fact_totals").set_text(total_string)
404
self.set_last_activity()
345
if self.button.get_active():
346
self.treeview.detach_model()
350
self._gui.get_object("today_box").set_size_request(-1, 360)
351
self._gui.get_object("today_box").set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
353
self._gui.get_object("today_box").set_size_request(-1, -1)
354
self._gui.get_object("today_box").set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
358
duration = 24 * 60 * fact["delta"].days + fact["delta"].seconds / 60
359
by_category[fact['category']] = \
360
by_category.setdefault(fact['category'], 0) + duration
361
self.treeview.add_fact(fact)
363
self.treeview.attach_model()
366
self._gui.get_object("today_box").hide()
367
self._gui.get_object("fact_totals").set_text(_("No records today"))
369
self._gui.get_object("today_box").show()
372
for category in by_category:
373
# listing of today's categories and time spent in them
374
duration = locale.format("%.1f", (by_category[category] / 60.0))
375
total_strings.append(_("%(category)s: %(duration)s") % \
376
({'category': category,
377
#duration in main drop-down per category in hours
378
'duration': _("%sh") % duration
381
total_string = ", ".join(total_strings)
382
self._gui.get_object("fact_totals").set_text(total_string)
384
self.set_last_activity()
406
386
def set_last_activity(self):
407
387
activity = self.last_activity
416
396
delta = dt.datetime.now() - activity['start_time']
417
397
duration = delta.seconds / 60
419
self.get_widget("last_activity_name").set_text(activity['name'])
420
399
if activity['category'] != _("Unsorted"):
421
self.get_widget("last_activity_category") \
422
.set_text(" - %s" % activity['category'])
423
self.get_widget("last_activity_category").show()
426
self._gui.get_object("more_info_button").hide()
400
self.get_widget("last_activity_name").set_text("%s - %s" % (activity['name'], activity['category']))
402
self.get_widget("last_activity_name").set_text(activity['name'])
428
405
self.get_widget("last_activity_duration").set_text(stuff.format_duration(duration) or _("Just started"))
429
406
self.get_widget("last_activity_description").set_text(activity['description'] or "")
447
421
fact = self.treeview.get_selected_fact()
448
422
runtime.storage.remove_fact(fact["id"])
450
def __update_fact(self):
451
"""dbus controller current fact updating"""
454
if not self.last_activity:
455
self.dbusController.TrackingStopped()
457
last_activity_id = self.last_activity['id']
459
self.dbusController.FactUpdated(last_activity_id)
461
def __show_toggle(self, event, is_active):
424
def __show_toggle(self, is_active):
462
425
"""main window display and positioning"""
463
426
self.button.set_active(is_active)
428
if is_active == False:
466
429
self.window.hide()
469
self.load_day() # reload day each time before showing to avoid outdated last activity
470
self.update_label() #update also label, otherwise we can get 1 minute difference in durations (due to timers refreshed once a minute)
472
432
self.position_popup()
475
435
# doing unstick / stick here, because sometimes while switching
476
# between workplaces window still manages to dissappear
436
# between workplaces window still manages to disappear
477
437
self.window.unstick()
478
438
self.window.stick() #show on all desktops
515
475
def _delayed_display(self):
516
476
"""show window only when gtk has become idle. otherwise we get
517
477
mixed results. TODO - this looks like a hack though"""
518
479
self.window.present()
519
480
self.new_name.grab_focus()
482
self.load_day() # reload day each time before showing to avoid outdated last activity
483
self.update_label() #update also label, otherwise we can get 1 minute difference in durations (due to timers refreshed once a minute)
487
def on_window_state_changed(self, window, event):
488
"""untoggle the button when window gets minimized"""
489
if (event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED \
490
and event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED):
491
self.button.set_active(False)
523
494
def on_toggle(self, widget):
524
self.__show_toggle(None, self.button.get_active())
495
self.__show_toggle(self.button.get_active())
527
498
def on_todays_keys(self, tree, event):
552
523
and event_key.state & gtk.gdk.CONTROL_MASK)):
553
524
if self.new_name.popup.get_property("visible") == False \
554
525
and self.new_tags.popup.get_property("visible") == False:
555
runtime.dispatcher.dispatch('panel_visible', False)
526
self.__show_toggle(False)
559
530
"""button events"""
560
531
def on_overview(self, menu_item):
561
runtime.dispatcher.dispatch('panel_visible', False)
562
532
dialogs.overview.show(self.applet)
533
self.__show_toggle(False)
564
535
def show_overview(self, menu_item, verb):
565
536
return self.on_overview(menu_item)
568
539
dialogs.edit.show(self.applet)
570
541
def on_about (self, component, verb):
542
dialogs.about.show(self.window)
573
544
def show_preferences(self, menu_item, verb):
574
runtime.dispatcher.dispatch('panel_visible', False)
575
545
dialogs.prefs.show(self.applet)
546
self.__show_toggle(False)
579
def after_activity_update(self, widget, renames):
550
def after_activity_update(self, widget):
580
551
self.new_name.refresh_activities()
582
553
self.update_label()
584
def after_fact_update(self, event, date):
555
def after_fact_update(self, event):
586
557
self.update_label()
589
559
def on_idle_changed(self, event, state):
590
560
# state values: 0 = active, 1 = idle
673
642
"hamster-applet")
674
643
self.notification.show()
676
"""global shortcuts"""
677
def on_keybinding_activated(self):
678
self.__show_toggle(None, not self.button.get_active())
681
def on_conf_changed(self, event, data):
645
def on_toggle_called(self, client):
646
self.__show_toggle(not self.button.get_active())
648
def on_conf_changed(self, event, key, value):
684
649
if key == "enable_timeout":
685
650
self.timeout_enabled = value
686
651
elif key == "notify_on_idle":
699
664
self.screen.disconnect(self.screen.workspace_handler)
700
665
self.screen = None
701
elif key == "keybinding":
703
keybinder.unbind(self.hotkey)
712
667
def on_activity_text_changed(self, widget):
713
668
self.get_widget("switch_activity").set_sensitive(widget.get_text() != "")
715
670
def on_switch_activity_clicked(self, widget):
716
if not self.new_name.get_text():
671
activity_name, temporary = self.new_name.get_value()
672
if not activity_name:
719
675
runtime.storage.add_fact(self.new_name.get_text().decode("utf8", "replace"),
720
self.new_tags.get_text().decode("utf8", "replace"))
676
self.new_tags.get_text().decode("utf8", "replace"),
677
temporary = temporary)
721
678
self.new_name.set_text("")
722
679
self.new_tags.set_text("")
723
runtime.dispatcher.dispatch('panel_visible', False)
680
self.__show_toggle(False)
725
682
def on_stop_tracking_clicked(self, widget):
726
runtime.storage.touch_fact(self.last_activity)
683
runtime.storage.stop_tracking()
727
684
self.last_activity = None
728
runtime.dispatcher.dispatch('panel_visible', False)
685
self.__show_toggle(False)
730
687
def on_window_size_request(self, window, event):
731
688
box = self.window.get_allocation()