GTG

Merge lp:~gtg-user/gtg/tomboy into lp:~gtg/gtg/old-trunk

Proposed by Luca Invernizzi
Status: Superseded
Proposed branch: lp:~gtg-user/gtg/tomboy
Merge into: lp:~gtg/gtg/old-trunk
Diff against target: 822 lines
16 files modified
.bzrignore (+2/-0)
GTG/core/plugins/api.py (+5/-1)
GTG/core/plugins/engine.py (+15/-4)
GTG/core/plugins/manager.py (+1/-7)
GTG/info.py (+2/-1)
GTG/plugins/rtm-sync.gtg-plugin (+2/-2)
GTG/plugins/rtm_sync/pyrtm/rtm.py (+5/-24)
GTG/plugins/rtm_sync/syncengine.py (+9/-11)
GTG/plugins/tomboy.gtg-plugin (+7/-0)
GTG/plugins/tomboy/__init__.py (+20/-0)
GTG/plugins/tomboy/combobox_enhanced.py (+76/-0)
GTG/plugins/tomboy/tomboy.glade (+110/-0)
GTG/plugins/tomboy/tomboy.py (+229/-0)
GTG/taskeditor/editor.py (+1/-0)
GTG/taskeditor/taskview.py (+3/-9)
locales/gtg.pot (+19/-3)
To merge this branch: bzr merge lp:~gtg-user/gtg/tomboy
Reviewer Review Type Date Requested Status
Gtg developers Pending
Review via email: mp+12056@code.launchpad.net

This proposal has been superseded by a proposal from 2009-09-24.

To post a comment you must log in.
Revision history for this message
Luca Invernizzi (invernizzi) wrote :

Tomboy notes support (via dbus).
A toolbar button displaying the Tomboy icon will appear in each task editor.
Clicking that, one can add a tomboy note just writing the note title in the combo box shown (which supports autocompletion). A new tomboy icon will appear in the task text, where the cursor was.

lp:~gtg-user/gtg/tomboy updated
319. By Lionel Dricot

instant apply plugins in the manager

320. By Bertrand Rousseau

Upgrade RTM plugin

321. By Lionel Dricot

left arrow for RTL locale bug #412578

322. By Lionel Dricot

non started tasks and tags now respect workview bug #409900

323. By Lionel Dricot

refresh task editors bug #339176

324. By Lionel Dricot

personal spelling (bug #427384) and better explanations in the first page (bug #400831)

325. By Lionel Dricot

allow capitalized days in quickadd bug #414116

326. By Lionel Dricot

loop when starting a new task

327. By Lionel Dricot

Solving crazy behaviour of the editor : bug #398651

It was really easier than I thought. I see two possible explanations :

1) I'm a semi-god

2) Our architecture is not bad at all once you know it, you can do a lot of
stuffs, even with the taskview madness.

328. By Lionel Dricot

fix subtask oddities in editor, bug #343251

329. By Lionel Dricot

plays well with text besides icons bug #406420

330. By Lionel Dricot

Don't save unmodified new tasks bug #398658

331. By Lionel Dricot

Fixed tags appearing twice (bug #439368 )

This was one of the most difficult and non-intuitive bug I had to fix in a
long time.

Root of the problem : the end of the title tag was not at the end of the title
line but on the beginning of the next line. Nothing wrong but the fact that a
"\n" was included in the title.

For a reason I don't understand, this led to consider the tag to be sometimes
part of the title, thus adding a new "first line".

Fixed by simply putting the end of title tag at the end of the title line.

332. By Lionel Dricot

all your tasks were deleted by the previous commit ! Also fix bugs #437509 and #341054

333. By Bertrand Rousseau <rousseau@lunchbox>

Merge tomboy plug-in update from Luca

334. By Lionel Dricot

this fixes a shitload of unreported bug in the editor, specially when playing with subtasks and tags. Please test it extensively and report bugs

335. By Lionel Dricot

Fixes bug #358785 and bug #440032

Nevertheless, the refresh is still not perfect. There should be a refresh for
both the tags and tasks when everything is loaded

336. By Lionel Dricot

 * Close calendar on single click (bug #343063 )
 * Patch from mrk to improve compatibility with non-GNU unix systems (bug
#430816
)

337. By Luca Falavigna

Update .pot file

338. By Luca Falavigna

Python 2.5 backward compatibility fixes

339. By Luca Falavigna

Use /usr/bin/python as shebang

340. By Lionel Dricot

Fixes bug #355293 bug #353814 and save open/close status of tasks (but does
    not restore the position, see bug #339852 )

341. By Lionel Dricot

tomboy merge

342. By Bertrand Rousseau

Change editor default size and fixes expanding properties of the bottom bar

343. By Lionel Dricot

fixes a lot of traceback but still very funky with threads

344. By Lionel Dricot

This is a very high experimental commit I want you to try out.

I've heavily refactored datastore so it uses one thread by backend, not
anymore one thread by tasks. It might be more efficient.

There are a lot of profiling to do as time increase with the number of stored
tasks. One low hanging fruit is to do a lot less with closed tasks instead of
considering as first class citizen.

345. By Lionel Dricot

new tasks were deleted if not modified, even children

346. By Lionel Dricot

This revision introduce a symetric relation between tasks and tags.

This allows a lot of optimizations (some are already done). If you find a
function where every task is parsed to check for a given tag, change it to
directly ask the tag for associated tasks

347. By Lionel Dricot

some fixes

348. By Bertrand Rousseau <rousseau@lunchbox>

Fixes bug #445640: Traceback when launching GTG. Also fixes Threadless mode.

349. By Luca Falavigna

Revert r339 to avoid breaking compatibility with non-Linux systems
where python interpreter is not in /usr/bin (LP: #448175)

350. By Lionel Dricot <ploum@spoutnik>

kill the tasks appearing twice

351. By Luca Invernizzi <luca@phoenix>

Bug #448114 fix

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2009-08-24 10:01:19 +0000
3+++ .bzrignore 2009-09-24 17:28:10 +0000
4@@ -37,3 +37,5 @@
5 _trial_temp
6 doc/api
7 **/**.glade.h
8+**/**.gladep
9+**/**.gladep.bak
10
11=== modified file 'GTG/core/plugins/api.py'
12--- GTG/core/plugins/api.py 2009-09-24 13:15:17 +0000
13+++ GTG/core/plugins/api.py 2009-09-24 17:28:10 +0000
14@@ -236,6 +236,10 @@
15 def show_window(self):
16 """Shows the main GTG window (task browser)"""
17 self.__window.show()
18+
19+ def get_window(self):
20+ """Returns the window for which the plug-in has been created"""
21+ return self.__window
22 #=== General Methods ==========================================================
23
24
25@@ -426,4 +430,4 @@
26 """
27 if func in self.__filter_cbs:
28 self.__filter_cbs.remove(func)
29-#=== Filtering methods ========================================================
30\ No newline at end of file
31+#=== Filtering methods ========================================================
32
33=== modified file 'GTG/core/plugins/engine.py'
34--- GTG/core/plugins/engine.py 2009-09-24 13:15:17 +0000
35+++ GTG/core/plugins/engine.py 2009-09-24 17:28:10 +0000
36@@ -176,9 +176,8 @@
37 # deactivate the enabled plugins
38 def deactivatePlugins(self, plugins, plugin_api):
39 for plugin in plugins:
40- if not plugin['state'] and not plugin['error'] and plugin['active']:
41+ if plugin['state'] and not plugin['error'] and plugin['active']:
42 plugin['instance'].deactivate(plugin_api)
43- plugin['instance'] = None
44 plugin['active'] = False
45
46 # loads the plug-in features for a task
47@@ -187,19 +186,31 @@
48 if plugin['state'] and plugin['active']:
49 plugin['instance'].onTaskOpened(plugin_api)
50
51+ # signals to the plug-ins that the task window is being closed
52+ def onTaskClose(self, plugins, plugin_api):
53+ for plugin in plugins:
54+ if plugin['state'] and plugin['active']:
55+ if hasattr(plugin['instance'],'onTaskClosed'):
56+ plugin['instance'].onTaskClosed(plugin_api)
57+
58 # rechecks the plug-ins to see if any changes where done to the state
59 def recheckPlugins(self, plugins, plugin_api):
60 for plugin in plugins:
61 if plugin['instance'] != None and plugin['state'] == False and plugin['active']:
62 try:
63- self.deactivatePlugins([plugin],plugin_api)
64+ #print "deactivating plugin: " + plgin['name']
65+ plugin['instance'].deactivate(plugin_api)
66+ plugin['instance'] = None
67+ plugin['active'] = False
68 except Exception, e:
69 print "Error: %s" % e
70 elif plugin['instance'] == None and plugin['state'] == True and not plugin['active']:
71 try:
72 #print "activating plugin: " + plgin['name']
73 if not plugin['error']:
74- self.activatePlugins([plugin],plugin_api)
75+ plugin['instance'] = plugin['class']()
76+ plugin['instance'].activate(plugin_api)
77+ plugin['active'] = True
78 else:
79 plugin['state'] = False
80 except Exception, e:
81
82=== modified file 'GTG/core/plugins/manager.py'
83--- GTG/core/plugins/manager.py 2009-09-24 13:15:17 +0000
84+++ GTG/core/plugins/manager.py 2009-09-24 17:28:10 +0000
85@@ -134,12 +134,6 @@
86 for plgin in self.plugins:
87 if model[path][1] == plgin['name'] and model[path][2] == plgin['version']:
88 plgin['state'] = not plgin['state']
89- #we instantly apply the plugin activation/deactivation
90- #to respect HIG
91- if plgin['state'] :
92- self.pengine.activatePlugins([plgin], self.plugin_api)
93- else :
94- self.pengine.deactivatePlugins([plgin], self.plugin_api)
95
96
97 def pluginExtraInfo(self, treeview, plugins):
98@@ -215,4 +209,4 @@
99 self.config_btn.set_sensitive(False)
100
101 def plugin_configure_dialog(self, widget, data=None):
102- self.current_plugin['instance'].configure_dialog(self.plugin_api)
103+ self.current_plugin['instance'].configure_dialog(self.plugin_api)
104\ No newline at end of file
105
106=== modified file 'GTG/info.py'
107--- GTG/info.py 2009-08-29 14:17:46 +0000
108+++ GTG/info.py 2009-09-24 17:28:10 +0000
109@@ -26,6 +26,7 @@
110 "\tJean-François Fortin Tam <nekohayo@gmail.com>", \
111 "\tJonathan Lange <jml@mumak.net>", \
112 "\tPaulo Cabido <paulo.cabido@gmail.com>", \
113+ "\tLuca Invernizzi <invernizzi.l@gmail.com>", \
114 ]
115 ARTISTS = ["Kalle Persson <kalle@kallepersson.se>", \
116 "Bertrand Rousseau <bertrand.rousseau@gmail.com>"]
117@@ -40,7 +41,7 @@
118 Finnish: Mika Tapojärvi
119 French: Lionel Dricot, Rafik Ouerchefani, Bertrand Rousseau, Pititjo
120 German: Philip Stewart, Thomas Pitlik
121-Italian: Luca Falavigna
122+Italian: Luca Falavigna, Luca Invernizzi
123 Malay: melayubuntu
124 Polish: Tomasz Maciejewski
125 Portuguese: Paulo Cabido
126
127=== modified file 'GTG/plugins/rtm-sync.gtg-plugin'
128--- GTG/plugins/rtm-sync.gtg-plugin 2009-09-20 14:10:30 +0000
129+++ GTG/plugins/rtm-sync.gtg-plugin 2009-09-24 17:28:10 +0000
130@@ -3,6 +3,6 @@
131 Name=Remember the milk
132 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.
133 Authors=Luca Invernizzi <invernizzi.l@gmail.com>
134-Version=0.1.2
135-Dependencies=python-xml,python-simplejson
136+Version=0.1.1
137+Dependencies=minidom,subprocess,BaseDirectory,threading,gobject,gtk,time,pickle,datetime,logging,md5,urllib,warnings,simplejson
138 Enabled=False
139
140=== modified file 'GTG/plugins/rtm_sync/pyrtm/rtm.py'
141--- GTG/plugins/rtm_sync/pyrtm/rtm.py 2009-09-20 14:10:30 +0000
142+++ GTG/plugins/rtm_sync/pyrtm/rtm.py 2009-09-24 17:28:10 +0000
143@@ -12,10 +12,8 @@
144 import warnings
145 import urllib
146 import logging
147-import time
148 from hashlib import md5
149 from GTG import _
150-import httplib
151
152 warnings.simplefilter('default', ImportWarning)
153
154@@ -93,7 +91,7 @@
155 params['format'] = 'json'
156 params['api_sig'] = self._sign(params)
157
158- json = openURL(SERVICE_URL, params)
159+ json = openURL(SERVICE_URL, params).read()
160
161 LOG.debug("JSON response: \n%s" % json)
162
163@@ -180,28 +178,11 @@
164 for key in keys:
165 yield key, dictionary[key]
166
167-def openURL(url, queryArgs = None):
168+def openURL(url, queryArgs=None):
169 if queryArgs:
170 url = url + '?' + urllib.urlencode(queryArgs)
171- LOG.debug("URL> %s", url)
172- time_to_wait = 0
173- while True:
174- try:
175- if time_to_wait !=0:
176- time.sleep(time_to_wait)
177- http_connection = httplib.HTTPConnection("api.rememberthemilk.com",80)
178- http_connection.request("GET", url)
179- http_response = http_connection.getresponse()
180- http_response_data = http_response.read()
181- break
182- except httplib.IncompleteRead as exception:
183- #rtm server issues incomplete responses if we hammer it too much
184- # this way we can be fast *and* safe
185- if time_to_wait == 0:
186- time_to_wait = 2
187- else:
188- raise exception
189- return http_response_data
190+ LOG.debug("URL> %s", url)
191+ return urllib.urlopen(url)
192
193 class dottedDict(object):
194 "Make dictionary items accessible via the object-dot notation."
195@@ -267,7 +248,7 @@
196 'getList':
197 [(), ()],
198 'removeContact':
199- [('timeline', 'group_id', 'contact_id'), ()]
200+ [('timeline', 'group_id', 'contact_id'), ()],
201 },
202 'lists': {
203 'add':
204
205=== modified file 'GTG/plugins/rtm_sync/syncengine.py'
206--- GTG/plugins/rtm_sync/syncengine.py 2009-09-21 17:30:00 +0000
207+++ GTG/plugins/rtm_sync/syncengine.py 2009-09-24 17:28:10 +0000
208@@ -90,11 +90,11 @@
209 try:
210 self.synchronizeWorker()
211 except rtm.RTMAPIError as exception:
212- self.close_gui(exception)
213+ self.close_gui(exception.message)
214 except rtm.RTMError as exception:
215- self.close_gui(exception)
216- except Exception as exception:
217- self.close_gui(_("Synchronization failed." + str(exception)))
218+ self.close_gui(exception.message)
219+ except:
220+ self.close_gui(_("Synchronization failed."))
221
222 def synchronizeWorker(self):
223 self.update_status(_("Downloading task list..."))
224@@ -157,9 +157,8 @@
225 for gtg_id in gtg_removed:
226 rtm_id = gtg_to_rtm_id_dict[gtg_id]
227 rtm_task = filterAttr(self.rtm_list, 'id', rtm_id)
228- if len(rtm_task) > 0:
229- self.update_substatus(_("Deleting ") + rtm_task[0].title)
230- map(lambda task: task.delete(), rtm_task)
231+ self.update_substatus(_("Deleting ") + rtm_task.title)
232+ map(lambda task: task.delete(), rtm_task)
233
234 #Delete from gtg the tasks that have been removed in rtm
235 if len(rtm_removed) > 0:
236@@ -168,10 +167,9 @@
237 for rtm_id in rtm_removed:
238 gtg_id = rtm_to_gtg_id_dict[rtm_id]
239 gtg_task = filterAttr(self.gtg_list, 'id', gtg_id)
240- if len (gtg_task) > 0:
241- self.update_substatus(_("Deleting ") + gtg_task[0].title)
242- map(lambda task: task.delete(), gtg_task)
243- gtg_common.discard(gtg_id)
244+ self.update_substatus(_("Deleting ") + gtg_task.title)
245+ map(lambda task: task.delete(), gtg_task)
246+ gtg_common.discard(gtg_id)
247
248 #tasks that must be added to RTM
249 #NOTE: should we check if the title is already present in the
250
251=== added directory 'GTG/plugins/tomboy'
252=== added file 'GTG/plugins/tomboy.gtg-plugin'
253--- GTG/plugins/tomboy.gtg-plugin 1970-01-01 00:00:00 +0000
254+++ GTG/plugins/tomboy.gtg-plugin 2009-09-24 17:28:10 +0000
255@@ -0,0 +1,7 @@
256+[GTG Plugin]
257+Module=tomboy
258+Name=Tomboy plugin
259+Description=This plugin lets you add as many links as you like to tomboy notes in your tasks.
260+Authors=Luca Invernizzi <invernizzi.l@gmail.com>
261+Version=0.1.1
262+Enabled=True
263
264=== added file 'GTG/plugins/tomboy/__init__.py'
265--- GTG/plugins/tomboy/__init__.py 1970-01-01 00:00:00 +0000
266+++ GTG/plugins/tomboy/__init__.py 2009-09-24 17:28:10 +0000
267@@ -0,0 +1,20 @@
268+# -*- coding: utf-8 -*-
269+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@gmail.com>
270+#
271+# This program is free software: you can redistribute it and/or modify it under
272+# the terms of the GNU General Public License as published by the Free Software
273+# Foundation, either version 3 of the License, or (at your option) any later
274+# version.
275+#
276+# This program is distributed in the hope that it will be useful, but WITHOUT
277+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
278+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
279+# details.
280+#
281+# You should have received a copy of the GNU General Public License along with
282+# this program. If not, see <http://www.gnu.org/licenses/>.
283+
284+import sys
285+import os
286+sys.path.insert(0, os.getcwd())
287+from tomboy import pluginTomboy
288
289=== added file 'GTG/plugins/tomboy/combobox_enhanced.py'
290--- GTG/plugins/tomboy/combobox_enhanced.py 1970-01-01 00:00:00 +0000
291+++ GTG/plugins/tomboy/combobox_enhanced.py 2009-09-24 17:28:10 +0000
292@@ -0,0 +1,76 @@
293+# -*- coding: utf-8 -*-
294+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@gmail.com>
295+#
296+# This program is free software: you can redistribute it and/or modify it under
297+# the terms of the GNU General Public License as published by the Free Software
298+# Foundation, either version 3 of the License, or (at your option) any later
299+# version.
300+#
301+# This program is distributed in the hope that it will be useful, but WITHOUT
302+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
303+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
304+# details.
305+#
306+# You should have received a copy of the GNU General Public License along with
307+# this program. If not, see <http://www.gnu.org/licenses/>.
308+import gtk
309+import gobject
310+
311+def ifKeyPressedCallback( widget, key, callback):
312+ def keyPress(combobox, event):
313+ keyname = gtk.gdk.keyval_name(event.keyval)
314+ if keyname == key:
315+ callback()
316+ widget.connect("key-press-event", keyPress)
317+
318+def ifClipboardTextIsInListCallback (clipboard_obj, list_obj, callback):
319+ def clipboardCallback(clipboard_obj, text, list_obj):
320+ if len(filter(lambda x: x == text, list_obj)) != 0:
321+ callback(text)
322+ clipboard_obj.request_text(clipboardCallback, list_obj)
323+
324+def listStoreFromList(list_obj):
325+ list_store = gtk.ListStore(gobject.TYPE_STRING)
326+ for elem in list_obj:
327+ iter = list_store.append()
328+ list_store.set(iter, 0, elem)
329+ return list_store
330+
331+def completionFromListStore (list_store):
332+ completion = gtk.EntryCompletion()
333+ completion.set_minimum_key_length(0)
334+ completion.set_text_column(0)
335+ completion.set_inline_completion(True)
336+ completion.set_model(list_store)
337+ return completion
338+
339+def smartifyComboboxEntry(combobox, list_obj, callback):
340+ entry = gtk.Entry()
341+ #check if Clipboard contains an element of the list
342+ clipboard = gtk.Clipboard()
343+ ifClipboardTextIsInListCallback(clipboard, list_obj, entry.set_text)
344+ #pressing Enter will cause the callback
345+ ifKeyPressedCallback(entry, "Return", callback)
346+ #wrap the combo-box if it's too long
347+ if len(list_obj) > 15:
348+ combobox.set_wrap_width(5)
349+ #populate the combo-box
350+ if len(list_obj) > 0:
351+ list_store = listStoreFromList(list_obj)
352+ entry.set_completion(completionFromListStore(list_store))
353+ combobox.set_model(list_store)
354+ combobox.set_active(0)
355+ combobox.add(entry)
356+ combobox.connect('changed', setText, entry )
357+ #render the combo-box drop down menu
358+ cell = gtk.CellRendererText()
359+ combobox.pack_start(cell, True)
360+ combobox.add_attribute(cell, 'text', 0)
361+ return entry
362+
363+def setText(combobox, entry):
364+ model = combobox.get_model()
365+ index = combobox.get_active()
366+ if index > -1:
367+ entry.set_text(model[index][0])
368+
369
370=== added file 'GTG/plugins/tomboy/tomboy.glade'
371--- GTG/plugins/tomboy/tomboy.glade 1970-01-01 00:00:00 +0000
372+++ GTG/plugins/tomboy/tomboy.glade 2009-09-24 17:28:10 +0000
373@@ -0,0 +1,110 @@
374+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
375+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
376+
377+<glade-interface>
378+
379+<widget class="GtkDialog" id="InsertNoteDialog">
380+ <property name="width_request">300</property>
381+ <property name="title" translatable="yes">Insert Note</property>
382+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
383+ <property name="window_position">GTK_WIN_POS_NONE</property>
384+ <property name="modal">False</property>
385+ <property name="resizable">True</property>
386+ <property name="destroy_with_parent">False</property>
387+ <property name="decorated">True</property>
388+ <property name="skip_taskbar_hint">False</property>
389+ <property name="skip_pager_hint">False</property>
390+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
391+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
392+ <property name="focus_on_map">True</property>
393+ <property name="urgency_hint">False</property>
394+ <property name="has_separator">True</property>
395+
396+ <child internal-child="vbox">
397+ <widget class="GtkVBox" id="dialog-vbox1">
398+ <property name="visible">True</property>
399+ <property name="homogeneous">False</property>
400+ <property name="spacing">0</property>
401+
402+ <child internal-child="action_area">
403+ <widget class="GtkHButtonBox" id="dialog-action_area1">
404+ <property name="visible">True</property>
405+ <property name="layout_style">GTK_BUTTONBOX_END</property>
406+
407+ <child>
408+ <widget class="GtkButton" id="btn_cancel">
409+ <property name="visible">True</property>
410+ <property name="can_default">True</property>
411+ <property name="can_focus">True</property>
412+ <property name="label">gtk-cancel</property>
413+ <property name="use_stock">True</property>
414+ <property name="relief">GTK_RELIEF_NORMAL</property>
415+ <property name="focus_on_click">True</property>
416+ <property name="response_id">-6</property>
417+ </widget>
418+ </child>
419+
420+ <child>
421+ <widget class="GtkButton" id="btn_add">
422+ <property name="visible">True</property>
423+ <property name="can_default">True</property>
424+ <property name="can_focus">True</property>
425+ <property name="label">gtk-add</property>
426+ <property name="use_stock">True</property>
427+ <property name="relief">GTK_RELIEF_NORMAL</property>
428+ <property name="focus_on_click">True</property>
429+ <property name="response_id">0</property>
430+ </widget>
431+ </child>
432+ </widget>
433+ <packing>
434+ <property name="padding">0</property>
435+ <property name="expand">False</property>
436+ <property name="fill">True</property>
437+ <property name="pack_type">GTK_PACK_END</property>
438+ </packing>
439+ </child>
440+
441+ <child>
442+ <widget class="GtkLabel" id="label_caption">
443+ <property name="visible">True</property>
444+ <property name="label" translatable="yes">Insert the title of the tomboy note </property>
445+ <property name="use_underline">False</property>
446+ <property name="use_markup">False</property>
447+ <property name="justify">GTK_JUSTIFY_LEFT</property>
448+ <property name="wrap">False</property>
449+ <property name="selectable">False</property>
450+ <property name="xalign">0.5</property>
451+ <property name="yalign">0.5</property>
452+ <property name="xpad">0</property>
453+ <property name="ypad">0</property>
454+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
455+ <property name="width_chars">-1</property>
456+ <property name="single_line_mode">False</property>
457+ <property name="angle">0</property>
458+ </widget>
459+ <packing>
460+ <property name="padding">0</property>
461+ <property name="expand">False</property>
462+ <property name="fill">False</property>
463+ </packing>
464+ </child>
465+
466+ <child>
467+ <widget class="GtkComboBox" id="titles_combobox">
468+ <property name="visible">True</property>
469+ <property name="add_tearoffs">False</property>
470+ <property name="has_frame">True</property>
471+ <property name="focus_on_click">True</property>
472+ </widget>
473+ <packing>
474+ <property name="padding">0</property>
475+ <property name="expand">True</property>
476+ <property name="fill">True</property>
477+ </packing>
478+ </child>
479+ </widget>
480+ </child>
481+</widget>
482+
483+</glade-interface>
484
485=== added file 'GTG/plugins/tomboy/tomboy.py'
486--- GTG/plugins/tomboy/tomboy.py 1970-01-01 00:00:00 +0000
487+++ GTG/plugins/tomboy/tomboy.py 2009-09-24 17:28:10 +0000
488@@ -0,0 +1,229 @@
489+# -*- coding: utf-8 -*-
490+# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@gmail.com>
491+#
492+# This program is free software: you can redistribute it and/or modify it under
493+# the terms of the GNU General Public License as published by the Free Software
494+# Foundation, either version 3 of the License, or (at your option) any later
495+# version.
496+#
497+# This program is distributed in the hope that it will be useful, but WITHOUT
498+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
499+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
500+# details.
501+#
502+# You should have received a copy of the GNU General Public License along with
503+# this program. If not, see <http://www.gnu.org/licenses/>.
504+
505+import gtk
506+import gobject
507+import os
508+import sys
509+import dbus
510+from GTG import _
511+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
512+import combobox_enhanced
513+
514+
515+class pluginTomboy:
516+
517+ def __init__(self):
518+ #These tokens are used to identify the beginning and the end of the
519+ #tomboy note point of insertion
520+ self.token_start = 'TOMBOY__'
521+ self.token_end = '|'
522+ self.path = os.path.dirname(os.path.abspath(__file__))
523+
524+ #Function called upon plug-in activation
525+ def activate(self, plugin_api):
526+ self.plugin_api = plugin_api
527+
528+ #Return a textual token to represent the Tomboy widget. It's useful
529+ # since the task is saved as pure text
530+ def widgetTotext(self, widget):
531+ return self.token_start+ widget.tomboy_note_title+self.token_end
532+
533+ # Converts all tomboy note widgets in the equivalent text
534+ def onTaskClosed(self, plugin_api):
535+ for anchor in self.anchors:
536+ widgets = anchor.get_widgets()
537+ if anchor.get_deleted():
538+ #The note has been deleted, skip
539+ continue
540+ iter_start = self.textview.buff.get_iter_at_child_anchor(anchor)
541+ iter_end = iter_start.copy()
542+ iter_end.forward_char()
543+ if type(widgets) == list and len(widgets) !=0:
544+ #the anchor still contains a widget.
545+ widget = widgets[0]
546+ self.textview.buff.delete(iter_start, iter_end)
547+ self.textview.buff.insert(iter_start,
548+ self.widgetTotext(widget))
549+
550+ # adds a item(button) to the ToolBar, with a nice icon
551+ def addButtonToToolbar(self, plugin_api):
552+ tb_Taskbutton_image = gtk.Image()
553+ tb_Taskbutton_image_path =\
554+ "/usr/share/icons/hicolor/16x16/apps/tomboy.png"
555+ tb_Taskbutton_pixbuf=gtk.gdk.\
556+ pixbuf_new_from_file_at_size(tb_Taskbutton_image_path, 16, 16)
557+ tb_Taskbutton_image.set_from_pixbuf(tb_Taskbutton_pixbuf)
558+ tb_Taskbutton_image.show()
559+ self.tb_Taskbutton = gtk.ToolButton(tb_Taskbutton_image)
560+ self.tb_Taskbutton.set_label(_("Add Tomboy note"))
561+ self.tb_Taskbutton.connect('clicked', self.onTbTaskButton, plugin_api)
562+ plugin_api.add_task_toolbar_item(gtk.SeparatorToolItem())
563+ plugin_api.add_task_toolbar_item(self.tb_Taskbutton)
564+
565+
566+ # Converts all the textual tokens in tomboy note widgets
567+ def convertTokensToWidgets(self):
568+ self.anchors=[]
569+ start_iter = self.textview.buff.get_start_iter()
570+ end_iter = self.textview.buff.get_end_iter()
571+ text = self.textview.buff.get_slice(start_iter, end_iter)
572+ text_offset = 0
573+ token_position = text.find(self.token_start)
574+ token_ending = text.find(self.token_end, token_position)
575+ while not token_position < 0 and not token_ending < 0:
576+ #delete the text of the token
577+ tomboy_note_title = text[token_position + len(self.token_start):
578+ token_ending]
579+ start_iter = self.textview.buff.get_iter_at_offset(text_offset +
580+ token_position)
581+ end_iter = self.textview.buff.get_iter_at_offset(text_offset+
582+ token_ending+1)
583+ self.textview.buff.delete(start_iter, end_iter)
584+ #add the widget
585+ widget =self.widgetCreate(tomboy_note_title)
586+ anchor = self.textviewInsertWidget(widget, start_iter)
587+ self.anchors.append(anchor)
588+ #find the next
589+ start_iter = self.textview.buff.get_iter_at_child_anchor(anchor)
590+ start_iter.forward_char()
591+ end_iter = self.textview.buff.get_end_iter()
592+ text = self.textview.buff.get_slice(start_iter, end_iter)
593+ text_offset = start_iter.get_offset()
594+ token_position = text.find(self.token_start)
595+ token_ending = text.find(self.token_end)
596+
597+ def onTaskOpened(self, plugin_api):
598+ #NOTE: get_textview() only works in this function
599+ # (see GTG/core/plugins/api.py docs)
600+ self.textview = plugin_api.get_textview()
601+ self.addButtonToToolbar(plugin_api)
602+ self.convertTokensToWidgets()
603+
604+ def deactivate(self, plugin_api):
605+ #nothing to do at all
606+ pass
607+
608+ def close_dialog(self, widget, data=None):
609+ self.dialog.destroy()
610+ return True
611+
612+ #opens a dbus connection to tomboy
613+ def getTomboyObject(self):
614+ bus = dbus.SessionBus()
615+ obj = bus.get_object("org.gnome.Tomboy",
616+ "/org/gnome/Tomboy/RemoteControl")
617+ return dbus.Interface(obj, "org.gnome.Tomboy.RemoteControl")
618+
619+ #gets the list of the titles of the notes
620+ def getTomboyNoteTitleList(self):
621+ tomboy = self.getTomboyObject()
622+ return map(lambda note: str(tomboy.GetNoteTitle(note)),
623+ tomboy.ListAllNotes())
624+
625+ def onTbTaskButton(self, widget, plugin_api):
626+ title_list = self.getTomboyNoteTitleList()
627+ #Create the dialog
628+ glade_file = os.path.join(self.path, "tomboy.glade")
629+ wTree = gtk.glade.XML(glade_file, "InsertNoteDialog")
630+ #objects
631+ self.dialog = wTree.get_widget("InsertNoteDialog")
632+ btn_add = wTree.get_widget("btn_add")
633+ btn_cancel = wTree.get_widget("btn_cancel")
634+ self.combobox = wTree.get_widget("titles_combobox")
635+ self.label_caption = wTree.get_widget("label_caption")
636+ #connections
637+ self.dialog.connect("delete_event", self.close_dialog)
638+ btn_cancel.connect("clicked", self.close_dialog)
639+ btn_add.connect("clicked", self.noteChosen)
640+ self.combobox_entry = combobox_enhanced.\
641+ smartifyComboboxEntry(self.combobox,title_list,self.noteChosen)
642+ self.dialog.show_all()
643+
644+ #A title has been chosen by the user. If the note exists, it will be
645+ # linked, otherwise the user will have the option to create the note.
646+ def noteChosen(self, widget=None, data=None):
647+ tomboy = self.getTomboyObject()
648+ supposed_title = self.combobox_entry.get_text()
649+ if filter(lambda x: tomboy.GetNoteTitle(x)==supposed_title,
650+ tomboy.ListAllNotes()) == []:
651+ self.label_caption.set_text(_("That note does not exist!"))
652+ dialog = gtk.MessageDialog(parent = self.dialog,
653+ flags = gtk.DIALOG_DESTROY_WITH_PARENT,
654+ type = gtk.MESSAGE_QUESTION,
655+ buttons=gtk.BUTTONS_YES_NO,
656+ message_format=_("That note does not \
657+exist. Do you want to create a new one?"))
658+ response = dialog.run()
659+ dialog.destroy()
660+ if response == gtk.RESPONSE_YES:
661+ tomboy.CreateNamedNote(supposed_title)
662+ else:
663+ return
664+ #note insertion
665+ mark_start = self.textview.buff.get_insert()
666+ iter_start = self.textview.buff.get_iter_at_mark(mark_start)
667+ tomboy_widget =self.widgetCreate(supposed_title)
668+ anchor = self.textviewInsertWidget(tomboy_widget, iter_start)
669+ self.anchors.append(anchor)
670+ self.dialog.destroy()
671+
672+ #Opens a note in tomboy application via dbus
673+ def tomboyDisplayNote(self, widget, data = None):
674+ tomboy = self.getTomboyObject()
675+ note = tomboy.FindNote(widget.tomboy_note_title)
676+ tomboy.DisplayNote(note)
677+
678+ #inserts a widget in the textview
679+ def textviewInsertWidget(self, widget, iter):
680+ anchor = self.textview.buff.create_child_anchor(iter)
681+ widget.show()
682+ self.textview.add_child_at_anchor(widget, anchor)
683+ return anchor
684+
685+ #creates the tomboy widget
686+ def widgetCreate(self, tomboy_note_title):
687+ image = gtk.Image()
688+ image_path = "/usr/share/icons/hicolor/16x16/apps/tomboy.png"
689+ pixbuf=gtk.gdk.\
690+ pixbuf_new_from_file_at_size(image_path, 16, 16)
691+ image.show()
692+ image.set_from_pixbuf(pixbuf)
693+ image.set_alignment(0.5,1.0)
694+ label = gtk.Label()
695+ label.show()
696+ label.set_alignment(0.5, 1.0)
697+ eventbox = gtk.EventBox()
698+ eventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK)
699+ eventbox.connect('button_press_event', self.tomboyDisplayNote)
700+ eventbox.show()
701+ window = self.plugin_api.get_window()
702+ hbox = gtk.HBox()
703+ hbox.show()
704+ hbox.add(image)
705+ hbox.add(label)
706+ eventbox.add(hbox)
707+ window.realize()
708+ style=window.get_style()
709+ color = str(style.text[gtk.STATE_PRELIGHT])
710+ label.set_markup("<span underline='low' color='" + color +"'>" + tomboy_note_title + "</span>")
711+ eventbox.tomboy_note_title = tomboy_note_title
712+ #cursor changes to a hand
713+ def realize_callback(widget):
714+ eventbox.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
715+ eventbox.connect("realize", realize_callback)
716+ return eventbox
717+
718
719=== modified file 'GTG/taskeditor/editor.py'
720--- GTG/taskeditor/editor.py 2009-09-24 14:51:45 +0000
721+++ GTG/taskeditor/editor.py 2009-09-24 17:28:10 +0000
722@@ -479,6 +479,7 @@
723 #Will be linked to this destruction method that will save the task
724 def destruction(self,a=None) :#pylint: disable-msg=W0613
725 #Save should be also called when buffer is modified
726+ self.pengine.onTaskClose(self.plugins, self.te_plugin_api)
727 self.save()
728 self.closing(self.task.get_id())
729
730
731=== modified file 'GTG/taskeditor/taskview.py'
732--- GTG/taskeditor/taskview.py 2009-09-24 15:45:17 +0000
733+++ GTG/taskeditor/taskview.py 2009-09-24 17:28:10 +0000
734@@ -39,8 +39,7 @@
735 separators = [' ', '.', ',', '/', '\n', '\t', '!', '?', ';', '\0']
736 url_separators = [' ', ',', '\n', '\t', '\0']
737
738-bullet1_ltr = '→'
739-bullet1_rtl = '←'
740+bullet1 = '→'
741 bullet2 = '↳'
742
743
744@@ -151,11 +150,6 @@
745 self.modified_sigid = self.buff.connect("changed" , self.modified)
746 self.connect("backspace",self.backspace)
747 self.tobe_refreshed = False
748-
749- if self.get_direction() == gtk.TEXT_DIR_RTL :
750- self.bullet1 = bullet1_rtl
751- else :
752- self.bullet1 = bullet1_ltr
753
754
755 #This function is called to refresh the editor
756@@ -883,7 +877,7 @@
757 indentation = indentation + (level-1)*spaces
758 #adding the symbol
759 if level == 1 :
760- indentation = "%s%s "%(indentation,self.bullet1)
761+ indentation = "%s%s "%(indentation,bullet1)
762 buff.insert(itera,indentation)
763 indenttag = self.create_indent_tag(buff,level)
764 self.__apply_tag_to_mark(start,end,tag=indenttag)
765@@ -985,7 +979,7 @@
766 #Then, if indent > 0, we increment it
767 #First step : we preserve it.
768 else :
769- if not line.lstrip("%s "%self.bullet1) :
770+ if not line.lstrip("%s "%bullet1) :
771 self.deindent(itera,newlevel=0)
772 tv.emit_stop_by_name('insert-text')
773
774
775=== modified file 'locales/gtg.pot'
776--- locales/gtg.pot 2009-09-12 23:03:15 +0000
777+++ locales/gtg.pot 2009-09-24 17:28:10 +0000
778@@ -8,7 +8,7 @@
779 msgstr ""
780 "Project-Id-Version: PACKAGE VERSION\n"
781 "Report-Msgid-Bugs-To: \n"
782-"POT-Creation-Date: 2009-09-13 01:03+0200\n"
783+"POT-Creation-Date: 2009-09-18 15:51+0200\n"
784 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
785 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
786 "Language-Team: LANGUAGE <LL@li.org>\n"
787@@ -632,6 +632,22 @@
788 msgid "due"
789 msgstr ""
790
791+#: GTG/plugins/tomboy/tomboy.py:67
792+msgid "Add Tomboy note"
793+msgstr ""
794+
795+#: GTG/plugins/tomboy/tomboy.py:170
796+msgid "That note does not exist!"
797+msgstr ""
798+
799+#: GTG/plugins/tomboy/tomboy.glade.h:1
800+msgid "Insert Note"
801+msgstr ""
802+
803+#: GTG/plugins/tomboy/tomboy.glade.h:2
804+msgid "Insert the title of the tomboy note "
805+msgstr ""
806+
807 #: GTG/plugins/rtm_sync/utility.py:54
808 msgid "saving critical object failed"
809 msgstr ""
810@@ -666,11 +682,11 @@
811 "now. When done, press OK"
812 msgstr ""
813
814-#: GTG/plugins/rtm_sync/pyrtm/rtm.py:56
815+#: GTG/plugins/rtm_sync/pyrtm/rtm.py:57
816 msgid "Invalid state"
817 msgstr ""
818
819-#: GTG/plugins/rtm_sync/pyrtm/rtm.py:105
820+#: GTG/plugins/rtm_sync/pyrtm/rtm.py:106
821 msgid "API call failed"
822 msgstr ""
823

Subscribers

People subscribed via source and target branches

to status/vote changes: