← Back to team overview

gtg team mailing list archive

[Merge] lp:~gtg-user/gtg/rtm-sync-plugin into lp:gtg

 

Luca Invernizzi has proposed merging lp:~gtg-user/gtg/rtm-sync-plugin into lp:gtg.

Requested reviews:
    Luca Invernizzi (invernizzi)
    Bertrand Rousseau (bertrand-rousseau)
-- 
https://code.launchpad.net/~gtg-user/gtg/rtm-sync-plugin/+merge/11650
Your team Gtg developers is subscribed to branch lp:gtg.
=== modified file 'AUTHORS'
--- AUTHORS	2009-07-30 08:53:25 +0000
+++ AUTHORS	2009-09-11 22:58:12 +0000
@@ -36,3 +36,4 @@
 * Kevin Mehall <km@xxxxxxxxxxxxxxx>
 * Paulo Cabido <paulo.cabido@xxxxxxxxx>
 * Patrick Coleman <blinken@xxxxxxxxx>
+* Luca Invernizzi <invernizzi.l@xxxxxxxxx>

=== modified file 'GTG/core/plugins/api.py'
--- GTG/core/plugins/api.py	2009-09-08 14:06:08 +0000
+++ GTG/core/plugins/api.py	2009-09-11 22:29:24 +0000
@@ -80,16 +80,12 @@
         """Removes a menu entry from the Plugin Menu of the Main Window 
         (task browser).
 
-        @param item: The gtk.MenuItem that is going to be removed.
-        @return: Returns C{True} if the operation has sucess or c{False} if it 
-        fails.  
+        @param item: The gtk.MenuItem that is going to be removed.  
         """
         try:
             self.__wTree.get_widget('menu_plugin').get_submenu().remove(item)
-            return True
         except Exception, e:
             print "Error removing menu item: %s" % e
-            return True
         
     def add_toolbar_item(self, item):
         """Adds a button to the task browser's toolbar.  
@@ -357,16 +353,9 @@
     def remove_menu_tagpopup(self, item):
         """Removes a menu from the tag popup menu of the tag view. 
         
-        @param item: The menu that is going to be removed from the tag popup 
-        menu.
-        @return: Returns C{True} if the operation has sucess or c{False} if it 
-        fails.
+        @param item: The menu that is going to be removed from the tag popup menu.
         """
-        try:
-            self.__tagpopup.remove(item)
-            return True
-        except Exception, e:
-            return False
+        self.__tagpopup.remove(item)
         
     def get_tagpopup_tag(self):
         """ Returns the selected tag in the tag view. 

=== modified file 'GTG/core/plugins/manager.py'
--- GTG/core/plugins/manager.py	2009-09-08 14:06:08 +0000
+++ GTG/core/plugins/manager.py	2009-09-11 22:29:24 +0000
@@ -47,8 +47,6 @@
         self.dialog = self.wTree.get_widget("PluginManagerDialog")
 		
         # stuff to populate
-        self.close_btn = self.wTree.get_widget("close_btn")
-        self.config_btn = self.wTree.get_widget("config_btn")
         self.lblPluginName = self.wTree.get_widget("lblPluginName")
         self.lblPluginVersion = self.wTree.get_widget("lblPluginVersion")
         self.lblPluginAuthors = self.wTree.get_widget("lblPluginAuthors")
@@ -105,13 +103,11 @@
         # properties
         
         self.dialog.set_transient_for(parent)
-        self.config_btn.set_sensitive(False)
 		
         # connect signals 
         self.dialog.connect("delete_event", self.close)
-        self.close_btn.connect("clicked", self.close)
+        self.dialog.connect("response", self.close)
         self.pluginTree.connect("cursor-changed", self.pluginExtraInfo, self.plugins)
-        self.config_btn.connect("clicked", self.plugin_configure_dialog)
 		
         self.dialog.show_all()
 		
@@ -134,7 +130,6 @@
             for plgin in self.plugins:
                 if model[path][1] == plgin['name'] and model[path][2] == plgin['version']:
                     plgin['state'] = not plgin['state']
-                    
 
     def pluginExtraInfo(self, treeview, plugins):
         path = treeview.get_cursor()[0]
@@ -195,18 +190,3 @@
                     else:
                         self.box_error.hide()
                         
-                    try:
-                        if plgin['state']:
-                            if not plgin['instance']:
-                                plgin['instance'] = plgin['class']()
-                                
-                            if plgin['instance'].is_configurable():
-                                self.config_btn.set_sensitive(True)
-                                self.current_plugin = plgin
-                        else:
-                            self.config_btn.set_sensitive(False)
-                    except Exception, e:
-                        self.config_btn.set_sensitive(False)
-                        
-    def plugin_configure_dialog(self, widget, data=None):
-        self.current_plugin['instance'].configure_dialog(self.plugin_api)
\ No newline at end of file

=== modified file 'GTG/core/plugins/pluginmanager.glade'
--- GTG/core/plugins/pluginmanager.glade	2009-09-08 14:06:08 +0000
+++ GTG/core/plugins/pluginmanager.glade	2009-09-11 22:29:24 +0000
@@ -224,33 +224,19 @@
             <property name="visible">True</property>
             <property name="layout_style">end</property>
             <child>
-              <widget class="GtkButton" id="config_btn">
-                <property name="label" translatable="yes">gtk-preferences</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_stock">True</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
               <widget class="GtkButton" id="close_btn">
                 <property name="label">gtk-close</property>
                 <property name="response_id">-7</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
-                <property name="receives_default">True</property>
+                <property name="receives_default">False</property>
                 <property name="use_stock">True</property>
               </widget>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
-                <property name="position">1</property>
+                <property name="position">0</property>
               </packing>
             </child>
           </widget>

=== modified file 'GTG/core/task.py'
--- GTG/core/task.py	2009-08-05 13:48:38 +0000
+++ GTG/core/task.py	2009-09-11 22:29:24 +0000
@@ -19,9 +19,11 @@
 
 from datetime import date
 import xml.dom.minidom
+import uuid
 
 from GTG import _
 from GTG.tools.dates import strtodate
+from datetime import datetime
 
 
 class Task:
@@ -37,6 +39,7 @@
         #the id of this task in the project should be set
         #tid is a string ! (we have to choose a type and stick to it)
         self.tid = str(ze_id)
+        self.set_uuid(uuid.uuid4())
         self.content = ""
         #self.content = \
         #    "<content>Press Escape or close this task to save it</content>"
@@ -59,6 +62,7 @@
         if self.loaded:
             self.req._task_loaded(self.tid)
         self.attributes={}
+        self._modified_update()
 
     def is_loaded(self):
         return self.loaded
@@ -78,6 +82,18 @@
     def get_id(self):
         return str(self.tid)
 
+    def set_uuid(self, value):
+        self.uuid = str(value)
+
+    def get_uuid(self):
+        #NOTE: Transitional if switch, needed to add
+        #      the uuid field to tasks created before
+        #      adding this field to the task description.
+        if self.uuid == "":
+            self.set_uuid(uuid.uuid4())
+            self.sync()
+        return self.uuid
+
     def get_title(self):
         return self.title
 
@@ -145,6 +161,12 @@
                 workable = False
         return workable
 
+    def get_modified(self):
+        return self.modified
+
+    def set_modified(self, string):
+        self.modified = string
+
     def set_due_date(self, fulldate, fromparent=False):
         # if fromparent, we set only a date if duedate is not set
         #Or if duedate is after the newly set date !
@@ -473,10 +495,15 @@
             self.sync()
 
     def sync(self):
+        self._modified_update()
         if self.sync_func and self.is_loaded():
             self.sync_func(self)
             self.req._task_modified(self.tid)
 
+    def _modified_update(self):
+        self.modified = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
+
+
 
 ### TAG FUNCTIONS ############################################################
 #

=== modified file 'GTG/plugins/geolocalized_tasks/geolocalized_tasks.py'
--- GTG/plugins/geolocalized_tasks/geolocalized_tasks.py	2009-09-08 14:06:08 +0000
+++ GTG/plugins/geolocalized_tasks/geolocalized_tasks.py	2009-09-11 22:29:24 +0000
@@ -34,6 +34,13 @@
 from GTG.core.plugins.engine import PluginEngine
 
 class geolocalizedTasks:
+    PLUGIN_NAME = 'Geolocalized Tasks'
+    PLUGIN_AUTHORS = 'Paulo Cabido <paulo.cabido@xxxxxxxxx>'
+    PLUGIN_VERSION = '0.1'
+    PLUGIN_DESCRIPTION = 'This plugin adds geolocalized tasks to GTG!.\n \
+                          WARNING: This plugin is still heavy development.'
+                          
+    PLUGIN_ENABLED = True
     
     def __init__(self):
         self.geoclue = Geoclue.DiscoverLocation()
@@ -45,33 +52,6 @@
         # the preference menu for the plugin
         self.menu_item = gtk.MenuItem("Geolocalized-tasks Preferences")
         
-        self.PROXIMITY_FACTOR = 5  # 5 km
-        #self.LOCATION_ACCURACY = 3 # Locality
-        self.LOCATION_DETERMINATION_METHOD = [] # "network", "gps", "cellphone"
-        
-        for provider in self.geoclue.get_available_providers():
-            if provider['name'].lower() == "hostip":
-                if self.geoclue.provider_status(provider['object']) == "available" or\
-                self.geoclue.provider_status(provider['object']) == "acquiring":
-                    self.LOCATION_DETERMINATION_METHOD.append("network")
-            elif provider['name'].lower() == "gpsd" or provider['name'].lower() == "gypsy":
-                if self.geoclue.provider_status(provider['object']) == "available" or\
-                self.geoclue.provider_status(provider['object']) == "acquiring":
-                    self.LOCATION_DETERMINATION_METHOD.append("gps")
-            elif provider['name'].lower() == "gsmloc":
-                if self.geoclue.provider_status(provider['object']) == "available" or\
-                self.geoclue.provider_status(provider['object']) == "acquiring":
-                    self.LOCATION_DETERMINATION_METHOD.append("cellphone")
-                    
-        self.location_filter = []
-                    
-    
-    def activate(self, plugin_api):
-        self.plugin_api = plugin_api
-        
-        #self.menu_item.connect('activate', self.on_geolocalized_preferences, plugin_api)
-        #plugin_api.add_menu_item(self.menu_item)
-        
         # toolbar button for the new Location view
         # create the pixbuf with the icon and it's size.
         # 24,24 is the TaskEditor's toolbar icon size
@@ -89,6 +69,31 @@
         self.context_item.set_image(image_assign_location)
         # TODO: add a short cut to the menu
         
+        self.PROXIMITY_FACTOR = 5  # 5 km
+        #self.LOCATION_ACCURACY = 3 # Locality
+        self.LOCATION_DETERMINATION_METHOD = [] # "network", "gps", "cellphone"
+        
+        for provider in self.geoclue.get_available_providers():
+            if provider['name'].lower() == "hostip":
+                if self.geoclue.provider_status(provider['object']) == "available" or\
+                self.geoclue.provider_status(provider['object']) == "acquiring":
+                    self.LOCATION_DETERMINATION_METHOD.append("network")
+            elif provider['name'].lower() == "gpsd" or provider['name'].lower() == "gypsy":
+                if self.geoclue.provider_status(provider['object']) == "available" or\
+                self.geoclue.provider_status(provider['object']) == "acquiring":
+                    self.LOCATION_DETERMINATION_METHOD.append("gps")
+            elif provider['name'].lower() == "gsmloc":
+                if self.geoclue.provider_status(provider['object']) == "available" or\
+                self.geoclue.provider_status(provider['object']) == "acquiring":
+                    self.LOCATION_DETERMINATION_METHOD.append("cellphone")
+                    
+    
+    def activate(self, plugin_api):
+        self.plugin_api = plugin_api
+        
+        self.menu_item.connect('activate', self.on_geolocalized_preferences, plugin_api)
+        plugin_api.add_menu_item(self.menu_item)
+        
         self.context_item.connect('activate', self.on_contextmenu_tag_location, plugin_api)
         plugin_api.add_menu_tagpopup(self.context_item)
         
@@ -161,25 +166,20 @@
             
         
         self.location = self.geoclue.get_location_info()
+        self.location_filter = []
         
         # registers the filter callback method
         plugin_api.register_filter_cb(self.task_location_filter)
     
     def deactivate(self, plugin_api):
-        try:
-            if self.context_item:
-                plugin_api.remove_menu_tagpopup(self.context_item)
-        except:
-            pass
+        plugin_api.remove_menu_item(self.menu_item)
+        plugin_api.remove_menu_tagpopup(self.context_item)
+        #plugin_api.RemoveToolbarItem(None, self.seperator_location_view)
         
-        try:
-            if self.config:
-                self.config["geolocalized-tasks"] = {}
-                self.config["geolocalized-tasks"]["proximity_factor"] = self.PROXIMITY_FACTOR
-                self.config["geolocalized-tasks"]["location_determination_method"] =\
-                self.LOCATION_DETERMINATION_METHOD
-        except:
-            pass
+        self.config["geolocalized-tasks"] = {}
+        self.config["geolocalized-tasks"]["proximity_factor"] = self.PROXIMITY_FACTOR
+        self.config["geolocalized-tasks"]["location_determination_method"] =\
+        self.LOCATION_DETERMINATION_METHOD
         
         # remove the filters
         for tid in self.location_filter:
@@ -187,6 +187,8 @@
         
         # unregister the filter callback
         plugin_api.unregister_filter_cb(self.task_location_filter)
+        
+        
     
     def onTaskOpened(self, plugin_api):
         image_geolocalization_path = os.path.join(self.plugin_path,\
@@ -213,12 +215,6 @@
         btn_set_location.connect('clicked', self.set_task_location, plugin_api)
         plugin_api.add_task_toolbar_item(btn_set_location)
     
-    def is_configurable(self):
-        return True
-    
-    def configure_dialog(self, plugin_api):
-        self.on_geolocalized_preferences(plugin_api)
-    
     def location_changed(self):
         # TODO: This should refresh the task ang tag list
         # update the location
@@ -285,7 +281,7 @@
     #                            self.plugin_api.add_task_to_filter(task.get_id())
                                 
     #=== GEOLOCALIZED PREFERENCES===================================================    
-    def on_geolocalized_preferences(self, plugin_api):
+    def on_geolocalized_preferences(self, widget, plugin_api):
         wTree = gtk.glade.XML(self.glade_file, "Preferences")
         dialog = wTree.get_widget("Preferences")
         dialog.connect("response", self.preferences_close)

=== modified file 'GTG/plugins/notification_area/notification_area.py'
--- GTG/plugins/notification_area/notification_area.py	2009-09-08 14:06:08 +0000
+++ GTG/plugins/notification_area/notification_area.py	2009-09-11 22:31:48 +0000
@@ -43,7 +43,7 @@
         #menuItem.set_image(image_new_task)
         ##menuItem.connect('activate', self.new_task)
         #menu.append(menuItem)
-        self.view_main_window = gtk.CheckMenuItem("_View Main Window")
+        self.view_main_window = gtk.CheckMenuItem("View Main Window")
         self.view_main_window.set_active(True)
         self.view_main_window.connect('activate', self.minimize, plugin_api)
         menu.append(self.view_main_window)

=== added file 'GTG/plugins/rtm-sync.gtg-plugin'
--- GTG/plugins/rtm-sync.gtg-plugin	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm-sync.gtg-plugin	2009-09-12 09:54:39 +0000
@@ -0,0 +1,8 @@
+[GTG Plugin]
+Module=rtm_sync
+Name=Remember the milk
+Description=Plugin for synchronising Getting Things Gnome! with the web service Remember the milk ( http://www.rememberthemilk.com ).\n\nLegal note: This product uses the Remember The Milk API but is not endorsed or certified by Remember The Milk.
+Authors=Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+Version=0.1.1
+Dependencies=python-xml,python-simplejson
+Enabled=False

=== added directory 'GTG/plugins/rtm_sync'
=== added file 'GTG/plugins/rtm_sync/__init__.py'
--- GTG/plugins/rtm_sync/__init__.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/__init__.py	2009-09-10 21:37:03 +0000
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import os
+sys.path.insert(0, os.getcwd())
+
+#pyflakes gives a warning on the following line,
+# but it's needed for the plugin to work
+from rtm_sync import RtmSync

=== added file 'GTG/plugins/rtm_sync/generic_proxy.py'
--- GTG/plugins/rtm_sync/generic_proxy.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/generic_proxy.py	2009-09-10 21:37:03 +0000
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+
+class GenericProxy(object):
+
+    def __init__(self):
+        super(GenericProxy, self).__init__()
+        self.task_list = []
+
+    def generateTaskList(self):
+        raise Exception()

=== added file 'GTG/plugins/rtm_sync/generic_task.py'
--- GTG/plugins/rtm_sync/generic_task.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/generic_task.py	2009-09-10 23:17:58 +0000
@@ -0,0 +1,244 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+import sys
+import os
+import xml.dom.minidom
+#import xml.utils.iso8601
+#import datetime
+#import time
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))+'/pyrtm')
+#import rtm
+from utility import iso8601toTime, timeToIso8601, dateToIso8601, timezone
+
+
+class GenericTask(object):
+    """GenericTask is the abstract interface that represents a generic task.
+    GtgTask and RtmTask are the implementation of this"""
+
+    title = property(lambda self: self._get_title(),
+                     lambda self, arg: self._set_title(arg))
+
+    id = property(lambda self: self._get_id())
+    #NOTE: text is the task extended description (or notes
+    #      in rtm)
+    text = property(lambda self: self._get_text(),
+                     lambda self, arg: self._set_text(arg))
+
+    modified = property(lambda self: self._get_modified())
+
+    due_date = property(lambda self: self._get_due_date(),
+                     lambda self, arg: self._set_due_date(arg))
+
+    tags = property(lambda self: self._get_tags(),
+                     lambda self, arg: self._set_tags(arg))
+
+    def __str__(self):
+        return "Task " + self.title + "(" + self.id + ")"
+
+    def copy(self, task):
+        self.title = task.title
+        self.tags = task.tags
+        self.text = task.text
+        self.due_date = task.due_date
+
+    #Interface specification that will be overwritten
+    # by the derived classes
+    def delete(self):
+        raise Exception()
+
+
+class RtmTask(GenericTask):
+
+    def __init__(self, task, list_id, taskseries_id, rtm, timeline):
+        super(RtmTask, self).__init__()
+        self.rtm = rtm
+        self.timeline = timeline
+        self.task = task
+        self.list_id = list_id
+        self.taskseries_id = taskseries_id
+
+    def _get_title(self):
+        return self.task.name
+
+    def _set_title(self, title):
+        self.rtm.tasks.setName(timeline=self.timeline, \
+                        list_id =self.list_id, \
+                        taskseries_id=self.taskseries_id, \
+                        task_id=self.id, \
+                        name = title)
+
+    def _get_id(self):
+        if hasattr(self.task, 'task'):
+            return self.task.task.id
+        else:
+            return self.task.id
+
+    def _get_tags(self):
+        if hasattr(self.task.tags, 'tag'):
+            if type(self.task.tags.tag) ==list:
+                return self.task.tags.tag
+            else:
+                return [self.task.tags.tag]
+        elif hasattr(self.task.tags, 'list'):
+            return map(lambda x: x.tag if hasattr(x, 'tag') else None, \
+                       self.task.tags.list)
+        return []
+
+    def _set_tags(self, tags):
+        tagstxt=""
+        for tag in tags:
+            name = tag.get_name()
+            name_fixed = name[name.find('@')+1:]
+            if tagstxt == "":
+                tagstxt = name_fixed
+            else:
+                tagstxt = tagstxt+ ",  " + name_fixed
+        self.rtm.tasks.setTags(timeline=self.timeline, \
+                        list_id =self.list_id, \
+                        taskseries_id=self.taskseries_id, \
+                        task_id=self.id, \
+                        tags=tagstxt)
+
+    def _get_text(self):
+        if hasattr(self.task, 'notes') and \
+           hasattr(self.task.notes, 'note'):
+            #Rtm saves the notes text inside the member "$t". Don't ask me why.
+            if type(self.task.notes.note) == list:
+                return "".join(map(lambda note: getattr(note, '$t') + "\n", \
+                                self.task.notes.note))
+            else:
+                return getattr(self.task.notes.note, '$t')
+        else:
+            return ""
+
+    def _set_text(self, text):
+        #delete old notes
+        #FIXME: the first check *should* not be necessary (but it is?).
+        if hasattr(self.task, 'notes') and \
+            hasattr(self.task.notes, 'note'):
+            if type(self.task.notes.note) == list:
+                note_ids =map(lambda note: note.id, self.task.notes.note)
+            else:
+                note_ids = [self.task.notes.note.id]
+            map(lambda id: self.rtm.tasksNotes.delete(timeline=self.timeline, \
+                    note_id=id), note_ids)
+        #add a new one
+        #TODO: investigate what is "Note title",  since there doesn't seem to
+        #be a note
+        # title in the web access.
+        #FIXME: minidom this way is ok, or do we suppose to get multiple
+        #      nodes in "content"?
+        if text == "":
+            return
+        document = xml.dom.minidom.parseString(text)
+        content =document.getElementsByTagName("content")
+        if len(content)>0 and hasattr(content[0], 'firstChild') \
+           and hasattr(content[0].firstChild, 'data'):
+            content = content[0].firstChild.data
+        else:
+            return
+        self.rtm.tasksNotes.add(timeline=self.timeline, \
+                                list_id = self.list_id,\
+                                taskseries_id = self.taskseries_id, \
+                                task_id = self.id, \
+                                note_title="",\
+                                note_text = content)
+
+    def _get_due_date(self):
+        if hasattr(self.task.task, 'due') and self.task.task.due != "":
+            return iso8601toTime(self.task.task.due) - timezone()
+        return None
+
+    def _set_due_date(self, due):
+        if type(due) != type(None):
+            due_string = timeToIso8601(due + timezone())
+            self.rtm.tasks.setDueDate(timeline=self.timeline, \
+                                      list_id = self.list_id,\
+                                      taskseries_id = self.taskseries_id, \
+                                      task_id = self.id, \
+                                      due=due_string)
+        else:
+            self.rtm.tasks.setDueDate(timeline=self.timeline, \
+                                      list_id = self.list_id,\
+                                      taskseries_id = self.taskseries_id, \
+                                      task_id = self.id)
+
+    def _get_modified(self):
+        if not hasattr(self.task, 'modified') or self.task.modified == "":
+            return None
+
+        return iso8601toTime(self.task.modified) - timezone()
+
+    def delete(self):
+        self.rtm.tasks.delete(timeline = self.timeline, \
+                              list_id = self.list_id, \
+                              taskseries_id = self.taskseries_id, \
+                              task_id = self.id)
+
+
+class GtgTask(GenericTask):
+
+    def __init__(self, task, plugin_api):
+        super(GtgTask, self).__init__()
+        self.task = task
+        self.plugin_api = plugin_api
+
+    def _get_title(self):
+        return self.task.get_title()
+
+    def _set_title(self, title):
+        self.task.set_title(title)
+
+    def _get_id(self):
+        return self.task.get_uuid()
+
+    def _get_tags(self):
+        return self.task.get_tags()
+
+    def _set_tags(self, tags):
+        #NOTE: isn't there a better mode than removing all tags?
+        #      need to add function in GTG/core/task.py
+        old_tags = self.tags
+        for tag in old_tags:
+            self.task.remove_tag(tag)
+        map(lambda tag: self.task.add_tag('@'+tag), tags)
+
+    def _get_text(self):
+        return self.task.get_text()
+
+    def _set_text(self, text):
+        self.task.set_text(text)
+
+    def _get_due_date(self):
+        due_string = self.task.get_due_date()
+        if due_string == "":
+            return None
+        return iso8601toTime(due_string)
+
+    def _set_due_date(self, due):
+        due_string = ""
+        if type(due) != None:
+            due_string = dateToIso8601(due)
+        self.task.set_due_date(due_string)
+
+    def _get_modified(self):
+        modified = self.task.get_modified()
+        if modified == None or modified == "":
+            return None
+        return iso8601toTime(modified)
+
+    def delete(self):
+        self.plugin_api.get_requester().delete_task(self.task.get_id())

=== added file 'GTG/plugins/rtm_sync/gtg_proxy.py'
--- GTG/plugins/rtm_sync/gtg_proxy.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/gtg_proxy.py	2009-09-10 21:37:03 +0000
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+import sys
+import os
+# IMPORTANT This add's the plugin's path to python sys path
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+from generic_task import GtgTask
+from generic_proxy import GenericProxy
+
+
+class GtgProxy(GenericProxy):
+
+    def __init__(self, plugin_api):
+        super(GtgProxy, self).__init__()
+        self.plugin_api = plugin_api
+
+    def generateTaskList(self):
+        tasks = map(self.plugin_api.get_task, \
+                     self.plugin_api.get_requester().get_active_tasks_list())
+        map(lambda task: self.task_list.append(GtgTask(task, \
+                                        self.plugin_api)), tasks)
+
+    def newTask(self, title, never_seen_before):
+        new_task = GtgTask(self.plugin_api.get_requester().new_task(
+                             newtask=never_seen_before), self.plugin_api)
+        new_task.title = title
+        self.task_list.append(new_task)
+        return new_task

=== added file 'GTG/plugins/rtm_sync/gtk.glade'
--- GTG/plugins/rtm_sync/gtk.glade	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/gtk.glade	2009-09-12 13:47:11 +0000
@@ -0,0 +1,356 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd";>
+
+<glade-interface>
+
+<widget class="GtkDialog" id="dialogtoken">
+  <property name="width_request">500</property>
+  <property name="height_request">150</property>
+  <property name="title" translatable="yes">Authentication</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+  <property name="modal">False</property>
+  <property name="default_width">502</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="icon_name">gtk-dialog-info</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">True</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_CENTER</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">True</property>
+  <property name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area1">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+	  <child>
+	    <widget class="GtkButton" id="btn_ok">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-ok</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-5</property>
+	      <signal name="clicked" handler="on_btn_ok_clicked" last_modification_time="Sat, 08 Aug 2009 13:27:39 GMT"/>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox" id="vbox1">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">True</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="lbl_title">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="no">&lt;b&gt;Remember the milk&lt;/b&gt;</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">True</property>
+	      <property name="justify">GTK_JUSTIFY_CENTER</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">6</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="lbl_dialog">
+	      <property name="visible">True</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">True</property>
+	      <property name="justify">GTK_JUSTIFY_CENTER</property>
+	      <property name="wrap">True</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">10</property>
+	      <property name="mnemonic_widget">btn_ok</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkWindow" id="dialogsync">
+  <property name="visible">True</property>
+  <property name="title" translatable="no">Synchronization with RTM</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+  <property name="modal">False</property>
+  <property name="resizable">False</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="icon">icons/hicolor/16x16/rtm_image.png</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkVBox" id="vbox2">
+      <property name="width_request">350</property>
+      <property name="height_request">120</property>
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child>
+	<widget class="GtkHBox" id="hbox2">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkImage" id="image1">
+	      <property name="visible">True</property>
+          <property name="pixbuf">icons/hicolor/svg/rtm_image.svg</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="lbl_dialog">
+	      <property name="visible">True</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">True</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">True</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox1">
+	  <property name="height_request">31</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkProgressBar" id="progressbar">
+	      <property name="visible">True</property>
+	      <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+	      <property name="fraction">0</property>
+	      <property name="pulse_step">0.10000000149</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="btn_ok">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-ok</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <signal name="clicked" handler="on_btn_ok_clicked" last_modification_time="Sat, 08 Aug 2009 14:30:02 GMT"/>
+	    </widget>
+	    <packing>
+	      <property name="padding">3</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">4</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkDialog" id="notification">
+  <property name="width_request">500</property>
+  <property name="height_request">150</property>
+  <property name="title" translatable="yes">Authentication</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+  <property name="modal">False</property>
+  <property name="default_width">502</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="icon_name">gtk-dialog-authentication</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">True</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_CENTER</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">True</property>
+  <property name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area1">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox" id="vbox1">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">True</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="lbl_title">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="no">&lt;b&gt;Remember the milk&lt;/b&gt;</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">True</property>
+	      <property name="justify">GTK_JUSTIFY_CENTER</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">6</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="lbl_dialog">
+	      <property name="visible">True</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">True</property>
+	      <property name="justify">GTK_JUSTIFY_CENTER</property>
+	      <property name="wrap">True</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">10</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>

=== added directory 'GTG/plugins/rtm_sync/icons'
=== added directory 'GTG/plugins/rtm_sync/icons/hicolor'
=== added directory 'GTG/plugins/rtm_sync/icons/hicolor/16x16'
=== added file 'GTG/plugins/rtm_sync/icons/hicolor/16x16/rtm_image.png'
Binary files GTG/plugins/rtm_sync/icons/hicolor/16x16/rtm_image.png	1970-01-01 00:00:00 +0000 and GTG/plugins/rtm_sync/icons/hicolor/16x16/rtm_image.png	2009-09-11 20:47:31 +0000 differ
=== added directory 'GTG/plugins/rtm_sync/icons/hicolor/24x24'
=== added file 'GTG/plugins/rtm_sync/icons/hicolor/24x24/rtm_image.png'
Binary files GTG/plugins/rtm_sync/icons/hicolor/24x24/rtm_image.png	1970-01-01 00:00:00 +0000 and GTG/plugins/rtm_sync/icons/hicolor/24x24/rtm_image.png	2009-09-11 20:47:31 +0000 differ
=== added directory 'GTG/plugins/rtm_sync/icons/hicolor/svg'
=== added file 'GTG/plugins/rtm_sync/icons/hicolor/svg/rtm_image.svg'
--- GTG/plugins/rtm_sync/icons/hicolor/svg/rtm_image.svg	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/icons/hicolor/svg/rtm_image.svg	2009-09-11 20:47:31 +0000
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="64px"
+   height="64px"
+   id="svg3727"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docname="rtm_image.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/luca/gtg/rtm-sync-plugin/GTG/plugins/rtm_sync/icons/hicolor/16x16/rtm_image.png"
+   inkscape:export-xdpi="22.5"
+   inkscape:export-ydpi="22.5">
+  <defs
+     id="defs3729">
+    <filter
+       inkscape:collect="always"
+       id="filter3502"
+       x="-0.044082869"
+       width="1.0881657"
+       y="-0.19633912"
+       height="1.3926782">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.4852688"
+         id="feGaussianBlur3504" />
+    </filter>
+    <linearGradient
+       id="linearGradient3661">
+      <stop
+         style="stop-color:#3399ff;stop-opacity:1;"
+         offset="0"
+         id="stop3663" />
+      <stop
+         id="stop3675"
+         offset="0.5"
+         style="stop-color:#3399ff;stop-opacity:1;" />
+      <stop
+         style="stop-color:#3399ff;stop-opacity:0.68627451;"
+         offset="0.75"
+         id="stop3685" />
+      <stop
+         id="stop3687"
+         offset="0.875"
+         style="stop-color:#3399ff;stop-opacity:0.52941176;" />
+      <stop
+         style="stop-color:#3399ff;stop-opacity:0.37719297;"
+         offset="1"
+         id="stop3665" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3661"
+       id="linearGradient3725"
+       gradientUnits="userSpaceOnUse"
+       x1="129.75728"
+       y1="658.44305"
+       x2="232.12813"
+       y2="657.89764" />
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath3545">
+      <rect
+         style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="rect3547"
+         width="188.70778"
+         height="271.60831"
+         x="83.991325"
+         y="571.32098" />
+    </clipPath>
+    <linearGradient
+       id="linearGradient3677">
+      <stop
+         style="stop-color:#ececec;stop-opacity:1"
+         offset="0"
+         id="stop3679" />
+      <stop
+         id="stop3689"
+         offset="0.5"
+         style="stop-color:#ffffff;stop-opacity:0.49803922;" />
+      <stop
+         style="stop-color:#ececec;stop-opacity:1"
+         offset="1"
+         id="stop3681" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3677"
+       id="linearGradient3723"
+       gradientUnits="userSpaceOnUse"
+       x1="115.46449"
+       y1="774.37683"
+       x2="235.97925"
+       y2="775.91943" />
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 32 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="64 : 32 : 1"
+       inkscape:persp3d-origin="32 : 21.333333 : 1"
+       id="perspective3735" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.9445436"
+     inkscape:cx="21.790111"
+     inkscape:cy="76.61186"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     inkscape:window-width="640"
+     inkscape:window-height="628"
+     inkscape:window-x="506"
+     inkscape:window-y="22" />
+  <metadata
+     id="metadata3732">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       id="g3711"
+       transform="matrix(0.1214085,0.1214085,-0.1214085,0.1214085,61.002827,-86.966337)"
+       inkscape:transform-center-x="-16.513266"
+       inkscape:transform-center-y="-36.581756">
+      <path
+         id="path3396"
+         d="M 414.51135,403.70408 C 414.51135,414.20049 395.48743,422.71931 372.04725,422.71931 C 348.60706,422.71931 329.58314,414.20049 329.58314,403.70408 C 329.58314,393.20767 348.60706,384.68885 372.04725,384.68885 C 395.48743,384.68885 414.51135,393.20767 414.51135,403.70408 z"
+         inkscape:transform-center-y="41.974325"
+         inkscape:transform-center-x="-9.0994502"
+         style="opacity:0.91085271;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path3400"
+         d="M 414.51135,393.70408 C 414.51135,404.20049 395.48743,412.71931 372.04725,412.71931 C 348.60706,412.71931 329.58314,404.20049 329.58314,393.70408 C 329.58314,383.20767 348.60706,374.68885 372.04725,374.68885 C 395.48743,374.68885 414.51135,383.20767 414.51135,393.70408 z"
+         inkscape:transform-center-y="41.974325"
+         inkscape:transform-center-x="-9.0994502"
+         style="opacity:0.91085271;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path3402"
+         d="M 414.51135,403.70408 C 414.51135,414.20049 395.48743,422.71931 372.04725,422.71931 C 348.60706,422.71931 329.58314,414.20049 329.58314,403.70408 C 329.58314,393.20767 348.60706,384.68885 372.04725,384.68885 C 395.48743,384.68885 414.51135,393.20767 414.51135,403.70408 z"
+         inkscape:transform-center-y="41.974325"
+         inkscape:transform-center-x="-9.0994502"
+         style="opacity:0.91085271;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         transform="matrix(0.9856192,0,0,1,199.34766,-2.7713095)"
+         clip-path="url(#clipPath3545)"
+         sodipodi:nodetypes="cccccccccccccccccccccc"
+         id="path3534"
+         d="M 138.90625,409.375 L 139,410.375 L 139.09375,411.34375 C 140.75679,432.38408 139.68245,453.56341 139.21875,474.59375 C 130.4306,497.46685 103.76148,508.08521 95.34375,531.25 C 94.719742,559.63205 95.8781,588.24637 95.84375,616.71875 C 96.525929,671.97803 96.53609,727.46035 97.21875,782.59375 C 100.28884,803.27664 119.73439,816.6627 138.25,823.09375 C 170.29613,833.76601 208.57233,831.13483 236.25,810.71875 C 247.88637,802.02197 256.05515,787.89092 254.28125,773.0625 C 254.46462,692.74557 255.55912,612.36257 256.0625,532.09375 C 249.30834,511.89397 228.57494,501.13943 217,484.1875 C 206.62003,465.60923 211.70898,431.09823 211.625,409.375 C 187.38542,409.37499 163.14583,409.375 138.90625,409.375 z M 173.96875,528.46875 C 180.68858,528.33398 187.4842,528.79077 194.09375,529.625 C 215.45628,532.8583 239.25025,540.68239 251.28125,559.875 C 259.12082,572.0293 254.62503,588.43585 244.15625,597.4375 C 224.14426,615.68821 195.2559,620.79378 168.875,619.71875 C 144.89679,618.14655 118.37989,611.34257 102.8125,591.625 C 93.866467,580.60806 95.372797,563.83495 105.125,553.8125 C 122.5306,535.22564 149.33071,529.07853 173.96875,528.46875 z"
+         style="fill:url(#linearGradient3723);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path3508"
+         d="M 369.94777,528.00618 C 345.30973,528.61593 318.50962,534.76307 301.10402,553.34993 C 291.35182,563.37235 289.84549,580.14549 298.79152,591.16243 C 314.35891,610.87997 340.87468,617.66905 364.85289,619.24125 C 391.23379,620.31629 420.12328,615.22564 440.13527,596.97493 C 450.60405,587.97325 455.09984,571.56673 447.26027,559.41243 C 435.22927,540.21979 411.4353,532.39573 390.07277,529.16243 C 383.46322,528.32817 376.6676,527.87141 369.94777,528.00618 z"
+         style="opacity:1;fill:#f2f2f2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         style="fill:url(#linearGradient3725);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+         d="M 138.90625,409.375 L 139,410.375 L 139.09375,411.34375 C 140.75679,432.38408 139.68245,453.56341 139.21875,474.59375 C 130.4306,497.46685 103.76148,508.08521 95.34375,531.25 C 94.719742,559.63205 95.8781,588.24637 95.84375,616.71875 C 96.525929,671.97803 96.53609,631.46035 97.21875,686.59375 C 100.28884,707.27664 119.73439,720.6627 138.25,727.09375 C 170.29613,737.76601 208.57233,735.13483 236.25,714.71875 C 247.88637,706.02197 256.05515,691.89092 254.28125,677.0625 C 254.46462,596.74557 255.55912,612.36257 256.0625,532.09375 C 249.30834,511.89397 228.57494,501.13943 217,484.1875 C 206.62003,465.60923 211.70898,431.09823 211.625,409.375 C 187.38542,409.37499 163.14583,409.375 138.90625,409.375 z M 173.96875,528.46875 C 180.68858,528.33398 187.4842,528.79077 194.09375,529.625 C 215.45628,532.8583 239.25025,540.68239 251.28125,559.875 C 259.12082,572.0293 254.62503,588.43585 244.15625,597.4375 C 224.14426,615.68821 195.2559,620.79378 168.875,619.71875 C 144.89679,618.14655 118.37989,611.34257 102.8125,591.625 C 93.866467,580.60806 95.372797,563.83495 105.125,553.8125 C 122.5306,535.22564 149.33071,529.07853 173.96875,528.46875 z"
+         id="path3659"
+         sodipodi:nodetypes="cccccccccccccccccccccc"
+         clip-path="url(#clipPath3545)"
+         transform="matrix(0.9856192,0,0,1,199.34766,37.228691)" />
+      <path
+         id="path3563"
+         d="M 333.14099,404.25022 C 335.49334,426.41602 333.99301,448.60995 333.79724,470.81275 C 333.33145,472.18625 332.79969,473.46195 332.20349,474.62525 C 322.27441,493.99985 294.30805,508.61965 289.39099,529.43775 C 291.10411,668.49535 291.42224,773.93775 291.42224,773.93775 C 291.40471,774.39025 291.42224,774.85755 291.42224,775.31275 C 291.42226,775.76805 291.40469,776.23525 291.42224,776.68775 L 291.42224,780.46905 L 291.79724,780.46905 C 295.756,807.17195 330.16049,828.06275 372.01599,828.09405 C 372.04741,828.09405 372.07833,828.09405 372.10974,828.09405 C 413.96526,828.06275 448.36974,807.17185 452.32849,780.46905 L 452.70349,780.46905 L 452.70349,776.68775 C 452.72106,776.23515 452.70348,775.76805 452.70349,775.31275 C 452.70351,774.85755 452.72103,774.39025 452.70349,773.93775 C 452.70351,773.93775 452.99041,668.49525 454.70349,529.43775 C 449.78645,508.61975 421.82008,493.99985 411.89099,474.62525 C 411.30699,473.48575 410.78728,472.21675 410.32849,470.87525 L 410.04724,404.25022 L 333.14099,404.25022 z M 337.54724,408.25022 C 360.39099,408.25022 383.23474,408.25022 406.07849,408.25022 C 406.12735,420.43772 406.18585,432.62522 406.23474,444.81275 C 407.10693,454.22905 404.81823,463.84295 406.98474,473.06275 C 415.68479,495.66375 441.89079,505.80335 450.29724,528.71905 C 450.30128,608.04355 448.768,687.54235 448.70349,766.93775 C 450.40115,779.71305 445.88556,792.90285 436.45349,801.71905 C 411.40508,824.19635 374.0633,828.57625 342.29724,820.15645 C 322.49257,814.98065 300.65448,802.54255 295.95349,780.96905 C 294.88669,751.98605 295.34734,722.85275 294.92224,693.81275 C 294.94021,638.71625 293.4657,583.62415 293.79724,528.53145 C 302.5795,505.57595 328.81514,495.25805 337.51599,472.21905 C 338.26259,451.56455 339.063,430.72372 337.67224,410.03142 L 337.60974,409.25022 L 337.54724,408.25022 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path3417"
+         d="M 414.47141,393.86713 C 414.16923,404.24732 395.25459,412.61713 372.00266,412.61713 C 349.29499,412.61714 330.73443,404.62835 329.59641,394.58588 L 329.56516,403.61713 C 329.50442,414.11336 348.56248,422.61714 372.00266,422.61713 C 395.44284,422.61713 414.47141,414.11354 414.47141,403.61713 C 414.10399,400.99347 414.71184,396.16626 414.47141,393.86713 z"
+         style="opacity:1;fill:#0060be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path3404"
+         d="M 414.51135,393.70408 C 414.51135,404.20049 395.48743,412.71931 372.04725,412.71931 C 348.60706,412.71931 329.58314,404.20049 329.58314,393.70408 C 329.58314,383.20767 348.60706,374.68885 372.04725,374.68885 C 395.48743,374.68885 414.51135,383.20767 414.51135,393.70408 z"
+         inkscape:transform-center-y="41.974325"
+         inkscape:transform-center-x="-9.0994502"
+         style="opacity:0.91085271;fill:#0060be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cccccccc"
+         id="path3434"
+         d="M 331.61606,403.625 C 332.77186,412.45706 343.31251,415.58934 350.58481,417.96875 C 369.00699,422.23097 390.1947,422.18632 406.67856,412.1875 C 409.76447,410.08284 413.03827,406.75523 412.39731,402.65625 C 399.53954,413.82702 381.02227,415.1994 364.61901,414.32955 C 352.97865,413.20127 340.32229,410.83165 331.61606,402.5 L 331.61606,403.5 L 331.61606,403.625 z"
+         style="opacity:1;fill:#004185;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:9.60000038;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter3502)" />
+    </g>
+  </g>
+</svg>

=== added directory 'GTG/plugins/rtm_sync/pyrtm'
=== added file 'GTG/plugins/rtm_sync/pyrtm/README'
--- GTG/plugins/rtm_sync/pyrtm/README	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/pyrtm/README	2009-08-07 04:13:36 +0000
@@ -0,0 +1,10 @@
+======================================================================
+Python library for Remember The Milk API
+======================================================================
+
+Copyright (c) 2008 by Sridhar Ratnakumar <http://nearfar.org/>
+
+Contributors:
+ - Mariano Draghi (cHagHi) <mariano at chaghi dot com dot ar>
+
+See app.py for examples

=== added file 'GTG/plugins/rtm_sync/pyrtm/rtm.py'
--- GTG/plugins/rtm_sync/pyrtm/rtm.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/pyrtm/rtm.py	2009-09-12 10:21:54 +0000
@@ -0,0 +1,398 @@
+# Python library for Remember The Milk API
+
+__author__ = 'Sridhar Ratnakumar <http://nearfar.org/>'
+__all__ = (
+    'API',
+    'createRTM',
+    'set_log_level'
+        )
+
+
+#import new
+import warnings
+import urllib
+import logging
+from hashlib import md5
+
+warnings.simplefilter('default', ImportWarning)
+
+_use_simplejson = False
+try:
+    import simplejson
+    _use_simplejson = True
+except ImportError:
+    pass
+
+if not _use_simplejson:
+    warnings.warn("simplejson module is not available, "
+             "falling back to the internal JSON parser. "
+             "Please consider installing the simplejson module from "
+             "http://pypi.python.org/pypi/simplejson.";, ImportWarning,
+             stacklevel=2)
+
+logging.basicConfig()
+LOG = logging.getLogger(__name__)
+LOG.setLevel(logging.INFO)
+
+SERVICE_URL = 'http://api.rememberthemilk.com/services/rest/'
+AUTH_SERVICE_URL = 'http://www.rememberthemilk.com/services/auth/'
+
+
+class RTMError(Exception): pass
+
+class RTMAPIError(RTMError): pass
+
+class AuthStateMachine(object):
+
+    class NoData(RTMError): pass
+
+    def __init__(self, states):
+        self.states = states
+        self.data = {}
+
+    def dataReceived(self, state, datum):
+        if state not in self.states:
+            error_string = _("Invalid state")+" <%s>" 
+
+            raise RTMError, error_string % state
+        self.data[state] = datum
+
+    def get(self, state):
+        if state in self.data:
+            return self.data[state]
+        else:
+            raise AuthStateMachine.NoData, 'No data for <%s>' % state
+
+
+class RTM(object):
+
+    def __init__(self, apiKey, secret, token=None):
+        self.apiKey = apiKey
+        self.secret = secret
+        self.authInfo = AuthStateMachine(['frob', 'token'])
+
+        # this enables one to do 'rtm.tasks.getList()', for example
+        for prefix, methods in API.items():
+            setattr(self, prefix,
+                    RTMAPICategory(self, prefix, methods))
+
+        if token:
+            self.authInfo.dataReceived('token', token)
+
+    def _sign(self, params):
+        "Sign the parameters with MD5 hash"
+        pairs = ''.join(['%s%s' % (k,v) for k,v in sortedItems(params)])
+        return md5(self.secret+pairs).hexdigest()
+
+    def get(self, **params):
+        "Get the XML response for the passed `params`."
+        params['api_key'] = self.apiKey
+        params['format'] = 'json'
+        params['api_sig'] = self._sign(params)
+
+        json = openURL(SERVICE_URL, params).read()
+
+        LOG.debug("JSON response: \n%s" % json)
+
+        if _use_simplejson:
+            data = dottedDict('ROOT', simplejson.loads(json))
+        else:
+            data = dottedJSON(json)
+        rsp = data.rsp
+
+        if rsp.stat == 'fail':
+            error_string = _("API call failed")+ " - %s (%s)"
+            raise RTMAPIError, error_string % (
+                rsp.err.msg, rsp.err.code)
+        else:
+            return rsp
+
+    def getNewFrob(self):
+        rsp = self.get(method='rtm.auth.getFrob')
+        self.authInfo.dataReceived('frob', rsp.frob)
+        return rsp.frob
+
+    def getAuthURL(self):
+        try:
+            frob = self.authInfo.get('frob')
+        except AuthStateMachine.NoData:
+            frob = self.getNewFrob()
+
+        params = {
+            'api_key': self.apiKey,
+            'perms'  : 'delete',
+            'frob'   : frob
+            }
+        params['api_sig'] = self._sign(params)
+        return AUTH_SERVICE_URL + '?' + urllib.urlencode(params)
+
+    def getToken(self):
+        frob = self.authInfo.get('frob')
+        rsp = self.get(method='rtm.auth.getToken', frob=frob)
+        self.authInfo.dataReceived('token', rsp.auth.token)
+        return rsp.auth.token
+
+class RTMAPICategory:
+    "See the `API` structure and `RTM.__init__`"
+
+    def __init__(self, rtm, prefix, methods):
+        self.rtm = rtm
+        self.prefix = prefix
+        self.methods = methods
+
+    def __getattr__(self, attr):
+        if attr in self.methods:
+            rargs, oargs = self.methods[attr]
+            if self.prefix == 'tasksNotes':
+                aname = 'rtm.tasks.notes.%s' % attr
+            else:
+                aname = 'rtm.%s.%s' % (self.prefix, attr)
+            return lambda **params: self.callMethod(
+                aname, rargs, oargs, **params)
+        else:
+            raise AttributeError, 'No such attribute: %s' % attr
+
+    def callMethod(self, aname, rargs, oargs, **params):
+        # Sanity checks
+        for requiredArg in rargs:
+            if requiredArg not in params:
+                raise TypeError, 'Required parameter (%s) missing' % requiredArg
+
+        for param in params:
+            if param not in rargs + oargs:
+                warnings.warn('Invalid parameter (%s)' % param)
+
+        return self.rtm.get(method=aname,
+                            auth_token=self.rtm.authInfo.get('token'),
+                            **params)
+
+
+
+# Utility functions
+
+def sortedItems(dictionary):
+    "Return a list of (key, value) sorted based on keys"
+    keys = dictionary.keys()
+    keys.sort()
+    for key in keys:
+        yield key, dictionary[key]
+
+def openURL(url, queryArgs=None):
+    if queryArgs:
+        url = url + '?' + urllib.urlencode(queryArgs)
+    LOG.debug("URL> %s", url)
+    return urllib.urlopen(url)
+
+class dottedDict(object):
+    "Make dictionary items accessible via the object-dot notation."
+
+    def __init__(self, name, dictionary):
+        self._name = name
+
+        if type(dictionary) is dict:
+            for key, value in dictionary.items():
+                if type(value) is dict:
+                    value = dottedDict(key, value)
+                elif type(value) in (list, tuple) and key != 'tag':
+                    value = [dottedDict('%s_%d' % (key, i), item)
+                             for i, item in indexed(value)]
+                setattr(self, key, value)
+
+    def __repr__(self):
+        children = [c for c in dir(self) if not c.startswith('_')]
+        return 'dotted <%s> : %s' % (
+            self._name,
+            ', '.join(children))
+
+
+def safeEval(string):
+    return eval(string, {}, {})
+
+def dottedJSON(json):
+    return dottedDict('ROOT', safeEval(json))
+
+def indexed(seq):
+    index = 0
+    for item in seq:
+        yield index, item
+        index += 1
+
+
+# API spec
+
+API = {
+   'auth': {
+       'checkToken':
+           [('auth_token'), ()],
+       'getFrob':
+           [(), ()],
+       'getToken':
+           [('frob'), ()]
+       },
+    'contacts': {
+        'add':
+            [('timeline', 'contact'), ()],
+        'delete':
+            [('timeline', 'contact_id'), ()],
+        'getList':
+            [(), ()]
+        },
+    'groups': {
+        'add':
+            [('timeline', 'group'), ()],
+        'addContact':
+            [('timeline', 'group_id', 'contact_id'), ()],
+        'delete':
+            [('timeline', 'group_id'), ()],
+        'getList':
+            [(), ()],
+        'removeContact':
+            [('timeline', 'group_id', 'contact_id'), ()],
+        },
+    'lists': {
+        'add':
+            [('timeline', 'name',), ('filter')],
+        'archive':
+            [('timeline', 'list_id'),()],
+        'delete':
+            [('timeline', 'list_id'),()],
+        'getList':
+            [(),()],
+        'setDefaultList':
+            [('timeline'), ('list_id')],
+        'setName':
+            [('timeline', 'list_id', 'name')],
+        'unarchive':
+            [('timeline'), ('list_id')]
+        },
+    'locations': {
+        'getList':
+            [(), ()]
+        },
+    'reflection': {
+        'getMethodInfo':
+            [('methodName',), ()],
+        'getMethods':
+            [(), ()]
+        },
+    'settings': {
+        'getList':
+            [(), ()]
+        },
+    'tasks': {
+        'add':
+            [('timeline', 'name',), ('list_id', 'parse',)],
+        'addTags':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id', 'tags'),
+             ()],
+        'complete':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id',), ()],
+        'delete':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'), ()],
+        'getList':
+            [(),
+             ('list_id', 'filter', 'last_sync')],
+        'movePriority':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id', 'direction'),
+             ()],
+        'moveTo':
+            [('timeline', 'from_list_id', 'to_list_id', 'taskseries_id', 'task_id'),
+             ()],
+        'postpone':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ()],
+        'removeTags':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id', 'tags'),
+             ()],
+        'setDueDate':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ('due', 'has_due_time', 'parse')],
+        'setEstimate':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ('estimate',)],
+        'setLocation':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ('location_id',)],
+        'setName':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id', 'name'),
+             ()],
+        'setPriority':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ('priority',)],
+        'setRecurrence':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ('repeat',)],
+        'setTags':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ('tags',)],
+        'setURL':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ('url',)],
+        'uncomplete':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id'),
+             ()],
+        },
+    'tasksNotes': {
+        'add':
+            [('timeline', 'list_id', 'taskseries_id', 'task_id', 'note_title', 'note_text'), ()],
+        'delete':
+            [('timeline', 'note_id'), ()],
+        'edit':
+            [('timeline', 'note_id', 'note_title', 'note_text'), ()]
+        },
+    'test': {
+        'echo':
+            [(), ()],
+        'login':
+            [(), ()]
+        },
+    'time': {
+        'convert':
+            [('to_timezone',), ('from_timezone', 'to_timezone', 'time')],
+        'parse':
+            [('text',), ('timezone', 'dateformat')]
+        },
+    'timelines': {
+        'create':
+            [(), ()]
+        },
+    'timezones': {
+        'getList':
+            [(), ()]
+        },
+    'transactions': {
+        'undo':
+            [('timeline', 'transaction_id'), ()]
+        },
+    }
+
+def createRTM(apiKey, secret, token=None):
+    rtm = RTM(apiKey, secret, token)
+#    if token is None:
+#        print 'No token found'
+#        print 'Give me access here:', rtm.getAuthURL()
+#        raw_input('Press enter once you gave access')
+#        print 'Note down this token for future use:', rtm.getToken()
+
+    return rtm
+
+def test(apiKey, secret, token=None):
+    rtm = createRTM(apiKey, secret, token)
+
+    rspTasks = rtm.tasks.getList(filter='dueWithin:"1 week of today"')
+    print [t.name for t in rspTasks.tasks.list.taskseries]
+    print rspTasks.tasks.list.id
+
+    rspLists = rtm.lists.getList()
+    # print rspLists.lists.list
+    print [(x.name, x.id) for x in rspLists.lists.list]
+
+def set_log_level(level):
+    '''Sets the log level of the logger used by the module.
+    
+    >>> import rtm
+    >>> import logging
+    >>> rtm.set_log_level(logging.INFO)
+    '''
+    
+    LOG.setLevel(level)

=== added file 'GTG/plugins/rtm_sync/rtm_proxy.py'
--- GTG/plugins/rtm_sync/rtm_proxy.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/rtm_proxy.py	2009-09-12 13:47:11 +0000
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+import os
+import sys
+#import time
+import subprocess
+#import gobject
+from xdg.BaseDirectory import xdg_config_home
+#import xml.utils.iso8601
+#from datetime import date
+
+#This add's the plugin's path to python sys path
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))+'/pyrtm')
+import rtm
+import utility
+from generic_task import RtmTask
+from generic_proxy import GenericProxy
+
+
+class RtmProxy(GenericProxy):
+
+    def __init__(self):
+        super(RtmProxy, self).__init__()
+        self.token = None
+
+    def getToken(self):
+        """gets a token from file (if a previous sync has been
+            performed), or opens a browser to request a new one
+            (in which case the function returns true). NOTE: token
+            is valid forever """
+        if self.token == None:
+            self.config_dir = \
+                os.path.join(xdg_config_home, 'gtg/plugins/rtm-sync')
+            self.token = utility.smartLoadFromFile(self.config_dir, 'token')
+        if self.token == None:
+            self.rtm=rtm.createRTM("2a440fdfe9d890c343c25a91afd84c7e", \
+                                   "ca078fee48d0bbfa")
+            subprocess.Popen(['xdg-open', self.rtm.getAuthURL()])
+            return False
+        return True
+
+    def login(self):
+        if hasattr(self, 'rtm'):
+            try:
+                self.token = self.rtm.getToken()
+            except:
+                self.token = None
+        if(self.getToken() == False):
+            return False
+        try:
+            self.rtm = rtm.createRTM("2a440fdfe9d890c343c25a91afd84c7e",\
+                               "ca078fee48d0bbfa", self.token)
+        except:
+            self.token = None
+        utility.smartSaveToFile(self.config_dir, 'token', self.token)
+        #NOTE: a timeline is an undo list for RTM. It can be used for
+        # journaling(timeline rollback is atomical)
+        self.timeline = self.rtm.timelines.create().timeline
+        return True
+
+    def downloadFromWeb(self):
+        #NOTE: syncing only incomplete tasks for now
+        #(it's easier to debug the things you see)
+        lists_id_list = map(lambda x: x.id, \
+                             self.rtm.lists.getList().lists.list)
+
+        def get_list_of_taskseries(x):
+            currentlist = self.rtm.tasks.getList(filter='status:incomplete', \
+                                                 list_id=x).tasks
+            if hasattr(currentlist, 'list'):
+                return currentlist.list
+            else:
+                return []
+        task_list_global= map(get_list_of_taskseries, lists_id_list)
+        taskseries_list = filter(lambda x: hasattr(x[0], 'taskseries'), \
+                                  zip(task_list_global, lists_id_list))
+        tasks_list_wrapped = map(lambda x: (x[0].taskseries, x[1]), \
+                                 taskseries_list)
+        tasks_list_normalized = map(lambda x: zip(x[0], [x[1]] * len(x[0]), \
+                map(lambda x: x.id, x[0])) if type(x[0]) == list \
+                else [(x[0], x[1], x[0].id)], tasks_list_wrapped)
+        tasks_list_unwrapped = []
+        task_objects_list = []
+        list_ids_list = []
+        taskseries_ids_list = []
+        if len(tasks_list_normalized)>0:
+            tasks_list_unwrapped = reduce(lambda x, y: x+y, \
+                                          tasks_list_normalized)
+            task_objects_list, list_ids_list, taskseries_ids_list = \
+                    utility.unziplist(tasks_list_unwrapped)
+
+        return zip(task_objects_list, list_ids_list, taskseries_ids_list)
+
+    def generateTaskList(self):
+        data = self.downloadFromWeb()
+        for task, list_id, taskseries_id in data:
+            self.task_list.append(RtmTask(task, list_id, taskseries_id, \
+                                          self.rtm, self.timeline))
+
+    def newTask(self, title):
+        result = self.rtm.tasks.add(timeline=self.timeline, name=title)
+        new_task= RtmTask(result.list.taskseries.task, result.list.id,\
+                          result.list.taskseries.id, self.rtm, self.timeline)
+        self.task_list.append(new_task)
+        return new_task

=== added file 'GTG/plugins/rtm_sync/rtm_sync.py'
--- GTG/plugins/rtm_sync/rtm_sync.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/rtm_sync.py	2009-09-12 13:47:11 +0000
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+#                    - Paulo Cabido <paulo.cabido@xxxxxxxxx> (example file)
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import gtk
+#import pygtk
+import os
+import sys
+from threading import Thread
+import gobject
+#import gobject
+#import logging
+from GTG import _
+# IMPORTANT This add's the plugin's path to python sys path
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))+'/pyrtm')
+import syncengine
+
+
+class RtmSync:
+    plugin_api = None
+    worker_thread = None
+    sync_engine = None
+    progressbar = None
+    progressbar_percent =0
+    status = None
+    lbl_dialog = None
+
+    def __init__(self):
+        #Icons! 
+        self.plugin_path = os.path.dirname(os.path.abspath(__file__))
+        rtm_image_path = os.path.join(self.plugin_path,\
+                   "icons/hicolor/16x16/rtm_image.png")
+        pixbug_rtm = gtk.gdk.\
+            pixbuf_new_from_file_at_size(rtm_image_path, 16, 16)
+        rtm_toolbar_image = gtk.Image()
+        rtm_menu_image = gtk.Image()
+        rtm_toolbar_image.set_from_pixbuf(pixbug_rtm)
+        rtm_menu_image.set_from_pixbuf(pixbug_rtm)
+        rtm_toolbar_image.show()
+        rtm_menu_image.show()
+
+        #drop down menu
+        self.menu_item = gtk.ImageMenuItem(_("Synchronize with RTM"))
+        self.menu_item.connect('activate', self.onTesteMenu)
+        self.menu_item.set_image(rtm_menu_image)
+
+        #toolbar button
+        self.tb_button = gtk.ToolButton(rtm_toolbar_image)
+        self.tb_button.set_label(_("Synchronize with RTM"))
+        self.tb_button.connect('clicked', self.onTbButton)
+
+        # plugin engine methods
+    def activate(self, plugin_api):
+        self.plugin_api = plugin_api
+        self.sync_engine = syncengine.SyncEngine(self)
+        # add a menu item to the menu bar
+        plugin_api.add_menu_item(self.menu_item)
+
+        # saves the separator's index to later remove it
+        self.separator = plugin_api.add_toolbar_item(gtk.SeparatorToolItem())
+        # add a item(button) to the ToolBar
+        plugin_api.add_toolbar_item(self.tb_button)
+
+    def deactivate(self, plugin_api):
+        plugin_api.remove_menu_item(self.menu_item)
+        plugin_api.remove_toolbar_item(self.tb_button)
+        plugin_api.remove_toolbar_item(None, self.separator)
+
+    #load a dialog with a String
+    def loadDialogToken(self, msg):
+        path = os.path.dirname(os.path.abspath(__file__))
+        glade_file = os.path.join(path, "gtk.glade")
+        wTree = gtk.glade.XML(glade_file, "dialogtoken")
+        self.dialog = wTree.get_widget("dialogtoken")
+        self.btn_ok = wTree.get_widget("btn_ok")
+        self.lbl_dialog = wTree.get_widget("lbl_dialog")
+        self.lbl_dialog.set_markup(msg)
+        self.dialog.connect("delete_event", self.close_dialog)
+        self.btn_ok.connect("clicked", self.callback)
+        self.dialog.show_all()
+
+    def loadDialogSync(self, msg):
+        path = os.path.dirname(os.path.abspath(__file__))
+        glade_file = os.path.join(path, "gtk.glade")
+        wTree = gtk.glade.XML(glade_file, "dialogsync")
+        self.dialog = wTree.get_widget("dialogsync")
+        self.btn_ok = wTree.get_widget("btn_ok")
+        self.btn_ok.set_sensitive(False)
+        self.lbl_dialog = wTree.get_widget("lbl_dialog")
+        self.lbl_dialog.set_text(msg)
+        self.progressbar = wTree.get_widget("progressbar")
+        self.dialog.connect("delete_event", self.close_dialog)
+        self.btn_ok.connect("clicked", self.close_dialog)
+        self.dialog.show_all()
+
+    def loadDialogNotification(self, msg):
+        path = os.path.dirname(os.path.abspath(__file__))
+        glade_file = os.path.join(path, "gtk.glade")
+        wTree = gtk.glade.XML(glade_file, "notification")
+        self.dialog = wTree.get_widget("notification")
+        self.lbl_dialog = wTree.get_widget("lbl_dialog")
+        self.lbl_dialog.set_text(msg)
+        self.dialog.show_all()
+
+    def close_dialog(self, widget, data=None):
+        self.dialog.destroy()
+
+    def set_progressbar(self):
+        self.progressbar.set_fraction(self.progressbar_percent)
+        if self.progressbar_percent == 1.0:
+            self.btn_ok.set_sensitive(True)
+
+    def set_status(self):
+        self.lbl_dialog.set_text(self.status)
+
+    def set_substatus(self):
+        self.progressbar.set_text(self.substatus)
+
+    def onTesteMenu(self, widget):
+        self.onTbButton(widget)
+
+    def lauchSynchronization(self):
+        self.loadDialogSync(_("Synchronization started"))
+        self.worker_thread = Thread(target = \
+                                self.sync_engine.synchronize).start()
+
+    def onTbButton(self, widget):
+        self.checkLogin()
+
+    def checkLoginBtn(self, widget):
+        self.dialog.destroy()
+        self.checkLogin(False)
+
+    def checkLogin (self, firstime = True):
+        self.firstime = firstime
+        self.loadDialogNotification(_("Trying to access, please stand by..."))
+        Thread(target = self.checkLoginThreadWatchdog).start()
+
+    def checkLoginThreadWatchdog(self):
+        login_thread = Thread(target = self.checkLoginThread)
+        try:
+            login_thread.start()
+            login_thread.join(10)
+        except:
+            pass
+        if login_thread.isAlive():
+            #Can't connect to RTM server
+            gobject.idle_add(self.loginHasFailed)
+        else:
+            gobject.idle_add(self.checkHasLogon)
+
+    def loginHasFailed(self):
+        self.dialog.destroy()
+        self.callback = self.close_dialog
+        self.loadDialogToken(_("Couldn't connect to Remember The Milk"))
+
+    def checkLoginThread(self):
+        try:
+            self.sync_engine.rtmLogin()
+        except:
+            pass
+
+    def checkHasLogon(self):
+        login = self.sync_engine.rtmHasLogon() 
+        self.dialog.destroy()
+        if login == False:
+            if not self.firstime:
+                self.callback = self.close_dialog
+                self.loadDialogToken(_("<b>Authentication failed</b>. Please retry."))
+            else:
+                self.callback = self.close_dialog
+                self.callback = self.checkLoginBtn
+                self.loadDialogToken(_("Please authenticate to Remember \
+The Milk in the browser that is being opened now. \
+When done, press OK"))
+        else:
+            self.lauchSynchronization()
+
+    def onTaskOpened(self, plugin_api):
+        pass

=== added file 'GTG/plugins/rtm_sync/syncengine.py'
--- GTG/plugins/rtm_sync/syncengine.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/syncengine.py	2009-09-12 13:47:11 +0000
@@ -0,0 +1,241 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+from time import sleep
+#import subprocess
+import gobject
+from xdg.BaseDirectory import xdg_cache_home
+#import pickle
+#import xml.utils.iso8601
+#from datetime import date
+from GTG import _
+
+# IMPORTANT This add's the plugin's path to python sys path
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))+'/pyrtm')
+from gtg_proxy import GtgProxy
+from rtm_proxy import RtmProxy
+from utility import smartSaveToFile, smartLoadFromFile, filterAttr, unziplist
+import rtm
+
+
+class SyncEngine (object):
+
+    def __init__(self, this_plugin):
+        super(SyncEngine, self).__init__()
+        self.this_plugin = this_plugin
+        self.rtm_proxy = RtmProxy()
+        self.gtg_proxy = GtgProxy(self.this_plugin.plugin_api)
+        self.rtm_has_logon = False
+
+    def rtmLogin(self):
+        self.rtm_has_logon = self.rtm_proxy.login()
+
+    def rtmHasLogon(self):
+        return self.rtm_has_logon
+
+    def _firstSynchronization(self):
+        gtg_to_rtm_id_mapping = []
+        #generating sets to perform intersection of tasks
+        #NOTE: assuming different titles!
+        gtg_task_titles_set = set(map(lambda x: x.title, self.gtg_list))
+        rtm_task_titles_set = set(map(lambda x: x.title, self.rtm_list))
+        #tasks in common
+        for title in rtm_task_titles_set.intersection(gtg_task_titles_set):
+            gtg_to_rtm_id_mapping.append(
+                   (filterAttr(self.gtg_list, 'title', title)[0].id,
+                     filterAttr(self.rtm_list, 'title', title)[0].id))
+
+        #tasks that must be added to GTG
+        rtm_added = rtm_task_titles_set.difference(gtg_task_titles_set)
+        if len(rtm_added) > 0:
+            self.update_status(_("Adding tasks to gtg.."))
+            self.update_progressbar(0.4)
+        for title in rtm_added:
+            self.update_substatus(_("Adding ") + title)
+            base_task = filterAttr(self.rtm_list, 'title', title)[0]
+            new_task = self.gtg_proxy.newTask(title, True)
+            new_task.copy(base_task)
+            gtg_to_rtm_id_mapping.append((new_task.id, base_task.id))
+
+        #tasks that must be added to RTM
+        gtg_added = gtg_task_titles_set.difference(rtm_task_titles_set)
+        if len(gtg_added) > 0:
+            self.update_status(_("Adding tasks to rtm.."))
+            self.update_progressbar(0.5)
+        for title in gtg_added:
+            self.update_substatus(_("Adding ") + title)
+            base_task = filterAttr(self.gtg_list, 'title', title)[0]
+            new_task = self.rtm_proxy.newTask(title)
+            new_task.copy(base_task)
+            gtg_to_rtm_id_mapping.append((base_task.id, new_task.id))
+        return gtg_to_rtm_id_mapping
+
+    def synchronize(self):
+        try:
+            self.synchronizeWorker()
+        except rtm.RTMAPIError as exception:
+            self.close_gui(exception.message)
+        except rtm.RTMError as exception:
+            self.close_gui(exception.message)
+        except:
+            self.close_gui(_("Synchronization failed."))
+
+    def synchronizeWorker(self):
+        self.update_status(_("Downloading task list..."))
+        self.update_progressbar(0.1)
+
+        self.gtg_proxy.generateTaskList()
+        self.rtm_proxy.generateTaskList()
+
+        self.update_status(_("Analyzing tasks..."))
+        self.update_progressbar(0.2)
+        self.gtg_list = self.gtg_proxy.task_list
+        self.rtm_list = self.rtm_proxy.task_list
+
+        ## loading the mapping of the last sync
+        cache_dir = os.path.join(xdg_cache_home, 'gtg/plugins/rtm-sync')
+        gtg_to_rtm_id_mapping = smartLoadFromFile(\
+                               cache_dir, 'gtg_to_rtm_id_mapping')
+        if gtg_to_rtm_id_mapping is None:
+            ###this is the first synchronization
+            self.update_status(_("Running first synchronization..."))
+            self.update_progressbar(0.3)
+            gtg_to_rtm_id_mapping = \
+                    self._firstSynchronization()
+        else:
+            ###this is an update
+            self.update_status(_("Analyzing last sync..."))
+            self.update_progressbar(0.3)
+            gtg_id_current_set = set(map(lambda x: x.id, self.gtg_list))
+            rtm_id_current_set = set(map(lambda x: x.id, self.rtm_list))
+            if len(gtg_to_rtm_id_mapping)>0:
+                gtg_id_previous_list, rtm_id_previous_list = \
+                        unziplist(gtg_to_rtm_id_mapping)
+            else:
+                gtg_id_previous_list, rtm_id_previous_list=[], []
+            gtg_id_previous_set = set(gtg_id_previous_list)
+            rtm_id_previous_set = set(rtm_id_previous_list)
+            gtg_to_rtm_id_dict = dict(gtg_to_rtm_id_mapping)
+            rtm_to_gtg_id_dict = dict(zip(rtm_id_previous_list, \
+                                          gtg_id_previous_list))
+
+            #We'll generate a new mapping between gtg and rtm task ids
+            gtg_to_rtm_id_mapping = []
+
+            #tasks removed from gtg since last synchronization
+            gtg_removed = gtg_id_previous_set.difference(gtg_id_current_set)
+            #tasks removed from rtm since last synchronization
+            rtm_removed = rtm_id_previous_set.difference(rtm_id_current_set)
+            #tasks added to gtg since last synchronization
+            gtg_added = gtg_id_current_set.difference(gtg_id_previous_set)
+            #tasks added to rtm since last synchronization
+            rtm_added = rtm_id_current_set.difference(rtm_id_previous_set)
+            #tasks still in common(which may need to be updated)
+            gtg_common = gtg_id_current_set.difference(gtg_added)\
+                    .difference(gtg_removed)
+
+            #Delete from rtm the tasks that have been removed in gtg
+            if len(gtg_removed) > 0:
+                self.update_status(_("Deleting tasks from rtm.."))
+                self.update_progressbar(0.4)
+            for gtg_id in gtg_removed:
+                rtm_id = gtg_to_rtm_id_dict[gtg_id]
+                rtm_task = filterAttr(self.rtm_list, 'id', rtm_id)
+                self.update_substatus(_("Deleting ") + rtm_task.title)
+                map(lambda task: task.delete(), rtm_task)
+
+            #Delete from gtg the tasks that have been removed in rtm
+            if len(rtm_removed) > 0:
+                self.update_status(_("Deleting tasks from gtg.."))
+                self.update_progressbar(0.5)
+            for rtm_id in rtm_removed:
+                gtg_id = rtm_to_gtg_id_dict[rtm_id]
+                gtg_task = filterAttr(self.gtg_list, 'id', gtg_id)
+                self.update_substatus(_("Deleting ") + gtg_task.title)
+                map(lambda task: task.delete(), gtg_task)
+                gtg_common.discard(gtg_id)
+
+            #tasks that must be added to RTM
+            #NOTE: should we check if the title is already present in the
+            #other backend, to be more robust?(Idem for vice-versa)
+            if len(gtg_added) >0:
+                self.update_status(_("Adding tasks to rtm.."))
+                self.update_progressbar(0.6)
+            for gtg_id in gtg_added:
+                gtg_task = filterAttr(self.gtg_list, 'id', gtg_id)[0]
+                self.update_substatus(_("Adding ") + gtg_task.title)
+                rtm_task = self.rtm_proxy.newTask(gtg_task.title)
+                rtm_task.copy(gtg_task)
+                gtg_to_rtm_id_mapping.append((gtg_id, rtm_task.id))
+
+            #tasks that must be added to GTG
+            if len(rtm_added) >0:
+                self.update_status(_("Adding tasks to rtm.."))
+                self.update_progressbar(0.7)
+            for rtm_id in rtm_added:
+                rtm_task = filterAttr(self.rtm_list, 'id', rtm_id)[0]
+                self.update_substatus(_("Adding ") + rtm_task.title)
+                gtg_task = self.gtg_proxy.newTask(rtm_task.title, True)
+                gtg_task.copy(rtm_task)
+                gtg_to_rtm_id_mapping.append((gtg_task.id, rtm_id))
+
+            if len(gtg_common) >0:
+                self.update_status(_("Updating remaining tasks.."))
+                self.update_progressbar(0.8)
+            for gtg_id in gtg_common:
+                rtm_id = gtg_to_rtm_id_dict[gtg_id]
+                gtg_task = filterAttr(self.gtg_list, 'id', gtg_id)[0]
+                rtm_task = filterAttr(self.rtm_list, 'id', rtm_id)[0]
+                if rtm_task.modified > gtg_task.modified:
+                    self.update_substatus(_("Updating ") + rtm_task.title)
+                    gtg_task.copy(rtm_task)
+                else:
+                    self.update_substatus(_("Updating ") + gtg_task.title)
+                    rtm_task.copy(gtg_task)
+
+                gtg_to_rtm_id_mapping.append((gtg_id, rtm_id))
+
+        self.update_status(_("Saving current state.."))
+        self.update_progressbar(0.9)
+
+        smartSaveToFile(cache_dir, 'gtg_to_rtm_id_mapping',\
+                        gtg_to_rtm_id_mapping)
+        #TODO: ask if ok or undo(easy on rtm(see timeline),
+        self.close_gui(_("Synchronization completed."))
+        
+
+    def close_gui(self,msg):
+        self.update_status(msg)
+        self.update_progressbar(1.0)
+        sleep(2)
+        self.update_status(_("Closing in one second"))
+        sleep(1)
+        gobject.idle_add(self.this_plugin.dialog.destroy)
+
+    def update_progressbar(self, percent):
+        self.this_plugin.progressbar_percent = percent
+        gobject.idle_add(self.this_plugin.set_progressbar)
+
+    def update_status(self, status):
+        self.this_plugin.status = status
+        gobject.idle_add(self.this_plugin.set_status)
+
+    def update_substatus(self, substatus):
+        self.this_plugin.substatus = substatus
+        gobject.idle_add(self.this_plugin.set_substatus)

=== added file 'GTG/plugins/rtm_sync/utility.py'
--- GTG/plugins/rtm_sync/utility.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/rtm_sync/utility.py	2009-09-12 10:21:54 +0000
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import pickle
+import os
+import datetime
+import time
+from GTG import _
+
+__all__ = ["smartSaveToFile",
+           "smartLoadFromFile",
+           "filterAttr",
+           "iso8601toTime",
+           "timeToIso8601",
+           "dateToIso8601",
+           "unziplist",
+           "timezone"]
+
+
+def smartLoadFromFile(dirname, filename):
+    path=dirname+'/'+filename
+    if os.path.isdir(dirname):
+        if os.path.isfile(path):
+            try:
+                with open(path, 'r') as file:
+                    item = pickle.load(file)
+            except:
+                return None
+            return item
+    else:
+        os.makedirs(dirname)
+
+
+def smartSaveToFile(dirname, filename, item, **kwargs):
+    path=dirname+'/'+filename
+    try:
+        with open(path, 'wb') as file:
+            pickle.dump(item, file)
+    except:
+        if kwargs.get('critical', False):
+            raise Exception(_("saving critical object failed"))
+
+
+def unziplist(a):
+    if len(a) == 0:
+        return [], []
+    return tuple(map(list, zip(*a)))
+
+
+def filterAttr(list, attr, value):
+    return filter(lambda elem: getattr(elem, attr) == value, list)
+
+
+def iso8601toTime(string):
+    #FIXME: need to handle time with TIMEZONES!
+    string = string.split('.')[0].split('Z')[0]
+    if string.find('T') == -1:
+        return datetime.datetime.strptime(string.split(".")[0], "%Y-%m-%d")
+    return datetime.datetime.strptime(string.split(".")[0], \
+                                      "%Y-%m-%dT%H:%M:%S")
+
+
+def timeToIso8601(timeobject):
+    if not hasattr(timeobject, 'strftime'):
+        return ""
+    return timeobject.strftime("%Y-%m-%dT%H:%M:%S")
+
+
+def dateToIso8601(timeobject):
+    if not hasattr(timeobject, 'strftime'):
+        return ""
+    return timeobject.strftime("%Y-%m-%d")
+
+
+def timezone():
+    if time.daylight:
+        return datetime.timedelta(seconds = time.altzone)
+    else:
+        return datetime.timedelta(seconds = time.timezone)

=== modified file 'GTG/tools/taskxml.py'
--- GTG/tools/taskxml.py	2009-08-02 09:12:28 +0000
+++ GTG/tools/taskxml.py	2009-09-09 23:10:48 +0000
@@ -26,6 +26,8 @@
 def task_from_xml(task,xmlnode) :
     cur_task = task
     cur_stat = "%s" %xmlnode.getAttribute("status")
+    uuid = "%s" %xmlnode.getAttribute("uuid")
+    cur_task.set_uuid(uuid)
     donedate = cleanxml.readTextNode(xmlnode,"donedate")
     cur_task.set_status(cur_stat,donedate=donedate)
     #we will fill the task with its content
@@ -51,6 +53,7 @@
             content = xml.dom.minidom.parseString(tas)
             cur_task.set_text(content.firstChild.toxml()) #pylint: disable-msg=E1103 
     cur_task.set_due_date(cleanxml.readTextNode(xmlnode,"duedate"))
+    cur_task.set_modified(cleanxml.readTextNode(xmlnode,"modified"))
     cur_task.set_start_date(cleanxml.readTextNode(xmlnode,"startdate"))
     cur_tags = xmlnode.getAttribute("tags").replace(' ','').split(",")
     if "" in cur_tags: cur_tags.remove("")
@@ -65,12 +68,14 @@
     t_xml = doc.createElement("task")
     t_xml.setAttribute("id",task.get_id())
     t_xml.setAttribute("status" , task.get_status())
+    t_xml.setAttribute("uuid" , task.get_uuid())
     tags_str = ""
     for tag in task.get_tags_name(): 
         tags_str = tags_str + str(tag) + ","
     t_xml.setAttribute("tags", tags_str[:-1])
     cleanxml.addTextNode(doc,t_xml,"title",task.get_title())
     cleanxml.addTextNode(doc,t_xml,"duedate",task.get_due_date())
+    cleanxml.addTextNode(doc,t_xml,"modified",task.get_modified())
     cleanxml.addTextNode(doc,t_xml,"startdate",task.get_start_date())
     cleanxml.addTextNode(doc,t_xml,"donedate",task.get_closed_date())
     childs = task.get_subtask_tids()

=== modified file 'locales/gtg.pot'
--- locales/gtg.pot	2009-08-24 10:01:19 +0000
+++ locales/gtg.pot	2009-09-12 10:51:18 +0000
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-08-24 11:59+0200\n"
+"POT-Creation-Date: 2009-09-12 12:45+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@xxxxxx>\n"
@@ -16,28 +16,107 @@
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: GTG/core/task.py:44
-msgid "My new task"
-msgstr ""
-
-#: GTG/core/plugins/pluginmanager.glade.h:1
-msgid "<b>No Plugin Selected</b>"
-msgstr ""
-
-#: GTG/core/plugins/pluginmanager.glade.h:2
-msgid "Authors:  "
-msgstr ""
-
-#: GTG/core/plugins/pluginmanager.glade.h:3
-msgid "Description:"
-msgstr ""
-
-#: GTG/core/plugins/pluginmanager.glade.h:4
-msgid "Plugin Manager"
-msgstr ""
-
-#: GTG/core/plugins/pluginmanager.glade.h:5
-msgid "Version: "
+#: GTG/gtg.py:80
+msgid "gtg is already running!"
+msgstr ""
+
+#: GTG/taskeditor/editor.py:258
+msgid "Due tomorrow !"
+msgstr ""
+
+#: GTG/taskeditor/editor.py:260
+#, python-format
+msgid "%s days left"
+msgstr ""
+
+#: GTG/taskeditor/editor.py:262
+msgid "Due today !"
+msgstr ""
+
+#: GTG/taskeditor/editor.py:264
+msgid "Due for yesterday"
+msgstr ""
+
+#: GTG/taskeditor/editor.py:266
+#, python-format
+msgid "Was %s days ago"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:29 GTG/taskbrowser/__init__.py:32
+msgid "Mark as done"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:30 GTG/taskbrowser/__init__.py:34
+msgid "Mark as not done"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:31 GTG/taskeditor/taskeditor.glade.h:4
+#: GTG/taskbrowser/__init__.py:36 GTG/taskbrowser/taskbrowser.glade.h:14
+msgid "Dismiss"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:32 GTG/taskbrowser/__init__.py:38
+msgid "Undismiss"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:33 GTG/taskeditor/taskeditor.glade.h:7
+msgid "Keep as Note"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:34
+msgid "Make a Task"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:36
+msgid "Mark this task as done"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:37 GTG/taskeditor/__init__.py:39
+msgid "Mark this task as to be done"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:38
+msgid "Mark this task as not to be done anymore"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:40
+msgid "Permanently remove this task"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:41
+msgid "Insert a subtask in this task"
+msgstr ""
+
+#: GTG/taskeditor/__init__.py:42
+msgid "Insert a tag in this task"
+msgstr ""
+
+#: GTG/taskeditor/taskeditor.glade.h:1
+msgid "<span weight=\"bold\">Due for</span>"
+msgstr ""
+
+#: GTG/taskeditor/taskeditor.glade.h:2
+msgid "<span weight=\"bold\">Starting on</span>"
+msgstr ""
+
+#: GTG/taskeditor/taskeditor.glade.h:3 GTG/taskbrowser/taskbrowser.glade.h:13
+msgid "Delete"
+msgstr ""
+
+#: GTG/taskeditor/taskeditor.glade.h:5
+msgid "Insert subtask"
+msgstr ""
+
+#: GTG/taskeditor/taskeditor.glade.h:6
+msgid "Insert tag"
+msgstr ""
+
+#: GTG/taskeditor/taskeditor.glade.h:8
+msgid "Mark Done"
+msgstr ""
+
+#: GTG/taskeditor/taskeditor.glade.h:9
+msgid "Task"
 msgstr ""
 
 #: GTG/core/firstrun_tasks.py:7
@@ -220,174 +299,42 @@
 "your problem really quickly."
 msgstr ""
 
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:1
-msgid "<b>Location Determination Method</b>"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:2
-msgid "<b>Proximity Factor</b>"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:3
-msgid ""
-"<small>Distance in kilometers from \n"
-"the current location.</small>"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:5
-msgid "Associate with existing tag"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:6
-msgid "Associate with new tag"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:7
-msgid "Geolocalized-tasks Preferences"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:8
-msgid "Set the task's location"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:9
-msgid "Use cellphone"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:10
-msgid "Use gps"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:11
-msgid "Use network"
-msgstr ""
-
-#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:12
-msgid "gtk-close"
-msgstr ""
-
-#: GTG/plugins/helloworld/hello_world.glade.h:1
-msgid "Hello World"
-msgstr ""
-
-#: GTG/plugins/helloworld/hello_world.glade.h:2
-msgid "Hello World Plugin"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:185
-msgid "All tasks"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:192
-msgid "Tasks with no tags"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:613
-msgid "(no active tasks)"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:615
-msgid "(1 active task)"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:627
-msgid "monday"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:627
-msgid "tuesday"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:627
-msgid "wednesday"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:628
-msgid "thursday"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:628
-msgid "friday"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:628
-msgid "saturday"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:629
-msgid "sunday"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:638
-msgid "today"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:645
-msgid "tomorrow"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:1071
-msgid "tags"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:1075
-msgid "defer"
-msgstr ""
-
-#: GTG/taskbrowser/browser.py:1080
-msgid "due"
-msgstr ""
-
-#: GTG/taskbrowser/tasktree.py:337 GTG/taskbrowser/tasktree.py:464
-#: GTG/taskbrowser/tagtree.py:292 GTG/taskbrowser/taskbrowser.glade.h:36
-msgid "Tags"
-msgstr ""
-
-#: GTG/taskbrowser/tasktree.py:352 GTG/taskbrowser/tasktree.py:488
-msgid "Title"
-msgstr ""
-
-#: GTG/taskbrowser/tasktree.py:365
-msgid "Due date"
-msgstr ""
-
-#: GTG/taskbrowser/tasktree.py:377
-msgid "Days left"
-msgstr ""
-
-#: GTG/taskbrowser/tasktree.py:476
-msgid "Closing date"
-msgstr ""
-
-#: GTG/taskbrowser/__init__.py:32 GTG/taskeditor/__init__.py:29
-msgid "Mark as done"
+#: GTG/core/plugins/pluginmanager.glade.h:1
+msgid "<b>No Plugin Selected</b>"
+msgstr ""
+
+#: GTG/core/plugins/pluginmanager.glade.h:2
+msgid "Authors:  "
+msgstr ""
+
+#: GTG/core/plugins/pluginmanager.glade.h:3
+msgid "Description:"
+msgstr ""
+
+#: GTG/core/plugins/pluginmanager.glade.h:4
+msgid "Plugin Manager"
+msgstr ""
+
+#: GTG/core/plugins/pluginmanager.glade.h:5
+msgid "Version: "
+msgstr ""
+
+#: GTG/core/task.py:47
+msgid "My new task"
 msgstr ""
 
 #: GTG/taskbrowser/__init__.py:33
 msgid "Mark the selected task as done"
 msgstr ""
 
-#: GTG/taskbrowser/__init__.py:34 GTG/taskeditor/__init__.py:30
-msgid "Mark as not done"
-msgstr ""
-
 #: GTG/taskbrowser/__init__.py:35 GTG/taskbrowser/__init__.py:39
 msgid "Mark the selected task as to be done"
 msgstr ""
 
-#: GTG/taskbrowser/__init__.py:36 GTG/taskbrowser/taskbrowser.glade.h:14
-#: GTG/taskeditor/taskeditor.glade.h:4 GTG/taskeditor/__init__.py:31
-msgid "Dismiss"
-msgstr ""
-
 #: GTG/taskbrowser/__init__.py:37
 msgid "Mark the task as not to be done anymore"
 msgstr ""
 
-#: GTG/taskbrowser/__init__.py:38 GTG/taskeditor/__init__.py:32
-msgid "Undismiss"
-msgstr ""
-
 #: GTG/taskbrowser/__init__.py:40
 msgid "Permanently remove the selected task"
 msgstr ""
@@ -446,10 +393,6 @@
 msgid "D_ismiss"
 msgstr ""
 
-#: GTG/taskbrowser/taskbrowser.glade.h:13 GTG/taskeditor/taskeditor.glade.h:3
-msgid "Delete"
-msgstr ""
-
 #: GTG/taskbrowser/taskbrowser.glade.h:15
 msgid "Edit"
 msgstr ""
@@ -531,6 +474,11 @@
 msgid "Tag is displayed in the workview"
 msgstr ""
 
+#: GTG/taskbrowser/taskbrowser.glade.h:36 GTG/taskbrowser/tasktree.py:337
+#: GTG/taskbrowser/tasktree.py:464 GTG/taskbrowser/tagtree.py:292
+msgid "Tags"
+msgstr ""
+
 #: GTG/taskbrowser/taskbrowser.glade.h:37
 msgid "Und_ismiss"
 msgstr ""
@@ -595,84 +543,248 @@
 msgid "_Work View"
 msgstr ""
 
-#: GTG/taskeditor/editor.py:256
-msgid "Due tomorrow !"
-msgstr ""
-
-#: GTG/taskeditor/editor.py:258
-#, python-format
-msgid "%s days left"
-msgstr ""
-
-#: GTG/taskeditor/editor.py:260
-msgid "Due today !"
-msgstr ""
-
-#: GTG/taskeditor/editor.py:262
-msgid "Due for yesterday"
-msgstr ""
-
-#: GTG/taskeditor/editor.py:264
-#, python-format
-msgid "Was %s days ago"
-msgstr ""
-
-#: GTG/taskeditor/taskeditor.glade.h:1
-msgid "<span weight=\"bold\">Due for</span>"
-msgstr ""
-
-#: GTG/taskeditor/taskeditor.glade.h:2
-msgid "<span weight=\"bold\">Starting on</span>"
-msgstr ""
-
-#: GTG/taskeditor/taskeditor.glade.h:5
-msgid "Insert subtask"
-msgstr ""
-
-#: GTG/taskeditor/taskeditor.glade.h:6
-msgid "Insert tag"
-msgstr ""
-
-#: GTG/taskeditor/taskeditor.glade.h:7 GTG/taskeditor/__init__.py:33
-msgid "Keep as Note"
-msgstr ""
-
-#: GTG/taskeditor/taskeditor.glade.h:8
-msgid "Mark Done"
-msgstr ""
-
-#: GTG/taskeditor/taskeditor.glade.h:9
-msgid "Task"
-msgstr ""
-
-#: GTG/taskeditor/__init__.py:34
-msgid "Make a Task"
-msgstr ""
-
-#: GTG/taskeditor/__init__.py:36
-msgid "Mark this task as done"
-msgstr ""
-
-#: GTG/taskeditor/__init__.py:37 GTG/taskeditor/__init__.py:39
-msgid "Mark this task as to be done"
-msgstr ""
-
-#: GTG/taskeditor/__init__.py:38
-msgid "Mark this task as not to be done anymore"
-msgstr ""
-
-#: GTG/taskeditor/__init__.py:40
-msgid "Permanently remove this task"
-msgstr ""
-
-#: GTG/taskeditor/__init__.py:41
-msgid "Insert a subtask in this task"
-msgstr ""
-
-#: GTG/taskeditor/__init__.py:42
-msgid "Insert a tag in this task"
-msgstr ""
-
-#: GTG/gtg.py:80
-msgid "gtg is already running!"
+#: GTG/taskbrowser/tasktree.py:352 GTG/taskbrowser/tasktree.py:488
+msgid "Title"
+msgstr ""
+
+#: GTG/taskbrowser/tasktree.py:365
+msgid "Due date"
+msgstr ""
+
+#: GTG/taskbrowser/tasktree.py:377
+msgid "Days left"
+msgstr ""
+
+#: GTG/taskbrowser/tasktree.py:476
+msgid "Closing date"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:185
+msgid "All tasks"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:192
+msgid "Tasks with no tags"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:613
+msgid "(no active tasks)"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:615
+msgid "(1 active task)"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:627
+msgid "monday"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:627
+msgid "tuesday"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:627
+msgid "wednesday"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:628
+msgid "thursday"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:628
+msgid "friday"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:628
+msgid "saturday"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:629
+msgid "sunday"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:638
+msgid "today"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:645
+msgid "tomorrow"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:1071
+msgid "tags"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:1075
+msgid "defer"
+msgstr ""
+
+#: GTG/taskbrowser/browser.py:1080
+msgid "due"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/utility.py:54
+msgid "saving critical object failed"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/gtk.glade.h:1
+msgid "Authentication"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/gtk.glade.h:2
+msgid "Synchronization with RTM"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/rtm_sync.py:56 GTG/plugins/rtm_sync/rtm_sync.py:62
+msgid "Synchronize with RTM"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/rtm_sync.py:135
+msgid "Synchronization started"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/rtm_sync.py:149
+msgid "Trying to access, please stand by..."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/rtm_sync.py:158
+msgid "<b>Authentication failed<b>. Please retry."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/rtm_sync.py:162
+msgid ""
+"Please authenticate to Remember The Milk in the browser that is being opened "
+"now. When done, press OK"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/pyrtm/rtm.py:55
+msgid "Invalid state"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/pyrtm/rtm.py:104
+msgid "API call failed"
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:62
+msgid "Adding tasks to gtg.."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:65 GTG/plugins/rtm_sync/syncengine.py:77
+#: GTG/plugins/rtm_sync/syncengine.py:177
+#: GTG/plugins/rtm_sync/syncengine.py:188
+msgid "Adding "
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:74
+#: GTG/plugins/rtm_sync/syncengine.py:173
+#: GTG/plugins/rtm_sync/syncengine.py:184
+msgid "Adding tasks to rtm.."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:92
+msgid "Synchronization failed."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:95
+msgid "Downloading task list..."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:101
+msgid "Analyzing tasks..."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:112
+msgid "Running first synchronization..."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:118
+msgid "Analyzing last sync..."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:150
+msgid "Deleting tasks from rtm.."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:155
+#: GTG/plugins/rtm_sync/syncengine.py:165
+msgid "Deleting "
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:160
+msgid "Deleting tasks from gtg.."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:194
+msgid "Updating remaining tasks.."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:201
+#: GTG/plugins/rtm_sync/syncengine.py:204
+msgid "Updating "
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:209
+msgid "Saving current state.."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:215
+msgid "Synchronization completed."
+msgstr ""
+
+#: GTG/plugins/rtm_sync/syncengine.py:222
+msgid "Closing in one second"
+msgstr ""
+
+#: GTG/plugins/helloworld/hello_world.glade.h:1
+msgid "Hello World"
+msgstr ""
+
+#: GTG/plugins/helloworld/hello_world.glade.h:2
+msgid "Hello World Plugin"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:1
+msgid "<b>Location Determination Method</b>"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:2
+msgid "<b>Proximity Factor</b>"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:3
+msgid ""
+"<small>Distance in kilometers from \n"
+"the current location.</small>"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:5
+msgid "Associate with existing tag"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:6
+msgid "Associate with new tag"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:7
+msgid "Geolocalized-tasks Preferences"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:8
+msgid "Set the task's location"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:9
+msgid "Use cellphone"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:10
+msgid "Use gps"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:11
+msgid "Use network"
+msgstr ""
+
+#: GTG/plugins/geolocalized_tasks/geolocalized.glade.h:12
+msgid "gtk-close"
 msgstr ""


Follow ups