← Back to team overview

gtg team mailing list archive

[Merge] lp:~gtg-user/gtg/tomboy into lp:gtg

 

Luca Invernizzi has proposed merging lp:~gtg-user/gtg/tomboy into lp:gtg.

    Requested reviews:
    Gtg developers (gtg)
Related bugs:
  #432503 add tomboy support (merge request)
  https://bugs.launchpad.net/bugs/432503

-- 
https://code.launchpad.net/~gtg-user/gtg/tomboy/+merge/12363
Your team Gtg developers is subscribed to branch lp:gtg.
=== modified file '.bzrignore'
--- .bzrignore	2009-08-24 10:01:19 +0000
+++ .bzrignore	2009-09-24 17:30:26 +0000
@@ -37,3 +37,5 @@
 _trial_temp
 doc/api
 **/**.glade.h
+**/**.gladep
+**/**.gladep.bak

=== modified file 'GTG/core/plugins/api.py'
--- GTG/core/plugins/api.py	2009-09-24 13:15:17 +0000
+++ GTG/core/plugins/api.py	2009-09-24 17:30:26 +0000
@@ -236,6 +236,10 @@
     def show_window(self):
         """Shows the main GTG window (task browser)"""
         self.__window.show()
+
+    def get_window(self):
+        """Returns the window for which the plug-in has been created"""
+        return self.__window
 #=== General Methods ==========================================================
 
 
@@ -426,4 +430,4 @@
         """
         if func in self.__filter_cbs:
             self.__filter_cbs.remove(func)    
-#=== Filtering methods ========================================================
\ No newline at end of file
+#=== Filtering methods ========================================================

=== modified file 'GTG/core/plugins/engine.py'
--- GTG/core/plugins/engine.py	2009-09-24 13:15:17 +0000
+++ GTG/core/plugins/engine.py	2009-09-24 17:30:26 +0000
@@ -176,9 +176,8 @@
     # deactivate the enabled plugins
     def deactivatePlugins(self, plugins, plugin_api):
         for plugin in plugins:
-            if not plugin['state'] and not plugin['error'] and plugin['active']:
+            if plugin['state'] and not plugin['error'] and plugin['active']:
                 plugin['instance'].deactivate(plugin_api)
-                plugin['instance'] = None
                 plugin['active'] = False
 				
     # loads the plug-in features for a task
@@ -187,19 +186,31 @@
             if plugin['state'] and plugin['active']:
                 plugin['instance'].onTaskOpened(plugin_api)
      
+    # signals to the plug-ins that the task window is being closed
+    def onTaskClose(self, plugins, plugin_api):
+        for plugin in plugins:
+            if plugin['state'] and plugin['active']:
+                if hasattr(plugin['instance'],'onTaskClosed'):
+                    plugin['instance'].onTaskClosed(plugin_api)
+
 	# rechecks the plug-ins to see if any changes where done to the state
     def recheckPlugins(self, plugins, plugin_api):
         for plugin in plugins:
             if plugin['instance'] != None and plugin['state'] == False and plugin['active']:
                 try:
-                    self.deactivatePlugins([plugin],plugin_api)
+                    #print "deactivating plugin: " + plgin['name']
+                    plugin['instance'].deactivate(plugin_api)
+                    plugin['instance'] = None
+                    plugin['active'] = False
                 except Exception, e:
                     print "Error: %s" % e
             elif plugin['instance'] == None and plugin['state'] == True and not plugin['active']: 	
                 try:    
                     #print "activating plugin: " + plgin['name']
                     if not plugin['error']:
-                        self.activatePlugins([plugin],plugin_api)
+                        plugin['instance'] = plugin['class']()
+                        plugin['instance'].activate(plugin_api)
+                        plugin['active'] = True
                     else:
                         plugin['state'] = False
                 except Exception, e:

=== modified file 'GTG/core/plugins/manager.py'
--- GTG/core/plugins/manager.py	2009-09-24 13:15:17 +0000
+++ GTG/core/plugins/manager.py	2009-09-24 17:30:26 +0000
@@ -134,12 +134,6 @@
             for plgin in self.plugins:
                 if model[path][1] == plgin['name'] and model[path][2] == plgin['version']:
                     plgin['state'] = not plgin['state']
-                    #we instantly apply the plugin activation/deactivation
-                    #to respect HIG
-                    if plgin['state'] :
-                        self.pengine.activatePlugins([plgin], self.plugin_api)
-                    else :
-                        self.pengine.deactivatePlugins([plgin], self.plugin_api)
                     
 
     def pluginExtraInfo(self, treeview, plugins):
@@ -215,4 +209,4 @@
                         self.config_btn.set_sensitive(False)
                         
     def plugin_configure_dialog(self, widget, data=None):
-        self.current_plugin['instance'].configure_dialog(self.plugin_api)
+        self.current_plugin['instance'].configure_dialog(self.plugin_api)
\ No newline at end of file

=== modified file 'GTG/info.py'
--- GTG/info.py	2009-08-29 14:17:46 +0000
+++ GTG/info.py	2009-09-24 17:30:26 +0000
@@ -26,6 +26,7 @@
                "\tJean-François Fortin Tam <nekohayo@xxxxxxxxx>", \
                "\tJonathan Lange <jml@xxxxxxxxx>", \
                "\tPaulo Cabido <paulo.cabido@xxxxxxxxx>", \
+               "\tLuca Invernizzi <invernizzi.l@xxxxxxxxx>", \
 ]
 ARTISTS     = ["Kalle Persson <kalle@xxxxxxxxxxxxxxx>", \
                 "Bertrand Rousseau <bertrand.rousseau@xxxxxxxxx>"]
@@ -40,7 +41,7 @@
 Finnish: Mika Tapojärvi
 French: Lionel Dricot, Rafik Ouerchefani, Bertrand Rousseau, Pititjo
 German: Philip Stewart, Thomas Pitlik
-Italian: Luca Falavigna
+Italian: Luca Falavigna, Luca Invernizzi
 Malay: melayubuntu
 Polish: Tomasz Maciejewski
 Portuguese: Paulo Cabido 

=== modified file 'GTG/plugins/rtm-sync.gtg-plugin'
--- GTG/plugins/rtm-sync.gtg-plugin	2009-09-20 14:10:30 +0000
+++ GTG/plugins/rtm-sync.gtg-plugin	2009-09-24 17:30:26 +0000
@@ -3,6 +3,6 @@
 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.2
-Dependencies=python-xml,python-simplejson
+Version=0.1.1
+Dependencies=minidom,subprocess,BaseDirectory,threading,gobject,gtk,time,pickle,datetime,logging,md5,urllib,warnings,simplejson
 Enabled=False

=== modified file 'GTG/plugins/rtm_sync/pyrtm/rtm.py'
--- GTG/plugins/rtm_sync/pyrtm/rtm.py	2009-09-20 14:10:30 +0000
+++ GTG/plugins/rtm_sync/pyrtm/rtm.py	2009-09-24 17:30:26 +0000
@@ -12,10 +12,8 @@
 import warnings
 import urllib
 import logging
-import time
 from hashlib import md5
 from GTG import _
-import httplib
 
 warnings.simplefilter('default', ImportWarning)
 
@@ -93,7 +91,7 @@
         params['format'] = 'json'
         params['api_sig'] = self._sign(params)
 
-        json = openURL(SERVICE_URL, params)
+        json = openURL(SERVICE_URL, params).read()
 
         LOG.debug("JSON response: \n%s" % json)
 
@@ -180,28 +178,11 @@
     for key in keys:
         yield key, dictionary[key]
 
-def openURL(url, queryArgs = None):
+def openURL(url, queryArgs=None):
     if queryArgs:
         url = url + '?' + urllib.urlencode(queryArgs)
-        LOG.debug("URL> %s", url)
-    time_to_wait = 0
-    while True:
-        try:
-            if time_to_wait !=0:
-                time.sleep(time_to_wait)
-            http_connection = httplib.HTTPConnection("api.rememberthemilk.com",80)
-            http_connection.request("GET", url)
-            http_response = http_connection.getresponse()
-            http_response_data = http_response.read()
-            break
-        except httplib.IncompleteRead as exception:
-            #rtm server issues incomplete responses if we hammer it too much
-            # this way we can be fast *and* safe
-            if time_to_wait == 0:
-                time_to_wait = 2
-            else:
-                raise exception
-    return http_response_data
+    LOG.debug("URL> %s", url)
+    return urllib.urlopen(url)
 
 class dottedDict(object):
     "Make dictionary items accessible via the object-dot notation."
@@ -267,7 +248,7 @@
         'getList':
             [(), ()],
         'removeContact':
-            [('timeline', 'group_id', 'contact_id'), ()]
+            [('timeline', 'group_id', 'contact_id'), ()],
         },
     'lists': {
         'add':

=== modified file 'GTG/plugins/rtm_sync/syncengine.py'
--- GTG/plugins/rtm_sync/syncengine.py	2009-09-21 17:30:00 +0000
+++ GTG/plugins/rtm_sync/syncengine.py	2009-09-24 17:30:26 +0000
@@ -90,11 +90,11 @@
         try:
             self.synchronizeWorker()
         except rtm.RTMAPIError as exception:
-            self.close_gui(exception)
+            self.close_gui(exception.message)
         except rtm.RTMError as exception:
-            self.close_gui(exception)
-        except Exception as exception:
-            self.close_gui(_("Synchronization failed." + str(exception)))
+            self.close_gui(exception.message)
+        except:
+            self.close_gui(_("Synchronization failed."))
 
     def synchronizeWorker(self):
         self.update_status(_("Downloading task list..."))
@@ -157,9 +157,8 @@
             for gtg_id in gtg_removed:
                 rtm_id = gtg_to_rtm_id_dict[gtg_id]
                 rtm_task = filterAttr(self.rtm_list, 'id', rtm_id)
-                if len(rtm_task) > 0:
-                    self.update_substatus(_("Deleting ") + rtm_task[0].title)
-                    map(lambda task: task.delete(), rtm_task)
+                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:
@@ -168,10 +167,9 @@
             for rtm_id in rtm_removed:
                 gtg_id = rtm_to_gtg_id_dict[rtm_id]
                 gtg_task = filterAttr(self.gtg_list, 'id', gtg_id)
-                if len (gtg_task) > 0:
-                    self.update_substatus(_("Deleting ") + gtg_task[0].title)
-                    map(lambda task: task.delete(), gtg_task)
-                    gtg_common.discard(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

=== added directory 'GTG/plugins/tomboy'
=== added file 'GTG/plugins/tomboy.gtg-plugin'
--- GTG/plugins/tomboy.gtg-plugin	1970-01-01 00:00:00 +0000
+++ GTG/plugins/tomboy.gtg-plugin	2009-09-24 17:30:26 +0000
@@ -0,0 +1,7 @@
+[GTG Plugin]
+Module=tomboy
+Name=Tomboy plugin
+Description=This plugin lets you add as many links as you like to tomboy notes in your tasks.
+Authors=Luca Invernizzi <invernizzi.l@xxxxxxxxx>
+Version=0.1.1
+Enabled=True

=== added file 'GTG/plugins/tomboy/__init__.py'
--- GTG/plugins/tomboy/__init__.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/tomboy/__init__.py	2009-09-24 17:30:26 +0000
@@ -0,0 +1,20 @@
+# -*- 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())
+from tomboy import pluginTomboy

=== added file 'GTG/plugins/tomboy/combobox_enhanced.py'
--- GTG/plugins/tomboy/combobox_enhanced.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/tomboy/combobox_enhanced.py	2009-09-24 17:30:26 +0000
@@ -0,0 +1,76 @@
+# -*- 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 gtk
+import gobject
+
+def ifKeyPressedCallback( widget, key, callback):
+    def keyPress(combobox, event):
+        keyname = gtk.gdk.keyval_name(event.keyval)
+        if keyname == key:
+            callback()
+    widget.connect("key-press-event", keyPress)
+
+def ifClipboardTextIsInListCallback (clipboard_obj, list_obj, callback):
+    def clipboardCallback(clipboard_obj, text, list_obj):
+        if len(filter(lambda x: x == text, list_obj)) != 0:
+            callback(text)
+    clipboard_obj.request_text(clipboardCallback, list_obj)
+
+def listStoreFromList(list_obj):
+    list_store = gtk.ListStore(gobject.TYPE_STRING)
+    for elem in list_obj:
+        iter = list_store.append()
+        list_store.set(iter, 0, elem)
+    return list_store
+
+def completionFromListStore (list_store):
+    completion = gtk.EntryCompletion()
+    completion.set_minimum_key_length(0)
+    completion.set_text_column(0)
+    completion.set_inline_completion(True)
+    completion.set_model(list_store)
+    return completion
+
+def smartifyComboboxEntry(combobox, list_obj, callback):
+    entry = gtk.Entry()
+    #check if Clipboard contains an element of the list
+    clipboard = gtk.Clipboard()
+    ifClipboardTextIsInListCallback(clipboard, list_obj, entry.set_text)
+    #pressing Enter will cause the callback
+    ifKeyPressedCallback(entry, "Return", callback)
+    #wrap the combo-box if it's too long
+    if len(list_obj) > 15:
+        combobox.set_wrap_width(5)
+    #populate the combo-box
+    if len(list_obj) > 0:
+        list_store = listStoreFromList(list_obj)
+        entry.set_completion(completionFromListStore(list_store))
+        combobox.set_model(list_store)
+        combobox.set_active(0)
+    combobox.add(entry)
+    combobox.connect('changed', setText, entry )
+    #render the combo-box drop down menu
+    cell = gtk.CellRendererText()
+    combobox.pack_start(cell, True)
+    combobox.add_attribute(cell, 'text', 0) 
+    return entry
+
+def setText(combobox, entry):
+    model = combobox.get_model()
+    index = combobox.get_active()
+    if index > -1:
+        entry.set_text(model[index][0])
+

=== added file 'GTG/plugins/tomboy/tomboy.glade'
--- GTG/plugins/tomboy/tomboy.glade	1970-01-01 00:00:00 +0000
+++ GTG/plugins/tomboy/tomboy.glade	2009-09-24 17:30:26 +0000
@@ -0,0 +1,110 @@
+<?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="InsertNoteDialog">
+  <property name="width_request">300</property>
+  <property name="title" translatable="yes">Insert Note</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</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>
+  <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_cancel">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-cancel</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">-6</property>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="btn_add">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-add</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">0</property>
+	    </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="GtkLabel" id="label_caption">
+	  <property name="visible">True</property>
+	  <property name="label" translatable="yes">Insert the title of the tomboy note </property>
+	  <property name="use_underline">False</property>
+	  <property name="use_markup">False</property>
+	  <property name="justify">GTK_JUSTIFY_LEFT</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">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">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkComboBox" id="titles_combobox">
+	  <property name="visible">True</property>
+	  <property name="add_tearoffs">False</property>
+	  <property name="has_frame">True</property>
+	  <property name="focus_on_click">True</property>
+	</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 file 'GTG/plugins/tomboy/tomboy.py'
--- GTG/plugins/tomboy/tomboy.py	1970-01-01 00:00:00 +0000
+++ GTG/plugins/tomboy/tomboy.py	2009-09-24 17:30:26 +0000
@@ -0,0 +1,229 @@
+# -*- 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 gtk
+import gobject
+import os
+import sys
+import dbus
+from GTG import _
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+import combobox_enhanced
+
+
+class pluginTomboy:
+
+    def __init__(self):
+        #These tokens are used to identify the beginning and the end of the 
+        #tomboy note point of insertion
+        self.token_start = 'TOMBOY__'
+        self.token_end = '|'
+        self.path = os.path.dirname(os.path.abspath(__file__))
+
+    #Function called upon plug-in activation
+    def activate(self, plugin_api):
+        self.plugin_api = plugin_api
+
+    #Return a textual token to represent the Tomboy widget. It's useful
+    # since the task is saved as pure text
+    def widgetTotext(self, widget):
+        return self.token_start+ widget.tomboy_note_title+self.token_end
+
+    # Converts all tomboy note widgets in the  equivalent text
+    def onTaskClosed(self, plugin_api):
+        for anchor in self.anchors:
+            widgets = anchor.get_widgets()
+            if anchor.get_deleted():
+                #The note has been deleted, skip
+                continue
+            iter_start = self.textview.buff.get_iter_at_child_anchor(anchor)
+            iter_end = iter_start.copy()
+            iter_end.forward_char()
+            if type(widgets) == list and len(widgets) !=0:
+                #the anchor still contains a widget.
+                widget = widgets[0]
+                self.textview.buff.delete(iter_start, iter_end)
+                self.textview.buff.insert(iter_start,
+                                          self.widgetTotext(widget))
+
+    # adds a item(button) to the ToolBar, with a nice icon
+    def addButtonToToolbar(self, plugin_api):
+        tb_Taskbutton_image = gtk.Image()
+        tb_Taskbutton_image_path =\
+            "/usr/share/icons/hicolor/16x16/apps/tomboy.png"
+        tb_Taskbutton_pixbuf=gtk.gdk.\
+                pixbuf_new_from_file_at_size(tb_Taskbutton_image_path, 16, 16)
+        tb_Taskbutton_image.set_from_pixbuf(tb_Taskbutton_pixbuf)
+        tb_Taskbutton_image.show()
+        self.tb_Taskbutton = gtk.ToolButton(tb_Taskbutton_image)
+        self.tb_Taskbutton.set_label(_("Add Tomboy note"))
+        self.tb_Taskbutton.connect('clicked', self.onTbTaskButton, plugin_api)
+        plugin_api.add_task_toolbar_item(gtk.SeparatorToolItem())
+        plugin_api.add_task_toolbar_item(self.tb_Taskbutton)
+
+
+    # Converts all the textual tokens in tomboy note widgets
+    def convertTokensToWidgets(self):
+        self.anchors=[]
+        start_iter = self.textview.buff.get_start_iter()
+        end_iter = self.textview.buff.get_end_iter()
+        text = self.textview.buff.get_slice(start_iter, end_iter)
+        text_offset = 0
+        token_position = text.find(self.token_start)
+        token_ending = text.find(self.token_end, token_position)
+        while not token_position < 0 and not token_ending < 0:
+            #delete the text of the token
+            tomboy_note_title = text[token_position + len(self.token_start):
+                                     token_ending]
+            start_iter = self.textview.buff.get_iter_at_offset(text_offset +
+                                                               token_position)
+            end_iter = self.textview.buff.get_iter_at_offset(text_offset+
+                                                             token_ending+1)
+            self.textview.buff.delete(start_iter, end_iter)
+            #add the widget
+            widget =self.widgetCreate(tomboy_note_title)
+            anchor = self.textviewInsertWidget(widget, start_iter)
+            self.anchors.append(anchor)
+            #find the next
+            start_iter = self.textview.buff.get_iter_at_child_anchor(anchor)
+            start_iter.forward_char()
+            end_iter = self.textview.buff.get_end_iter()
+            text = self.textview.buff.get_slice(start_iter, end_iter)
+            text_offset = start_iter.get_offset()
+            token_position = text.find(self.token_start)
+            token_ending = text.find(self.token_end)
+
+    def onTaskOpened(self, plugin_api):
+        #NOTE: get_textview() only works in this function
+        # (see GTG/core/plugins/api.py docs)
+        self.textview = plugin_api.get_textview()
+        self.addButtonToToolbar(plugin_api)
+        self.convertTokensToWidgets()
+
+    def deactivate(self, plugin_api):
+        #nothing to do at all 
+        pass
+
+    def close_dialog(self, widget, data=None):
+        self.dialog.destroy()
+        return True
+
+    #opens a dbus connection to tomboy
+    def getTomboyObject(self):
+        bus = dbus.SessionBus()
+        obj = bus.get_object("org.gnome.Tomboy",
+                               "/org/gnome/Tomboy/RemoteControl")
+        return dbus.Interface(obj, "org.gnome.Tomboy.RemoteControl")
+
+    #gets the list of the titles of the notes
+    def getTomboyNoteTitleList(self):
+        tomboy = self.getTomboyObject()
+        return map(lambda note: str(tomboy.GetNoteTitle(note)),
+                   tomboy.ListAllNotes())
+
+    def onTbTaskButton(self, widget, plugin_api):
+        title_list = self.getTomboyNoteTitleList()
+        #Create the dialog
+        glade_file = os.path.join(self.path, "tomboy.glade")
+        wTree = gtk.glade.XML(glade_file, "InsertNoteDialog")
+        #objects
+        self.dialog = wTree.get_widget("InsertNoteDialog")
+        btn_add = wTree.get_widget("btn_add")
+        btn_cancel = wTree.get_widget("btn_cancel")
+        self.combobox = wTree.get_widget("titles_combobox")
+        self.label_caption = wTree.get_widget("label_caption")
+        #connections
+        self.dialog.connect("delete_event", self.close_dialog)
+        btn_cancel.connect("clicked", self.close_dialog)
+        btn_add.connect("clicked", self.noteChosen)
+        self.combobox_entry = combobox_enhanced.\
+                smartifyComboboxEntry(self.combobox,title_list,self.noteChosen)
+        self.dialog.show_all()
+
+    #A title has been chosen by the user. If the note exists, it will be 
+    # linked, otherwise the user will have the option to create the note.
+    def noteChosen(self, widget=None, data=None):
+        tomboy = self.getTomboyObject()
+        supposed_title = self.combobox_entry.get_text()
+        if filter(lambda x: tomboy.GetNoteTitle(x)==supposed_title,
+                  tomboy.ListAllNotes()) == []:
+            self.label_caption.set_text(_("That note does not exist!"))
+            dialog = gtk.MessageDialog(parent = self.dialog,
+                                       flags = gtk.DIALOG_DESTROY_WITH_PARENT,
+                                       type = gtk.MESSAGE_QUESTION,
+                                       buttons=gtk.BUTTONS_YES_NO,
+                                       message_format=_("That note does not \
+exist. Do you want to create a new one?"))
+            response = dialog.run() 
+            dialog.destroy()
+            if response == gtk.RESPONSE_YES:
+                tomboy.CreateNamedNote(supposed_title)
+            else:
+                return
+        #note insertion
+        mark_start = self.textview.buff.get_insert()
+        iter_start = self.textview.buff.get_iter_at_mark(mark_start)
+        tomboy_widget =self.widgetCreate(supposed_title)
+        anchor = self.textviewInsertWidget(tomboy_widget, iter_start)
+        self.anchors.append(anchor)
+        self.dialog.destroy()
+
+    #Opens a note in tomboy application via dbus
+    def tomboyDisplayNote(self, widget, data = None):
+        tomboy = self.getTomboyObject()
+        note = tomboy.FindNote(widget.tomboy_note_title)
+        tomboy.DisplayNote(note)
+
+    #inserts a widget in the textview
+    def textviewInsertWidget(self, widget, iter):
+        anchor = self.textview.buff.create_child_anchor(iter)
+        widget.show()
+        self.textview.add_child_at_anchor(widget, anchor)
+        return anchor
+
+    #creates the tomboy widget
+    def widgetCreate(self, tomboy_note_title):
+        image = gtk.Image()
+        image_path = "/usr/share/icons/hicolor/16x16/apps/tomboy.png"
+        pixbuf=gtk.gdk.\
+                pixbuf_new_from_file_at_size(image_path, 16, 16)
+        image.show()
+        image.set_from_pixbuf(pixbuf)
+        image.set_alignment(0.5,1.0)
+        label = gtk.Label()
+        label.show()
+        label.set_alignment(0.5, 1.0)
+        eventbox = gtk.EventBox()
+        eventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK)
+        eventbox.connect('button_press_event', self.tomboyDisplayNote)
+        eventbox.show()
+        window = self.plugin_api.get_window()
+        hbox = gtk.HBox()
+        hbox.show()
+        hbox.add(image)
+        hbox.add(label)
+        eventbox.add(hbox)
+        window.realize()
+        style=window.get_style()
+        color = str(style.text[gtk.STATE_PRELIGHT])
+        label.set_markup("<span underline='low' color='" + color +"'>" + tomboy_note_title + "</span>")
+        eventbox.tomboy_note_title = tomboy_note_title
+        #cursor changes to a hand
+        def realize_callback(widget):
+            eventbox.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
+        eventbox.connect("realize", realize_callback)
+        return eventbox
+

=== modified file 'GTG/taskeditor/editor.py'
--- GTG/taskeditor/editor.py	2009-09-24 14:51:45 +0000
+++ GTG/taskeditor/editor.py	2009-09-24 17:30:26 +0000
@@ -479,6 +479,7 @@
     #Will be linked to this destruction method that will save the task
     def destruction(self,a=None) :#pylint: disable-msg=W0613
         #Save should be also called when buffer is modified
+        self.pengine.onTaskClose(self.plugins, self.te_plugin_api)
         self.save()
         self.closing(self.task.get_id())
         

=== modified file 'GTG/taskeditor/taskview.py'
--- GTG/taskeditor/taskview.py	2009-09-24 15:45:17 +0000
+++ GTG/taskeditor/taskview.py	2009-09-24 17:30:26 +0000
@@ -39,8 +39,7 @@
 separators = [' ', '.', ',', '/', '\n', '\t', '!', '?', ';', '\0']
 url_separators = [' ', ',', '\n', '\t', '\0']
 
-bullet1_ltr = '→'
-bullet1_rtl = '←'
+bullet1 = '→'
 bullet2 = '↳'
 
 
@@ -151,11 +150,6 @@
         self.modified_sigid = self.buff.connect("changed" , self.modified)
         self.connect("backspace",self.backspace)
         self.tobe_refreshed = False
-        
-        if self.get_direction() == gtk.TEXT_DIR_RTL :
-            self.bullet1 = bullet1_rtl
-        else :
-            self.bullet1 = bullet1_ltr
 
     
     #This function is called to refresh the editor 
@@ -883,7 +877,7 @@
         indentation = indentation + (level-1)*spaces
         #adding the symbol 
         if level == 1 :
-            indentation = "%s%s "%(indentation,self.bullet1)
+            indentation = "%s%s "%(indentation,bullet1)
         buff.insert(itera,indentation)
         indenttag = self.create_indent_tag(buff,level)
         self.__apply_tag_to_mark(start,end,tag=indenttag)
@@ -985,7 +979,7 @@
                 #Then, if indent > 0, we increment it
                 #First step : we preserve it.
                 else :
-                    if not line.lstrip("%s "%self.bullet1) :
+                    if not line.lstrip("%s "%bullet1) :
                         self.deindent(itera,newlevel=0)
                         tv.emit_stop_by_name('insert-text')
                         

=== modified file 'locales/gtg.pot'
--- locales/gtg.pot	2009-09-12 23:03:15 +0000
+++ locales/gtg.pot	2009-09-24 17:30:26 +0000
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-09-13 01:03+0200\n"
+"POT-Creation-Date: 2009-09-18 15:51+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"
@@ -632,6 +632,22 @@
 msgid "due"
 msgstr ""
 
+#: GTG/plugins/tomboy/tomboy.py:67
+msgid "Add Tomboy note"
+msgstr ""
+
+#: GTG/plugins/tomboy/tomboy.py:170
+msgid "That note does not exist!"
+msgstr ""
+
+#: GTG/plugins/tomboy/tomboy.glade.h:1
+msgid "Insert Note"
+msgstr ""
+
+#: GTG/plugins/tomboy/tomboy.glade.h:2
+msgid "Insert the title of the tomboy note "
+msgstr ""
+
 #: GTG/plugins/rtm_sync/utility.py:54
 msgid "saving critical object failed"
 msgstr ""
@@ -666,11 +682,11 @@
 "now. When done, press OK"
 msgstr ""
 
-#: GTG/plugins/rtm_sync/pyrtm/rtm.py:56
+#: GTG/plugins/rtm_sync/pyrtm/rtm.py:57
 msgid "Invalid state"
 msgstr ""
 
-#: GTG/plugins/rtm_sync/pyrtm/rtm.py:105
+#: GTG/plugins/rtm_sync/pyrtm/rtm.py:106
 msgid "API call failed"
 msgstr ""
 


Follow ups