Example #1
0
class Overview(Controller):
    def __init__(self, parent = None):
        Controller.__init__(self, parent)

        self.window.set_position(gtk.WindowPosition.CENTER)
        self.window.set_default_icon_name("hamster-time-tracker")
        self.window.set_default_size(700, 500)

        self.storage = hamster.client.Storage()
        self.storage.connect("facts-changed", self.on_facts_changed)
        self.storage.connect("activities-changed", self.on_facts_changed)

        self.header_bar = HeaderBar()
        self.window.set_titlebar(self.header_bar)

        main = gtk.Box(orientation=1)
        self.window.add(main)

        self.report_chooser = None


        self.search_box = gtk.Revealer()

        space = gtk.Box(border_width=5)
        self.search_box.add(space)
        self.filter_entry = gtk.Entry()
        self.filter_entry.set_icon_from_icon_name(gtk.EntryIconPosition.PRIMARY,
                                                  "edit-find-symbolic")
        self.filter_entry.connect("changed", self.on_search_changed)
        self.filter_entry.connect("icon-press", self.on_search_icon_press)

        space.pack_start(self.filter_entry, True, True, 0)
        main.pack_start(self.search_box, False, True, 0)


        window = gtk.ScrolledWindow()
        window.set_policy(gtk.PolicyType.NEVER, gtk.PolicyType.AUTOMATIC)
        self.fact_tree = FactTree()
        self.fact_tree.connect("on-activate-row", self.on_row_activated)
        self.fact_tree.connect("on-delete-called", self.on_row_delete_called)

        window.add(self.fact_tree)
        main.pack_start(window, True, True, 1)

        self.totals = Totals()
        main.pack_start(self.totals, False, True, 1)

        # FIXME: should store and recall date_range from hamster.lib.configuration.conf
        hamster_day = stuff.datetime_to_hamsterday(dt.datetime.today())
        self.header_bar.range_pick.set_range(hamster_day)
        self.header_bar.range_pick.connect("range-selected", self.on_range_selected)
        self.header_bar.add_activity_button.connect("clicked", self.on_add_activity_clicked)
        self.header_bar.search_button.connect("toggled", self.on_search_toggled)

        self.header_bar.menu_prefs.connect("activate", self.on_prefs_clicked)
        self.header_bar.menu_export.connect("activate", self.on_export_clicked)


        self.window.connect("key-press-event", self.on_key_press)

        self.facts = []
        self.find_facts()

        # update every minute (necessary if an activity is running)
        gobject.timeout_add_seconds(60, self.on_timeout)
        self.window.show_all()


    def on_key_press(self, window, event):
        if self.filter_entry.has_focus():
            if event.keyval == gdk.KEY_Escape:
                self.filter_entry.set_text("")
                self.header_bar.search_button.set_active(False)
                return True
            elif event.keyval in (gdk.KEY_Up, gdk.KEY_Down,
                                  gdk.KEY_Page_Up, gdk.KEY_Page_Down,
                                  gdk.KEY_Return):
                self.fact_tree.on_key_press(self, event)
                return True

        if self.fact_tree.has_focus() or self.totals.has_focus():
            if event.keyval == gdk.KEY_Tab:
                pass # TODO - deal with tab as our scenes eat up navigation

        if event.state & gdk.ModifierType.CONTROL_MASK:
            # the ctrl+things
            if event.keyval == gdk.KEY_f:
                self.header_bar.search_button.set_active(True)
            elif event.keyval == gdk.KEY_n:
                dialogs.edit.show(self)

        if event.keyval == gdk.KEY_Escape:
            self.close_window()

    def find_facts(self):
        start, end = self.header_bar.range_pick.get_range()
        search_active = self.header_bar.search_button.get_active()
        search = "" if not search_active else self.filter_entry.get_text()
        search = "%s*" % search if search else "" # search anywhere
        self.facts = self.storage.get_facts(start, end, search_terms=search, ongoing_days=31)
        self.fact_tree.set_facts(self.facts)
        self.totals.set_facts(self.facts)

    def on_range_selected(self, button, range_type, start, end):
        self.find_facts()

    def on_search_changed(self, entry):
        if entry.get_text():
            self.filter_entry.set_icon_from_icon_name(gtk.EntryIconPosition.SECONDARY,
                                                      "edit-clear-symbolic")
        else:
            self.filter_entry.set_icon_from_icon_name(gtk.EntryIconPosition.SECONDARY,
                                                      None)
        self.find_facts()

    def on_search_icon_press(self, entry, position, event):
        if position == gtk.EntryIconPosition.SECONDARY:
            self.filter_entry.set_text("")

    def on_facts_changed(self, event):
        self.find_facts()

    def on_add_activity_clicked(self, button):
        dialogs.edit.show(self, base_fact=self.fact_tree.current_fact)

    def on_row_activated(self, tree, day, fact):
        dialogs.edit.show(self, fact_id=fact.id)

    def on_row_delete_called(self, tree, fact):
        self.storage.remove_fact(fact.id)
        self.find_facts()

    def on_search_toggled(self, button):
        active = button.get_active()
        self.search_box.set_reveal_child(active)
        if active:
            self.filter_entry.grab_focus()

    def on_timeout(self):
        # TODO: should update only the running FactTree row (if any), and totals
        self.find_facts()
        # The timeout will stop if returning False
        return True

    def on_prefs_clicked(self, menu):
        dialogs.prefs.show(self)


    def on_export_clicked(self, menu):
        if self.report_chooser:
            self.report_chooser.present()
            return

        start, end = self.header_bar.range_pick.get_range()

        def on_report_chosen(widget, format, path):
            self.report_chooser = None
            reports.simple(self.facts, start, end, format, path)

            if format == ("html"):
                webbrowser.open_new("file://%s" % path)
            else:
                try:
                    gtk.show_uri(gdk.Screen(), "file://%s" % os.path.split(path)[0], 0)
                except:
                    pass # bug 626656 - no use in capturing this one i think

        def on_report_chooser_closed(widget):
            self.report_chooser = None


        self.report_chooser = widgets.ReportChooserDialog()
        self.report_chooser.connect("report-chosen", on_report_chosen)
        self.report_chooser.connect("report-chooser-closed", on_report_chooser_closed)
        self.report_chooser.show(start, end)

    def close_window(self):
        self.window.destroy()
        self.window = None
        self._gui = None
        self.emit("on-close")
Example #2
0
    def __init__(self, parent = None):
        Controller.__init__(self, parent)

        self.window.set_position(gtk.WindowPosition.CENTER)
        self.window.set_default_icon_name("hamster-time-tracker")
        self.window.set_default_size(700, 500)

        self.storage = hamster.client.Storage()
        self.storage.connect("facts-changed", self.on_facts_changed)
        self.storage.connect("activities-changed", self.on_facts_changed)

        self.header_bar = HeaderBar()
        self.window.set_titlebar(self.header_bar)

        main = gtk.Box(orientation=1)
        self.window.add(main)

        self.report_chooser = None


        self.search_box = gtk.Revealer()

        space = gtk.Box(border_width=5)
        self.search_box.add(space)
        self.filter_entry = gtk.Entry()
        self.filter_entry.set_icon_from_icon_name(gtk.EntryIconPosition.PRIMARY,
                                                  "edit-find-symbolic")
        self.filter_entry.connect("changed", self.on_search_changed)
        self.filter_entry.connect("icon-press", self.on_search_icon_press)

        space.pack_start(self.filter_entry, True, True, 0)
        main.pack_start(self.search_box, False, True, 0)


        window = gtk.ScrolledWindow()
        window.set_policy(gtk.PolicyType.NEVER, gtk.PolicyType.AUTOMATIC)
        self.fact_tree = FactTree()
        self.fact_tree.connect("on-activate-row", self.on_row_activated)
        self.fact_tree.connect("on-delete-called", self.on_row_delete_called)

        window.add(self.fact_tree)
        main.pack_start(window, True, True, 1)

        self.totals = Totals()
        main.pack_start(self.totals, False, True, 1)

        # FIXME: should store and recall date_range from hamster.lib.configuration.conf
        hamster_day = stuff.datetime_to_hamsterday(dt.datetime.today())
        self.header_bar.range_pick.set_range(hamster_day)
        self.header_bar.range_pick.connect("range-selected", self.on_range_selected)
        self.header_bar.add_activity_button.connect("clicked", self.on_add_activity_clicked)
        self.header_bar.search_button.connect("toggled", self.on_search_toggled)

        self.header_bar.menu_prefs.connect("activate", self.on_prefs_clicked)
        self.header_bar.menu_export.connect("activate", self.on_export_clicked)


        self.window.connect("key-press-event", self.on_key_press)

        self.facts = []
        self.find_facts()

        # update every minute (necessary if an activity is running)
        gobject.timeout_add_seconds(60, self.on_timeout)
        self.window.show_all()
Example #3
0
class Overview(Controller):
    def __init__(self, parent = None):
        Controller.__init__(self, parent)

        self.prefs_dialog = None  # preferences dialog controller

        self.window.set_position(gtk.WindowPosition.CENTER)
        self.window.set_default_icon_name("org.gnome.Hamster.GUI")
        self.window.set_default_size(700, 500)

        self.storage = hamster.client.Storage()
        self.storage.connect("facts-changed", self.on_facts_changed)
        self.storage.connect("activities-changed", self.on_facts_changed)

        self.header_bar = HeaderBar()
        self.window.set_titlebar(self.header_bar)

        main = gtk.Box(orientation=1)
        self.window.add(main)

        self.report_chooser = None


        self.search_box = gtk.Revealer()

        space = gtk.Box(border_width=5)
        self.search_box.add(space)
        self.filter_entry = gtk.Entry()
        self.filter_entry.set_icon_from_icon_name(gtk.EntryIconPosition.PRIMARY,
                                                  "edit-find-symbolic")
        self.filter_entry.connect("changed", self.on_search_changed)
        self.filter_entry.connect("icon-press", self.on_search_icon_press)

        space.pack_start(self.filter_entry, True, True, 0)
        main.pack_start(self.search_box, False, True, 0)


        window = gtk.ScrolledWindow()
        window.set_policy(gtk.PolicyType.NEVER, gtk.PolicyType.AUTOMATIC)
        self.fact_tree = FactTree()
        self.fact_tree.connect("on-activate-row", self.on_row_activated)
        self.fact_tree.connect("on-delete-called", self.on_row_delete_called)

        window.add(self.fact_tree)
        main.pack_start(window, True, True, 1)

        self.totals = Totals()
        main.pack_start(self.totals, False, True, 1)

        # FIXME: should store and recall date_range from hamster.lib.configuration.conf
        hamster_day = dt.hday.today()
        self.header_bar.range_pick.set_range(hamster_day)
        self.header_bar.range_pick.connect("range-selected", self.on_range_selected)
        self.header_bar.add_activity_button.connect("clicked", self.on_add_activity_clicked)
        self.header_bar.stop_button.connect("clicked", self.on_stop_clicked)
        self.header_bar.search_button.connect("toggled", self.on_search_toggled)

        self.header_bar.menu_prefs.connect("activate", self.on_prefs_clicked)
        self.header_bar.menu_export.connect("activate", self.on_export_clicked)
        self.header_bar.menu_help.connect("activate", self.on_help_clicked)


        self.window.connect("key-press-event", self.on_key_press)

        self.facts = []
        self.find_facts()

        # update every minute (necessary if an activity is running)
        gobject.timeout_add_seconds(60, self.on_timeout)
        self.window.show_all()


    def on_key_press(self, window, event):
        if self.filter_entry.has_focus():
            if event.keyval == gdk.KEY_Escape:
                self.filter_entry.set_text("")
                self.header_bar.search_button.set_active(False)
                return True
        elif event.keyval in (gdk.KEY_Up, gdk.KEY_Down,
                              gdk.KEY_Home, gdk.KEY_End,
                              gdk.KEY_Page_Up, gdk.KEY_Page_Down,
                              gdk.KEY_Return, gdk.KEY_Delete):
            # These keys should work even when fact_tree does not have focus
            self.fact_tree.on_key_press(self, event)
            return True  # stop event propagation
        elif event.keyval == gdk.KEY_Left:
            self.header_bar.time_back.emit("clicked")
            return True
        elif event.keyval == gdk.KEY_Right:
            self.header_bar.time_forth.emit("clicked")
            return True

        if self.fact_tree.has_focus() or self.totals.has_focus():
            if event.keyval == gdk.KEY_Tab:
                pass # TODO - deal with tab as our scenes eat up navigation

        if event.state & gdk.ModifierType.CONTROL_MASK:
            # the ctrl+things
            if event.keyval == gdk.KEY_f:
                self.header_bar.search_button.set_active(True)
            elif event.keyval == gdk.KEY_n:
                self.start_new_fact(clone_selected=False)
            elif event.keyval == gdk.KEY_r:
                # Resume/run; clear separation between Ctrl-R and Ctrl-N
                self.start_new_fact(clone_selected=True, fallback=False)
            elif event.keyval == gdk.KEY_space:
                self.storage.stop_tracking()
            elif event.keyval in (gdk.KEY_KP_Add, gdk.KEY_plus):
                # same as pressing the + icon
                self.start_new_fact(clone_selected=True, fallback=True)

        if event.keyval == gdk.KEY_Escape:
            self.close_window()

    def find_facts(self):
        start, end = self.header_bar.range_pick.get_range()
        search_active = self.header_bar.search_button.get_active()
        search = "" if not search_active else self.filter_entry.get_text()
        search = "%s*" % search if search else "" # search anywhere
        self.facts = self.storage.get_facts(start, end, search_terms=search)
        self.fact_tree.set_facts(self.facts)
        self.totals.set_facts(self.facts)
        self.header_bar.stop_button.set_sensitive(
            self.facts and not self.facts[-1].end_time)

    def on_range_selected(self, button, range_type, start, end):
        self.find_facts()

    def on_search_changed(self, entry):
        if entry.get_text():
            self.filter_entry.set_icon_from_icon_name(gtk.EntryIconPosition.SECONDARY,
                                                      "edit-clear-symbolic")
        else:
            self.filter_entry.set_icon_from_icon_name(gtk.EntryIconPosition.SECONDARY,
                                                      None)
        self.find_facts()

    def on_search_icon_press(self, entry, position, event):
        if position == gtk.EntryIconPosition.SECONDARY:
            self.filter_entry.set_text("")

    def on_facts_changed(self, event):
        self.find_facts()

    def on_add_activity_clicked(self, button):
        self.start_new_fact(clone_selected=True, fallback=True)

    def on_stop_clicked(self, button):
        self.storage.stop_tracking()

    def on_row_activated(self, tree, day, fact):
        dialogs.edit.show(self, fact_id=fact.id)

    def on_row_delete_called(self, tree, fact):
        self.storage.remove_fact(fact.id)
        self.find_facts()

    def on_search_toggled(self, button):
        active = button.get_active()
        self.search_box.set_reveal_child(active)
        if active:
            self.filter_entry.grab_focus()

    def on_timeout(self):
        # TODO: should update only the running FactTree row (if any), and totals
        self.find_facts()
        # The timeout will stop if returning False
        return True

    def on_help_clicked(self, menu):
        uri = "help:hamster"
        try:
            gtk.show_uri(None, uri, gdk.CURRENT_TIME)
        except glib.Error:
            msg = sys.exc_info()[1].args[0]
            dialog = gtk.MessageDialog(self.window, 0, gtk.MessageType.ERROR,
                                       gtk.ButtonsType.CLOSE,
                                       _("Failed to open {}").format(uri))
            fmt = _('Error: "{}" - is a help browser installed on this computer?')
            dialog.format_secondary_text(fmt.format(msg))
            dialog.run()
            dialog.destroy()

    def on_prefs_clicked(self, menu):
        if self.prefs_dialog:
            self.prefs_dialog.present()
        else:
            self.prefs_dialog = PreferencesEditor(parent=self.window)

    def on_export_clicked(self, menu):
        if self.report_chooser:
            self.report_chooser.present()
            return

        start, end = self.header_bar.range_pick.get_range()

        def on_report_chosen(widget, format, path):
            self.report_chooser = None
            reports.simple(self.facts, start, end, format, path)

            if format == ("html"):
                webbrowser.open_new("file://%s" % path)
            else:
                try:
                    gtk.show_uri(None, "file://%s" % path, gdk.CURRENT_TIME)
                except:
                    pass # bug 626656 - no use in capturing this one i think

        def on_report_chooser_closed(widget):
            self.report_chooser = None


        self.report_chooser = widgets.ReportChooserDialog()
        self.report_chooser.connect("report-chosen", on_report_chosen)
        self.report_chooser.connect("report-chooser-closed", on_report_chooser_closed)
        self.report_chooser.show(start, end)

    def start_new_fact(self, clone_selected=True, fallback=True):
        """Start now a new fact.
        clone_selected (bool): whether to start a clone of currently
            selected fact or to create a new fact from scratch.
        fallback (bool): if True, fall back to creating from scratch
                         in case of no selected fact.
        """
        if not clone_selected:
            dialogs.edit.show(self, base_fact=None)
        elif self.fact_tree.current_fact or fallback:
            dialogs.edit.show(self, base_fact=self.fact_tree.current_fact)

    def close_window(self):
        self.window.destroy()
        self.window = None
        self._gui = None
        self.emit("on-close")