[hamster-applet/windows] Merged win32 into src



commit 8123c3ef3ae9efd2b2ecb90e46c6b4b88b12bfe5
Author: Matthew Howle <matthew howle org>
Date:   Fri Mar 25 08:14:36 2011 -0400

    Merged win32 into src

 src/.gitignore                                |    1 -
 src/docky_control/2.0/hamster_control.py      |  115 --
 src/docky_control/2.0/hamster_control.py.info |    3 -
 src/docky_control/2.1/hamster_control.py      |  115 --
 src/docky_control/2.1/hamster_control.py.info |    4 -
 src/hamster-applet                            |  135 --
 src/hamster-cli                               |  284 ----
 src/hamster-service                           |   23 -
 src/hamster-time-tracker                      |   51 +-
 src/hamster/applet.py                         |  712 ----------
 src/hamster/client.py                         |  259 +++-
 src/hamster/configuration.py                  |  137 ++-
 src/hamster/db.py                             |   18 +-
 src/hamster/external.py                       |    1 -
 src/hamster/idle.py                           |  242 ++--
 src/hamster/lib/graphics.py                   |    2 +
 src/hamster/lib/stuff.py                      |   21 +-
 src/hamster/overview.py                       |    4 +-
 src/hamster/storage.py                        |  320 -----
 src/hamster/widgets/rangepick.py              |    2 +
 win32/hamster-time-tracker                    |  515 -------
 win32/hamster/.gitignore                      |    4 -
 win32/hamster/__init__.py                     |    1 -
 win32/hamster/about.py                        |   64 -
 win32/hamster/client.py                       |  452 ------
 win32/hamster/configuration.py                |  332 -----
 win32/hamster/db.py                           | 1206 ----------------
 win32/hamster/defs.py.in                      |    5 -
 win32/hamster/edit_activity.py                |  306 ----
 win32/hamster/external.py                     |  110 --
 win32/hamster/idle.py                         |  138 --
 win32/hamster/lib/charting.py                 |  346 -----
 win32/hamster/lib/graphics.py                 | 1839 -------------------------
 win32/hamster/lib/i18n.py                     |   42 -
 win32/hamster/lib/pytweener.py                |  605 --------
 win32/hamster/lib/stuff.py                    |  362 -----
 win32/hamster/lib/trophies.py                 |  201 ---
 win32/hamster/overview.py                     |  415 ------
 win32/hamster/overview_activities.py          |  203 ---
 win32/hamster/overview_totals.py              |  253 ----
 win32/hamster/preferences.py                  |  721 ----------
 win32/hamster/reports.py                      |  326 -----
 win32/hamster/stats.py                        |  450 ------
 win32/hamster/widgets/__init__.py             |   91 --
 win32/hamster/widgets/activityentry.py        |  339 -----
 win32/hamster/widgets/dateinput.py            |  186 ---
 win32/hamster/widgets/dayline.py              |  375 -----
 win32/hamster/widgets/facttree.py             |  662 ---------
 win32/hamster/widgets/rangepick.py            |  138 --
 win32/hamster/widgets/reportchooserdialog.py  |  139 --
 win32/hamster/widgets/tags.py                 |  349 -----
 win32/hamster/widgets/timechart.py            |  426 ------
 win32/hamster/widgets/timeinput.py            |  267 ----
 53 files changed, 470 insertions(+), 13847 deletions(-)
---
diff --git a/src/hamster-time-tracker b/src/hamster-time-tracker
index f57ebc4..a138481 100755
--- a/src/hamster-time-tracker
+++ b/src/hamster-time-tracker
@@ -23,7 +23,6 @@ import logging
 import datetime as dt
 
 import gtk, gobject
-import dbus, dbus.service, dbus.mainloop.glib
 
 class ProjectHamster(object):
     def __init__(self, window_name = None):
@@ -59,16 +58,6 @@ class ProjectHamster(object):
         self.get_widget("today_box").add(self.treeview)
         self.new_name.grab_focus()
 
-        # DBus Setup
-        try:
-            dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-            # Set up connection to the screensaver
-            self.dbusIdleListener = idle.DbusIdleListener()
-            self.dbusIdleListener.connect('idle-changed', self.on_idle_changed)
-
-        except dbus.DBusException, e:
-            logging.error("Can't init dbus: %s" % e)
-
         # configuration
         self.timeout_enabled = conf.get("enable_timeout")
         self.notify_on_idle = conf.get("notify_on_idle")
@@ -151,8 +140,8 @@ class ProjectHamster(object):
             return True
 
     def check_user(self):
-        if not self.notification:
-            return
+#        if not self.notification:
+#            return
 
         if self.notify_interval <= 0 or self.notify_interval >= 121:
             return
@@ -174,7 +163,7 @@ class ProjectHamster(object):
                 message = _(u"No activity")
 
 
-        if message:
+        if self.notification and message:
             self.notification.update(_("Time Tracker"), message, "hamster-applet")
             self.notification.show()
 
@@ -281,7 +270,8 @@ class ProjectHamster(object):
     def on_menu_preferences_activate(self, menu_item):
         dialogs.prefs.show(self.window)
     def on_menu_help_contents_activate(self, *args):
-        gtk.show_uri(gtk.gdk.Screen(), "ghelp:hamster-applet", 0L)
+        #TODO: provide some help; maybe local HTML files and launch the default browser
+        #gtk.show_uri(gtk.gdk.Screen(), "ghelp:hamster-applet", 0L)
         trophies.unlock("basic_instructions")
 
 
@@ -302,8 +292,8 @@ class ProjectHamster(object):
             self.refresh_hamster()
         elif self.timeout_enabled and self.last_activity and \
              self.last_activity.end_time is None:
-
-            runtime.storage.stop_tracking(end_time = self.dbusIdleListener.getIdleFrom())
+                 #TODO: need a Windows way to get idle time
+                 runtime.storage.stop_tracking(end_time = dt.datetime.now())
 
     def on_workspace_changed(self, screen, previous_workspace):
         if not previous_workspace:
@@ -437,11 +427,11 @@ class ProjectHamster(object):
 
     def close_window(self, *args):
         # properly saving window state and position
-        maximized = self.window.get_window().get_state() & gtk.gdk.WINDOW_STATE_MAXIMIZED
+        maximized = self.window.get_window().get_state() == gtk.gdk.WINDOW_STATE_MAXIMIZED
         conf.set("standalone_window_maximized", maximized)
 
         # make sure to remember dimensions only when in normal state
-        if maximized == False and not self.window.get_window().get_state() & gtk.gdk.WINDOW_STATE_ICONIFIED:
+        if maximized == False and not self.window.get_window().get_state() == gtk.gdk.WINDOW_STATE_ICONIFIED:
             x, y = self.window.get_position()
             w, h = self.window.get_size()
             conf.set("standalone_window_box", [x, y, w, h])
@@ -451,16 +441,10 @@ class ProjectHamster(object):
 
 
 # maintain just one instance. this code feels hackish
-class WindowServer(dbus.service.Object):
-    __dbus_object_path__ = "/org/gnome/Hamster/WindowServer"
-
+class WindowServer(gobject.GObject):
     def __init__(self):
         self.app = None
-        self.bus = dbus.SessionBus()
-        bus_name = dbus.service.BusName("org.gnome.Hamster.WindowServer", bus=self.bus)
-        dbus.service.Object.__init__(self, bus_name, self.__dbus_object_path__)
 
-    @dbus.service.method("org.gnome.Hamster.WindowServer")
     def main(self):
         if self.app:
             self.app.window.show()
@@ -468,19 +452,14 @@ class WindowServer(dbus.service.Object):
         else:
             self.app = ProjectHamster()
 
-    @dbus.service.method("org.gnome.Hamster.WindowServer")
     def edit(self): dialogs.edit.show(self.app)
 
-    @dbus.service.method("org.gnome.Hamster.WindowServer")
     def overview(self): dialogs.overview.show(self.app)
 
-    @dbus.service.method("org.gnome.Hamster.WindowServer")
     def about(self): dialogs.about.show(self.app)
 
-    @dbus.service.method("org.gnome.Hamster.WindowServer")
     def statistics(self): dialogs.stats.show(self.app)
 
-    @dbus.service.method("org.gnome.Hamster.WindowServer")
     def preferences(self): dialogs.prefs.show(self.app)
 
 
@@ -509,14 +488,6 @@ if __name__ == "__main__":
 
     from hamster.configuration import runtime, dialogs, conf, load_ui_file
 
-    # if there is windowserver hanging in dbus - call that and exit
-    bus = dbus.SessionBus()
-    if "org.gnome.Hamster.WindowServer" in dbus.SessionBus().list_names():
-        server = bus.get_object("org.gnome.Hamster.WindowServer", WindowServer.__dbus_object_path__)
-        getattr(server, window)()
-        sys.exit(0)
-
-
     # otherwise proceed and do all the import and everything
     gtk.gdk.threads_init()
     gtk.window_set_default_icon_name("hamster-applet")
@@ -539,4 +510,6 @@ if __name__ == "__main__":
 
 
     getattr(WindowServer(), window)()
+    gtk.gdk.threads_enter()
     gtk.main()
+    gtk.gdk.threads_leave()
diff --git a/src/hamster/client.py b/src/hamster/client.py
index 1473f36..050692b 100644
--- a/src/hamster/client.py
+++ b/src/hamster/client.py
@@ -21,11 +21,24 @@
 
 import datetime as dt
 from calendar import timegm
-import dbus, dbus.mainloop.glib
+import db
 import gobject
 from lib import stuff, trophies
 
-
+def to_dbus_fact(fact):
+    """Perform the conversion between fact database query and
+    dbus supported data types
+    """
+    return (fact['id'],
+            timegm(fact['start_time'].timetuple()),
+            timegm(fact['end_time'].timetuple()) if fact['end_time'] else 0,
+            fact['description'] or '',
+            fact['name'] or '',
+            fact['activity_id'] or 0,
+            fact['category'] or '',
+            fact['tags'],
+            timegm(fact['date'].timetuple()),
+            fact['delta'].days * 24 * 60 * 60 + fact['delta'].seconds)
 
 def from_dbus_fact(fact):
     """unpack the struct into a proper dict"""
@@ -65,17 +78,8 @@ class Storage(gobject.GObject):
     def __init__(self):
         gobject.GObject.__init__(self)
 
-        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-        self.bus = dbus.SessionBus()
         self._connection = None # will be initiated on demand
 
-        self.bus.add_signal_receiver(self._on_tags_changed, 'TagsChanged', 'org.gnome.Hamster')
-        self.bus.add_signal_receiver(self._on_facts_changed, 'FactsChanged', 'org.gnome.Hamster')
-        self.bus.add_signal_receiver(self._on_activities_changed, 'ActivitiesChanged', 'org.gnome.Hamster')
-        self.bus.add_signal_receiver(self._on_toggle_called, 'ToggleCalled', 'org.gnome.Hamster')
-
-        self.bus.add_signal_receiver(self._on_dbus_connection_change, 'NameOwnerChanged',
-                                     'org.freedesktop.DBus', arg0='org.gnome.Hamster')
     @staticmethod
     def _to_dict(columns, result_list):
         return [dict(zip(columns, row)) for row in result_list]
@@ -83,9 +87,7 @@ class Storage(gobject.GObject):
     @property
     def conn(self):
         if not self._connection:
-            self._connection = dbus.Interface(self.bus.get_object('org.gnome.Hamster',
-                                                              '/org/gnome/Hamster'),
-                                              dbus_interface='org.gnome.Hamster')
+            self._connection = db.Storage()
         return self._connection
 
     def _on_dbus_connection_change(self, name, old, new):
@@ -111,7 +113,7 @@ class Storage(gobject.GObject):
         """returns facts of the current date, respecting hamster midnight
            hamster midnight is stored in gconf, and presented in minutes
         """
-        return [from_dbus_fact(fact) for fact in self.conn.GetTodaysFacts()]
+        return [from_dbus_fact(fact) for fact in self.GetTodaysFacts()]
 
     def get_facts(self, date, end_date = None, search_terms = ""):
         """Returns facts for the time span matching the optional filter criteria.
@@ -124,7 +126,7 @@ class Storage(gobject.GObject):
         if end_date:
             end_date = timegm(end_date.timetuple())
 
-        return [from_dbus_fact(fact) for fact in self.conn.GetFacts(date,
+        return [from_dbus_fact(fact) for fact in self.GetFacts(date,
                                                                     end_date,
                                                                     search_terms)]
 
@@ -133,15 +135,15 @@ class Storage(gobject.GObject):
            results are sorted by most recent usage.
            search is case insensitive
         """
-        return self._to_dict(('name', 'category'), self.conn.GetActivities(search))
+        return self._to_dict(('name', 'category'), self.GetActivities(search))
 
     def get_categories(self):
         """returns list of categories"""
-        return self._to_dict(('id', 'name'), self.conn.GetCategories())
+        return self._to_dict(('id', 'name'), self.GetCategories())
 
     def get_tags(self, only_autocomplete = False):
         """returns list of all tags. by default only those that have been set for autocomplete"""
-        return self._to_dict(('id', 'name', 'autocomplete'), self.conn.GetTags(only_autocomplete))
+        return self._to_dict(('id', 'name', 'autocomplete'), self.GetTags(only_autocomplete))
 
 
     def get_tag_ids(self, tags):
@@ -151,16 +153,16 @@ class Storage(gobject.GObject):
            be created.
            on database changes the `tags-changed` signal is emitted.
         """
-        return self._to_dict(('id', 'name', 'autocomplete'), self.conn.GetTagIds(tags))
+        return self._to_dict(('id', 'name', 'autocomplete'), self.GetTagIds(tags))
 
     def update_autocomplete_tags(self, tags):
         """update list of tags that should autocomplete. this list replaces
            anything that is currently set"""
-        self.conn.SetTagsAutocomplete(tags)
+        self.SetTagsAutocomplete(tags)
 
     def get_fact(self, id):
         """returns fact by it's ID"""
-        return from_dbus_fact(self.conn.GetFact(id))
+        return from_dbus_fact(self.GetFact(id))
 
     def add_fact(self, fact, temporary_activity = False):
         """Add fact. activity name can use the
@@ -180,7 +182,7 @@ class Storage(gobject.GObject):
         if end_timestamp:
             end_timestamp = timegm(end_timestamp.timetuple())
 
-        new_id = self.conn.AddFact(serialized,
+        new_id = self.AddFact(serialized,
                                    start_timestamp,
                                    end_timestamp,
                                    temporary_activity)
@@ -195,11 +197,11 @@ class Storage(gobject.GObject):
         """Stop tracking current activity. end_time can be passed in if the
         activity should have other end time than the current moment"""
         end_time = timegm((end_time or dt.datetime.now()).timetuple())
-        return self.conn.StopTracking(end_time)
+        return self.StopTracking(end_time)
 
     def remove_fact(self, fact_id):
         "delete fact from database"
-        self.conn.RemoveFact(fact_id)
+        self.RemoveFact(fact_id)
 
     def update_fact(self, fact_id, fact, temporary_activity = False):
         """Update fact values. See add_fact for rules.
@@ -214,7 +216,7 @@ class Storage(gobject.GObject):
         if end_time:
             end_time = timegm(end_time.timetuple())
 
-        new_id =  self.conn.UpdateFact(fact_id,
+        new_id =  self.UpdateFact(fact_id,
                                        fact.serialized_name(),
                                        start_time,
                                        end_time,
@@ -228,11 +230,11 @@ class Storage(gobject.GObject):
         """Return activities for category. If category is not specified, will
         return activities that have no category"""
         category_id = category_id or -1
-        return self._to_dict(('id', 'name', 'category_id', 'category'), self.conn.GetCategoryActivities(category_id))
+        return self._to_dict(('id', 'name', 'category_id', 'category'), self.GetCategoryActivities(category_id))
 
     def get_category_id(self, category_name):
         """returns category id by name"""
-        return self.conn.GetCategoryId(category_name)
+        return self.GetCategoryId(category_name)
 
     def get_activity_by_name(self, activity, category_id = None, resurrect = True):
         """returns activity dict by name and optionally filtering by category.
@@ -240,26 +242,211 @@ class Storage(gobject.GObject):
            unless told otherise in the resurrect param
         """
         category_id = category_id or 0
-        return self.conn.GetActivityByName(activity, category_id, resurrect)
+        return self.GetActivityByName(activity, category_id, resurrect)
 
     # category and activity manipulations (normally just via preferences)
     def remove_activity(self, id):
-        self.conn.RemoveActivity(id)
+        self.RemoveActivity(id)
 
     def remove_category(self, id):
-        self.conn.RemoveCategory(id)
+        self.RemoveCategory(id)
 
     def change_category(self, id, category_id):
-        return self.conn.ChangeCategory(id, category_id)
+        return self.ChangeCategory(id, category_id)
 
     def update_activity(self, id, name, category_id):
-        return self.conn.UpdateActivity(id, name, category_id)
+        return self.UpdateActivity(id, name, category_id)
 
     def add_activity(self, name, category_id = -1):
-        return self.conn.AddActivity(name, category_id)
+        return self.AddActivity(name, category_id)
 
     def update_category(self, id, name):
-        return self.conn.UpdateCategory(id, name)
+        return self.UpdateCategory(id, name)
 
     def add_category(self, name):
-        return self.conn.AddCategory(name)
+        return self.AddCategory(name)
+
+    def AddFact(self, fact, start_time, end_time, temporary = False):
+        start_time = start_time or None
+        if start_time:
+            start_time = dt.datetime.utcfromtimestamp(start_time)
+
+        end_time = end_time or None
+        if end_time:
+            end_time = dt.datetime.utcfromtimestamp(end_time)
+
+#        self.start_transaction()
+        result = self.conn.__add_fact(fact, start_time, end_time, temporary)
+#        self.end_transaction()
+
+        if result:
+            self._on_facts_changed()
+
+        return result or 0
+
+    def GetFact(self, fact_id):
+        """Get fact by id. For output format see GetFacts"""
+        fact = dict(self.conn.__get_fact(fact_id))
+        fact['date'] = fact['start_time'].date()
+        fact['delta'] = dt.timedelta()
+        return to_dbus_fact(fact)
+
+    def UpdateFact(self, fact_id, fact, start_time, end_time, temporary = False):
+        if start_time:
+            start_time = dt.datetime.utcfromtimestamp(start_time)
+        else:
+            start_time = None
+
+        if end_time:
+            end_time = dt.datetime.utcfromtimestamp(end_time)
+        else:
+            end_time = None
+
+#        self.start_transaction()
+        self.conn.__remove_fact(fact_id)
+        result = self.conn.__add_fact(fact, start_time, end_time, temporary)
+
+#        self.end_transaction()
+
+        if result:
+            self._on_facts_changed()
+        return result
+
+    def StopTracking(self, end_time):
+        """Stops tracking the current activity"""
+        end_time = dt.datetime.utcfromtimestamp(end_time)
+
+        facts = self.conn.__get_todays_facts()
+        if facts:
+            self.conn.__touch_fact(facts[-1], end_time)
+            self._on_facts_changed()
+
+    def RemoveFact(self, fact_id):
+        """Remove fact from storage by it's ID"""
+        fact = self.conn.__get_fact(fact_id)
+        if fact:
+            self.conn.__remove_fact(fact_id)
+            self._on_facts_changed()
+
+
+    def GetFacts(self, start_date, end_date, search_terms):
+        """Gets facts between the day of start_date and the day of end_date.
+        Parameters:
+        i start_date: Seconds since epoch (timestamp). Use 0 for today
+        i end_date: Seconds since epoch (timestamp). Use 0 for today
+        s search_terms: Bleh
+        Returns Array of fact where fact is struct of:
+            i  id
+            i  start_time
+            i  end_time
+            s  description
+            s  activity name
+            i  activity id
+            i  category name
+            as List of fact tags
+            i  date
+            i  delta
+        """
+        #TODO: Assert start > end ?
+        start = dt.date.today()
+        if start_date:
+            start = dt.datetime.utcfromtimestamp(start_date).date()
+
+        end = None
+        if end_date:
+            end = dt.datetime.utcfromtimestamp(end_date).date()
+
+        return [to_dbus_fact(fact) for fact in self.conn.__get_facts(start, end, search_terms)]
+
+    def GetTodaysFacts(self):
+        """Gets facts of today, respecting hamster midnight. See GetFacts for
+        return info"""
+        return [to_dbus_fact(fact) for fact in self.conn.__get_todays_facts()]
+
+
+    # categories
+
+    def AddCategory(self, name):
+        res = self.conn.__add_category(name)
+        self._on_activities_changed()
+        return res
+
+
+    def GetCategoryId(self, category):
+        return self.conn.__get_category_id(category)
+
+
+    def UpdateCategory(self, id, name):
+        self.conn.__update_category(id, name)
+        self._on_activities_changed()
+
+    def RemoveCategory(self, id):
+        self.conn.__remove_category(id)
+        self._on_activities_changed()
+
+    def GetCategories(self):
+        return [(category['id'], category['name']) for category in self.conn.__get_categories()]
+
+    # activities
+
+    def AddActivity(self, name, category_id = -1):
+        new_id = self.conn.__add_activity(name, category_id)
+        self._on_activities_changed()
+        return new_id
+
+    def UpdateActivity(self, id, name, category_id):
+        self.conn.__update_activity(id, name, category_id)
+        self._on_activities_changed()
+
+
+
+    def RemoveActivity(self, id):
+        result = self.conn.__remove_activity(id)
+        self._on_activities_changed()
+        return result
+
+    def GetCategoryActivities(self, category_id = -1):
+
+        return [(row['id'],
+                 row['name'],
+                 row['category_id'],
+                 row['name'] or '') for row in
+                      self.conn.__get_category_activities(category_id = category_id)]
+
+
+    def GetActivities(self, search = ""):
+        return [(row['name'], row['category'] or '') for row in self.conn.__get_activities(search)]
+
+
+    def ChangeCategory(self, id, category_id):
+        changed = self.conn.__change_category(id, category_id)
+        if changed:
+            self._on_activities_changed()
+        return changed
+
+
+    def GetActivityByName(self, activity, category_id, resurrect = True):
+        category_id = category_id or None
+
+        if activity:
+            return dict(self.conn.__get_activity_by_name(activity, category_id, resurrect))
+        else:
+            return {}
+
+    # tags
+    def GetTags(self, only_autocomplete):
+        return [(tag['id'], tag['name'], tag['autocomplete']) for tag in self.conn.__get_tags(only_autocomplete)]
+
+
+    def GetTagIds(self, tags):
+        tags, new_added = self.conn.__get_tag_ids(tags)
+        if new_added:
+            self._on_tags_changed()
+        return [(tag['id'], tag['name'], tag['autocomplete']) for tag in tags]
+
+
+    def SetTagsAutocomplete(self, tags):
+        changes = self.conn.__update_autocomplete_tags(tags)
+        if changes:
+            self._on_tags_changed()
+
diff --git a/src/hamster/configuration.py b/src/hamster/configuration.py
index f4aafee..8a78d97 100644
--- a/src/hamster/configuration.py
+++ b/src/hamster/configuration.py
@@ -17,15 +17,13 @@
 # You should have received a copy of the GNU General Public License
 # along with Project Hamster.  If not, see <http://www.gnu.org/licenses/>.
 
-"""
-gconf part of this code copied from Gimmie (c) Alex Gravely via Conduit (c) John Stowers, 2006
-License: GPLv2
-"""
+try:
+    import ConfigParser as configparser
+except ImportError:
+    import configparser
 
-import gconf
 import os
 from client import Storage
-from xdg.BaseDirectory import xdg_data_home
 import logging
 import datetime as dt
 import gobject, gtk
@@ -57,7 +55,7 @@ class RuntimeStore(Singleton):
             import defs
             self.data_dir = os.path.join(defs.DATA_DIR, "hamster-applet")
             self.version = defs.VERSION
-        except:
+        except ImportError:
             # if defs is not there, we are running from sources
             module_dir = os.path.dirname(os.path.realpath(__file__))
             self.data_dir = os.path.join(module_dir, '..', '..', 'data')
@@ -68,8 +66,13 @@ class RuntimeStore(Singleton):
 
         self.storage = Storage()
 
+        if os.environ.has_key('APPDATA'):
+            self.home_data_dir = os.path.realpath(os.path.join(os.environ['APPDATA'], "hamster-applet"))
+        else:
+            logging.error("APPDATA variable is not set")
+            raise Exception("APPDATA environment variable is not defined")
 
-        self.home_data_dir = os.path.realpath(os.path.join(xdg_data_home, "hamster-applet"))
+            
 
     @property
     def art_dir(self):
@@ -153,13 +156,13 @@ def load_ui_file(name):
     ui.add_from_file(os.path.join(runtime.data_dir, name))
     return ui
 
-class GConfStore(gobject.GObject, Singleton):
+class INIStore(gobject.GObject, Singleton):
     """
-    Settings implementation which stores settings in GConf
-    Snatched from the conduit project (http://live.gnome.org/Conduit)
+    Settings implementation which stores settings in an INI file.
     """
-    GCONF_DIR = "/apps/hamster-applet/"
+    SECTION = 'Settings'    # Section to read/store settings in INI file
     VALID_KEY_TYPES = (bool, str, int, list, tuple)
+    #TODO: Remove non-Windows related settings
     DEFAULTS = {
         'enable_timeout'              :   False,       # Should hamster stop tracking on idle
         'stop_on_shutdown'            :   False,       # Should hamster stop tracking on shutdown
@@ -180,11 +183,34 @@ class GConfStore(gobject.GObject, Singleton):
         "conf-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
     }
     def __init__(self):
+        self._client = configparser.ConfigParser()
+
+        #TODO: Store file in home_data_dir
+        self.config = "hamster.ini"
+        if not os.path.isfile(self.config):
+            self._client.add_section(self.SECTION)
+            self._flush()
+        try:
+            fcfg = open(self.config,'r')
+            self._client.readfp(fcfg)
+            fcfg.close()
+        except IOError,e:
+            log.error("Error reading configurationfile: %s" % e)
+            raise
+        
         gobject.GObject.__init__(self)
-        self._client = gconf.client_get_default()
-        self._client.add_dir(self.GCONF_DIR[:-1], gconf.CLIENT_PRELOAD_RECURSIVE)
         self._notifications = []
 
+    def _flush(self):
+        """Write configuration values to INI file"""
+        try:
+            fcfg = open(self.config,'w')
+            self._client.write(fcfg)
+            fcfg.close()
+        except IOError,e:
+            log.error("Error writing to configuration file: %s" % e)
+            raise
+
     def _fix_key(self, key):
         """
         Appends the GCONF_PREFIX to the key if needed
@@ -194,43 +220,50 @@ class GConfStore(gobject.GObject, Singleton):
         @returns: The fixed key
         @rtype: C{string}
         """
-        if not key.startswith(self.GCONF_DIR):
-            return self.GCONF_DIR + key
-        else:
-            return key
+        #TODO: Remove calls to this function
+        return key
 
-    def _key_changed(self, client, cnxn_id, entry, data=None):
+#    def _key_changed(self, client, cnxn_id, entry, data=None):
+    def _key_changed(self, key):
         """
         Callback when a gconf key changes
         """
-        key = self._fix_key(entry.key)[len(self.GCONF_DIR):]
-        value = self._get_value(entry.value, self.DEFAULTS[key])
+        return #TODO: Fix or remove calls
+        #key = self._fix_key(entry.key)[len(self.GCONF_DIR):]
+        #value = self._get_value(entry.value, self.DEFAULTS[key])
 
-        self.emit('conf-changed', key, value)
+        #self.emit('conf-changed', key, value)
 
 
-    def _get_value(self, value, default):
-        """calls appropriate gconf function by the default value"""
+    def _get_value(self, key, default):
+        """calls appropriate configparser function by the default value"""
         vtype = type(default)
-
-        if vtype is bool:
-            return value.get_bool()
-        elif vtype is str:
-            return value.get_string()
-        elif vtype is int:
-            return value.get_int()
-        elif vtype in (list, tuple):
-            l = []
-            for i in value.get_list():
-                l.append(i.get_string())
-            return l
+        try:
+            if vtype is bool:
+                return self._client.getboolean(self.SECTION, key)
+            elif vtype is str:
+                return self._client.get(self.SECTION, key)
+            elif vtype is int:
+                return self._client.getint(self.SECTION, key)
+            elif vtype in (list, tuple):
+                l = []
+                temp = self._client.get(self.SECTION, key)
+                for i in temp.split(','):
+                    l.append(i.strip())
+                return l
+        except configparser.NoOptionError:
+            return None
+        except TypeError:
+            return None
+        except AttributeError:
+            return None
 
         return None
 
     def get(self, key, default=None):
         """
         Returns the value of the key or the default value if the key is
-        not yet in gconf
+        not yet in config
         """
 
         #function arguments override defaults
@@ -248,19 +281,16 @@ class GConfStore(gobject.GObject, Singleton):
             return None
 
         #for gconf refer to the full key path
-        key = self._fix_key(key)
+        #key = self._fix_key(key)
 
-        if key not in self._notifications:
-            self._client.notify_add(key, self._key_changed)
-            self._notifications.append(key)
+        #if key not in self._notifications:
+        #    self._notifications.append(key)
 
-        value = self._client.get(key)
+        value = self._get_value(key, default)
         if value is None:
             self.set(key, default)
             return default
-
-        value = self._get_value(value, default)
-        if value is not None:
+        elif value is not None:
             return value
 
         log.warn("Unknown gconf key: %s" % key)
@@ -282,20 +312,21 @@ class GConfStore(gobject.GObject, Singleton):
             return False
 
         #for gconf refer to the full key path
-        key = self._fix_key(key)
+        #key = self._fix_key(key)
 
         if vtype is bool:
-            self._client.set_bool(key, value)
+            self._client.set(self.SECTION, key, value)
         elif vtype is str:
-            self._client.set_string(key, value)
+            self._client.set(self.SECTION, key, value)
         elif vtype is int:
-            self._client.set_int(key, value)
+            self._client.set(self.SECTION, key, value)
         elif vtype in (list, tuple):
-            #Save every value as a string
-            strvalues = [str(i) for i in value]
-            self._client.set_list(key, gconf.VALUE_STRING, strvalues)
+            # flatten list/tuple
+            self._client.set(self.SECTION, key, ",".join([str(i) for i in value]))
+
+        self._flush()
 
         return True
 
 
-conf = GConfStore()
+conf = INIStore()
diff --git a/src/hamster/db.py b/src/hamster/db.py
index 33da02e..893518b 100644
--- a/src/hamster/db.py
+++ b/src/hamster/db.py
@@ -34,22 +34,19 @@ except ImportError:
 
 import os, time
 import datetime
-import storage
 from shutil import copy as copyfile
 import itertools
 import datetime as dt
 import gio
-from xdg.BaseDirectory import xdg_data_home
 
 from lib import stuff, trophies
 
-class Storage(storage.Storage):
+class Storage(object):
     con = None # Connection will be created on demand
-    def __init__(self, loop):
+    def __init__(self):
         """
         Delayed setup so we don't do everything at the same time
         """
-        storage.Storage.__init__(self, loop)
 
         self.__con = None
         self.__cur = None
@@ -84,7 +81,12 @@ class Storage(storage.Storage):
         self.run_fixtures()
 
     def __init_db_file(self):
-        home_data_dir = os.path.realpath(os.path.join(xdg_data_home, "hamster-applet"))
+        if os.environ.has_key('APPDATA'):
+            home_data_dir = os.path.realpath(os.path.join(os.environ['APPDATA'], "hamster-applet"))
+        else:
+            logging.error("APPDATA is not defined")
+            raise Exception("APPDATA environment variable is not defined")
+
         if not os.path.exists(home_data_dir):
             os.makedirs(home_data_dir, 0744)
 
@@ -161,6 +163,10 @@ class Storage(storage.Storage):
             return self.__get_tag_ids(tags)[0], True # all done, recurse
         else:
             return db_tags, changes
+    
+    def GetTagIds(self, tags):
+        tags, new_added = self.__get_tag_ids(tags)
+        return [(tag['id'], tag['name'], tag['autocomplete']) for tag in tags]
 
     def __update_autocomplete_tags(self, tags):
         tags = [tag.strip() for tag in tags.split(",") if tag.strip()]  # split by comma
diff --git a/src/hamster/external.py b/src/hamster/external.py
index 05624c3..774c5c0 100644
--- a/src/hamster/external.py
+++ b/src/hamster/external.py
@@ -21,7 +21,6 @@
 import logging
 from configuration import conf
 import gobject
-import dbus, dbus.mainloop.glib
 
 try:
     import evolution
diff --git a/src/hamster/idle.py b/src/hamster/idle.py
index 886cbcb..f305db7 100644
--- a/src/hamster/idle.py
+++ b/src/hamster/idle.py
@@ -16,125 +16,123 @@
 
 # You should have received a copy of the GNU General Public License
 # along with Project Hamster.  If not, see <http://www.gnu.org/licenses/>.
-
-import logging
-import dbus
-from dbus.lowlevel import Message
-import gconf
-import datetime as dt
-import gobject
-
-class DbusIdleListener(gobject.GObject):
-    """
-    Listen for idleness coming from org.gnome.ScreenSaver
-
-    Monitors org.gnome.ScreenSaver for idleness. There are two types,
-    implicit (due to inactivity) and explicit (lock screen), that need to be
-    handled differently. An implicit idle state should subtract the
-    time-to-become-idle (as specified in the gconf) from the last activity,
-    but an explicit idle state should not.
-
-    The signals are inspected for the "ActiveChanged" and "Lock"
-    members coming from the org.gnome.ScreenSaver interface and the
-    and is_screen_locked members are updated appropriately.
-    """
-    __gsignals__ = {
-        "idle-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
-    }
-    def __init__(self):
-        gobject.GObject.__init__(self)
-
-        self.screensaver_uri = "org.gnome.ScreenSaver"
-        self.screen_locked = False
-        self.idle_from = None
-        self.timeout_minutes = 0 # minutes after session is considered idle
-        self.idle_was_there = False # a workaround variable for pre 2.26
-
-        try:
-            self.bus = dbus.SessionBus()
-        except:
-            return 0
-        # Listen for chatter on the screensaver interface.
-        # We cannot just add additional match strings to narrow down
-        # what we hear because match strings are ORed together.
-        # E.g., if we were to make the match string
-        # "interface='org.gnome.ScreenSaver', type='method_call'",
-        # we would not get only screensaver's method calls, rather
-        # we would get anything on the screensaver interface, as well
-        # as any method calls on *any* interface. Therefore the
-        # bus_inspector needs to do some additional filtering.
-        self.bus.add_match_string_non_blocking("interface='%s'" %
-                                                           self.screensaver_uri)
-        self.bus.add_message_filter(self.bus_inspector)
-
-
-    def bus_inspector(self, bus, message):
-        """
-        Inspect the bus for screensaver messages of interest
-        """
-
-        # We only care about stuff on this interface.  We did filter
-        # for it above, but even so we still hear from ourselves
-        # (hamster messages).
-        if message.get_interface() != self.screensaver_uri:
-            return True
-
-        member = message.get_member()
-
-        if member in ("SessionIdleChanged", "ActiveChanged"):
-            logging.debug("%s -> %s" % (member, message.get_args_list()))
-
-            idle_state = message.get_args_list()[0]
-            if idle_state:
-                self.idle_from = dt.datetime.now()
-
-                # from gnome screensaver 2.24 to 2.28 they have switched
-                # configuration keys and signal types.
-                # luckily we can determine key by signal type
-                if member == "SessionIdleChanged":
-                    delay_key = "/apps/gnome-screensaver/idle_delay"
-                else:
-                    delay_key = "/desktop/gnome/session/idle_delay"
-
-                client = gconf.client_get_default()
-                self.timeout_minutes = client.get_int(delay_key)
-
-            else:
-                self.screen_locked = False
-                self.idle_from = None
-
-            if member == "ActiveChanged":
-                # ActiveChanged comes before SessionIdleChanged signal
-                # as a workaround for pre 2.26, we will wait a second - maybe
-                # SessionIdleChanged signal kicks in
-                def dispatch_active_changed(idle_state):
-                    if not self.idle_was_there:
-                        self.emit('idle-changed', idle_state)
-                    self.idle_was_there = False
-
-                gobject.timeout_add_seconds(1, dispatch_active_changed, idle_state)
-
-            else:
-                # dispatch idle status change to interested parties
-                self.idle_was_there = True
-                self.emit('idle-changed', idle_state)
-
-        elif member == "Lock":
-            # in case of lock, lock signal will be sent first, followed by
-            # ActiveChanged and SessionIdle signals
-            logging.debug("Screen Lock Requested")
-            self.screen_locked = True
-
-        return
-
-
-    def getIdleFrom(self):
-        if not self.idle_from:
-            return dt.datetime.now()
-
-        if self.screen_locked:
-            return self.idle_from
-        else:
-            # Only subtract idle time from the running task when
-            # idleness is due to time out, not a screen lock.
-            return self.idle_from - dt.timedelta(minutes = self.timeout_minutes)
+#
+#import logging
+#import dbus
+#import datetime as dt
+#import gobject
+# TODO: Create a Windows alternative
+#class DbusIdleListener(gobject.GObject):
+#    """
+#    Listen for idleness coming from org.gnome.ScreenSaver
+#
+#    Monitors org.gnome.ScreenSaver for idleness. There are two types,
+#    implicit (due to inactivity) and explicit (lock screen), that need to be
+#    handled differently. An implicit idle state should subtract the
+#    time-to-become-idle (as specified in the gconf) from the last activity,
+#    but an explicit idle state should not.
+#
+#    The signals are inspected for the "ActiveChanged" and "Lock"
+#    members coming from the org.gnome.ScreenSaver interface and the
+#    and is_screen_locked members are updated appropriately.
+#    """
+#    __gsignals__ = {
+#        "idle-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
+#    }
+#    def __init__(self):
+#        gobject.GObject.__init__(self)
+#
+#        self.screensaver_uri = "org.gnome.ScreenSaver"
+#        self.screen_locked = False
+#        self.idle_from = None
+#        self.timeout_minutes = 0 # minutes after session is considered idle
+#        self.idle_was_there = False # a workaround variable for pre 2.26
+#
+#        try:
+#            self.bus = dbus.SessionBus()
+#        except:
+#            return 0
+#        # Listen for chatter on the screensaver interface.
+#        # We cannot just add additional match strings to narrow down
+#        # what we hear because match strings are ORed together.
+#        # E.g., if we were to make the match string
+#        # "interface='org.gnome.ScreenSaver', type='method_call'",
+#        # we would not get only screensaver's method calls, rather
+#        # we would get anything on the screensaver interface, as well
+#        # as any method calls on *any* interface. Therefore the
+#        # bus_inspector needs to do some additional filtering.
+#        self.bus.add_match_string_non_blocking("interface='%s'" %
+#                                                           self.screensaver_uri)
+#        self.bus.add_message_filter(self.bus_inspector)
+#
+#
+#    def bus_inspector(self, bus, message):
+#        """
+#        Inspect the bus for screensaver messages of interest
+#        """
+#
+#        # We only care about stuff on this interface.  We did filter
+#        # for it above, but even so we still hear from ourselves
+#        # (hamster messages).
+#        if message.get_interface() != self.screensaver_uri:
+#            return True
+#
+#        member = message.get_member()
+#
+#        if member in ("SessionIdleChanged", "ActiveChanged"):
+#            logging.debug("%s -> %s" % (member, message.get_args_list()))
+#
+#            idle_state = message.get_args_list()[0]
+#            if idle_state:
+#                self.idle_from = dt.datetime.now()
+#
+#                # from gnome screensaver 2.24 to 2.28 they have switched
+#                # configuration keys and signal types.
+#                # luckily we can determine key by signal type
+#                if member == "SessionIdleChanged":
+#                    delay_key = "/apps/gnome-screensaver/idle_delay"
+#                else:
+#                    delay_key = "/desktop/gnome/session/idle_delay"
+#
+#                client = gconf.client_get_default()
+#                self.timeout_minutes = client.get_int(delay_key)
+#
+#            else:
+#                self.screen_locked = False
+#                self.idle_from = None
+#
+#            if member == "ActiveChanged":
+#                # ActiveChanged comes before SessionIdleChanged signal
+#                # as a workaround for pre 2.26, we will wait a second - maybe
+#                # SessionIdleChanged signal kicks in
+#                def dispatch_active_changed(idle_state):
+#                    if not self.idle_was_there:
+#                        self.emit('idle-changed', idle_state)
+#                    self.idle_was_there = False
+#
+#                gobject.timeout_add_seconds(1, dispatch_active_changed, idle_state)
+#
+#            else:
+#                # dispatch idle status change to interested parties
+#                self.idle_was_there = True
+#                self.emit('idle-changed', idle_state)
+#
+#        elif member == "Lock":
+#            # in case of lock, lock signal will be sent first, followed by
+#            # ActiveChanged and SessionIdle signals
+#            logging.debug("Screen Lock Requested")
+#            self.screen_locked = True
+#
+#        return
+#
+#
+#    def getIdleFrom(self):
+#        if not self.idle_from:
+#            return dt.datetime.now()
+#
+#        if self.screen_locked:
+#            return self.idle_from
+#        else:
+#            # Only subtract idle time from the running task when
+#            # idleness is due to time out, not a screen lock.
+#            return self.idle_from - dt.timedelta(minutes = self.timeout_minutes)
diff --git a/src/hamster/lib/graphics.py b/src/hamster/lib/graphics.py
index e6ca0cf..5dcc6b7 100644
--- a/src/hamster/lib/graphics.py
+++ b/src/hamster/lib/graphics.py
@@ -1643,6 +1643,8 @@ class Scene(gtk.DrawingArea):
         if self.tweener:
             self.tweener.update(delta)
 
+        if delta <= delta:
+            delta = 1
         self.fps = 1 / delta
 
 
diff --git a/src/hamster/lib/stuff.py b/src/hamster/lib/stuff.py
index d874d82..247909a 100644
--- a/src/hamster/lib/stuff.py
+++ b/src/hamster/lib/stuff.py
@@ -33,6 +33,14 @@ import time
 import re
 import locale
 import os
+try:
+    import _winreg as winreg
+except ImportError:
+    try:
+        import winreg
+    except ImportError:
+        logging.warn("WARNING - Could not load Registry module.")
+        winreg = None
 
 def format_duration(minutes, human = True):
     """formats duration in a human readable format.
@@ -152,16 +160,13 @@ def locale_to_utf8(locale_str):
 
 def locale_first_weekday():
     """figure if week starts on monday or sunday"""
-    first_weekday = 6 #by default settle on monday
+    first_weekday = 1 #by default settle on monday
+    if not winreg: return first_weekday
 
     try:
-        process = os.popen("locale first_weekday week-1stday")
-        week_offset, week_start = process.read().split('\n')[:2]
-        process.close()
-        week_start = dt.date(*time.strptime(week_start, "%Y%m%d")[:3])
-        week_offset = dt.timedelta(int(week_offset) - 1)
-        beginning = week_start + week_offset
-        first_weekday = int(beginning.strftime("%w"))
+        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Control Panel\International")
+        value, vtype = winreg.QueryValueEx(key, "iFirstDayOfWeek")
+        first_weekday = (int(value)+1) % 7
     except:
         logging.warn("WARNING - Failed to get first weekday from locale")
 
diff --git a/src/hamster/overview.py b/src/hamster/overview.py
index 3898aed..ab20f49 100644
--- a/src/hamster/overview.py
+++ b/src/hamster/overview.py
@@ -395,11 +395,11 @@ class Overview(object):
 
     def close_window(self):
         # properly saving window state and position
-        maximized = self.window.get_window().get_state() & gtk.gdk.WINDOW_STATE_MAXIMIZED
+        maximized = self.window.get_window().get_state() == gtk.gdk.WINDOW_STATE_MAXIMIZED
         conf.set("overview_window_maximized", maximized)
 
         # make sure to remember dimensions only when in normal state
-        if maximized == False and not self.window.get_window().get_state() & gtk.gdk.WINDOW_STATE_ICONIFIED:
+        if maximized == False and not self.window.get_window().get_state() == gtk.gdk.WINDOW_STATE_ICONIFIED:
             x, y = self.window.get_position()
             w, h = self.window.get_size()
             conf.set("overview_window_box", [x, y, w, h])
diff --git a/src/hamster/widgets/rangepick.py b/src/hamster/widgets/rangepick.py
index a9abb74..a49392b 100644
--- a/src/hamster/widgets/rangepick.py
+++ b/src/hamster/widgets/rangepick.py
@@ -102,10 +102,12 @@ class RangePick(gtk.ToggleButton):
         end_cal.select_month(self.end_date.month - 1, self.end_date.year)
         end_cal.select_day(self.end_date.day)
 
+        self.popup.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
         self.popup.show_all()
         self.get_widget("day").grab_focus()
         self.set_active(True)
 
+
     def emit_range(self, range, start, end):
         self.hide()
         self.emit("range-selected", range, start, end)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]