--- src/hamster/about.py 2010-05-14 16:29:15 +0000 +++ src/hamster/about.py 2010-09-03 15:50:08 +0000 @@ -39,7 +39,7 @@ "program-name" : _("Time Tracker"), "name" : _("Time Tracker"), #this should be deprecated in gtk 2.10 "version" : VERSION, - "comments" : _("Project Hamster — track your time"), + "comments" : _(u"Project Hamster — track your time"), "copyright" : _(u"Copyright © 2007–2009 Toms Bauģis and others"), "website" : "http://projecthamster.wordpress.com/", "website-label" : _("Project Hamster Website"), --- src/hamster/applet.py 2010-05-14 16:29:15 +0000 +++ src/hamster/applet.py 2010-09-03 15:50:08 +0000 @@ -200,6 +200,7 @@ self.window = self._gui.get_object('hamster-window') # on close don't destroy the popup, just hide it instead self.window.connect("delete_event", lambda *args: self.__show_toggle(None, False)) + self.window.connect("window-state-event", self.on_window_state_changed) self.new_name = widgets.ActivityEntry() self.new_name.connect("value-entered", self.on_switch_activity_clicked) @@ -345,13 +346,6 @@ self.notification.show() - def edit_cb(self, n, action): - dialogs.edit.show(self.applet, activity_id = self.last_activity['id']) - - def switch_cb(self, n, action): - self.__show_toggle(None, not self.button.get_active()) - - def load_day(self): """sets up today's tree and fills it with records returns information about last activity""" @@ -473,7 +467,7 @@ # doing unstick / stick here, because sometimes while switching - # between workplaces window still manages to dissappear + # between workplaces window still manages to disappear self.window.unstick() self.window.stick() #show on all desktops @@ -515,11 +509,19 @@ def _delayed_display(self): """show window only when gtk has become idle. otherwise we get mixed results. TODO - this looks like a hack though""" + self.window.show() self.window.present() self.new_name.grab_focus() """events""" + def on_window_state_changed(self, window, event): + """untoggle the button when window gets minimized""" + if (event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED \ + and event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED): + self.button.set_active(False) + + def on_toggle(self, widget): self.__show_toggle(None, self.button.get_active()) --- src/hamster/defs.py 2010-05-14 16:29:15 +0000 +++ src/hamster/defs.py 2010-09-03 15:50:08 +0000 @@ -1,5 +1,5 @@ -DATA_DIR = "/usr/share" -LIB_DIR = "/usr/lib" -VERSION = "2.30.1" +DATA_DIR = "/usr/local/share" +LIB_DIR = "/usr/local/lib" +VERSION = "2.30.2" PACKAGE = "hamster-applet" -PYTHONDIR = "/usr/lib/python2.6/dist-packages" +PYTHONDIR = "/usr/local/lib/python2.6/dist-packages" --- src/hamster/graphics.py 2010-05-14 16:29:15 +0000 +++ src/hamster/graphics.py 2010-09-03 15:50:08 +0000 @@ -119,10 +119,6 @@ """ animation bits """ def __interpolate(self): - if not self.window: #will wait until window comes - return True - - time_since_last_frame = (dt.datetime.now() - self.last_frame_time).microseconds / 1000000.0 self.tweener.update(time_since_last_frame) self.__drawing_queued = self.tweener.hasTweens() --- src/hamster/overview.py 2010-05-14 16:29:15 +0000 +++ src/hamster/overview.py 2010-09-03 15:50:08 +0000 @@ -258,12 +258,12 @@ def on_start_date_entered(self, input): - self.start_date = input.get_date().date() + self.start_date = input.get_date() self.view_date = self.start_date self.search() def on_end_date_entered(self, input): - self.end_date = input.get_date().date() + self.end_date = input.get_date() self.search() def _chosen_range(self): @@ -319,6 +319,7 @@ elif pagenum == 1: self.get_widget('remove').set_sensitive(False) self.get_widget('edit').set_sensitive(False) + self.reports.do_charts() def on_add_clicked(self, button): --- src/hamster/overview_totals.py 2010-05-14 16:29:15 +0000 +++ src/hamster/overview_totals.py 2010-09-03 15:50:08 +0000 @@ -33,6 +33,7 @@ from configuration import runtime, dialogs from hamster.i18n import C_ +import locale from collections import defaultdict @@ -111,9 +112,6 @@ self.do_charts() - def on_reports_box_expose_event(self, box, someth): - self.do_charts() - def search(self, start_date, end_date, facts): self.facts = facts self.category_sums, self.activity_sums, self.tag_sums = [], [], [] @@ -141,18 +139,17 @@ def do_charts(self): if not self.facts: return - - import copy - facts = copy.deepcopy(self.facts) - - total_hours = sum([stuff.duration_minutes(fact["delta"]) for fact in facts]) - total_label = _("%s hours tracked total") % ("%.1f" % (total_hours / 60.0)) - self.get_widget("total_hours").set_text(total_label) + facts = self.facts + + total_hours = dt.timedelta() + category_sums, activity_sums, tag_sums = defaultdict(dt.timedelta), defaultdict(dt.timedelta), defaultdict(dt.timedelta), for fact in facts: + total_hours += fact["delta"] + if self.selected_categories and fact["category"] not in self.selected_categories: continue if self.selected_activities and fact["name"] not in self.selected_activities: @@ -165,6 +162,9 @@ for tag in fact["tags"]: tag_sums[tag] += fact["delta"] + total_label = _("%s hours tracked total") % locale.format("%.1f", stuff.duration_minutes(total_hours) / 60.0) + self.get_widget("total_hours").set_text(total_label) + for key in category_sums: category_sums[key] = stuff.duration_minutes(category_sums[key]) / 60.0 --- src/hamster/preferences.py 2010-05-14 16:29:15 +0000 +++ src/hamster/preferences.py 2010-09-03 15:50:08 +0000 @@ -269,7 +269,10 @@ self.get_widget("idle_track").set_active(conf.get("enable_timeout")) self.get_widget("notify_interval").set_value(conf.get("notify_interval")) self.get_widget("keybinding").set_text(conf.get("keybinding")) + self.get_widget("notify_on_idle").set_active(conf.get("notify_on_idle")) + self.get_widget("notify_on_idle").set_sensitive(conf.get("notify_interval") <=120) + self.get_widget("workspace_tracking_name").set_active("name" in conf.get("workspace_tracking")) self.get_widget("workspace_tracking_memory").set_active("memory" in conf.get("workspace_tracking")) @@ -448,8 +451,8 @@ #look for dupes activities = runtime.storage.get_activities(category_id) for activity in activities: - if activity['name'].lower() == new_text.lower(): - if id == -1: # that was a new category + if id != activity['id'] and activity['name'].lower() == new_text.lower(): + if id == -1: # that was a new activity self.activity_store.remove(model.get_iter(path)) self.select_activity(activity['id']) return False @@ -771,7 +774,7 @@ def on_day_start_changed(self, widget): day_start = self.day_start.get_time() - if not day_start: + if day_start is None: return day_start = day_start.hour * 60 + day_start.minute --- src/hamster/stats.py 2010-05-14 16:29:15 +0000 +++ src/hamster/stats.py 2010-09-03 15:50:08 +0000 @@ -176,7 +176,7 @@ label.set_text(_("""There is no data to generate statistics yet. A week of usage would be nice!""")) else: - label.set_text(_("Collecting data — check back after a week has passed!")) + label.set_text(_(u"Collecting data — check back after a week has passed!")) label.show() return --- src/hamster/widgets/activityentry.py 2010-05-14 16:29:15 +0000 +++ src/hamster/widgets/activityentry.py 2010-09-03 15:50:08 +0000 @@ -92,6 +92,7 @@ self.connect("focus-out-event", self._on_focus_out_event) self.connect("changed", self._on_text_changed) self.connect("parent-set", self._on_parent_set) + self._parent_click_watcher = None # bit lame but works runtime.dispatcher.add_handler('activity_updated', self.after_activity_update) @@ -99,6 +100,9 @@ self.populate_suggestions() def hide_popup(self): + if self._parent_click_watcher and self.get_toplevel().handler_is_connected(self._parent_click_watcher): + self.get_toplevel().disconnect(self._parent_click_watcher) + self._parent_click_watcher = None self.popup.hide() def show_popup(self): @@ -107,6 +111,9 @@ self.hide_popup() return + if not self._parent_click_watcher: + self._parent_click_watcher = self.get_toplevel().connect("button-press-event", self._on_focus_out_event) + activity = stuff.parse_activity_input(self.filter) time = '' if activity.start_time: --- src/hamster/widgets/dateinput.py 2010-05-14 16:29:15 +0000 +++ src/hamster/widgets/dateinput.py 2010-09-03 15:50:08 +0000 @@ -34,7 +34,7 @@ def __init__(self, date = None): gtk.Entry.__init__(self) - self.set_width_chars(12) #12 is enough for 12-oct-2009, which is verbose + self.set_width_chars(len(dt.datetime.now().strftime("%x"))) # size to default format length self.date = date if date: self.set_date(date) @@ -58,6 +58,8 @@ self.connect("key-press-event", self._on_key_press_event) self.connect("focus-in-event", self._on_focus_in_event) self.connect("focus-out-event", self._on_focus_out_event) + self._parent_click_watcher = None # bit lame but works + self.connect("changed", self._on_text_changed) self.show() @@ -74,7 +76,7 @@ def _figure_date(self, date_str): try: - return dt.datetime.strptime(date_str, "%x") + return dt.datetime.strptime(date_str, "%x").date() except: return self.date @@ -106,15 +108,23 @@ self.date = dt.date(cal_date[0], cal_date[1] + 1, cal_date[2]) self.set_text(self._format_date(self.date)) - self.popup.hide() + self.hide_popup() if self.news: self.emit("date-entered") self.news = False + def hide_popup(self): + self.popup.hide() + if self._parent_click_watcher and self.get_toplevel().handler_is_connected(self._parent_click_watcher): + self.get_toplevel().disconnect(self._parent_click_watcher) + self._parent_click_watcher = None def show_popup(self): + if not self._parent_click_watcher: + self._parent_click_watcher = self.get_toplevel().connect("button-press-event", self._on_focus_out_event) + window = self.get_parent_window() - x, y= window.get_origin() + x, y = window.get_origin() alloc = self.get_allocation() @@ -135,7 +145,7 @@ def _on_focus_out_event(self, event, something): - self.popup.hide() + self.hide_popup() if self.news: self.emit("date-entered") self.news = False @@ -159,11 +169,11 @@ event.keyval == gtk.keysyms.KP_Enter): enter_pressed = True elif (event.keyval == gtk.keysyms.Escape): - self.popup.hide() + self.hide_popup() elif event.keyval in (gtk.keysyms.Left, gtk.keysyms.Right): return False #keep calendar open and allow user to walk in text else: - self.popup.hide() + self.hide_popup() return False if enter_pressed: --- src/hamster/widgets/reportchooserdialog.py 2010-05-14 16:29:15 +0000 +++ src/hamster/widgets/reportchooserdialog.py 2010-09-03 15:50:08 +0000 @@ -35,7 +35,7 @@ gtk.Dialog.__init__(self) - self.dialog = gtk.FileChooserDialog(title = _("Save report - Time Tracker"), + self.dialog = gtk.FileChooserDialog(title = _(u"Save report - Time Tracker"), parent = self, action = gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL, --- src/hamster/widgets/tags.py 2010-05-14 16:29:15 +0000 +++ src/hamster/widgets/tags.py 2010-09-03 15:50:08 +0000 @@ -58,6 +58,7 @@ self.connect("key-release-event", self._on_key_release_event) self.connect("focus-out-event", self._on_focus_out_event) self.connect("parent-set", self._on_parent_set) + self._parent_click_watcher = None # bit lame but works runtime.dispatcher.add_handler('new_tags_added', self.refresh_tags) self.show() @@ -71,14 +72,23 @@ return [tag.strip() for tag in self.get_text().decode('utf8', 'replace').split(",") if tag.strip()] def on_tag_selected(self, tag_box, tag): - tags = self.get_tags() - tags.append(tag) + cursor_tag = self.get_cursor_tag() + if cursor_tag and tag.startswith(cursor_tag): + self.replace_tag(cursor_tag, tag) + tags = self.get_tags() + else: + tags = self.get_tags() + tags.append(tag) self.tag_box.selected_tags = tags - self.set_text(", ".join(tags)) + + self.set_text("%s, " % ", ".join(tags)) self.set_position(len(self.get_text())) + self.populate_suggestions() + self.show_popup() + def on_tag_unselected(self, tag_box, tag): tags = self.get_tags() while tag in tags: #it could be that dear user is mocking us and entering same tag over and over again @@ -86,18 +96,24 @@ self.tag_box.selected_tags = tags - self.set_text(", ".join(tags)) + self.set_text("%s, " % ", ".join(tags)) self.set_position(len(self.get_text())) def hide_popup(self): self.popup.hide() + if self._parent_click_watcher and self.get_toplevel().handler_is_connected(self._parent_click_watcher): + self.get_toplevel().disconnect(self._parent_click_watcher) + self._parent_click_watcher = None def show_popup(self): if not self.filter_tags: self.popup.hide() return + if not self._parent_click_watcher: + self._parent_click_watcher = self.get_toplevel().connect("button-press-event", self._on_focus_out_event) + alloc = self.get_allocation() x, y = self.get_parent_window().get_origin() @@ -190,7 +206,7 @@ cursor = self.get_position() self.set_text(", ".join(tags)) - self.set_position(cursor + len(new_tag)-len(old_tag)) # put the cursor back + self.set_position(len(self.get_text())) def _on_key_press_event(self, entry, event): if event.keyval == gtk.keysyms.Tab: @@ -240,6 +256,9 @@ self.redraw_canvas() def on_tag_click(self, widget, regions): + if not regions: + return + tag = regions[0] if tag in self.selected_tags: #self.selected_tags.remove(tag) --- src/hamster/widgets/timeinput.py 2010-05-14 16:29:15 +0000 +++ src/hamster/widgets/timeinput.py 2010-09-03 15:50:08 +0000 @@ -19,6 +19,7 @@ from .hamster.stuff import format_duration import gtk +from gtk import keysyms import datetime as dt import calendar import gobject @@ -60,6 +61,8 @@ self.connect("key-press-event", self._on_key_press_event) self.connect("focus-in-event", self._on_focus_in_event) self.connect("focus-out-event", self._on_focus_out_event) + self._parent_click_watcher = None # bit lame but works + self.connect("changed", self._on_text_changed) self.show() @@ -119,7 +122,7 @@ self.set_text(time_text) self.set_position(len(time_text)) - self.popup.hide() + self.hide_popup() if self.news: self.emit("time-entered") self.news = False @@ -142,15 +145,22 @@ self.show_popup() def _on_focus_out_event(self, event, something): - self.popup.hide() + self.hide_popup() if self.news: self.emit("time-entered") self.news = False + def hide_popup(self): + if self._parent_click_watcher and self.get_toplevel().handler_is_connected(self._parent_click_watcher): + self.get_toplevel().disconnect(self._parent_click_watcher) + self._parent_click_watcher = None + self.popup.hide() def show_popup(self): + if not self._parent_click_watcher: + self._parent_click_watcher = self.get_toplevel().connect("button-press-event", self._on_focus_out_event) + # will be going either 24 hours or from start time to start time + 12 hours - start_time = dt.datetime.combine(dt.date.today(), self.start_time) # we will be adding things i_time = start_time # we will be adding things @@ -218,13 +228,17 @@ def _on_key_press_event(self, entry, event): - cursor = self.time_tree.get_cursor() + if event.keyval not in (keysyms.Up, keysyms.Down, keysyms.Return, keysyms.KP_Enter): + #any kind of other input + self.hide_popup() + return False - if not cursor or not cursor[0]: + model, iter = self.time_tree.get_selection().get_selected() + if not iter: return - i = cursor[0][0] + i = model.get_path(iter)[0] if event.keyval == gtk.keysyms.Up: i-=1 elif event.keyval == gtk.keysyms.Down: @@ -237,15 +251,18 @@ else: self._select_time(entry.get_text()) elif (event.keyval == gtk.keysyms.Escape): - self.popup.hide() - else: - #any kind of other input - self.popup.hide() - return False + self.hide_popup() + return - # keep it in the sane borders + # keep it in sane limits i = min(max(i, 0), len(self.time_tree.get_model()) - 1) self.time_tree.set_cursor(i) self.time_tree.scroll_to_cell(i, use_align = True, row_align = 0.4) + + # if popup is not visible, display it on up and down + if event.keyval in (gtk.keysyms.Up, gtk.keysyms.Down) and self.popup.props.visible == False: + self.show_popup() + return True +