~ubuntu-branches/ubuntu/oneiric/hamster-applet/oneiric

« back to all changes in this revision

Viewing changes to src/hamster/applet.py

  • Committer: Bazaar Package Importer
  • Author(s): Krzysztof Klimonda
  • Date: 2010-09-19 08:51:59 UTC
  • mfrom: (1.5.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20100919085159-kac5xse5ccn11ysw
Tags: 2.31.92-0ubuntu1
* New upstream release. (LP: #629495) Fixes bugs:
  - Hamster is always using 1% of the CPU (LP: #620113)
* removed 01_startup-fix.patch and 02_fix_imports.patch
* debian/rules:
  - upstream has switched to waf, modified debian/rules accordingly.
* debian/control.in:
  - bump debhelper dependency to 5.0.51~ for dh_icons
  - bump Standards-Version to 3.9.1, no changes we required
  - add Build-Dependency on gnome-control-center-dev
  - add Dependency on python-wnck
  - changed Homepage
* debian/copyright:
  - Removed licenses for files that were removed from archive
  - Refreshed Copyright Holders list

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# - coding: utf-8 -
2
2
 
3
 
# Copyright (C) 2007-2009 Toms Bauģis <toms.baugis at gmail.com>
 
3
# Copyright (C) 2007-2010 Toms Bauģis <toms.baugis at gmail.com>
4
4
# Copyright (C) 2007-2009 Patryk Zawadzki <patrys at pld-linux.org>
5
5
# Copyright (C) 2008 Pēteris Caune <cuu508 at gmail.com>
6
6
 
29
29
import gnomeapplet
30
30
import gobject
31
31
import dbus, dbus.service, dbus.mainloop.glib
 
32
import locale
32
33
 
33
 
import eds
34
34
from configuration import conf, runtime, dialogs
35
35
 
36
36
import stuff
37
 
import keybinder
38
 
from hamsterdbus import HAMSTER_URI, HamsterDbusController
39
37
 
40
38
# controllers for other windows
41
39
import widgets
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),
196
194
                                          ])
197
195
 
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)
203
202
 
204
203
        self.new_name = widgets.ActivityEntry()
205
204
        self.new_name.connect("value-entered", self.on_switch_activity_clicked)
 
205
 
 
206
        self.new_name.set_property("secondary-icon-name", "help")
 
207
        self.new_name.connect("icon-press", self.on_more_info_button_clicked)
 
208
 
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)
225
228
        # DBus Setup
226
229
        try:
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)
230
 
 
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)
234
234
 
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")
243
243
 
244
 
        self.hotkey = conf.get("keybinding")
245
 
        self.bind_hotkey()
246
 
 
247
 
        runtime.dispatcher.add_handler('conf_changed', self.on_conf_changed)
 
244
        conf.connect('conf-changed', self.on_conf_changed)
248
245
 
249
246
        # Load today's data, activities and set label
250
247
        self.last_activity = None
251
248
        self.load_day()
252
249
        self.update_label()
253
250
 
254
 
        # Hamster DBusController current fact initialising
255
 
        self.__update_fact()
256
251
 
257
252
        # refresh hamster every 60 seconds to update duration
258
253
        gobject.timeout_add_seconds(60, self.refresh_hamster)
259
 
 
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)
263
257
 
264
258
        self.screen = None
265
259
        if self.workspace_tracking:
275
269
 
276
270
        self.prev_size = None
277
271
 
278
 
 
279
 
    def bind_hotkey(self):
280
 
        try:
281
 
            print 'Binding shortcut %s to popup hamster' % self.hotkey
282
 
            keybinder.bind(self.hotkey, self.on_keybinding_activated)
283
 
        except KeyError:
284
 
            pass # don't care
285
 
 
286
 
 
287
272
    def init_workspace_tracking(self):
288
273
        if not wnck: # can't track if we don't have the trackable
289
274
            return
345
330
            self.notification.show()
346
331
 
347
332
 
348
 
    def edit_cb(self, n, action):
349
 
        dialogs.edit.show(self.applet, activity_id = self.last_activity['id'])
350
 
 
351
 
    def switch_cb(self, n, action):
352
 
        self.__show_toggle(None, not self.button.get_active())
353
 
 
354
 
 
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"""
358
336
 
359
337
        facts = runtime.storage.get_todays_facts()
360
338
 
361
 
        self.treeview.detach_model()
362
 
        self.treeview.clear()
363
 
 
364
339
        if facts and facts[-1]["end_time"] == None:
365
340
            self.last_activity = facts[-1]
366
341
        else:
367
342
            self.last_activity = None
368
343
 
369
 
        if len(facts) > 15:
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)
372
 
        else:
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)
375
 
 
376
 
        by_category = {}
377
 
        for fact in facts:
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)
382
 
 
383
 
        self.treeview.attach_model()
384
 
 
385
 
        if not facts:
386
 
            self._gui.get_object("today_box").hide()
387
 
            self._gui.get_object("fact_totals").set_text(_("No records today"))
388
 
        else:
389
 
            self._gui.get_object("today_box").show()
390
 
 
391
 
            total_strings = []
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
399
 
                          }))
400
 
 
401
 
            total_string = ", ".join(total_strings)
402
 
            self._gui.get_object("fact_totals").set_text(total_string)
403
 
 
404
 
        self.set_last_activity()
 
344
 
 
345
        if self.button.get_active():
 
346
            self.treeview.detach_model()
 
347
 
 
348
 
 
349
            if len(facts) > 15:
 
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)
 
352
            else:
 
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)
 
355
 
 
356
            by_category = {}
 
357
            for fact in facts:
 
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)
 
362
 
 
363
            self.treeview.attach_model()
 
364
 
 
365
            if not facts:
 
366
                self._gui.get_object("today_box").hide()
 
367
                self._gui.get_object("fact_totals").set_text(_("No records today"))
 
368
            else:
 
369
                self._gui.get_object("today_box").show()
 
370
 
 
371
                total_strings = []
 
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
 
379
                              }))
 
380
 
 
381
                total_string = ", ".join(total_strings)
 
382
                self._gui.get_object("fact_totals").set_text(total_string)
 
383
 
 
384
            self.set_last_activity()
405
385
 
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
418
398
 
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()
424
 
 
425
 
 
426
 
            self._gui.get_object("more_info_button").hide()
 
400
                self.get_widget("last_activity_name").set_text("%s - %s" % (activity['name'], activity['category']))
 
401
            else:
 
402
                self.get_widget("last_activity_name").set_text(activity['name'])
 
403
 
427
404
 
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 "")
435
412
            self.get_widget("start_tracking").show()
436
413
 
437
414
            self.get_widget("last_activity_name").set_text(_("No activity"))
438
 
            self.get_widget("last_activity_category").hide()
439
415
 
440
416
            self.get_widget("activity_info_box").hide()
441
 
            self._gui.get_object("more_info_button").show()
442
 
 
443
417
            self.tag_box.draw([])
444
418
 
445
419
 
447
421
        fact = self.treeview.get_selected_fact()
448
422
        runtime.storage.remove_fact(fact["id"])
449
423
 
450
 
    def __update_fact(self):
451
 
        """dbus controller current fact updating"""
452
 
        last_activity_id = 0
453
 
 
454
 
        if not self.last_activity:
455
 
            self.dbusController.TrackingStopped()
456
 
        else:
457
 
            last_activity_id = self.last_activity['id']
458
 
 
459
 
        self.dbusController.FactUpdated(last_activity_id)
460
 
 
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)
464
427
 
465
 
        if not is_active:
 
428
        if is_active == False:
466
429
            self.window.hide()
467
430
            return True
468
431
 
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)
471
 
 
472
432
        self.position_popup()
473
433
 
474
434
 
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
479
439
 
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"""
 
478
        self.window.show()
518
479
        self.window.present()
519
480
        self.new_name.grab_focus()
520
481
 
 
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)
 
484
 
521
485
 
522
486
    """events"""
 
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)
 
492
 
 
493
 
523
494
    def on_toggle(self, widget):
524
 
        self.__show_toggle(None, self.button.get_active())
 
495
        self.__show_toggle(self.button.get_active())
525
496
 
526
497
 
527
498
    def on_todays_keys(self, tree, event):
543
514
                                     ", ".join(fact["tags"]),
544
515
                                     category_name = fact["category"],
545
516
                                     description = fact["description"])
546
 
            runtime.dispatcher.dispatch('panel_visible', False)
 
517
            self.__show_toggle(False)
547
518
 
548
519
 
549
520
    def on_windows_keys(self, tree, event_key):
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)
556
527
                return True
557
528
        return False
558
529
 
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)
563
534
 
564
535
    def show_overview(self, menu_item, verb):
565
536
        return self.on_overview(menu_item)
568
539
        dialogs.edit.show(self.applet)
569
540
 
570
541
    def on_about (self, component, verb):
571
 
        dialogs.about.show()
 
542
        dialogs.about.show(self.window)
572
543
 
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)
576
547
 
577
548
 
578
549
    """signals"""
579
 
    def after_activity_update(self, widget, renames):
 
550
    def after_activity_update(self, widget):
580
551
        self.new_name.refresh_activities()
581
552
        self.load_day()
582
553
        self.update_label()
583
554
 
584
 
    def after_fact_update(self, event, date):
 
555
    def after_fact_update(self, event):
585
556
        self.load_day()
586
557
        self.update_label()
587
 
        self.__update_fact()
588
558
 
589
559
    def on_idle_changed(self, event, state):
590
560
        # state values: 0 = active, 1 = idle
596
566
        elif self.timeout_enabled and self.last_activity and \
597
567
             self.last_activity['end_time'] is None:
598
568
 
599
 
            runtime.storage.touch_fact(self.last_activity,
600
 
                                       end_time = self.dbusIdleListener.getIdleFrom())
 
569
            runtime.storage.stop_tracking(self.dbusIdleListener.getIdleFrom())
601
570
 
602
571
    def on_workspace_changed(self, screen, previous_workspace):
603
572
        if not previous_workspace:
631
600
            if parsed_activity:
632
601
                category_id = None
633
602
                if parsed_activity.category_name:
634
 
                    category_id = runtime.storage.get_category_by_name(parsed_activity.category_name)
 
603
                    category_id = runtime.storage.get_category_id(parsed_activity.category_name)
635
604
 
636
605
                activity = runtime.storage.get_activity_by_name(parsed_activity.activity_name,
637
606
                                                                category_id,
638
 
                                                                ressurect = False)
 
607
                                                                resurrect = False)
639
608
                if activity:
640
609
                    # we need dict below
641
610
                    activity = dict(name = activity['name'],
673
642
                                     "hamster-applet")
674
643
            self.notification.show()
675
644
 
676
 
    """global shortcuts"""
677
 
    def on_keybinding_activated(self):
678
 
        self.__show_toggle(None, not self.button.get_active())
679
 
 
680
 
 
681
 
    def on_conf_changed(self, event, data):
682
 
        key, value = data
683
 
 
 
645
    def on_toggle_called(self, client):
 
646
        self.__show_toggle(not self.button.get_active())
 
647
 
 
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":
698
663
                if self.screen:
699
664
                    self.screen.disconnect(self.screen.workspace_handler)
700
665
                    self.screen = None
701
 
        elif key == "keybinding":
702
 
            try:
703
 
                keybinder.unbind(self.hotkey)
704
 
            except KeyError:
705
 
                pass # don't care
706
 
 
707
 
            self.hotkey = value
708
 
            self.bind_hotkey()
709
 
 
710
 
 
711
666
 
712
667
    def on_activity_text_changed(self, widget):
713
668
        self.get_widget("switch_activity").set_sensitive(widget.get_text() != "")
714
669
 
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:
717
673
            return False
718
674
 
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)
724
681
 
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)
729
686
 
730
687
    def on_window_size_request(self, window, event):
731
688
        box = self.window.get_allocation()
741
698
        return self._gui.get_object(name)
742
699
 
743
700
    def on_more_info_button_clicked(self, *args):
 
701
        gtk.show_uri(gtk.gdk.Screen(), "ghelp:hamster-applet#input", 0L)
 
702
        return False
 
703
 
 
704
    def on_help_clicked(self, *args):
744
705
        gtk.show_uri(gtk.gdk.Screen(), "ghelp:hamster-applet", 0L)
745
706
        return False