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")
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()
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")