Merge lp:~gtg-user/gtg/backends-window into lp:~gtg/gtg/old-trunk
- backends-window
- Merge into old-trunk
Proposed by
Luca Invernizzi
Status: | Merged |
---|---|
Merged at revision: | 880 |
Proposed branch: | lp:~gtg-user/gtg/backends-window |
Merge into: | lp:~gtg/gtg/old-trunk |
Diff against target: |
3479 lines (+2700/-285) 25 files modified
AUTHORS (+1/-0) CHANGELOG (+1/-0) GTG/backends/backendsignals.py (+0/-127) GTG/core/requester.py (+3/-0) GTG/gtk/__init__.py (+3/-1) GTG/gtk/backends_dialog.glade (+166/-0) GTG/gtk/backends_dialog/__init__.py (+294/-0) GTG/gtk/backends_dialog/addpanel.py (+214/-0) GTG/gtk/backends_dialog/backendscombo.py (+92/-0) GTG/gtk/backends_dialog/backendstree.py (+253/-0) GTG/gtk/backends_dialog/configurepanel.py (+304/-0) GTG/gtk/backends_dialog/parameters_ui/__init__.py (+149/-0) GTG/gtk/backends_dialog/parameters_ui/checkboxui.py (+72/-0) GTG/gtk/backends_dialog/parameters_ui/importtagsui.py (+135/-0) GTG/gtk/backends_dialog/parameters_ui/passwordui.py (+84/-0) GTG/gtk/backends_dialog/parameters_ui/pathui.py (+112/-0) GTG/gtk/backends_dialog/parameters_ui/periodui.py (+97/-0) GTG/gtk/backends_dialog/parameters_ui/textui.py (+78/-0) GTG/gtk/browser/browser.py (+100/-3) GTG/gtk/browser/custominfobar.py (+210/-0) GTG/gtk/browser/taskbrowser.glade (+161/-147) GTG/gtk/colors.py (+27/-1) GTG/gtk/manager.py (+18/-6) GTG/tests/test_interruptible.py (+69/-0) GTG/tools/networkmanager.py (+57/-0) |
To merge this branch: | bzr merge lp:~gtg-user/gtg/backends-window |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gtg developers | Pending | ||
Review via email: mp+32647@code.launchpad.net |
This proposal supersedes a proposal from 2010-08-13.
Commit message
Description of the change
This merge contains all the code relative to the window used to add, remove and edit backends.
All the functions should be documented.
To post a comment you must log in.
Revision history for this message
Marko Kevac (mkevac) wrote : | # |
Revision history for this message
Luca Invernizzi (invernizzi) wrote : | # |
Fixed
lp:~gtg-user/gtg/backends-window
updated
- 875. By Luca Invernizzi
-
small fix for marko kevec comment
- 876. By Luca Invernizzi
-
small fix in the backends tree
Revision history for this message
Luca Invernizzi (invernizzi) wrote : | # |
Maybe the "Quit" button to dismiss the window could be better named. Suggestions welcome!
lp:~gtg-user/gtg/backends-window
updated
- 877. By Luca Invernizzi
-
cherrypicking from my development branch
- 878. By Luca Invernizzi
-
cherrypicking from my development branch
- 879. By Luca Invernizzi
-
merge w/ trunk
- 880. By Luca Invernizzi
-
Workaround for arch linux, as for bug #lp 624204
- 881. By Luca Invernizzi
-
Bugfix for bug lp #624298 by Andrew Starr-Bochicchio
New backends window should have close button not quit - 882. By Luca Invernizzi
-
cherrypicking from my development branch
- 883. By Luca Invernizzi
-
cherrypicking from my development branch
- 884. By Luca Invernizzi
-
cherrypicking from my development branch
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'AUTHORS' |
2 | --- AUTHORS 2010-08-15 19:12:35 +0000 |
3 | +++ AUTHORS 2010-09-04 17:54:43 +0000 |
4 | @@ -70,3 +70,4 @@ |
5 | * Volodymyr Floreskul <exufer@gmail.com> |
6 | * Jeff Oliver <kaiserfro@gmail.com> |
7 | * Thibault Fevry <https://edge.launchpad.net/~thibaultfevry> (no email provided) |
8 | +* Andrew Starr-Bochicchio <andrewsomething@ubuntu.com> |
9 | |
10 | === modified file 'CHANGELOG' |
11 | --- CHANGELOG 2010-08-04 00:30:22 +0000 |
12 | +++ CHANGELOG 2010-09-04 17:54:43 +0000 |
13 | @@ -4,6 +4,7 @@ |
14 | * Fixed bug with data consistency #579189, by Marko Kevac |
15 | * Added samba bugzilla to the bugzilla plugin, by Jelmer Vernoij |
16 | * Fixed bug #532392, a start date is later than a due date, by Volodymyr Floreskul |
17 | + * Added a window to add/delete/edit backends by Luca Invernizzi |
18 | |
19 | 2010-03-01 Getting Things GNOME! 0.2.2 |
20 | * Autostart on login, by Luca Invernizzi |
21 | |
22 | === added file 'GTG/backends/backendsignals.py' |
23 | --- GTG/backends/backendsignals.py 1970-01-01 00:00:00 +0000 |
24 | +++ GTG/backends/backendsignals.py 2010-09-04 17:54:43 +0000 |
25 | @@ -0,0 +1,148 @@ |
26 | +# -*- coding: utf-8 -*- |
27 | +# ----------------------------------------------------------------------------- |
28 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
29 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
30 | +# |
31 | +# This program is free software: you can redistribute it and/or modify it under |
32 | +# the terms of the GNU General Public License as published by the Free Software |
33 | +# Foundation, either version 3 of the License, or (at your option) any later |
34 | +# version. |
35 | +# |
36 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
37 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
38 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
39 | +# details. |
40 | +# |
41 | +# You should have received a copy of the GNU General Public License along with |
42 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
43 | +# ----------------------------------------------------------------------------- |
44 | + |
45 | +import gobject |
46 | + |
47 | +from GTG.tools.borg import Borg |
48 | + |
49 | + |
50 | + |
51 | +class BackendSignals(Borg): |
52 | + ''' |
53 | + This class handles the signals that involve backends. |
54 | + In particular, it's a wrapper Borg class around a _BackendSignalsGObject |
55 | + class, and all method of the wrapped class can be used as if they were part |
56 | + of this class |
57 | + ''' |
58 | + |
59 | + #error codes to send along with the BACKEND_FAILED signal |
60 | + ERRNO_AUTHENTICATION = "authentication failed" |
61 | + ERRNO_NETWORK = "network is down" |
62 | + ERRNO_DBUS = "Dbus interface cannot be connected" |
63 | + |
64 | + def __init__(self): |
65 | + '''Checks that this is the only instance, and instantiates the |
66 | + gobject''' |
67 | + super(BackendSignals, self).__init__() |
68 | + if hasattr(self, "_gobject"): |
69 | + return |
70 | + self._gobject = _BackendSignalsGObject() |
71 | + |
72 | + def __getattr__(self, attr): |
73 | + ''' |
74 | + From outside the class, there should be no difference between self's |
75 | + attributes and self._gobject's attributes. |
76 | + ''' |
77 | + if attr == "_gobject" and not "_gobject" in self.__dict__: |
78 | + raise AttributeError |
79 | + return getattr(self._gobject, attr) |
80 | + |
81 | + |
82 | +def signal_type_factory(*args): |
83 | + ''' |
84 | + Simply returns a gobject signal type |
85 | + |
86 | + @returns tuple |
87 | + ''' |
88 | + return (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, args) |
89 | + |
90 | + |
91 | + |
92 | +class _BackendSignalsGObject(gobject.GObject): |
93 | + |
94 | + #signal name constants |
95 | + BACKEND_STATE_TOGGLED = 'backend-state-toggled' #emitted when a |
96 | + #backend is |
97 | + #enabled or disabled |
98 | + BACKEND_RENAMED = 'backend-renamed' #emitted when a backend is renamed |
99 | + BACKEND_ADDED = 'backend-added' |
100 | + BACKEND_REMOVED = 'backend-added' #when a backend is deleted |
101 | + DEFAULT_BACKEND_LOADED = 'default-backend-loaded' #emitted after all |
102 | + # tasks have been |
103 | + # loaded from the |
104 | + # default backend |
105 | + BACKEND_FAILED = 'backend-failed' #something went wrong with a backend |
106 | + BACKEND_SYNC_STARTED = 'backend-sync-started' |
107 | + BACKEND_SYNC_ENDED = 'backend-sync-ended' |
108 | + INTERACTION_REQUESTED = 'user-interaction-requested' |
109 | + |
110 | + INTERACTION_CONFIRM = 'confirm' |
111 | + INTERACTION_TEXT = 'text' |
112 | + |
113 | + __gsignals__ = {BACKEND_STATE_TOGGLED : signal_type_factory(str), \ |
114 | + BACKEND_RENAMED : signal_type_factory(str), \ |
115 | + BACKEND_ADDED : signal_type_factory(str), \ |
116 | + BACKEND_REMOVED : signal_type_factory(str), \ |
117 | + BACKEND_SYNC_STARTED : signal_type_factory(str), \ |
118 | + BACKEND_SYNC_ENDED : signal_type_factory(str), \ |
119 | + DEFAULT_BACKEND_LOADED: signal_type_factory(), \ |
120 | + BACKEND_FAILED : signal_type_factory(str, str), \ |
121 | + INTERACTION_REQUESTED : signal_type_factory(str, str, \ |
122 | + str, str)} |
123 | + |
124 | + def __init__(self): |
125 | + super(_BackendSignalsGObject, self).__init__() |
126 | + self.backends_currently_syncing = [] |
127 | + |
128 | + ############# Signals ######### |
129 | + #connecting to signals is fine, but keep an eye if you should emit them. |
130 | + #As a general rule, signals should only be emitted in the GenericBackend |
131 | + #class |
132 | + |
133 | + def _emit_signal(self, signal, backend_id): |
134 | + gobject.idle_add(self.emit, signal, backend_id) |
135 | + |
136 | + def backend_state_changed(self, backend_id): |
137 | + self._emit_signal(self.BACKEND_STATE_TOGGLED, backend_id) |
138 | + |
139 | + def backend_renamed(self, backend_id): |
140 | + self._emit_signal(self.BACKEND_RENAMED, backend_id) |
141 | + |
142 | + def backend_added(self, backend_id): |
143 | + self._emit_signal(self.BACKEND_ADDED, backend_id) |
144 | + |
145 | + def backend_removed(self, backend_id): |
146 | + self._emit_signal(self.BACKEND_REMOVED, backend_id) |
147 | + |
148 | + def default_backend_loaded(self): |
149 | + gobject.idle_add(self.emit, self.DEFAULT_BACKEND_LOADED) |
150 | + |
151 | + def backend_failed(self, backend_id, error_code): |
152 | + gobject.idle_add(self.emit, self.BACKEND_FAILED, backend_id, \ |
153 | + error_code) |
154 | + |
155 | + def interaction_requested(self, backend_id, description, \ |
156 | + interaction_type, callback_str): |
157 | + gobject.idle_add(self.emit, self.INTERACTION_REQUESTED, \ |
158 | + backend_id, description, interaction_type, callback_str) |
159 | + |
160 | + def backend_sync_started(self, backend_id): |
161 | + self._emit_signal(self.BACKEND_SYNC_STARTED, backend_id) |
162 | + self.backends_currently_syncing.append(backend_id) |
163 | + |
164 | + def backend_sync_ended(self, backend_id): |
165 | + self._emit_signal(self.BACKEND_SYNC_ENDED, backend_id) |
166 | + try: |
167 | + self.backends_currently_syncing.remove(backend_id) |
168 | + except: |
169 | + pass |
170 | + |
171 | + def is_backend_syncing(self, backend_id): |
172 | + return backend_id in self.backends_currently_syncing |
173 | + |
174 | |
175 | === removed file 'GTG/backends/backendsignals.py' |
176 | --- GTG/backends/backendsignals.py 2010-06-23 12:49:28 +0000 |
177 | +++ GTG/backends/backendsignals.py 1970-01-01 00:00:00 +0000 |
178 | @@ -1,127 +0,0 @@ |
179 | -# -*- coding: utf-8 -*- |
180 | -# ----------------------------------------------------------------------------- |
181 | -# Getting Things Gnome! - a personal organizer for the GNOME desktop |
182 | -# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
183 | -# |
184 | -# This program is free software: you can redistribute it and/or modify it under |
185 | -# the terms of the GNU General Public License as published by the Free Software |
186 | -# Foundation, either version 3 of the License, or (at your option) any later |
187 | -# version. |
188 | -# |
189 | -# This program is distributed in the hope that it will be useful, but WITHOUT |
190 | -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
191 | -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
192 | -# details. |
193 | -# |
194 | -# You should have received a copy of the GNU General Public License along with |
195 | -# this program. If not, see <http://www.gnu.org/licenses/>. |
196 | -# ----------------------------------------------------------------------------- |
197 | - |
198 | -import gobject |
199 | - |
200 | -from GTG.tools.borg import Borg |
201 | - |
202 | - |
203 | - |
204 | -class BackendSignals(Borg): |
205 | - ''' |
206 | - This class handles the signals that involve backends. |
207 | - In particular, it's a wrapper Borg class around a _BackendSignalsGObject |
208 | - class, and all method of the wrapped class can be used as if they were part |
209 | - of this class |
210 | - ''' |
211 | - |
212 | - #error codes to send along with the BACKEND_FAILED signal |
213 | - ERRNO_AUTHENTICATION = "authentication failed" |
214 | - ERRNO_NETWORK = "network is down" |
215 | - ERRNO_DBUS = "Dbus interface cannot be connected" |
216 | - |
217 | - def __init__(self): |
218 | - super(BackendSignals, self).__init__() |
219 | - if hasattr(self, "_gobject"): |
220 | - return |
221 | - self._gobject = _BackendSignalsGObject() |
222 | - |
223 | - def __getattr__(self, attr): |
224 | - if attr == "_gobject" and not "_gobject" in self.__dict__: |
225 | - raise AttributeError |
226 | - return getattr(self._gobject, attr) |
227 | - |
228 | - |
229 | -class _BackendSignalsGObject(gobject.GObject): |
230 | - |
231 | - #signal name constants |
232 | - BACKEND_STATE_TOGGLED = 'backend-state-toggled' #emitted when a |
233 | - #backend is |
234 | - #enabled or disabled |
235 | - BACKEND_RENAMED = 'backend-renamed' #emitted when a backend is renamed |
236 | - BACKEND_ADDED = 'backend-added' |
237 | - BACKEND_REMOVED = 'backend-added' #when a backend is deleted |
238 | - DEFAULT_BACKEND_LOADED = 'default-backend-loaded' #emitted after all |
239 | - # tasks have been |
240 | - # loaded from the |
241 | - # default backend |
242 | - BACKEND_FAILED = 'backend-failed' #something went wrong with a backend |
243 | - BACKEND_SYNC_STARTED = 'backend-sync-started' |
244 | - BACKEND_SYNC_ENDED = 'backend-sync-ended' |
245 | - |
246 | - __string_signal__ = (gobject.SIGNAL_RUN_FIRST, \ |
247 | - gobject.TYPE_NONE, (str, )) |
248 | - __none_signal__ = (gobject.SIGNAL_RUN_FIRST, \ |
249 | - gobject.TYPE_NONE, ( )) |
250 | - __string_string_signal__ = (gobject.SIGNAL_RUN_FIRST, \ |
251 | - gobject.TYPE_NONE, (str, str, )) |
252 | - |
253 | - __gsignals__ = {BACKEND_STATE_TOGGLED : __string_signal__, \ |
254 | - BACKEND_RENAMED : __string_signal__, \ |
255 | - BACKEND_ADDED : __string_signal__, \ |
256 | - BACKEND_REMOVED : __string_signal__, \ |
257 | - BACKEND_SYNC_STARTED : __string_signal__, \ |
258 | - BACKEND_SYNC_ENDED : __string_signal__, \ |
259 | - DEFAULT_BACKEND_LOADED: __none_signal__, \ |
260 | - BACKEND_FAILED : __string_string_signal__} |
261 | - |
262 | - def __init__(self): |
263 | - super(_BackendSignalsGObject, self).__init__() |
264 | - self.backends_currently_syncing = [] |
265 | - |
266 | - ############# Signals ######### |
267 | - #connecting to signals is fine, but keep an eye if you should emit them. |
268 | - #As a general rule, signals should only be emitted in the GenericBackend |
269 | - #class |
270 | - |
271 | - def _emit_signal(self, signal, backend_id): |
272 | - gobject.idle_add(self.emit, signal, backend_id) |
273 | - |
274 | - def backend_state_changed(self, backend_id): |
275 | - self._emit_signal(self.BACKEND_STATE_TOGGLED, backend_id) |
276 | - |
277 | - def backend_renamed(self, backend_id): |
278 | - self._emit_signal(self.BACKEND_RENAMED, backend_id) |
279 | - |
280 | - def backend_added(self, backend_id): |
281 | - self._emit_signal(self.BACKEND_ADDED, backend_id) |
282 | - |
283 | - def backend_removed(self, backend_id): |
284 | - self._emit_signal(self.BACKEND_REMOVED, backend_id) |
285 | - |
286 | - def default_backend_loaded(self): |
287 | - gobject.idle_add(self.emit, self.DEFAULT_BACKEND_LOADED) |
288 | - |
289 | - def backend_failed(self, backend_id, error_code): |
290 | - gobject.idle_add(self.emit, self.BACKEND_FAILED, backend_id, \ |
291 | - error_code) |
292 | - |
293 | - def backend_sync_started(self, backend_id): |
294 | - self._emit_signal(self.BACKEND_SYNC_STARTED, backend_id) |
295 | - self.backends_currently_syncing.append(backend_id) |
296 | - |
297 | - def backend_sync_ended(self, backend_id): |
298 | - self._emit_signal(self.BACKEND_SYNC_ENDED, backend_id) |
299 | - try: |
300 | - self.backends_currently_syncing.remove(backend_id) |
301 | - except: |
302 | - pass |
303 | - |
304 | - def is_backend_syncing(self, backend_id): |
305 | - return backend_id in self.backends_currently_syncing |
306 | |
307 | === modified file 'GTG/core/requester.py' |
308 | --- GTG/core/requester.py 2010-06-22 19:55:15 +0000 |
309 | +++ GTG/core/requester.py 2010-09-04 17:54:43 +0000 |
310 | @@ -284,3 +284,6 @@ |
311 | |
312 | def backend_change_attached_tags(self, backend_id, tags): |
313 | return self.ds.backend_change_attached_tags(backend_id, tags) |
314 | + |
315 | + def save_datastore(self): |
316 | + return self.ds.save() |
317 | |
318 | === modified file 'GTG/gtk/__init__.py' |
319 | --- GTG/gtk/__init__.py 2010-06-02 18:12:23 +0000 |
320 | +++ GTG/gtk/__init__.py 2010-09-04 17:54:43 +0000 |
321 | @@ -28,7 +28,9 @@ |
322 | |
323 | |
324 | class ViewConfig: |
325 | + |
326 | + |
327 | current_rep = os.path.dirname(os.path.abspath(__file__)) |
328 | DELETE_GLADE_FILE = os.path.join(current_rep, "deletion.glade") |
329 | PREFERENCES_GLADE_FILE = os.path.join(current_rep, "preferences.glade") |
330 | - |
331 | + BACKENDS_GLADE_FILE = os.path.join(current_rep, "backends_dialog.glade") |
332 | |
333 | === added directory 'GTG/gtk/backends_dialog' |
334 | === added file 'GTG/gtk/backends_dialog.glade' |
335 | --- GTG/gtk/backends_dialog.glade 1970-01-01 00:00:00 +0000 |
336 | +++ GTG/gtk/backends_dialog.glade 2010-09-04 17:54:43 +0000 |
337 | @@ -0,0 +1,166 @@ |
338 | +<?xml version="1.0"?> |
339 | +<interface> |
340 | + <requires lib="gtk+" version="2.16"/> |
341 | + <!-- interface-naming-policy project-wide --> |
342 | + <object class="GtkWindow" id="backends_dialog"> |
343 | + <property name="window_position">mouse</property> |
344 | + <signal name="delete_event" handler="on_BackendsDialog_delete_event"/> |
345 | + <child> |
346 | + <object class="GtkAlignment" id="alignment1"> |
347 | + <property name="visible">True</property> |
348 | + <property name="top_padding">10</property> |
349 | + <property name="bottom_padding">10</property> |
350 | + <property name="left_padding">10</property> |
351 | + <property name="right_padding">10</property> |
352 | + <child> |
353 | + <object class="GtkVBox" id="vbox1"> |
354 | + <property name="visible">True</property> |
355 | + <property name="spacing">10</property> |
356 | + <child> |
357 | + <object class="GtkHBox" id="big_central_hbox"> |
358 | + <property name="visible">True</property> |
359 | + <property name="spacing">10</property> |
360 | + <child> |
361 | + <object class="GtkVBox" id="vbox2"> |
362 | + <property name="visible">True</property> |
363 | + <child> |
364 | + <object class="GtkAlignment" id="treeview_window"> |
365 | + <property name="height_request">400</property> |
366 | + <property name="visible">True</property> |
367 | + <child> |
368 | + <placeholder/> |
369 | + </child> |
370 | + </object> |
371 | + <packing> |
372 | + <property name="position">0</property> |
373 | + </packing> |
374 | + </child> |
375 | + <child> |
376 | + <object class="GtkAlignment" id="alignment2"> |
377 | + <property name="height_request">30</property> |
378 | + <property name="visible">True</property> |
379 | + <property name="yalign">1</property> |
380 | + <property name="top_padding">20</property> |
381 | + <property name="bottom_padding">10</property> |
382 | + <property name="left_padding">10</property> |
383 | + <property name="right_padding">10</property> |
384 | + <child> |
385 | + <object class="GtkHButtonBox" id="hbuttonbox3"> |
386 | + <property name="visible">True</property> |
387 | + <property name="spacing">10</property> |
388 | + <property name="homogeneous">True</property> |
389 | + <child> |
390 | + <object class="GtkButton" id="add_button"> |
391 | + <property name="label">gtk-add</property> |
392 | + <property name="visible">True</property> |
393 | + <property name="can_focus">True</property> |
394 | + <property name="receives_default">True</property> |
395 | + <property name="use_stock">True</property> |
396 | + <signal name="clicked" handler="on_add_button_clicked"/> |
397 | + </object> |
398 | + <packing> |
399 | + <property name="expand">False</property> |
400 | + <property name="fill">False</property> |
401 | + <property name="position">0</property> |
402 | + </packing> |
403 | + </child> |
404 | + <child> |
405 | + <object class="GtkButton" id="remove_button"> |
406 | + <property name="label">gtk-remove</property> |
407 | + <property name="visible">True</property> |
408 | + <property name="can_focus">True</property> |
409 | + <property name="receives_default">True</property> |
410 | + <property name="use_stock">True</property> |
411 | + <signal name="clicked" handler="on_remove_button_clicked"/> |
412 | + </object> |
413 | + <packing> |
414 | + <property name="expand">False</property> |
415 | + <property name="fill">False</property> |
416 | + <property name="position">1</property> |
417 | + </packing> |
418 | + </child> |
419 | + </object> |
420 | + </child> |
421 | + </object> |
422 | + <packing> |
423 | + <property name="expand">False</property> |
424 | + <property name="fill">False</property> |
425 | + <property name="position">1</property> |
426 | + </packing> |
427 | + </child> |
428 | + </object> |
429 | + <packing> |
430 | + <property name="position">0</property> |
431 | + </packing> |
432 | + </child> |
433 | + <child> |
434 | + <object class="GtkScrolledWindow" id="central_pane_window"> |
435 | + <property name="width_request">450</property> |
436 | + <property name="visible">True</property> |
437 | + <property name="can_focus">True</property> |
438 | + <property name="vadjustment">adjustment1</property> |
439 | + <property name="hscrollbar_policy">automatic</property> |
440 | + <property name="vscrollbar_policy">automatic</property> |
441 | + <child> |
442 | + <object class="GtkViewport" id="central_pane1"> |
443 | + <property name="visible">True</property> |
444 | + <property name="resize_mode">queue</property> |
445 | + <child> |
446 | + <object class="GtkAlignment" id="central_pane"> |
447 | + <property name="visible">True</property> |
448 | + <property name="left_padding">10</property> |
449 | + <property name="right_padding">10</property> |
450 | + <child> |
451 | + <placeholder/> |
452 | + </child> |
453 | + </object> |
454 | + </child> |
455 | + </object> |
456 | + </child> |
457 | + </object> |
458 | + <packing> |
459 | + <property name="position">1</property> |
460 | + </packing> |
461 | + </child> |
462 | + </object> |
463 | + <packing> |
464 | + <property name="position">0</property> |
465 | + </packing> |
466 | + </child> |
467 | + <child> |
468 | + <object class="GtkHButtonBox" id="hbuttonbox2"> |
469 | + <property name="visible">True</property> |
470 | + <property name="layout_style">end</property> |
471 | + <child> |
472 | + <object class="GtkButton" id="close_button"> |
473 | + <property name="label">gtk-close</property> |
474 | + <property name="visible">True</property> |
475 | + <property name="can_focus">True</property> |
476 | + <property name="receives_default">True</property> |
477 | + <property name="use_stock">True</property> |
478 | + <signal name="clicked" handler="on_close_button_clicked"/> |
479 | + </object> |
480 | + <packing> |
481 | + <property name="expand">False</property> |
482 | + <property name="fill">False</property> |
483 | + <property name="position">0</property> |
484 | + </packing> |
485 | + </child> |
486 | + </object> |
487 | + <packing> |
488 | + <property name="expand">False</property> |
489 | + <property name="position">1</property> |
490 | + </packing> |
491 | + </child> |
492 | + </object> |
493 | + </child> |
494 | + </object> |
495 | + </child> |
496 | + </object> |
497 | + <object class="GtkAdjustment" id="adjustment1"> |
498 | + <property name="upper">100</property> |
499 | + <property name="step_increment">1</property> |
500 | + <property name="page_increment">10</property> |
501 | + <property name="page_size">10</property> |
502 | + </object> |
503 | +</interface> |
504 | |
505 | === added file 'GTG/gtk/backends_dialog/__init__.py' |
506 | --- GTG/gtk/backends_dialog/__init__.py 1970-01-01 00:00:00 +0000 |
507 | +++ GTG/gtk/backends_dialog/__init__.py 2010-09-04 17:54:43 +0000 |
508 | @@ -0,0 +1,294 @@ |
509 | +# -*- coding: utf-8 -*- |
510 | +# ----------------------------------------------------------------------------- |
511 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
512 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
513 | +# |
514 | +# This program is free software: you can redistribute it and/or modify it under |
515 | +# the terms of the GNU General Public License as published by the Free Software |
516 | +# Foundation, either version 3 of the License, or (at your option) any later |
517 | +# version. |
518 | +# |
519 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
520 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
521 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
522 | +# details. |
523 | +# |
524 | +# You should have received a copy of the GNU General Public License along with |
525 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
526 | +# ----------------------------------------------------------------------------- |
527 | + |
528 | +''' |
529 | +This file contains BackendsDialog, a class that manages the window that |
530 | +lets you add and configure backends. |
531 | +This window is divided in two: |
532 | + - a treeview of the currently loaded backends (the ones added by the user) |
533 | + - a big space, that can be filled by the configuration panel or the add |
534 | + panel (these are called also "views" in this class) |
535 | +''' |
536 | + |
537 | +import gtk |
538 | + |
539 | +from GTG.gtk import ViewConfig |
540 | +from GTG.core import CoreConfig |
541 | +from GTG.gtk.backends_dialog.backendstree import BackendsTree |
542 | +from GTG.gtk.backends_dialog.addpanel import AddPanel |
543 | +from GTG.gtk.backends_dialog.configurepanel import ConfigurePanel |
544 | +from GTG.backends import BackendFactory |
545 | +from GTG.tools.logger import Log |
546 | +from GTG import _ |
547 | +from GTG.backends.genericbackend import GenericBackend |
548 | + |
549 | + |
550 | + |
551 | +class BackendsDialog(object): |
552 | + ''' |
553 | + BackendsDialog manages a window that lets you manage and configure backends. |
554 | + It can display two "views", or "panels": |
555 | + - the backend configuration view |
556 | + - the backend adding view |
557 | + ''' |
558 | + |
559 | + |
560 | + def __init__(self, req): |
561 | + ''' |
562 | + Initializes the gtk objects and signals. |
563 | + @param req: a Requester object |
564 | + ''' |
565 | + self.req = req |
566 | + self._configure_icon_theme() |
567 | + builder = gtk.Builder() |
568 | + self._load_widgets_from_glade(builder) |
569 | + self._create_widgets_for_add_panel() |
570 | + self._create_widgets_for_configure_panel() |
571 | + self._setup_signal_connections(builder) |
572 | + self._create_widgets_for_backends_tree() |
573 | + |
574 | +######################################## |
575 | +### INTERFACE WITH THE VIEWMANAGER ##### |
576 | +######################################## |
577 | + |
578 | + def activate(self): |
579 | + '''Shows this window, refreshing the current view''' |
580 | + self.config_panel.set_hidden(False) |
581 | + self.dialog.show_all() |
582 | + self.backends_tv.refresh() |
583 | + self.backends_tv.select_backend() |
584 | + self.dialog.present() |
585 | + |
586 | + def on_close(self, widget, data = None): |
587 | + ''' |
588 | + Hides this window, saving the backends configuration. |
589 | + |
590 | + @param widget: not used, here only for using this as signal callback |
591 | + @param data: same as widget, disregard the content |
592 | + ''' |
593 | + self.dialog.hide() |
594 | + self.config_panel.set_hidden(True) |
595 | + self.req.save_datastore() |
596 | + |
597 | +######################################## |
598 | +### HELPER FUNCTIONS ################### |
599 | +######################################## |
600 | + |
601 | + def get_requester(self): |
602 | + ''' |
603 | + Helper function: returns the requester. |
604 | + It's used by the "views" displayed by this class (backend editing and |
605 | + adding views) to access the requester |
606 | + ''' |
607 | + return self.req |
608 | + |
609 | + def get_pixbuf_from_icon_name(self, name, height, width): |
610 | + ''' |
611 | + Helper function: returns a pixbuf of an icon given its name in the |
612 | + loaded icon theme |
613 | + |
614 | + @param name: the name of the icon |
615 | + @param height: the height of the returned pixbuf |
616 | + @param width: the width of the returned pixbuf |
617 | + |
618 | + @returns gtk.gdk.Pixbuf: a pixbuf containing the wanted icon, or None |
619 | + (if the icon is not present) |
620 | + ''' |
621 | + #NOTE: loading icons directly from the theme and scaling them results in |
622 | + # blurry icons. So, instead of doing that, I'm loading them |
623 | + # directly from file. |
624 | + icon_info = self.icon_theme.lookup_icon(name, gtk.ICON_SIZE_MENU, 0) |
625 | + if icon_info == None: |
626 | + return None |
627 | + pixbuf = gtk.gdk.pixbuf_new_from_file(icon_info.get_filename()) |
628 | + return pixbuf.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR) |
629 | + |
630 | + def _show_panel(self, panel_name): |
631 | + ''' |
632 | + Helper function to switch between panels. |
633 | + |
634 | + @param panel_name: the name of the wanted panel. Choose between |
635 | + "configuration" or "add" |
636 | + ''' |
637 | + if panel_name == "configuration": |
638 | + panel_to_remove = self.add_panel |
639 | + panel_to_add = self.config_panel |
640 | + side_is_enabled = True |
641 | + elif panel_name == "add": |
642 | + panel_to_remove = self.config_panel |
643 | + panel_to_add = self.add_panel |
644 | + side_is_enabled = False |
645 | + else: |
646 | + Log.error("panel name unknown") |
647 | + return |
648 | + ##Central pane |
649 | + #NOTE: self.central_pane is the gtk.Container in which we load panels |
650 | + if panel_to_remove in self.central_pane: |
651 | + self.central_pane.remove(panel_to_remove) |
652 | + if not panel_to_add in self.central_pane: |
653 | + self.central_pane.add(panel_to_add) |
654 | + self.central_pane.show_all() |
655 | + #Side treeview |
656 | + # disabled if we're adding a new backend |
657 | + try: |
658 | + #when this is called upon initialization of this class, the |
659 | + # backends_tv object has not been created yet. |
660 | + self.add_button.set_sensitive(side_is_enabled) |
661 | + self.remove_button.set_sensitive(side_is_enabled) |
662 | + self.backends_tv.set_sensitive(side_is_enabled) |
663 | + except AttributeError: |
664 | + pass |
665 | + |
666 | +######################################## |
667 | +### WIDGETS AND SIGNALS ################ |
668 | +######################################## |
669 | + |
670 | + def _load_widgets_from_glade(self, builder): |
671 | + ''' |
672 | + Loads widgets from the glade file |
673 | + |
674 | + @param builder: a gtk.Builder |
675 | + ''' |
676 | + builder.add_from_file(ViewConfig.BACKENDS_GLADE_FILE) |
677 | + widgets = { |
678 | + 'dialog' : 'backends_dialog', |
679 | + 'treeview_window' : 'treeview_window', |
680 | + 'central_pane' : 'central_pane', |
681 | + 'add_button' : 'add_button', |
682 | + 'remove_button' : 'remove_button', |
683 | + } |
684 | + for attr, widget in widgets.iteritems(): |
685 | + setattr(self, attr, builder.get_object(widget)) |
686 | + |
687 | + def _setup_signal_connections(self, builder): |
688 | + ''' |
689 | + Creates some GTK signals connections |
690 | + |
691 | + @param builder: a gtk.Builder |
692 | + ''' |
693 | + signals = { |
694 | + 'on_add_button_clicked': self.on_add_button, |
695 | + 'on_BackendsDialog_delete_event': self.on_close, |
696 | + 'on_close_button_clicked': self.on_close, |
697 | + 'on_remove_button_clicked': self.on_remove_button, |
698 | + } |
699 | + builder.connect_signals(signals) |
700 | + |
701 | + def _configure_icon_theme(self): |
702 | + ''' |
703 | + Inform gtk on the location of the backends icons (which is in |
704 | + the GTG directory tree, and not in the default location for icons |
705 | + ''' |
706 | + self.icon_theme = gtk.icon_theme_get_default() |
707 | + for directory in CoreConfig().get_icons_directories(): |
708 | + self.icon_theme.prepend_search_path(directory) |
709 | + |
710 | + def _create_widgets_for_backends_tree(self): |
711 | + ''' |
712 | + Creates the widgets for the lateral treeview displaying the |
713 | + backends the user has added |
714 | + ''' |
715 | + self.backends_tv = BackendsTree(self) |
716 | + self.treeview_window.add(self.backends_tv) |
717 | + |
718 | + def _create_widgets_for_configure_panel(self): |
719 | + '''simply creates the panel to configure backends''' |
720 | + self.config_panel = ConfigurePanel(self) |
721 | + |
722 | + def _create_widgets_for_add_panel(self): |
723 | + '''simply creates the panel to add backends''' |
724 | + self.add_panel = AddPanel(self) |
725 | + |
726 | +######################################## |
727 | +### EVENT HANDLING ##################### |
728 | +######################################## |
729 | + |
730 | + def on_backend_selected(self, backend_id): |
731 | + ''' |
732 | + When a backend in the treeview gets selected, show |
733 | + its configuration pane |
734 | + |
735 | + @param backend_id: the id of the selected backend |
736 | + ''' |
737 | + if backend_id: |
738 | + self._show_panel("configuration") |
739 | + self.config_panel.set_backend(backend_id) |
740 | + backend = self.req.get_backend(backend_id) |
741 | + self.remove_button.set_sensitive(not backend.is_default()) |
742 | + |
743 | + def on_add_button(self, widget = None, data = None): |
744 | + ''' |
745 | + When the add button is pressed, the add panel is shown |
746 | + |
747 | + @param widget: not used, here only for using this as signal callback |
748 | + @param data: same as widget, disregard the content |
749 | + ''' |
750 | + self._show_panel("add") |
751 | + self.add_panel.refresh_backends() |
752 | + |
753 | + def on_backend_added(self, backend_name): |
754 | + ''' |
755 | + When a backend is added, it is created and registered in the Datastore. |
756 | + Also, the configuration panel is shown. |
757 | + |
758 | + @param backend_name: the name of the type of the backend to add |
759 | + (identified as BACKEND_NAME in the Backend class) |
760 | + ''' |
761 | + backend_id = None |
762 | + #Create Backend |
763 | + backend_dic = BackendFactory().get_new_backend_dict(backend_name) |
764 | + if backend_dic: |
765 | + backend_id = backend_dic["backend"].get_id() |
766 | + backend_dic[GenericBackend.KEY_ENABLED] = False |
767 | + self.req.register_backend(backend_dic) |
768 | + #Restore UI |
769 | + self._show_panel("configuration") |
770 | + |
771 | + def show_config_for_backend(self, backend_id): |
772 | + ''' |
773 | + Selects a backend in the lateral treeview |
774 | + |
775 | + @param backend_id: the id of the backend that must be selected |
776 | + ''' |
777 | + self.backends_tv.select_backend(backend_id) |
778 | + |
779 | + def on_remove_button(self, widget = None, data = None): |
780 | + ''' |
781 | + When the remove button is pressed, a confirmation dialog is shown, |
782 | + and if the answer is positive, the backend is deleted. |
783 | + ''' |
784 | + backend_id = self.backends_tv.get_selected_backend_id() |
785 | + if backend_id == None: |
786 | + #no backend selected |
787 | + return |
788 | + backend = self.req.get_backend(backend_id) |
789 | + dialog = gtk.MessageDialog( \ |
790 | + parent = self.dialog, |
791 | + flags = gtk.DIALOG_DESTROY_WITH_PARENT, |
792 | + type = gtk.MESSAGE_QUESTION, |
793 | + buttons = gtk.BUTTONS_YES_NO, |
794 | + message_format = \ |
795 | + _("Do you really want to remove the backend '%s'?") % \ |
796 | + backend.get_human_name()) |
797 | + response = dialog.run() |
798 | + dialog.destroy() |
799 | + if response == gtk.RESPONSE_YES: |
800 | + #delete the backend and remove it from the lateral treeview |
801 | + self.req.remove_backend(backend_id) |
802 | + self.backends_tv.remove_backend(backend_id) |
803 | |
804 | === added file 'GTG/gtk/backends_dialog/addpanel.py' |
805 | --- GTG/gtk/backends_dialog/addpanel.py 1970-01-01 00:00:00 +0000 |
806 | +++ GTG/gtk/backends_dialog/addpanel.py 2010-09-04 17:54:43 +0000 |
807 | @@ -0,0 +1,214 @@ |
808 | +# -*- coding: utf-8 -*- |
809 | +# ----------------------------------------------------------------------------- |
810 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
811 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
812 | +# |
813 | +# This program is free software: you can redistribute it and/or modify it under |
814 | +# the terms of the GNU General Public License as published by the Free Software |
815 | +# Foundation, either version 3 of the License, or (at your option) any later |
816 | +# version. |
817 | +# |
818 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
819 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
820 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
821 | +# details. |
822 | +# |
823 | +# You should have received a copy of the GNU General Public License along with |
824 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
825 | +# ----------------------------------------------------------------------------- |
826 | + |
827 | +import gtk |
828 | + |
829 | +from GTG.gtk.backends_dialog.backendscombo import BackendsCombo |
830 | +from GTG.backends import BackendFactory |
831 | +from GTG import _, ngettext |
832 | + |
833 | +#The code for showing the required modules has been disabled since it |
834 | +# seems that backends will be packaged separately (as plugins). I'm |
835 | +# leaving this here in case we change that decision (invernizzi). |
836 | +#from GTG.tools.moduletopackage import ModuleToPackage |
837 | + |
838 | + |
839 | + |
840 | +class AddPanel(gtk.VBox): |
841 | + ''' |
842 | + A VBox filled with gtk widgets to let the user choose a new backend. |
843 | + ''' |
844 | + |
845 | + |
846 | + def __init__(self, backends_dialog): |
847 | + ''' |
848 | + Constructor, just initializes the gtk widgets |
849 | + |
850 | + @param backends_dialog: a reference to the dialog in which this is |
851 | + loaded |
852 | + ''' |
853 | + super(AddPanel, self).__init__() |
854 | + self.dialog = backends_dialog |
855 | + self._create_widgets() |
856 | + |
857 | + def _create_widgets(self): |
858 | + ''' |
859 | + gtk widgets initialization |
860 | + ''' |
861 | + #Division of the available space in three segments: |
862 | + # top, middle and bottom. |
863 | + top = gtk.HBox() |
864 | + middle = gtk.HBox() |
865 | + bottom = gtk.HBox() |
866 | + self._fill_top_hbox(top) |
867 | + self._fill_middle_hbox(middle) |
868 | + self._fill_bottom_hbox(bottom) |
869 | + self.pack_start(top, False) |
870 | + self.pack_start(middle, True) |
871 | + self.pack_start(bottom, True) |
872 | + |
873 | + def _fill_top_hbox(self, hbox): |
874 | + ''' |
875 | + Helper function to fill and hbox with a combobox that lists the |
876 | + available backends and a gtk.Label. |
877 | + |
878 | + @param hbox: the gtk.HBox to fill |
879 | + ''' |
880 | + label = gtk.Label("Select a backend") |
881 | + label.set_size_request(-1, 30) |
882 | + self.combo_types = BackendsCombo(self.dialog) |
883 | + self.combo_types.child.connect('changed', self.on_combo_changed) |
884 | + hbox.pack_start(label, True, True) |
885 | + hbox.pack_start(self.combo_types, False, True) |
886 | + |
887 | + def _fill_middle_hbox(self, hbox): |
888 | + ''' |
889 | + Helper function to fill an hbox with a label describing the backend |
890 | + and a gtk.Image (that loads the backend image) |
891 | + |
892 | + @param hbox: the gtk.HBox to fill |
893 | + ''' |
894 | + self.label_name = gtk.Label("name") |
895 | + self.label_name.set_alignment(xalign = 0.5, yalign = 1) |
896 | + self.label_description = gtk.Label() |
897 | + self.label_description.set_justify(gtk.JUSTIFY_FILL) |
898 | + self.label_description.set_line_wrap(True) |
899 | + self.label_description.set_size_request(300, -1) |
900 | + self.label_description.set_alignment(xalign = 0, yalign = 0.5) |
901 | + self.label_author = gtk.Label("") |
902 | + self.label_author.set_line_wrap(True) |
903 | + self.label_author.set_alignment(xalign = 0, yalign = 0) |
904 | + self.label_modules = gtk.Label("") |
905 | + self.label_modules.set_line_wrap(True) |
906 | + self.label_modules.set_alignment(xalign = 0, yalign = 0) |
907 | + self.image_icon = gtk.Image() |
908 | + self.image_icon.set_size_request(100, 100) |
909 | + align_image = gtk.Alignment(xalign = 1, yalign = 0) |
910 | + align_image.add(self.image_icon) |
911 | + labels_vbox = gtk.VBox() |
912 | + labels_vbox.pack_start(self.label_description, True, True) |
913 | + labels_vbox.pack_start(self.label_author, True, True) |
914 | + labels_vbox.pack_start(self.label_modules, True, True) |
915 | + low_hbox = gtk.HBox() |
916 | + low_hbox.pack_start(labels_vbox, True, True) |
917 | + low_hbox.pack_start(align_image, True, True) |
918 | + vbox = gtk.VBox() |
919 | + vbox.pack_start(self.label_name, True, True) |
920 | + vbox.pack_start(low_hbox, True, True) |
921 | + hbox.pack_start(vbox, True, True) |
922 | + |
923 | + def _fill_bottom_hbox(self, hbox): |
924 | + ''' |
925 | + Helper function to fill and hbox with a buttonbox, featuring |
926 | + and ok and cancel buttons. |
927 | + |
928 | + @param hbox: the gtk.HBox to fill |
929 | + ''' |
930 | + cancel_button = gtk.Button(stock = gtk.STOCK_CANCEL) |
931 | + cancel_button.connect('clicked', self.on_cancel) |
932 | + self.ok_button = gtk.Button(stock = gtk.STOCK_OK) |
933 | + self.ok_button.connect('clicked', self.on_confirm) |
934 | + align =gtk.Alignment(xalign = 0.5, \ |
935 | + yalign = 1, \ |
936 | + xscale = 1) |
937 | + align.set_padding(0, 10, 0, 0) |
938 | + buttonbox = gtk.HButtonBox() |
939 | + buttonbox.set_layout(gtk.BUTTONBOX_EDGE) |
940 | + buttonbox.add(cancel_button) |
941 | + buttonbox.set_child_secondary(cancel_button, False) |
942 | + buttonbox.add(self.ok_button) |
943 | + align.add(buttonbox) |
944 | + hbox.pack_start(align, True, True) |
945 | + |
946 | + def refresh_backends(self): |
947 | + '''Populates the combo box containing the available backends''' |
948 | + self.combo_types.refresh() |
949 | + |
950 | + def on_confirm(self, widget = None): |
951 | + ''' |
952 | + Notifies the dialog holding this VBox that a backend has been |
953 | + chosen |
954 | + |
955 | + @param widget: just to make this function usable as a signal callback. |
956 | + Not used. |
957 | + ''' |
958 | + backend_name = self.combo_types.get_selected() |
959 | + self.dialog.on_backend_added(backend_name) |
960 | + |
961 | + def on_cancel(self, widget = None): |
962 | + ''' |
963 | + Aborts the addition of a new backend. Shows the configuration panel |
964 | + previously loaded. |
965 | + |
966 | + @param widget: just to make this function usable as a signal callback. |
967 | + Not used. |
968 | + ''' |
969 | + self.dialog.show_config_for_backend(None) |
970 | + |
971 | + def on_combo_changed(self, widget = None): |
972 | + ''' |
973 | + Updates the backend description and icon. |
974 | + |
975 | + @param widget: just to make this function usable as a signal callback. |
976 | + Not used. |
977 | + ''' |
978 | + backend_name = self.combo_types.get_selected() |
979 | + if backend_name == None: |
980 | + return |
981 | + backend = BackendFactory().get_backend(backend_name) |
982 | + self.label_description.set_markup(backend.Backend.get_description()) |
983 | + |
984 | + label = _('Syncing is <span color="red">disabled</span>') |
985 | + markup = '<big><big><big><b>%s</b></big></big></big>' % \ |
986 | + backend.Backend.get_human_default_name() |
987 | + self.label_name.set_markup(markup) |
988 | + authors = backend.Backend.get_authors() |
989 | + author_txt = '<b>%s</b>:\n - %s' % \ |
990 | + (ngettext("Author", "Authors", len(authors)), |
991 | + reduce(lambda a, b: a + "\n" + " - " + b, authors)) |
992 | + self.label_author.set_markup(author_txt) |
993 | + #The code for showing the required modules has been disabled since it |
994 | + # seems that backends will be packaged separately (as plugins). I'm |
995 | + # leaving this here in case we change that decision (invernizzi). |
996 | + #self._build_module_list(backend.Backend) |
997 | + pixbuf = self.dialog.get_pixbuf_from_icon_name(backend_name, 100, 100) |
998 | + self.image_icon.set_from_pixbuf(pixbuf) |
999 | + self.show_all() |
1000 | + |
1001 | + #The code for showing the required modules has been disabled since it |
1002 | + # seems that backends will be packaged separately (as plugins). I'm |
1003 | + # leaving this here in case we change that decision (invernizzi). |
1004 | +# def _build_module_list(self, backend): |
1005 | +# missing_modules = [] |
1006 | +# for module in backend.get_required_modules(): |
1007 | +# try: |
1008 | +# __import__(module) |
1009 | +# except ImportError: |
1010 | +# missing_modules.append(module) |
1011 | +# if missing_modules: |
1012 | +# text = "<b> Missing modules:</b>\n - " |
1013 | +# module2package = ModuleToPackage() |
1014 | +# missing_modules = map(lambda a: \ |
1015 | +# "<span color='red'>" + \ |
1016 | +# module2package.lookup(a) +\ |
1017 | +# "</span>", missing_modules) |
1018 | +# text += reduce(lambda a, b: a + "\n - " + b, missing_modules) |
1019 | +# self.label_modules.set_markup(text) |
1020 | +# self.ok_button.set_sensitive(missing_modules == []) |
1021 | + |
1022 | |
1023 | === added file 'GTG/gtk/backends_dialog/backendscombo.py' |
1024 | --- GTG/gtk/backends_dialog/backendscombo.py 1970-01-01 00:00:00 +0000 |
1025 | +++ GTG/gtk/backends_dialog/backendscombo.py 2010-09-04 17:54:43 +0000 |
1026 | @@ -0,0 +1,92 @@ |
1027 | +# -*- coding: utf-8 -*- |
1028 | +# ----------------------------------------------------------------------------- |
1029 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
1030 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
1031 | +# |
1032 | +# This program is free software: you can redistribute it and/or modify it under |
1033 | +# the terms of the GNU General Public License as published by the Free Software |
1034 | +# Foundation, either version 3 of the License, or (at your option) any later |
1035 | +# version. |
1036 | +# |
1037 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
1038 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
1039 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
1040 | +# details. |
1041 | +# |
1042 | +# You should have received a copy of the GNU General Public License along with |
1043 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
1044 | +# ----------------------------------------------------------------------------- |
1045 | + |
1046 | +import gtk |
1047 | + |
1048 | +from GTG.backends import BackendFactory |
1049 | + |
1050 | + |
1051 | + |
1052 | +class BackendsCombo(gtk.ComboBoxEntry): |
1053 | + ''' |
1054 | + A combobox listing all the available backends types |
1055 | + ''' |
1056 | + |
1057 | + |
1058 | + COLUMN_NAME = 0 #unique name for the backend type. It's never |
1059 | + # displayed, it's used to find which backend has |
1060 | + # been selected |
1061 | + COLUMN_HUMAN_NAME = 1 #human friendly name (which is localized). |
1062 | + COLUMN_ICON = 2 |
1063 | + |
1064 | + def __init__(self, backends_dialog): |
1065 | + ''' |
1066 | + Constructor, itializes gtk widgets. |
1067 | + @param backends_dialog: reference to the dialog in which this combo is |
1068 | + loaded. |
1069 | + ''' |
1070 | + super(BackendsCombo, self).__init__() |
1071 | + self.dialog = backends_dialog |
1072 | + self._liststore_init() |
1073 | + self._renderers_init() |
1074 | + self.set_size_request(-1, 30) |
1075 | + self.show_all() |
1076 | + |
1077 | + def _liststore_init(self): |
1078 | + '''Setup the gtk.ListStore''' |
1079 | + self.liststore = gtk.ListStore(str, str, gtk.gdk.Pixbuf) |
1080 | + self.set_model(self.liststore) |
1081 | + |
1082 | + def _renderers_init(self): |
1083 | + '''Configure the cell renderers''' |
1084 | + #Text renderer |
1085 | + text_cell = gtk.CellRendererText() |
1086 | + self.pack_start(text_cell, False) |
1087 | + self.set_text_column(self.COLUMN_HUMAN_NAME) |
1088 | + #Icon renderer |
1089 | + pixbuf_cell = gtk.CellRendererPixbuf() |
1090 | + self.pack_start(pixbuf_cell, False) |
1091 | + self.add_attribute(pixbuf_cell, "pixbuf", self.COLUMN_ICON) |
1092 | + |
1093 | + def refresh(self): |
1094 | + ''' |
1095 | + Populates the combo box with the available backends |
1096 | + ''' |
1097 | + self.liststore.clear() |
1098 | + backend_types = BackendFactory().get_all_backends() |
1099 | + for name, module in backend_types.iteritems(): |
1100 | + pixbuf = self.dialog.get_pixbuf_from_icon_name(name, 16, 16) |
1101 | + self.liststore.append((name, \ |
1102 | + module.Backend.get_human_default_name(), \ |
1103 | + pixbuf)) |
1104 | + if backend_types: |
1105 | + #triggers a "changed" signal, which is used in the AddPanel to |
1106 | + #refresh the backend description and icon |
1107 | + self.set_active(0) |
1108 | + |
1109 | + def get_selected(self): |
1110 | + ''' |
1111 | + Returns the name of the selected backend, or None |
1112 | + ''' |
1113 | + selected_iter = self.get_active_iter() |
1114 | + if selected_iter: |
1115 | + return self.liststore.get_value(selected_iter, \ |
1116 | + BackendsCombo.COLUMN_NAME) |
1117 | + else: |
1118 | + return None |
1119 | |
1120 | === added file 'GTG/gtk/backends_dialog/backendstree.py' |
1121 | --- GTG/gtk/backends_dialog/backendstree.py 1970-01-01 00:00:00 +0000 |
1122 | +++ GTG/gtk/backends_dialog/backendstree.py 2010-09-04 17:54:43 +0000 |
1123 | @@ -0,0 +1,253 @@ |
1124 | +# -*- coding: utf-8 -*- |
1125 | +# ----------------------------------------------------------------------------- |
1126 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
1127 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
1128 | +# |
1129 | +# This program is free software: you can redistribute it and/or modify it under |
1130 | +# the terms of the GNU General Public License as published by the Free Software |
1131 | +# Foundation, either version 3 of the License, or (at your option) any later |
1132 | +# version. |
1133 | +# |
1134 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
1135 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
1136 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
1137 | +# details. |
1138 | +# |
1139 | +# You should have received a copy of the GNU General Public License along with |
1140 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
1141 | +# ----------------------------------------------------------------------------- |
1142 | + |
1143 | +import gtk |
1144 | + |
1145 | +from GTG.gtk.colors import get_colored_tags_markup |
1146 | +from GTG.backends.genericbackend import GenericBackend |
1147 | +from GTG.backends.backendsignals import BackendSignals |
1148 | + |
1149 | + |
1150 | + |
1151 | +class BackendsTree(gtk.TreeView): |
1152 | + ''' |
1153 | + gtk.TreeView that shows the currently loaded backends. |
1154 | + ''' |
1155 | + |
1156 | + |
1157 | + COLUMN_BACKEND_ID = 0 #never shown, used for internal lookup. |
1158 | + COLUMN_ICON = 1 |
1159 | + COLUMN_TEXT = 2 # holds the backend "human-readable" name |
1160 | + COLUMN_TAGS = 3 |
1161 | + |
1162 | + def __init__(self, backendsdialog): |
1163 | + ''' |
1164 | + Constructor, just initializes the gtk widgets |
1165 | + |
1166 | + @param backends_dialog: a reference to the dialog in which this is |
1167 | + loaded |
1168 | + ''' |
1169 | + super(BackendsTree,self).__init__() |
1170 | + self.dialog = backendsdialog |
1171 | + self.req = backendsdialog.get_requester() |
1172 | + self._init_liststore() |
1173 | + self._init_renderers() |
1174 | + self._init_signals() |
1175 | + self.refresh() |
1176 | + |
1177 | + def refresh(self): |
1178 | + '''refreshes the gtk.Liststore''' |
1179 | + self.backendid_to_iter = {} |
1180 | + self.liststore.clear() |
1181 | + for backend in self.req.get_all_backends(disabled = True): |
1182 | + self.add_backend(backend) |
1183 | + self.on_backend_state_changed(None, backend.get_id()) |
1184 | + |
1185 | + def on_backend_added(self, sender, backend_id): |
1186 | + ''' |
1187 | + Signal callback executed when a new backend is loaded |
1188 | + |
1189 | + @param sender: not used, only here to let this function be used as a |
1190 | + callback |
1191 | + @param backend_id: the id of the backend to add |
1192 | + ''' |
1193 | + #Add |
1194 | + backend = self.req.get_backend(backend_id) |
1195 | + if not backend: |
1196 | + return |
1197 | + self.add_backend(backend) |
1198 | + #Select |
1199 | + self.select_backend(backend_id) |
1200 | + #Update it's enabled state |
1201 | + self.on_backend_state_changed(None, backend.get_id()) |
1202 | + |
1203 | + def add_backend(self, backend): |
1204 | + ''' |
1205 | + Adds a new backend to the list |
1206 | + |
1207 | + @param backend_id: the id of the backend to add |
1208 | + ''' |
1209 | + if backend: |
1210 | + backend_iter = self.liststore.append([ \ |
1211 | + backend.get_id(), \ |
1212 | + self.dialog.get_pixbuf_from_icon_name(backend.get_name(), \ |
1213 | + 16, 16), \ |
1214 | + backend.get_human_name(), \ |
1215 | + self._get_markup_for_tags(backend.get_attached_tags()), \ |
1216 | + ]) |
1217 | + self.backendid_to_iter[backend.get_id()] = backend_iter |
1218 | + |
1219 | + |
1220 | + def on_backend_state_changed(self, sender, backend_id): |
1221 | + ''' |
1222 | + Signal callback executed when a backend is enabled/disabled. |
1223 | + |
1224 | + @param sender: not used, only here to let this function be used as a |
1225 | + callback |
1226 | + @param backend_id: the id of the backend to add |
1227 | + ''' |
1228 | + if backend_id in self.backendid_to_iter: |
1229 | + style = self.get_style() |
1230 | + b_iter = self.backendid_to_iter[backend_id] |
1231 | + b_path = self.liststore.get_path(b_iter) |
1232 | + backend = self.req.get_backend(backend_id) |
1233 | + backend_name = backend.get_human_name() |
1234 | + if backend.is_enabled(): |
1235 | + text = backend_name |
1236 | + else: |
1237 | + color = str(style.text[gtk.STATE_INSENSITIVE]) |
1238 | + text = "<span color='%s'>%s</span>" % \ |
1239 | + (color, backend_name) |
1240 | + self.liststore[b_path][self.COLUMN_TEXT] = text |
1241 | + |
1242 | + def _get_markup_for_tags(self, tag_names): |
1243 | + '''Given a list of tags names, generates the pango markup to render that |
1244 | + list with the tag colors used in GTG |
1245 | + |
1246 | + @param tag_names: the list of the tags (strings) |
1247 | + @return str: the pango markup string |
1248 | + ''' |
1249 | + if GenericBackend.ALLTASKS_TAG in tag_names: |
1250 | + tags_txt = "" |
1251 | + else: |
1252 | + tags_txt = get_colored_tags_markup(self.req, tag_names) |
1253 | + return "<small>" + tags_txt + "</small>" |
1254 | + |
1255 | + |
1256 | + def remove_backend(self, backend_id): |
1257 | + ''' Removes a backend from the treeview, and selects the first (to show |
1258 | + something in the configuration panel |
1259 | + |
1260 | + @param backend_id: the id of the backend to remove |
1261 | + ''' |
1262 | + if backend_id in self.backendid_to_iter: |
1263 | + self.liststore.remove(self.backendid_to_iter[backend_id]) |
1264 | + del self.backendid_to_iter[backend_id] |
1265 | + self.select_backend() |
1266 | + |
1267 | + def _init_liststore(self): |
1268 | + '''Creates the liststore''' |
1269 | + self.liststore = gtk.ListStore(object, gtk.gdk.Pixbuf, str, str) |
1270 | + self.set_model(self.liststore) |
1271 | + |
1272 | + def _init_renderers(self): |
1273 | + '''Initializes the cell renderers''' |
1274 | + # We hide the columns headers |
1275 | + self.set_headers_visible(False) |
1276 | + # For the backend icon |
1277 | + pixbuf_cell = gtk.CellRendererPixbuf() |
1278 | + tvcolumn_pixbuf = gtk.TreeViewColumn('Icon', pixbuf_cell) |
1279 | + tvcolumn_pixbuf.add_attribute(pixbuf_cell, 'pixbuf', self.COLUMN_ICON) |
1280 | + self.append_column(tvcolumn_pixbuf) |
1281 | + # For the backend name |
1282 | + text_cell = gtk.CellRendererText() |
1283 | + tvcolumn_text = gtk.TreeViewColumn('Name', text_cell) |
1284 | + tvcolumn_text.add_attribute(text_cell, 'markup', self.COLUMN_TEXT) |
1285 | + self.append_column(tvcolumn_text) |
1286 | + text_cell.connect('edited', self.cell_edited_callback) |
1287 | + text_cell.set_property('editable', True) |
1288 | + # For the backend tags |
1289 | + tags_cell = gtk.CellRendererText() |
1290 | + tvcolumn_tags = gtk.TreeViewColumn('Tags', tags_cell) |
1291 | + tvcolumn_tags.add_attribute(tags_cell, 'markup', self.COLUMN_TAGS) |
1292 | + self.append_column(tvcolumn_tags) |
1293 | + |
1294 | + def cell_edited_callback(self, text_cell, path, new_text): |
1295 | + '''If a backend name is changed, it saves the changes in the Backend |
1296 | + |
1297 | + @param text_cell: not used. The gtk.CellRendererText that emitted the |
1298 | + signal. Only here because it's passed by the signal |
1299 | + @param path: the gtk.TreePath of the edited cell |
1300 | + @param new_text: the new name of the backend |
1301 | + ''' |
1302 | + #we strip everything not permitted in backend names |
1303 | + new_text = ''.join(c for c in new_text if (c.isalnum() or\ |
1304 | + c in [" ", "-", "_"])) |
1305 | + selected_iter = self.liststore.get_iter(path) |
1306 | + # update the backend name |
1307 | + backend_id = self.liststore.get_value(selected_iter, \ |
1308 | + self.COLUMN_BACKEND_ID) |
1309 | + backend = self.dialog.get_requester().get_backend(backend_id) |
1310 | + if backend: |
1311 | + backend.set_human_name(new_text) |
1312 | + # update the text in the liststore |
1313 | + self.liststore.set(selected_iter, self.COLUMN_TEXT, new_text) |
1314 | + |
1315 | + def _init_signals(self): |
1316 | + '''Initializes the backends and gtk signals ''' |
1317 | + self.connect("cursor-changed", self.on_select_row) |
1318 | + _signals = BackendSignals() |
1319 | + _signals.connect(_signals.BACKEND_ADDED, self.on_backend_added) |
1320 | + _signals.connect(_signals.BACKEND_STATE_TOGGLED, |
1321 | + self.on_backend_state_changed) |
1322 | + |
1323 | + def on_select_row(self, treeview = None): |
1324 | + '''When a row is selected, displays the corresponding editing panel |
1325 | + |
1326 | + @treeview: not used |
1327 | + ''' |
1328 | + self.dialog.on_backend_selected(self.get_selected_backend_id()) |
1329 | + |
1330 | + def _get_selected_path(self): |
1331 | + ''' |
1332 | + Helper function to get the selected path |
1333 | + |
1334 | + @return gtk.TreePath : returns exactly one path for the selected object or |
1335 | + None |
1336 | + ''' |
1337 | + selection = self.get_selection() |
1338 | + if selection: |
1339 | + model, selected_paths = self.get_selection().get_selected_rows() |
1340 | + if selected_paths: |
1341 | + return selected_paths[0] |
1342 | + return None |
1343 | + |
1344 | + def select_backend(self, backend_id = None): |
1345 | + ''' |
1346 | + Selects the backend corresponding to backend_id. |
1347 | + If backend_id is none, refreshes the current configuration panel. |
1348 | + |
1349 | + @param backend_id: the id of the backend to select |
1350 | + ''' |
1351 | + selection = self.get_selection() |
1352 | + if backend_id in self.backendid_to_iter: |
1353 | + backend_iter = self.backendid_to_iter[backend_id] |
1354 | + if selection: |
1355 | + selection.select_iter(backend_iter) |
1356 | + else: |
1357 | + if self._get_selected_path(): |
1358 | + #We just reselect the currently selected entry |
1359 | + self.on_select_row() |
1360 | + else: |
1361 | + #If nothing is selected, we select the first entry |
1362 | + if selection: |
1363 | + selection.select_path("0") |
1364 | + self.dialog.on_backend_selected(self.get_selected_backend_id()) |
1365 | + |
1366 | + def get_selected_backend_id(self): |
1367 | + ''' |
1368 | + returns the selected backend id, or none |
1369 | + |
1370 | + @return string: the selected backend id (or None) |
1371 | + ''' |
1372 | + selected_path = self._get_selected_path() |
1373 | + if not selected_path: |
1374 | + return None |
1375 | + selected_iter = self.liststore.get_iter(selected_path) |
1376 | + return self.liststore.get_value(selected_iter, self.COLUMN_BACKEND_ID) |
1377 | |
1378 | === added file 'GTG/gtk/backends_dialog/configurepanel.py' |
1379 | --- GTG/gtk/backends_dialog/configurepanel.py 1970-01-01 00:00:00 +0000 |
1380 | +++ GTG/gtk/backends_dialog/configurepanel.py 2010-09-04 17:54:43 +0000 |
1381 | @@ -0,0 +1,304 @@ |
1382 | +# -*- coding: utf-8 -*- |
1383 | +# ----------------------------------------------------------------------------- |
1384 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
1385 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
1386 | +# |
1387 | +# This program is free software: you can redistribute it and/or modify it under |
1388 | +# the terms of the GNU General Public License as published by the Free Software |
1389 | +# Foundation, either version 3 of the License, or (at your option) any later |
1390 | +# version. |
1391 | +# |
1392 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
1393 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
1394 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
1395 | +# details. |
1396 | +# |
1397 | +# You should have received a copy of the GNU General Public License along with |
1398 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
1399 | +# ----------------------------------------------------------------------------- |
1400 | + |
1401 | +import gtk |
1402 | + |
1403 | +from GTG.gtk.colors import get_colored_tags_markup |
1404 | +from GTG import _, ngettext |
1405 | +from GTG.backends.genericbackend import GenericBackend |
1406 | +from GTG.gtk.backends_dialog.parameters_ui import ParametersUI |
1407 | +from GTG.backends.backendsignals import BackendSignals |
1408 | + |
1409 | + |
1410 | +class ConfigurePanel(gtk.VBox): |
1411 | + ''' |
1412 | + A VBox that lets you configure a backend |
1413 | + ''' |
1414 | + |
1415 | + |
1416 | + def __init__(self, backends_dialog): |
1417 | + ''' |
1418 | + Constructor, creating all the gtk widgets |
1419 | + |
1420 | + @param backends_dialog: a reference to the dialog in which this is |
1421 | + loaded |
1422 | + ''' |
1423 | + super(ConfigurePanel, self).__init__() |
1424 | + self.dialog = backends_dialog |
1425 | + self.should_spinner_be_shown = False |
1426 | + self.task_deleted_handle = None |
1427 | + self.task_added_handle = None |
1428 | + self.req = backends_dialog.get_requester() |
1429 | + self._create_widgets() |
1430 | + self._connect_signals() |
1431 | + |
1432 | + def _connect_signals(self): |
1433 | + ''' Connects the backends generated signals ''' |
1434 | + _signals = BackendSignals() |
1435 | + _signals.connect(_signals.BACKEND_RENAMED, self.refresh_title) |
1436 | + _signals.connect(_signals.BACKEND_STATE_TOGGLED, \ |
1437 | + self.refresh_sync_status) |
1438 | + _signals.connect(_signals.BACKEND_SYNC_STARTED, self.on_sync_started) |
1439 | + _signals.connect(_signals.BACKEND_SYNC_ENDED, self.on_sync_ended) |
1440 | + |
1441 | + def _create_widgets(self): |
1442 | + ''' |
1443 | + This function fills this Vbox with widgets |
1444 | + ''' |
1445 | + #Division of the available space in three segments: |
1446 | + # top, middle and bottom |
1447 | + top = gtk.HBox() |
1448 | + middle = gtk.HBox() |
1449 | + self._fill_top_hbox(top) |
1450 | + self._fill_middle_hbox(middle) |
1451 | + self.pack_start(top, False) |
1452 | + self.pack_start(middle, False) |
1453 | + align = gtk.Alignment(xalign = 0, yalign = 0, xscale = 1) |
1454 | + align.set_padding(10, 0, 0, 0) |
1455 | + self.parameters_ui = ParametersUI(self.req) |
1456 | + align.add(self.parameters_ui) |
1457 | + self.pack_start(align, False) |
1458 | + |
1459 | + def _fill_top_hbox(self, hbox): |
1460 | + ''' |
1461 | + Helper function to fill an hbox with an image, a spinner and |
1462 | + three labels |
1463 | + |
1464 | + @param hbox: the gtk.HBox to fill |
1465 | + ''' |
1466 | + hbox.set_spacing(10) |
1467 | + self.image_icon = gtk.Image() |
1468 | + self.image_icon.set_size_request(100, 100) |
1469 | + vbox = gtk.VBox() |
1470 | + hbox_top = gtk.HBox() |
1471 | + self.human_name_label = gtk.Label() |
1472 | + self.human_name_label.set_alignment(xalign = 0, yalign = 0.5) |
1473 | + try: |
1474 | + self.spinner = gtk.Spinner() |
1475 | + except AttributeError: |
1476 | + #worarkound for archlinux: bug #624204 |
1477 | + self.spinner = gtk.HBox() |
1478 | + self.spinner.connect("show", self.on_spinner_show) |
1479 | + self.spinner.set_size_request(32, 32) |
1480 | + align_spin = gtk.Alignment(xalign = 1, yalign = 0) |
1481 | + align_spin.add(self.spinner) |
1482 | + hbox_top.pack_start(self.human_name_label, True) |
1483 | + hbox_top.pack_start(align_spin, False) |
1484 | + self.sync_desc_label = gtk.Label() |
1485 | + self.sync_desc_label.set_alignment(xalign = 0, yalign = 1) |
1486 | + self.sync_desc_label.set_line_wrap(True) |
1487 | + vbox.pack_start(hbox_top, True) |
1488 | + vbox.pack_start(self.sync_desc_label, True) |
1489 | + hbox.pack_start(self.image_icon, False) |
1490 | + align_vbox = gtk.Alignment(xalign = 0, yalign = 0, xscale = 1) |
1491 | + align_vbox.set_padding(10, 0, 20, 0) |
1492 | + align_vbox.add(vbox) |
1493 | + hbox.pack_start(align_vbox, True) |
1494 | + |
1495 | + def _fill_middle_hbox(self, hbox): |
1496 | + ''' |
1497 | + Helper function to fill an hbox with a label and a button |
1498 | + |
1499 | + @param hbox: the gtk.HBox to fill |
1500 | + ''' |
1501 | + self.sync_status_label = gtk.Label() |
1502 | + self.sync_status_label.set_alignment(xalign = 0.8, yalign = 0.5) |
1503 | + self.sync_button = gtk.Button() |
1504 | + self.sync_button.connect("clicked", self.on_sync_button_clicked) |
1505 | + hbox.pack_start(self.sync_status_label, True) |
1506 | + hbox.pack_start(self.sync_button, True) |
1507 | + |
1508 | + def set_backend(self, backend_id): |
1509 | + '''Changes the backend to configure, refreshing this view. |
1510 | + |
1511 | + @param backend_id: the id of the backend to configure |
1512 | + ''' |
1513 | + self.backend = self.dialog.get_requester().get_backend(backend_id) |
1514 | + self.refresh_title() |
1515 | + self.refresh_sync_status() |
1516 | + self.parameters_ui.refresh(self.backend) |
1517 | + self.image_icon.set_from_pixbuf(self.dialog.get_pixbuf_from_icon_name(\ |
1518 | + self.backend.get_name(), 80, 80)) |
1519 | + |
1520 | + def refresh_title(self, sender = None, data = None): |
1521 | + ''' |
1522 | + Callback for the signal that notifies backends name changes. It changes |
1523 | + the title of this view |
1524 | + |
1525 | + @param sender: not used, here only for signal callback compatibility |
1526 | + @param data: not used, here only for signal callback compatibility |
1527 | + ''' |
1528 | + markup = "<big><big><big><b>%s</b></big></big></big>" % \ |
1529 | + self.backend.get_human_name() |
1530 | + self.human_name_label.set_markup(markup) |
1531 | + |
1532 | + def refresh_number_of_tasks(self): |
1533 | + '''refreshes the number of synced tasks by this backend''' |
1534 | + #FIXME: disabled for now. I'm not sure that this is nice because the |
1535 | + # count is correct only after the backend has synced all the pending |
1536 | + # tasks, and this is quite misleading (invernizzi) |
1537 | + return |
1538 | + #This will have to be changed for import/export.. |
1539 | + tags = self.backend.get_attached_tags() |
1540 | + tasks_number = self.backend.get_number_of_tasks() |
1541 | + if GenericBackend.ALLTASKS_TAG in tags: |
1542 | + if tasks_number == 0: |
1543 | + markup = _("Ready to start syncing") |
1544 | + else: |
1545 | + markup = ngettext("Syncing your only task", \ |
1546 | + "Syncing all %d tasks" % tasks_number, tasks_number) |
1547 | + else: |
1548 | + tags_txt = get_colored_tags_markup(self.req, tags) |
1549 | + if tasks_number == 0: |
1550 | + markup = _("There's no task tagged %s") % tags_txt |
1551 | + else: |
1552 | + markup = ngettext("Syncing a task tagged %s" % tags_txt, \ |
1553 | + "Syncing %d tasks tagged %s" % (tasks_number, tags_txt), \ |
1554 | + tasks_number) |
1555 | + self.sync_desc_label.set_markup(markup) |
1556 | + |
1557 | + def refresh_sync_button(self): |
1558 | + ''' |
1559 | + Refreshes the state of the button that enables the backend |
1560 | + ''' |
1561 | + self.sync_button.set_sensitive(not self.backend.is_default()) |
1562 | + if self.backend.is_enabled(): |
1563 | + label = _("Disable syncing") |
1564 | + else: |
1565 | + label = _("Enable syncing") |
1566 | + self.sync_button.set_label(label) |
1567 | + |
1568 | + def refresh_sync_status_label(self): |
1569 | + ''' |
1570 | + Refreshes the gtk.Label that shows the current state of this backend |
1571 | + ''' |
1572 | + if self.backend.is_default(): |
1573 | + label = _("This is the default backend") |
1574 | + else: |
1575 | + if self.backend.is_enabled(): |
1576 | + label = _("Syncing is enabled") |
1577 | + else: |
1578 | + label = _('Syncing is <span color="red">disabled</span>') |
1579 | + self.sync_status_label.set_markup(label) |
1580 | + |
1581 | + def refresh_sync_status(self, sender = False, data = False): |
1582 | + '''Signal callback function, called when a backend state |
1583 | + (enabled/disabled) changes. Refreshes this view. |
1584 | + |
1585 | + @param sender: not used, here only for signal callback compatibility |
1586 | + @param data: not used, here only for signal callback compatibility |
1587 | + ''' |
1588 | + self.refresh_number_of_tasks() |
1589 | + self.refresh_sync_button() |
1590 | + self.refresh_sync_status_label() |
1591 | + |
1592 | + def set_hidden(self, is_hidden): |
1593 | + ''' |
1594 | + Notifies this pane if it's hidden or not. We disconnect signals when |
1595 | + hidden, since there is no need to keep the UI updated. |
1596 | + Hopefully, this should make GTG faster :) |
1597 | + |
1598 | + @param is_hidden: boolean, True if the window is not visible |
1599 | + ''' |
1600 | + #These is only needed to refresh the number of synced tasks. |
1601 | + #since that is disabled for now, there is no need for this |
1602 | + |
1603 | +# if is_hidden: |
1604 | +# if self.task_added_handle: |
1605 | +# self.req.disconnect(self.task_added_handle) |
1606 | +# self.task_added_handle = None |
1607 | +# if self.task_deleted_handle: |
1608 | +# self.req.disconnect(self.task_deleted_handle) |
1609 | +# self.task_deleted_handle = None |
1610 | +# else: |
1611 | +# self.task_added_handle = self.req.connect("task-added", \ |
1612 | +# self.__on_task_changed) |
1613 | +# self.task_added_handle = self.req.connect("task-modified", \ |
1614 | +# self.__on_task_changed) |
1615 | +# self.task_deleted_handle = self.req.connect("task-deleted", \ |
1616 | +# self.__on_task_changed) |
1617 | +# |
1618 | +# def __on_task_changed(self, sender, task_id): |
1619 | +# ''' |
1620 | +# If tasks are added, modified or removed, updates the number of |
1621 | +# tasks of the current backend |
1622 | +# ''' |
1623 | +# self.refresh_sync_status() |
1624 | + |
1625 | + def on_sync_button_clicked(self, sender): |
1626 | + ''' |
1627 | + Signal callback when a backend is enabled/disabled via the UI button |
1628 | + |
1629 | + @param sender: not used, here only for signal callback compatibility |
1630 | + ''' |
1631 | + self.parameters_ui.commit_changes() |
1632 | + self.req.set_backend_enabled(self.backend.get_id(), \ |
1633 | + not self.backend.is_enabled()) |
1634 | + |
1635 | + def on_sync_started(self, sender, backend_id): |
1636 | + ''' |
1637 | + If the backend has started syncing tasks, update the state of the |
1638 | + gtk.Spinner |
1639 | + |
1640 | + @param sender: not used, here only for signal callback compatibility |
1641 | + @param backend_id: the id of the backend that emitted this signal |
1642 | + ''' |
1643 | + if backend_id == self.backend.get_id(): |
1644 | + self.spinner_set_active(True) |
1645 | + |
1646 | + def on_sync_ended(self, sender, backend_id): |
1647 | + ''' |
1648 | + If the backend has stopped syncing tasks, update the state of the |
1649 | + gtk.Spinner |
1650 | + |
1651 | + @param sender: not used, here only for signal callback compatibility |
1652 | + @param backend_id: the id of the backend that emitted this signal |
1653 | + ''' |
1654 | + |
1655 | + if backend_id == self.backend.get_id(): |
1656 | + self.spinner_set_active(False) |
1657 | + |
1658 | + def on_spinner_show(self, sender): |
1659 | + '''This signal callback hides the spinner if it's not supposed to be |
1660 | + seen. It's a workaround to let us call show_all on the whole window |
1661 | + while keeping this hidden (it's the only widget that requires special |
1662 | + attention) |
1663 | + |
1664 | + @param sender: not used, here only for signal callback compatibility |
1665 | + ''' |
1666 | + if self.should_spinner_be_shown == False: |
1667 | + self.spinner.hide() |
1668 | + |
1669 | + def spinner_set_active(self, active): |
1670 | + ''' |
1671 | + Enables/disables the gtk.Spinner, while showing/hiding it at the same |
1672 | + time |
1673 | + |
1674 | + @param active: True if the spinner should spin |
1675 | + ''' |
1676 | + self.should_spinner_be_shown = active |
1677 | + if active: |
1678 | + if isinstance(self.spinner, gtk.Spinner): |
1679 | + self.spinner.start() |
1680 | + self.spinner.show() |
1681 | + else: |
1682 | + self.spinner.hide() |
1683 | + if isinstance(self.spinner, gtk.Spinner): |
1684 | + self.spinner.stop() |
1685 | + |
1686 | |
1687 | === added directory 'GTG/gtk/backends_dialog/parameters_ui' |
1688 | === added file 'GTG/gtk/backends_dialog/parameters_ui/__init__.py' |
1689 | --- GTG/gtk/backends_dialog/parameters_ui/__init__.py 1970-01-01 00:00:00 +0000 |
1690 | +++ GTG/gtk/backends_dialog/parameters_ui/__init__.py 2010-09-04 17:54:43 +0000 |
1691 | @@ -0,0 +1,149 @@ |
1692 | +# -*- coding: utf-8 -*- |
1693 | +# ----------------------------------------------------------------------------- |
1694 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
1695 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
1696 | +# |
1697 | +# This program is free software: you can redistribute it and/or modify it under |
1698 | +# the terms of the GNU General Public License as published by the Free Software |
1699 | +# Foundation, either version 3 of the License, or (at your option) any later |
1700 | +# version. |
1701 | +# |
1702 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
1703 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
1704 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
1705 | +# details. |
1706 | +# |
1707 | +# You should have received a copy of the GNU General Public License along with |
1708 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
1709 | +# ----------------------------------------------------------------------------- |
1710 | +''' |
1711 | +This modules reads a bakcn configuration and generates a series of widgets to |
1712 | +let the user see the configuration and modify it. |
1713 | +In this manner, backends do not need to know anything about their UI since it's |
1714 | +built for them: it should play along the lines of the separation between GTG |
1715 | +server and client |
1716 | +''' |
1717 | + |
1718 | +import gtk |
1719 | +import functools |
1720 | + |
1721 | +from GTG import _ |
1722 | +from GTG.backends.genericbackend import GenericBackend |
1723 | +from GTG.gtk.backends_dialog.parameters_ui.importtagsui import ImportTagsUI |
1724 | +from GTG.gtk.backends_dialog.parameters_ui.textui import TextUI |
1725 | +from GTG.gtk.backends_dialog.parameters_ui.passwordui import PasswordUI |
1726 | +from GTG.gtk.backends_dialog.parameters_ui.periodui import PeriodUI |
1727 | +from GTG.gtk.backends_dialog.parameters_ui.checkboxui import CheckBoxUI |
1728 | +from GTG.gtk.backends_dialog.parameters_ui.pathui import PathUI |
1729 | + |
1730 | + |
1731 | + |
1732 | +class ParametersUI(gtk.VBox): |
1733 | + ''' |
1734 | + Given a bakcend, this gtk.VBox populates itself with all the necessary |
1735 | + widgets to view and edit a backend configuration |
1736 | + ''' |
1737 | + |
1738 | + |
1739 | + COMMON_WIDTH = 170 |
1740 | + |
1741 | + def __init__(self, requester): |
1742 | + '''Constructs the list of the possible widgets. |
1743 | + |
1744 | + @param requester: a GTG.core.requester.Requester object |
1745 | + ''' |
1746 | + super(ParametersUI, self).__init__(False) |
1747 | + self.req = requester |
1748 | + self.set_spacing(10) |
1749 | + |
1750 | + #builds a list of widget generators. More precisely, it's a |
1751 | + # list of tuples: (backend_parameter_name, widget_generator) |
1752 | + self.parameter_widgets = ( \ |
1753 | + ("import-tags", self.UI_generator(ImportTagsUI, \ |
1754 | + {"title": _("Import tags"), \ |
1755 | + "anybox_text": _("All tags"), \ |
1756 | + "somebox_text": _("Just these tags"), \ |
1757 | + "parameter_name": "import-tags"}) \ |
1758 | + ),\ |
1759 | + ("attached-tags", self.UI_generator(ImportTagsUI, \ |
1760 | + {"title": _("Tags to sync"), \ |
1761 | + "anybox_text": _("All tasks"), \ |
1762 | + "somebox_text": _("Tasks with these tags"), \ |
1763 | + "parameter_name": "attached-tags"}) \ |
1764 | + ),\ |
1765 | + ("path", self.UI_generator(PathUI)), \ |
1766 | + ("username", self.UI_generator(TextUI, \ |
1767 | + {"description": _("Username"), |
1768 | + "parameter_name": "username"}) |
1769 | + ), \ |
1770 | + ("password" , self.UI_generator(PasswordUI)), \ |
1771 | + ("period" , self.UI_generator(PeriodUI)), \ |
1772 | + ("import-from-replies", self.UI_generator(CheckBoxUI, \ |
1773 | + {"text": _("Import tasks from @ replies " + \ |
1774 | + "directed to you"), \ |
1775 | + "parameter": "import-from-replies"}) \ |
1776 | + ),\ |
1777 | + ("import-from-direct-messages", self.UI_generator(CheckBoxUI, \ |
1778 | + {"text": _("Import tasks from direct messages"), \ |
1779 | + "parameter": "import-from-direct-messages"}) \ |
1780 | + ),\ |
1781 | + ("import-from-my-tweets", self.UI_generator(CheckBoxUI, \ |
1782 | + {"text": _("Import tasks from your tweets"), \ |
1783 | + "parameter": "import-from-my-tweets"}) \ |
1784 | + ),\ |
1785 | + ("import-bug-tags", self.UI_generator(CheckBoxUI, \ |
1786 | + {"text": _("Tag your GTG tasks with the bug tags"), \ |
1787 | + "parameter": "import-bug-tags"}) \ |
1788 | + ),\ |
1789 | + ("tag-with-project-name", self.UI_generator(CheckBoxUI, \ |
1790 | + {"text": _("Tag your GTG tasks with the project " |
1791 | + "targeted by the bug"), \ |
1792 | + "parameter": "tag-with-project-name"}) \ |
1793 | + ),\ |
1794 | + ) |
1795 | + def UI_generator(self, param_type, special_arguments = {}): |
1796 | + '''A helper function to build a widget type from a template. |
1797 | + It passes to the created widget generator a series of common parameters, |
1798 | + plus the ones needed to specialize the given template |
1799 | + |
1800 | + @param param_type: the template to specialize |
1801 | + @param special_arguments: the arguments used for this particular widget |
1802 | + generator. |
1803 | + |
1804 | + @return function: return a widget generator, not a widget. the widget can |
1805 | + be obtained by calling widget_generator(backend) |
1806 | + ''' |
1807 | + return lambda backend: param_type(req = self.req, \ |
1808 | + backend = backend, \ |
1809 | + width = self.COMMON_WIDTH, \ |
1810 | + **special_arguments) |
1811 | + |
1812 | + def refresh(self, backend): |
1813 | + '''Builds the widgets necessary to configure the backend. If it doesn't |
1814 | + know how to render a widget, it simply skips it. |
1815 | + |
1816 | + @param backend: the backend that is being configured |
1817 | + ''' |
1818 | + #remove the old parameters UIs |
1819 | + def _remove_child(self, child): |
1820 | + self.remove(child) |
1821 | + self.foreach(functools.partial(_remove_child, self)) |
1822 | + #add new widgets |
1823 | + backend_parameters = backend.get_parameters() |
1824 | + if backend_parameters[GenericBackend.KEY_DEFAULT_BACKEND]: |
1825 | + #if it's the default backend, the user should not mess with it |
1826 | + return |
1827 | + for parameter_name, widget in self.parameter_widgets: |
1828 | + if parameter_name in backend_parameters: |
1829 | + self.pack_start(widget(backend), True) |
1830 | + self.show_all() |
1831 | + |
1832 | + def commit_changes(self): |
1833 | + ''' |
1834 | + Saves all the parameters at their current state (the user may have |
1835 | + modified them) |
1836 | + ''' |
1837 | + def _commit_changes(child): |
1838 | + child.commit_changes() |
1839 | + self.foreach(_commit_changes) |
1840 | + |
1841 | |
1842 | === added file 'GTG/gtk/backends_dialog/parameters_ui/checkboxui.py' |
1843 | --- GTG/gtk/backends_dialog/parameters_ui/checkboxui.py 1970-01-01 00:00:00 +0000 |
1844 | +++ GTG/gtk/backends_dialog/parameters_ui/checkboxui.py 2010-09-04 17:54:43 +0000 |
1845 | @@ -0,0 +1,72 @@ |
1846 | +# -*- coding: utf-8 -*- |
1847 | +# ----------------------------------------------------------------------------- |
1848 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
1849 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
1850 | +# |
1851 | +# This program is free software: you can redistribute it and/or modify it under |
1852 | +# the terms of the GNU General Public License as published by the Free Software |
1853 | +# Foundation, either version 3 of the License, or (at your option) any later |
1854 | +# version. |
1855 | +# |
1856 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
1857 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
1858 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
1859 | +# details. |
1860 | +# |
1861 | +# You should have received a copy of the GNU General Public License along with |
1862 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
1863 | +# ----------------------------------------------------------------------------- |
1864 | + |
1865 | +import gtk |
1866 | + |
1867 | + |
1868 | + |
1869 | +class CheckBoxUI(gtk.HBox): |
1870 | + ''' |
1871 | + It's a widget displaying a simple checkbox, with some text to explain its |
1872 | + meaning |
1873 | + ''' |
1874 | + |
1875 | + |
1876 | + def __init__(self, req, backend, width, text, parameter): |
1877 | + ''' |
1878 | + Creates the checkbox and the related label. |
1879 | + |
1880 | + @param req: a Requester |
1881 | + @param backend: a backend object |
1882 | + @param width: the width of the gtk.Label object |
1883 | + @param parameter: the backend parameter this checkbox should display and |
1884 | + modify |
1885 | + ''' |
1886 | + super(CheckBoxUI, self).__init__() |
1887 | + self.backend = backend |
1888 | + self.req = req |
1889 | + self.text = text |
1890 | + self.parameter = parameter |
1891 | + self._populate_gtk(width) |
1892 | + |
1893 | + def _populate_gtk(self, width): |
1894 | + '''Creates the checkbox and the related label |
1895 | + |
1896 | + @param width: the width of the gtk.Label object |
1897 | + ''' |
1898 | + self.checkbutton =gtk.CheckButton(label = self.text) |
1899 | + self.checkbutton.set_active(self.backend.get_parameters()[self.parameter]) |
1900 | + self.checkbutton.connect("toggled", self.on_modified) |
1901 | + self.pack_start(self.checkbutton, False) |
1902 | + |
1903 | + def commit_changes(self): |
1904 | + '''Saves the changes to the backend parameter''' |
1905 | + self.backend.set_parameter(self.parameter,\ |
1906 | + self.checkbutton.get_active()) |
1907 | + |
1908 | + def on_modified(self, sender = None): |
1909 | + ''' Signal callback, executed when the user clicks on the checkbox. |
1910 | + Disables the backend. The user will re-enable it to confirm the changes |
1911 | + (s)he made. |
1912 | + |
1913 | + @param sender: not used, only here for signal compatibility |
1914 | + ''' |
1915 | + if self.backend.is_enabled() and not self.backend.is_default(): |
1916 | + self.req.set_backend_enabled(self.backend.get_id(), False) |
1917 | + |
1918 | |
1919 | === added file 'GTG/gtk/backends_dialog/parameters_ui/importtagsui.py' |
1920 | --- GTG/gtk/backends_dialog/parameters_ui/importtagsui.py 1970-01-01 00:00:00 +0000 |
1921 | +++ GTG/gtk/backends_dialog/parameters_ui/importtagsui.py 2010-09-04 17:54:43 +0000 |
1922 | @@ -0,0 +1,135 @@ |
1923 | +# -*- coding: utf-8 -*- |
1924 | +# ----------------------------------------------------------------------------- |
1925 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
1926 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
1927 | +# |
1928 | +# This program is free software: you can redistribute it and/or modify it under |
1929 | +# the terms of the GNU General Public License as published by the Free Software |
1930 | +# Foundation, either version 3 of the License, or (at your option) any later |
1931 | +# version. |
1932 | +# |
1933 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
1934 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
1935 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
1936 | +# details. |
1937 | +# |
1938 | +# You should have received a copy of the GNU General Public License along with |
1939 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
1940 | +# ----------------------------------------------------------------------------- |
1941 | + |
1942 | +import gtk |
1943 | + |
1944 | +from GTG.backends.genericbackend import GenericBackend |
1945 | + |
1946 | + |
1947 | + |
1948 | +class ImportTagsUI(gtk.VBox): |
1949 | + ''' |
1950 | + It's a widget displaying a couple of radio buttons, a label and a textbox |
1951 | + to let the user change the attached tags (or imported) |
1952 | + ''' |
1953 | + |
1954 | + |
1955 | + def __init__(self, req, backend, width, title, anybox_text, somebox_text, \ |
1956 | + parameter_name): |
1957 | + '''Populates the widgets and refresh the tags to display |
1958 | + |
1959 | + @param req: a requester |
1960 | + @param backend: the backend to configure |
1961 | + @param width: the length of the radio buttons |
1962 | + @param title: the text for the label describing what this collection |
1963 | + of gtk widgets is used for |
1964 | + @param anybox_text: the text for the "Any tag matches" radio button |
1965 | + @param somebox_text: the text for the "only this set of tags matches" |
1966 | + radio button |
1967 | + @param parameter_name: the backend parameter this widget should modify |
1968 | + ''' |
1969 | + super(ImportTagsUI, self).__init__() |
1970 | + self.backend = backend |
1971 | + self.req = req |
1972 | + self.title = title |
1973 | + self.anybox_text = anybox_text |
1974 | + self.somebox_text = somebox_text |
1975 | + self.parameter_name = parameter_name |
1976 | + self._populate_gtk(width) |
1977 | + self._refresh_tags() |
1978 | + self._connect_signals() |
1979 | + |
1980 | + def _populate_gtk(self, width): |
1981 | + ''' |
1982 | + Populates the widgets |
1983 | + |
1984 | + @param width: the length of the radio buttons |
1985 | + ''' |
1986 | + title_label = gtk.Label() |
1987 | + title_label.set_alignment(xalign = 0, yalign = 0) |
1988 | + title_label.set_markup("<big><b>%s</b></big>" % self.title) |
1989 | + self.pack_start(title_label, True) |
1990 | + align = gtk.Alignment(xalign = 0, yalign = 0, xscale = 1) |
1991 | + align.set_padding(0, 0, 10, 0) |
1992 | + self.pack_start(align, True) |
1993 | + vbox = gtk.VBox() |
1994 | + align.add(vbox) |
1995 | + self.all_tags_radio = gtk.RadioButton(group = None, \ |
1996 | + label = self.anybox_text) |
1997 | + vbox.pack_start(self.all_tags_radio, True) |
1998 | + self.some_tags_radio = gtk.RadioButton(group = self.all_tags_radio, |
1999 | + label = self.somebox_text) |
2000 | + self.some_tags_radio.set_size_request(width = width, height = -1) |
2001 | + hbox = gtk.HBox() |
2002 | + vbox.pack_start(hbox, True) |
2003 | + hbox.pack_start(self.some_tags_radio, False) |
2004 | + self.tags_entry = gtk.Entry() |
2005 | + hbox.pack_start(self.tags_entry, True) |
2006 | + |
2007 | + def on_changed(self, radio, data = None): |
2008 | + ''' Signal callback, executed when the user modifies something. |
2009 | + Disables the backend. The user will re-enable it to confirm the changes |
2010 | + (s)he made. |
2011 | + |
2012 | + @param sender: not used, only here for signal compatibility |
2013 | + @param data: not used, only here for signal compatibility |
2014 | + ''' |
2015 | + #every change in the config disables the backend |
2016 | + self.req.set_backend_enabled(self.backend.get_id(), False) |
2017 | + self._refresh_textbox_state() |
2018 | + |
2019 | + def commit_changes(self): |
2020 | + '''Saves the changes to the backend parameter''' |
2021 | + if self.all_tags_radio.get_active(): |
2022 | + tags = [GenericBackend.ALLTASKS_TAG] |
2023 | + else: |
2024 | + tags = self.tags_entry.get_text().split(",") |
2025 | + #stripping spaces |
2026 | + tags = map(lambda t: t.strip(), tags) |
2027 | + #removing empty tags |
2028 | + tags = filter(lambda t: t, tags) |
2029 | + |
2030 | + self.backend.set_parameter(self.parameter_name, tags) |
2031 | + |
2032 | + def _refresh_textbox_state(self): |
2033 | + '''Refreshes the content of the textbox''' |
2034 | + self.tags_entry.set_sensitive(self.some_tags_radio.get_active()) |
2035 | + |
2036 | + def _refresh_tags(self): |
2037 | + ''' |
2038 | + Refreshes the list of tags to display in the textbox, and selects |
2039 | + the correct radio button |
2040 | + ''' |
2041 | + tags_list = self.backend.get_parameters()[self.parameter_name] |
2042 | + has_all_tasks = GenericBackend.ALLTASKS_TAG in tags_list |
2043 | + self.all_tags_radio.set_active(has_all_tasks) |
2044 | + self.some_tags_radio.set_active(not has_all_tasks) |
2045 | + self._refresh_textbox_state() |
2046 | + if not has_all_tasks: |
2047 | + tags_text = "" |
2048 | + if tags_list: |
2049 | + tags_text = reduce(lambda a, b: a + ", " + b, tags_list) |
2050 | + self.tags_entry.set_text(tags_text) |
2051 | + |
2052 | + def _connect_signals(self): |
2053 | + '''Connects the gtk signals''' |
2054 | + self.some_tags_radio.connect("toggled", self.on_changed) |
2055 | + self.all_tags_radio.connect("toggled", self.on_changed) |
2056 | + self.tags_entry.connect("changed", self.on_changed) |
2057 | + |
2058 | |
2059 | === added file 'GTG/gtk/backends_dialog/parameters_ui/passwordui.py' |
2060 | --- GTG/gtk/backends_dialog/parameters_ui/passwordui.py 1970-01-01 00:00:00 +0000 |
2061 | +++ GTG/gtk/backends_dialog/parameters_ui/passwordui.py 2010-09-04 17:54:43 +0000 |
2062 | @@ -0,0 +1,84 @@ |
2063 | +# -*- coding: utf-8 -*- |
2064 | +# ----------------------------------------------------------------------------- |
2065 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
2066 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
2067 | +# |
2068 | +# This program is free software: you can redistribute it and/or modify it under |
2069 | +# the terms of the GNU General Public License as published by the Free Software |
2070 | +# Foundation, either version 3 of the License, or (at your option) any later |
2071 | +# version. |
2072 | +# |
2073 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
2074 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
2075 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
2076 | +# details. |
2077 | +# |
2078 | +# You should have received a copy of the GNU General Public License along with |
2079 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
2080 | +# ----------------------------------------------------------------------------- |
2081 | + |
2082 | +import gtk |
2083 | + |
2084 | +from GTG import _ |
2085 | + |
2086 | + |
2087 | + |
2088 | +class PasswordUI(gtk.HBox): |
2089 | + '''Widget displaying a gtk.Label and a textbox to input a password''' |
2090 | + |
2091 | + |
2092 | + def __init__(self, req, backend, width): |
2093 | + '''Creates the gtk widgets and loads the current password in the text |
2094 | + field |
2095 | + |
2096 | + @param req: a Requester |
2097 | + @param backend: a backend object |
2098 | + @param width: the width of the gtk.Label object |
2099 | + ''' |
2100 | + super(PasswordUI, self).__init__() |
2101 | + self.backend = backend |
2102 | + self.req = req |
2103 | + self._populate_gtk(width) |
2104 | + self._load_password() |
2105 | + self._connect_signals() |
2106 | + |
2107 | + def _populate_gtk(self, width): |
2108 | + '''Creates the text box and the related label |
2109 | + |
2110 | + @param width: the width of the gtk.Label object |
2111 | + ''' |
2112 | + password_label = gtk.Label(_("Password:")) |
2113 | + password_label.set_alignment(xalign = 0, yalign = 0.5) |
2114 | + password_label.set_size_request(width = width, height = -1) |
2115 | + self.pack_start(password_label, False) |
2116 | + align = gtk.Alignment(xalign = 0, yalign = 0.5, xscale = 1) |
2117 | + align.set_padding(0, 0, 10, 0) |
2118 | + self.pack_start(align, True) |
2119 | + self.password_textbox = gtk.Entry() |
2120 | + align.add(self.password_textbox) |
2121 | + |
2122 | + def _load_password(self): |
2123 | + '''Loads the password from the backend''' |
2124 | + password = self.backend.get_parameters()['password'] |
2125 | + self.password_textbox.set_invisible_char('*') |
2126 | + self.password_textbox.set_visibility(False) |
2127 | + self.password_textbox.set_text(password) |
2128 | + |
2129 | + def _connect_signals(self): |
2130 | + '''Connects the gtk signals''' |
2131 | + self.password_textbox.connect('changed', self.on_password_modified) |
2132 | + |
2133 | + def commit_changes(self): |
2134 | + '''Saves the changes to the backend parameter ('password')''' |
2135 | + self.backend.set_parameter('password', self.password_textbox.get_text()) |
2136 | + |
2137 | + def on_password_modified(self, sender): |
2138 | + ''' Signal callback, executed when the user edits the password. |
2139 | + Disables the backend. The user will re-enable it to confirm the changes |
2140 | + (s)he made. |
2141 | + |
2142 | + @param sender: not used, only here for signal compatibility |
2143 | + ''' |
2144 | + if self.backend.is_enabled() and not self.backend.is_default(): |
2145 | + self.req.set_backend_enabled(self.backend.get_id(), False) |
2146 | + |
2147 | |
2148 | === added file 'GTG/gtk/backends_dialog/parameters_ui/pathui.py' |
2149 | --- GTG/gtk/backends_dialog/parameters_ui/pathui.py 1970-01-01 00:00:00 +0000 |
2150 | +++ GTG/gtk/backends_dialog/parameters_ui/pathui.py 2010-09-04 17:54:43 +0000 |
2151 | @@ -0,0 +1,112 @@ |
2152 | +# -*- coding: utf-8 -*- |
2153 | +# ----------------------------------------------------------------------------- |
2154 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
2155 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
2156 | +# |
2157 | +# This program is free software: you can redistribute it and/or modify it under |
2158 | +# the terms of the GNU General Public License as published by the Free Software |
2159 | +# Foundation, either version 3 of the License, or (at your option) any later |
2160 | +# version. |
2161 | +# |
2162 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
2163 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
2164 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
2165 | +# details. |
2166 | +# |
2167 | +# You should have received a copy of the GNU General Public License along with |
2168 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
2169 | +# ----------------------------------------------------------------------------- |
2170 | + |
2171 | +import gtk |
2172 | +import os.path |
2173 | + |
2174 | +from GTG import _ |
2175 | + |
2176 | + |
2177 | + |
2178 | + |
2179 | +class PathUI(gtk.HBox): |
2180 | + '''Gtk widgets to show a path in a textbox, and a button to bring up a |
2181 | + filesystem explorer to modify that path (also, a label to describe those) |
2182 | + ''' |
2183 | + |
2184 | + |
2185 | + def __init__(self, req, backend, width): |
2186 | + ''' |
2187 | + Creates the textbox, the button and loads the current path. |
2188 | + |
2189 | + @param req: a Requester |
2190 | + @param backend: a backend object |
2191 | + @param width: the width of the gtk.Label object |
2192 | + ''' |
2193 | + super(PathUI, self).__init__() |
2194 | + self.backend = backend |
2195 | + self.req = req |
2196 | + self._populate_gtk(width) |
2197 | + |
2198 | + def _populate_gtk(self, width): |
2199 | + '''Creates the gtk.Label, the textbox and the button |
2200 | + |
2201 | + @param width: the width of the gtk.Label object |
2202 | + ''' |
2203 | + label = gtk.Label(_("Filename:")) |
2204 | + label.set_line_wrap(True) |
2205 | + label.set_alignment(xalign = 0, yalign = 0.5) |
2206 | + label.set_size_request(width = width, height = -1) |
2207 | + self.pack_start(label, False) |
2208 | + align = gtk.Alignment(xalign = 0, yalign = 0.5, xscale = 1) |
2209 | + align.set_padding(0, 0, 10, 0) |
2210 | + self.pack_start(align, True) |
2211 | + self.textbox = gtk.Entry() |
2212 | + self.textbox.set_text(self.backend.get_parameters()['path']) |
2213 | + self.textbox.connect('changed', self.on_path_modified) |
2214 | + align.add(self.textbox) |
2215 | + self.button = gtk.Button(stock = gtk.STOCK_EDIT) |
2216 | + self.button.connect('clicked', self.on_button_clicked) |
2217 | + self.pack_start(self.button, False) |
2218 | + |
2219 | + def commit_changes(self): |
2220 | + '''Saves the changes to the backend parameter''' |
2221 | + self.backend.set_parameter('path', self.textbox.get_text()) |
2222 | + |
2223 | + def on_path_modified(self, sender): |
2224 | + ''' Signal callback, executed when the user edits the path. |
2225 | + Disables the backend. The user will re-enable it to confirm the changes |
2226 | + (s)he made. |
2227 | + |
2228 | + @param sender: not used, only here for signal compatibility |
2229 | + ''' |
2230 | + if self.backend.is_enabled() and not self.backend.is_default(): |
2231 | + self.req.set_backend_enabled(self.backend.get_id(), False) |
2232 | + |
2233 | + def on_button_clicked(self, sender): |
2234 | + '''Shows the filesystem explorer to choose a new file |
2235 | + |
2236 | + @param sender: not used, only here for signal compatibility |
2237 | + ''' |
2238 | + self.chooser = gtk.FileChooserDialog( \ |
2239 | + title=None, |
2240 | + action=gtk.FILE_CHOOSER_ACTION_SAVE, |
2241 | + buttons=(gtk.STOCK_CANCEL, |
2242 | + gtk.RESPONSE_CANCEL, \ |
2243 | + gtk.STOCK_OK, \ |
2244 | + gtk.RESPONSE_OK)) |
2245 | + self.chooser.set_default_response(gtk.RESPONSE_OK) |
2246 | + #set default file as the current self.path |
2247 | + self.chooser.set_current_name(os.path.basename(self.textbox.get_text())) |
2248 | + self.chooser.set_current_folder(os.path.dirname(self.textbox.get_text())) |
2249 | + |
2250 | + #filter files |
2251 | + afilter = gtk.FileFilter() |
2252 | + afilter.set_name("All files") |
2253 | + afilter.add_pattern("*") |
2254 | + self.chooser.add_filter(afilter) |
2255 | + afilter = gtk.FileFilter() |
2256 | + afilter.set_name("XML files") |
2257 | + afilter.add_mime_type("text/plain") |
2258 | + afilter.add_pattern("*.xml") |
2259 | + self.chooser.add_filter(afilter) |
2260 | + response = self.chooser.run() |
2261 | + if response == gtk.RESPONSE_OK: |
2262 | + self.textbox.set_text(self.chooser.get_filename()) |
2263 | + self.chooser.destroy() |
2264 | |
2265 | === added file 'GTG/gtk/backends_dialog/parameters_ui/periodui.py' |
2266 | --- GTG/gtk/backends_dialog/parameters_ui/periodui.py 1970-01-01 00:00:00 +0000 |
2267 | +++ GTG/gtk/backends_dialog/parameters_ui/periodui.py 2010-09-04 17:54:43 +0000 |
2268 | @@ -0,0 +1,97 @@ |
2269 | +# -*- coding: utf-8 -*- |
2270 | +# ----------------------------------------------------------------------------- |
2271 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
2272 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
2273 | +# |
2274 | +# This program is free software: you can redistribute it and/or modify it under |
2275 | +# the terms of the GNU General Public License as published by the Free Software |
2276 | +# Foundation, either version 3 of the License, or (at your option) any later |
2277 | +# version. |
2278 | +# |
2279 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
2280 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
2281 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
2282 | +# details. |
2283 | +# |
2284 | +# You should have received a copy of the GNU General Public License along with |
2285 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
2286 | +# ----------------------------------------------------------------------------- |
2287 | + |
2288 | +import gtk |
2289 | + |
2290 | +from GTG import _, ngettext |
2291 | + |
2292 | + |
2293 | + |
2294 | +class PeriodUI(gtk.HBox): |
2295 | + '''A widget to change the frequency of a backend synchronization |
2296 | + ''' |
2297 | + |
2298 | + |
2299 | + def __init__(self, req, backend, width): |
2300 | + ''' |
2301 | + Creates the gtk.Adjustment and the related label. Loads the current |
2302 | + period. |
2303 | + |
2304 | + @param req: a Requester |
2305 | + @param backend: a backend object |
2306 | + @param width: the width of the gtk.Label object |
2307 | + ''' |
2308 | + super(PeriodUI, self).__init__() |
2309 | + self.backend = backend |
2310 | + self.req = req |
2311 | + self._populate_gtk(width) |
2312 | + self._connect_signals() |
2313 | + |
2314 | + def _populate_gtk(self, width): |
2315 | + '''Creates the gtk widgets |
2316 | + |
2317 | + @param width: the width of the gtk.Label object |
2318 | + ''' |
2319 | + period_label = gtk.Label(_("Check for new tasks every")) |
2320 | + period_label.set_alignment(xalign = 0, yalign = 0.5) |
2321 | + period_label.set_line_wrap(True) |
2322 | + period_label.set_size_request(width = width, height = -1) |
2323 | + self.pack_start(period_label, False) |
2324 | + align = gtk.Alignment(xalign = 0, yalign = 0.5, xscale = 1) |
2325 | + align.set_padding(0, 0, 10, 0) |
2326 | + self.pack_start(align, False) |
2327 | + period = self.backend.get_parameters()['period'] |
2328 | + self.adjustment = gtk.Adjustment(value = period, |
2329 | + lower = 1, |
2330 | + upper = 120, |
2331 | + step_incr = 1, |
2332 | + page_incr = 0, |
2333 | + page_size = 0) |
2334 | + self.period_spin = gtk.SpinButton(adjustment = self.adjustment, |
2335 | + climb_rate = 0.3, |
2336 | + digits = 0) |
2337 | + self.minutes_label = gtk.Label() |
2338 | + self.update_minutes_label() |
2339 | + self.minutes_label.set_alignment(xalign = 0, yalign = 0.5) |
2340 | + self.pack_start(self.minutes_label, False) |
2341 | + align.add(self.period_spin) |
2342 | + self.show_all() |
2343 | + |
2344 | + def _connect_signals(self): |
2345 | + '''Connects the gtk signals''' |
2346 | + self.period_spin.connect('changed', self.on_spin_changed) |
2347 | + |
2348 | + def commit_changes(self): |
2349 | + '''Saves the changes to the backend parameter''' |
2350 | + self.backend.set_parameter('period', int(self.adjustment.get_value())) |
2351 | + |
2352 | + def on_spin_changed(self, sender): |
2353 | + ''' Signal callback, executed when the user changes the period. |
2354 | + Disables the backend. The user will re-enable it to confirm the changes |
2355 | + (s)he made. |
2356 | + |
2357 | + @param sender: not used, only here for signal compatibility |
2358 | + ''' |
2359 | + self.update_minutes_label() |
2360 | + if self.backend.is_enabled() and not self.backend.is_default(): |
2361 | + self.req.set_backend_enabled(self.backend.get_id(), False) |
2362 | + |
2363 | + def update_minutes_label(self): |
2364 | + self.minutes_label.set_markup(ngettext(" minute", " minutes", |
2365 | + int(self.adjustment.get_value()))) |
2366 | |
2367 | === added file 'GTG/gtk/backends_dialog/parameters_ui/textui.py' |
2368 | --- GTG/gtk/backends_dialog/parameters_ui/textui.py 1970-01-01 00:00:00 +0000 |
2369 | +++ GTG/gtk/backends_dialog/parameters_ui/textui.py 2010-09-04 17:54:43 +0000 |
2370 | @@ -0,0 +1,78 @@ |
2371 | +# -*- coding: utf-8 -*- |
2372 | +# ----------------------------------------------------------------------------- |
2373 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
2374 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
2375 | +# |
2376 | +# This program is free software: you can redistribute it and/or modify it under |
2377 | +# the terms of the GNU General Public License as published by the Free Software |
2378 | +# Foundation, either version 3 of the License, or (at your option) any later |
2379 | +# version. |
2380 | +# |
2381 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
2382 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
2383 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
2384 | +# details. |
2385 | +# |
2386 | +# You should have received a copy of the GNU General Public License along with |
2387 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
2388 | +# ----------------------------------------------------------------------------- |
2389 | + |
2390 | +import gtk |
2391 | + |
2392 | + |
2393 | + |
2394 | +class TextUI(gtk.HBox): |
2395 | + '''A widget to display a simple textbox and a label to describe its content |
2396 | + ''' |
2397 | + |
2398 | + |
2399 | + def __init__(self, req, backend, width, description, parameter_name): |
2400 | + ''' |
2401 | + Creates the textbox and the related label. Loads the current |
2402 | + content. |
2403 | + |
2404 | + @param req: a Requester |
2405 | + @param backend: a backend object |
2406 | + @param width: the width of the gtk.Label object |
2407 | + ''' |
2408 | + super(TextUI, self).__init__() |
2409 | + self.backend = backend |
2410 | + self.req = req |
2411 | + self.parameter_name = parameter_name |
2412 | + self.description = description |
2413 | + self._populate_gtk(width) |
2414 | + |
2415 | + def _populate_gtk(self, width): |
2416 | + '''Creates the gtk widgets |
2417 | + |
2418 | + @param width: the width of the gtk.Label object |
2419 | + ''' |
2420 | + label = gtk.Label("%s:" % self.description) |
2421 | + label.set_line_wrap(True) |
2422 | + label.set_alignment(xalign = 0, yalign = 0.5) |
2423 | + label.set_size_request(width = width, height = -1) |
2424 | + self.pack_start(label, False) |
2425 | + align = gtk.Alignment(xalign = 0, yalign = 0.5, xscale = 1) |
2426 | + align.set_padding(0, 0, 10, 0) |
2427 | + self.pack_start(align, True) |
2428 | + self.textbox = gtk.Entry() |
2429 | + self.textbox.set_text(\ |
2430 | + self.backend.get_parameters()[self.parameter_name]) |
2431 | + self.textbox.connect('changed', self.on_text_modified) |
2432 | + align.add(self.textbox) |
2433 | + |
2434 | + def commit_changes(self): |
2435 | + '''Saves the changes to the backend parameter''' |
2436 | + self.backend.set_parameter(self.parameter_name,\ |
2437 | + self.textbox.get_text()) |
2438 | + |
2439 | + def on_text_modified(self, sender): |
2440 | + ''' Signal callback, executed when the user changes the text. |
2441 | + Disables the backend. The user will re-enable it to confirm the changes |
2442 | + (s)he made. |
2443 | + |
2444 | + @param sender: not used, only here for signal compatibility |
2445 | + ''' |
2446 | + if self.backend.is_enabled() and not self.backend.is_default(): |
2447 | + self.req.set_backend_enabled(self.backend.get_id(), False) |
2448 | + |
2449 | |
2450 | === modified file 'GTG/gtk/browser/browser.py' |
2451 | --- GTG/gtk/browser/browser.py 2010-08-23 01:33:43 +0000 |
2452 | +++ GTG/gtk/browser/browser.py 2010-09-04 17:54:43 +0000 |
2453 | @@ -34,7 +34,9 @@ |
2454 | |
2455 | #our own imports |
2456 | import GTG |
2457 | -from GTG.core import CoreConfig |
2458 | +from GTG.backends.backendsignals import BackendSignals |
2459 | +from GTG.gtk.browser.custominfobar import CustomInfoBar |
2460 | +from GTG.core import CoreConfig |
2461 | from GTG import _, info, ngettext |
2462 | from GTG.core.task import Task |
2463 | from GTG.gtk.browser import GnomeConfig, tasktree, tagtree |
2464 | @@ -207,6 +209,7 @@ |
2465 | self.sidebar_notebook = self.builder.get_object("sidebar_notebook") |
2466 | self.main_notebook = self.builder.get_object("main_notebook") |
2467 | self.accessory_notebook = self.builder.get_object("accessory_notebook") |
2468 | + self.vbox_toolbars = self.builder.get_object("vbox_toolbars") |
2469 | |
2470 | self.closed_pane = None |
2471 | |
2472 | @@ -314,6 +317,8 @@ |
2473 | self.on_nonworkviewtag_toggled, |
2474 | "on_preferences_activate": |
2475 | self.open_preferences, |
2476 | + "on_edit_backends_activate": |
2477 | + self.open_edit_backends, |
2478 | } |
2479 | self.builder.connect_signals(SIGNAL_CONNECTIONS_DIC) |
2480 | |
2481 | @@ -347,6 +352,16 @@ |
2482 | # Connect requester signals to TreeModels |
2483 | self.req.connect("task-added", self.on_task_added) |
2484 | self.req.connect("task-deleted", self.on_task_deleted) |
2485 | + #this causes changed be shouwn only on save |
2486 | + #tree = self.task_tree_model.get_tree() |
2487 | + #tree.connect("task-added-inview", self.on_task_added) |
2488 | + #tree.connect("task-deleted-inview", self.on_task_deleted) |
2489 | + b_signals = BackendSignals() |
2490 | + b_signals.connect(b_signals.BACKEND_FAILED, self.on_backend_failed) |
2491 | + b_signals.connect(b_signals.BACKEND_STATE_TOGGLED, \ |
2492 | + self.remove_backend_infobar) |
2493 | + b_signals.connect(b_signals.INTERACTION_REQUESTED, \ |
2494 | + self.on_backend_needing_interaction) |
2495 | |
2496 | # Connect signals from models |
2497 | self.task_modelsort.connect("row-has-child-toggled",\ |
2498 | @@ -426,9 +441,12 @@ |
2499 | |
2500 | ### HELPER FUNCTIONS ######################################################## |
2501 | |
2502 | - def open_preferences(self,widget): |
2503 | + def open_preferences(self, widget): |
2504 | self.vmanager.open_preferences(self.priv) |
2505 | |
2506 | + def open_edit_backends(self, widget): |
2507 | + self.vmanager.open_edit_backends() |
2508 | + |
2509 | def quit(self,widget=None): |
2510 | self.vmanager.close_browser() |
2511 | |
2512 | @@ -523,7 +541,7 @@ |
2513 | col_id,\ |
2514 | self.priv["tasklist"]["sort_order"]) |
2515 | except: |
2516 | - print "Invalid configuration for sorting columns" |
2517 | + Log.error("Invalid configuration for sorting columns") |
2518 | |
2519 | if "view" in self.config["browser"]: |
2520 | view = self.config["browser"]["view"] |
2521 | @@ -1513,3 +1531,82 @@ |
2522 | """ Returns true if window is the currently active window """ |
2523 | return self.window.get_property("is-active") |
2524 | |
2525 | +## BACKENDS RELATED METHODS ################################################## |
2526 | + |
2527 | + def on_backend_failed(self, sender, backend_id, error_code): |
2528 | + ''' |
2529 | + Signal callback. |
2530 | + When a backend fails to work, loads a gtk.Infobar to alert the user |
2531 | + |
2532 | + @param sender: not used, only here for signal compatibility |
2533 | + @param backend_id: the id of the failing backend |
2534 | + @param error_code: a backend error code, as specified in BackendsSignals |
2535 | + ''' |
2536 | + infobar = self._new_infobar(backend_id) |
2537 | + infobar.set_error_code(error_code) |
2538 | + |
2539 | + def on_backend_needing_interaction(self, sender, backend_id, description, \ |
2540 | + interaction_type, callback): |
2541 | + ''' |
2542 | + Signal callback. |
2543 | + When a backend needs some kind of feedback from the user, |
2544 | + loads a gtk.Infobar to alert the user. |
2545 | + This is used, for example, to request confirmation after authenticating |
2546 | + via OAuth. |
2547 | + |
2548 | + @param sender: not used, only here for signal compatibility |
2549 | + @param backend_id: the id of the failing backend |
2550 | + @param description: a string describing the interaction needed |
2551 | + @param interaction_type: a string describing the type of interaction |
2552 | + (yes/no, only confirm, ok/cancel...) |
2553 | + @param callback: the function to call when the user provides the |
2554 | + feedback |
2555 | + ''' |
2556 | + infobar = self._new_infobar(backend_id) |
2557 | + infobar.set_interaction_request(description, interaction_type, callback) |
2558 | + |
2559 | + |
2560 | + def __remove_backend_infobar(self, child, backend_id): |
2561 | + ''' |
2562 | + Helper function to remove an gtk.Infobar related to a backend |
2563 | + |
2564 | + @param child: a gtk.Infobar |
2565 | + @param backend_id: the id of the backend which gtk.Infobar should be |
2566 | + removed. |
2567 | + ''' |
2568 | + if isinstance(child, CustomInfoBar) and\ |
2569 | + child.get_backend_id() == backend_id: |
2570 | + if self.vbox_toolbars: |
2571 | + self.vbox_toolbars.remove(child) |
2572 | + |
2573 | + def remove_backend_infobar(self, sender, backend_id): |
2574 | + ''' |
2575 | + Signal callback. |
2576 | + Deletes the gtk.Infobars related to a backend |
2577 | + |
2578 | + @param sender: not used, only here for signal compatibility |
2579 | + @param backend_id: the id of the backend which gtk.Infobar should be |
2580 | + removed. |
2581 | + ''' |
2582 | + backend = self.req.get_backend(backend_id) |
2583 | + if not backend or (backend and backend.is_enabled()): |
2584 | + #remove old infobar related to backend_id, if any |
2585 | + if self.vbox_toolbars: |
2586 | + self.vbox_toolbars.foreach(self.__remove_backend_infobar, \ |
2587 | + backend_id) |
2588 | + |
2589 | + def _new_infobar(self, backend_id): |
2590 | + ''' |
2591 | + Helper function to create a new infobar for a backend |
2592 | + |
2593 | + @param backend_id: the backend for which we're creating the infobar |
2594 | + @returns gtk.Infobar: the created infobar |
2595 | + ''' |
2596 | + #remove old infobar related to backend_id, if any |
2597 | + if not self.vbox_toolbars: |
2598 | + return |
2599 | + self.vbox_toolbars.foreach(self.__remove_backend_infobar, backend_id) |
2600 | + #add a new one |
2601 | + infobar = CustomInfoBar(self.req, self, self.vmanager, backend_id) |
2602 | + self.vbox_toolbars.pack_start(infobar, True) |
2603 | + return infobar |
2604 | |
2605 | === added file 'GTG/gtk/browser/custominfobar.py' |
2606 | --- GTG/gtk/browser/custominfobar.py 1970-01-01 00:00:00 +0000 |
2607 | +++ GTG/gtk/browser/custominfobar.py 2010-09-04 17:54:43 +0000 |
2608 | @@ -0,0 +1,210 @@ |
2609 | +# -*- coding: utf-8 -*- |
2610 | +# ----------------------------------------------------------------------------- |
2611 | +# Getting Things Gnome! - a personal organizer for the GNOME desktop |
2612 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
2613 | +# |
2614 | +# This program is free software: you can redistribute it and/or modify it under |
2615 | +# the terms of the GNU General Public License as published by the Free Software |
2616 | +# Foundation, either version 3 of the License, or (at your option) any later |
2617 | +# version. |
2618 | +# |
2619 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
2620 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
2621 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
2622 | +# details. |
2623 | +# |
2624 | +# You should have received a copy of the GNU General Public License along with |
2625 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
2626 | +# ----------------------------------------------------------------------------- |
2627 | + |
2628 | +import gtk |
2629 | +import threading |
2630 | + |
2631 | +from GTG import _ |
2632 | +from GTG.backends.backendsignals import BackendSignals |
2633 | +from GTG.tools.networkmanager import is_connection_up |
2634 | + |
2635 | + |
2636 | + |
2637 | +class CustomInfoBar(gtk.InfoBar): |
2638 | + ''' |
2639 | + A gtk.InfoBar specialized for displaying errors and requests for |
2640 | + interaction coming from the backends |
2641 | + ''' |
2642 | + |
2643 | + |
2644 | + AUTHENTICATION_MESSAGE = _("The <b>%s</b> backend cannot login with the " |
2645 | + "supplied authentication data and has been" |
2646 | + " disabled. To retry the login, re-enable the backend.") |
2647 | + |
2648 | + NETWORK_MESSAGE = _("Due to a network problem, I cannot contact " |
2649 | + "the <b>%s</b> backend.") |
2650 | + |
2651 | + DBUS_MESSAGE = _("Cannot connect to DBUS, I've disabled " |
2652 | + "the <b>%s</b> backend.") |
2653 | + |
2654 | + def __init__(self, req, browser, vmanager, backend_id): |
2655 | + ''' |
2656 | + Constructor, Prepares the infobar. |
2657 | + |
2658 | + @param req: a Requester object |
2659 | + @param browser: a TaskBrowser object |
2660 | + @param vmanager: a ViewManager object |
2661 | + @param backend_id: the id of the backend linked to the infobar |
2662 | + ''' |
2663 | + super(CustomInfoBar, self).__init__() |
2664 | + self.req = req |
2665 | + self.browser = browser |
2666 | + self.vmanager = vmanager |
2667 | + self.backend_id = backend_id |
2668 | + self.backend = self.req.get_backend(backend_id) |
2669 | + |
2670 | + def get_backend_id(self): |
2671 | + ''' |
2672 | + Getter function to return the id of the backend for which this |
2673 | + gtk.InfoBar was created |
2674 | + ''' |
2675 | + return self.backend_id |
2676 | + |
2677 | + def _populate(self): |
2678 | + '''Setting up gtk widgets''' |
2679 | + content_hbox = self.get_content_area() |
2680 | + content_hbox.set_homogeneous(False) |
2681 | + self.label = gtk.Label() |
2682 | + self.label.set_line_wrap(True) |
2683 | + self.label.set_alignment(0.5, 0.5) |
2684 | + self.label.set_justify(gtk.JUSTIFY_FILL) |
2685 | + content_hbox.pack_start(self.label, True, True) |
2686 | + |
2687 | + def _on_error_response(self, widget, event): |
2688 | + ''' |
2689 | + Signal callback executed when the user acknowledges the error displayed |
2690 | + in the infobar |
2691 | + |
2692 | + @param widget: not used, here for compatibility with signals callbacks |
2693 | + @param event: the code of the gtk response |
2694 | + ''' |
2695 | + self.hide() |
2696 | + if event == gtk.RESPONSE_ACCEPT: |
2697 | + self.vmanager.configure_backend(backend_id = self.backend_id) |
2698 | + |
2699 | + def set_error_code(self, error_code): |
2700 | + ''' |
2701 | + Sets this infobar to show an error to the user |
2702 | + |
2703 | + @param error_code: the code of the error to show. Error codes are listed |
2704 | + in BackendSignals |
2705 | + ''' |
2706 | + self._populate() |
2707 | + self.connect("response", self._on_error_response) |
2708 | + backend_name = self.backend.get_human_name() |
2709 | + |
2710 | + if error_code == BackendSignals.ERRNO_AUTHENTICATION: |
2711 | + self.set_message_type(gtk.MESSAGE_ERROR) |
2712 | + self.label.set_markup(self.AUTHENTICATION_MESSAGE % backend_name) |
2713 | + self.add_button(_('Configure backend'), gtk.RESPONSE_ACCEPT) |
2714 | + self.add_button(_('Ignore'), gtk.RESPONSE_CLOSE) |
2715 | + |
2716 | + elif error_code == BackendSignals.ERRNO_NETWORK: |
2717 | + if not is_connection_up(): |
2718 | + return |
2719 | + self.set_message_type(gtk.MESSAGE_WARNING) |
2720 | + self.label.set_markup(self.NETWORK_MESSAGE % backend_name) |
2721 | + #FIXME: use gtk stock button instead |
2722 | + self.add_button(_('Ok'), gtk.RESPONSE_CLOSE) |
2723 | + |
2724 | + elif error_code == BackendSignals.ERRNO_DBUS: |
2725 | + self.set_message_type(gtk.MESSAGE_WARNING) |
2726 | + self.label.set_markup(self.DBUS_MESSAGE % backend_name) |
2727 | + self.add_button(_('Ok'), gtk.RESPONSE_CLOSE) |
2728 | + |
2729 | + self.show_all() |
2730 | + |
2731 | + def set_interaction_request(self, description, interaction_type, callback): |
2732 | + ''' |
2733 | + Sets this infobar to request an interaction from the user |
2734 | + |
2735 | + @param description: a string describing the interaction needed |
2736 | + @param interaction_type: a string describing the type of interaction |
2737 | + (yes/no, only confirm, ok/cancel...) |
2738 | + @param callback: the function to call when the user provides the |
2739 | + feedback |
2740 | + ''' |
2741 | + self._populate() |
2742 | + self.callback = callback |
2743 | + self.set_message_type(gtk.MESSAGE_INFO) |
2744 | + self.label.set_markup(description) |
2745 | + self.connect("response", self._on_interaction_response) |
2746 | + self.interaction_type = interaction_type |
2747 | + if interaction_type == BackendSignals().INTERACTION_CONFIRM: |
2748 | + self.add_button(_('Confirm'), gtk.RESPONSE_ACCEPT) |
2749 | + elif interaction_type == BackendSignals().INTERACTION_TEXT: |
2750 | + self.add_button(_('Continue'), gtk.RESPONSE_ACCEPT) |
2751 | + self.show_all() |
2752 | + |
2753 | + def _on_interaction_response(self, widget, event): |
2754 | + ''' |
2755 | + Signal callback executed when the user gives the feedback for a |
2756 | + requested interaction |
2757 | + |
2758 | + @param widget: not used, here for compatibility with signals callbacks |
2759 | + @param event: the code of the gtk response |
2760 | + ''' |
2761 | + if event == gtk.RESPONSE_ACCEPT: |
2762 | + if self.interaction_type == BackendSignals().INTERACTION_TEXT: |
2763 | + self._prepare_textual_interaction() |
2764 | + print "done" |
2765 | + elif self.interaction_type == BackendSignals().INTERACTION_CONFIRM: |
2766 | + self.hide() |
2767 | + threading.Thread(target = getattr(self.backend, |
2768 | + self.callback)).start() |
2769 | + |
2770 | + def _prepare_textual_interaction(self): |
2771 | + ''' |
2772 | + Helper function. gtk calls to populate the infobar in the case of |
2773 | + interaction request |
2774 | + ''' |
2775 | + title, description\ |
2776 | + = getattr(self.backend, self.callback)("get_title") |
2777 | + self.dialog = gtk.Window()#type = gtk.WINDOW_POPUP) |
2778 | + self.dialog.set_title(title) |
2779 | + self.dialog.set_transient_for(self.browser.window) |
2780 | + self.dialog.set_destroy_with_parent(True) |
2781 | + self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT) |
2782 | + self.dialog.set_modal(True) |
2783 | + # self.dialog.set_size_request(300,170) |
2784 | + vbox = gtk.VBox() |
2785 | + self.dialog.add(vbox) |
2786 | + description_label = gtk.Label() |
2787 | + description_label.set_justify(gtk.JUSTIFY_FILL) |
2788 | + description_label.set_line_wrap(True) |
2789 | + description_label.set_markup(description) |
2790 | + align = gtk.Alignment(0.5, 0.5, 1, 1) |
2791 | + align.set_padding(10, 0, 20, 20) |
2792 | + align.add(description_label) |
2793 | + vbox.pack_start(align) |
2794 | + self.text_box = gtk.Entry() |
2795 | + self.text_box.set_size_request(-1, 40) |
2796 | + align = gtk.Alignment(0.5, 0.5, 1, 1) |
2797 | + align.set_padding(20, 20, 20, 20) |
2798 | + align.add(self.text_box) |
2799 | + vbox.pack_start(align) |
2800 | + button = gtk.Button(stock = gtk.STOCK_OK) |
2801 | + button.connect("clicked", self._on_text_confirmed) |
2802 | + button.set_size_request(-1, 40) |
2803 | + vbox.pack_start(button, False) |
2804 | + self.dialog.show_all() |
2805 | + self.hide() |
2806 | + |
2807 | + def _on_text_confirmed(self, widget): |
2808 | + ''' |
2809 | + Signal callback, used when the interaction needs a textual input to be |
2810 | + completed (e.g, the twitter OAuth, requesting a pin) |
2811 | + |
2812 | + @param widget: not used, here for signal callback compatibility |
2813 | + ''' |
2814 | + text = self.text_box.get_text() |
2815 | + self.dialog.destroy() |
2816 | + threading.Thread(target = getattr(self.backend, self.callback), |
2817 | + args = ("set_text", text)).start() |
2818 | + |
2819 | |
2820 | === modified file 'GTG/gtk/browser/taskbrowser.glade' |
2821 | --- GTG/gtk/browser/taskbrowser.glade 2010-05-22 22:41:44 +0000 |
2822 | +++ GTG/gtk/browser/taskbrowser.glade 2010-09-04 17:54:43 +0000 |
2823 | @@ -11,7 +11,6 @@ |
2824 | <child> |
2825 | <object class="GtkVBox" id="master_vbox"> |
2826 | <property name="visible">True</property> |
2827 | - <property name="orientation">vertical</property> |
2828 | <child> |
2829 | <object class="GtkMenuBar" id="browser_menu"> |
2830 | <property name="visible">True</property> |
2831 | @@ -154,6 +153,13 @@ |
2832 | <signal name="activate" handler="on_preferences_activate"/> |
2833 | </object> |
2834 | </child> |
2835 | + <child> |
2836 | + <object class="GtkMenuItem" id="backends_mi"> |
2837 | + <property name="label">Backends</property> |
2838 | + <property name="visible">True</property> |
2839 | + <signal name="activate" handler="on_edit_backends_activate"/> |
2840 | + </object> |
2841 | + </child> |
2842 | </object> |
2843 | </child> |
2844 | </object> |
2845 | @@ -253,6 +259,7 @@ |
2846 | <property name="label">gtk-help</property> |
2847 | <property name="visible">True</property> |
2848 | <property name="tooltip_text" translatable="yes">Open GTG documentation in your web browser</property> |
2849 | + <property name="use_underline">True</property> |
2850 | <property name="use_stock">True</property> |
2851 | <property name="accel_group">accelgroup1</property> |
2852 | <signal name="activate" handler="on_documentation_clicked"/> |
2853 | @@ -279,146 +286,156 @@ |
2854 | </packing> |
2855 | </child> |
2856 | <child> |
2857 | - <object class="GtkToolbar" id="task_toolbar"> |
2858 | + <object class="GtkVBox" id="vbox_toolbars"> |
2859 | <property name="visible">True</property> |
2860 | <child> |
2861 | - <object class="GtkToolButton" id="new_task_b"> |
2862 | - <property name="visible">True</property> |
2863 | - <property name="is_important">True</property> |
2864 | - <property name="label" translatable="yes">New Task</property> |
2865 | - <property name="icon_name">gtg-task-new</property> |
2866 | - <signal name="clicked" handler="on_add_task"/> |
2867 | - </object> |
2868 | - <packing> |
2869 | - <property name="expand">False</property> |
2870 | - <property name="homogeneous">True</property> |
2871 | - </packing> |
2872 | - </child> |
2873 | - <child> |
2874 | - <object class="GtkToolButton" id="new_subtask_b"> |
2875 | - <property name="label" translatable="yes">New Subtask</property> |
2876 | - <property name="icon_name">gtg-task-new</property> |
2877 | - <signal name="clicked" handler="on_add_subtask"/> |
2878 | - </object> |
2879 | - <packing> |
2880 | - <property name="expand">False</property> |
2881 | - <property name="homogeneous">True</property> |
2882 | - </packing> |
2883 | - </child> |
2884 | - <child> |
2885 | - <object class="GtkToolButton" id="edit_b"> |
2886 | - <property name="visible">True</property> |
2887 | - <property name="visible_horizontal">False</property> |
2888 | - <property name="visible_vertical">False</property> |
2889 | - <property name="label" translatable="yes">Edit</property> |
2890 | - <property name="stock_id">gtk-edit</property> |
2891 | - <signal name="clicked" handler="on_edit_active_task"/> |
2892 | - </object> |
2893 | - <packing> |
2894 | - <property name="expand">False</property> |
2895 | - <property name="homogeneous">True</property> |
2896 | - </packing> |
2897 | - </child> |
2898 | - <child> |
2899 | - <object class="GtkSeparatorToolItem" id="<separateur>"/> |
2900 | - <packing> |
2901 | - <property name="expand">False</property> |
2902 | - <property name="homogeneous">True</property> |
2903 | - </packing> |
2904 | - </child> |
2905 | - <child> |
2906 | - <object class="GtkToolButton" id="Undo"> |
2907 | - <property name="label" translatable="yes">Undo</property> |
2908 | - <property name="stock_id">gtk-undo</property> |
2909 | - </object> |
2910 | - <packing> |
2911 | - <property name="expand">False</property> |
2912 | - <property name="homogeneous">True</property> |
2913 | - </packing> |
2914 | - </child> |
2915 | - <child> |
2916 | - <object class="GtkToolButton" id="Redo"> |
2917 | - <property name="label" translatable="yes">Redo</property> |
2918 | - <property name="stock_id">gtk-redo</property> |
2919 | - </object> |
2920 | - <packing> |
2921 | - <property name="expand">False</property> |
2922 | - <property name="homogeneous">True</property> |
2923 | - </packing> |
2924 | - </child> |
2925 | - <child> |
2926 | - <object class="GtkSeparatorToolItem" id="separator_6"> |
2927 | - <property name="visible">True</property> |
2928 | - </object> |
2929 | - <packing> |
2930 | - <property name="expand">False</property> |
2931 | - <property name="homogeneous">True</property> |
2932 | - </packing> |
2933 | - </child> |
2934 | - <child> |
2935 | - <object class="GtkToolButton" id="done_b"> |
2936 | - <property name="visible">True</property> |
2937 | - <property name="sensitive">False</property> |
2938 | - <property name="is_important">True</property> |
2939 | - <property name="label" translatable="yes">Mark as Done</property> |
2940 | - <property name="icon_name">gtg-task-done</property> |
2941 | - <signal name="clicked" handler="on_mark_as_done"/> |
2942 | - </object> |
2943 | - <packing> |
2944 | - <property name="expand">False</property> |
2945 | - <property name="homogeneous">True</property> |
2946 | - </packing> |
2947 | - </child> |
2948 | - <child> |
2949 | - <object class="GtkToolButton" id="dismiss_b"> |
2950 | - <property name="visible">True</property> |
2951 | - <property name="sensitive">False</property> |
2952 | - <property name="label" translatable="yes">Dismiss</property> |
2953 | - <property name="icon_name">gtg-task-dismiss</property> |
2954 | - <signal name="clicked" handler="on_dismiss_task"/> |
2955 | - </object> |
2956 | - <packing> |
2957 | - <property name="expand">False</property> |
2958 | - <property name="homogeneous">True</property> |
2959 | - </packing> |
2960 | - </child> |
2961 | - <child> |
2962 | - <object class="GtkToolButton" id="delete_b"> |
2963 | - <property name="sensitive">False</property> |
2964 | - <property name="label" translatable="yes">Delete</property> |
2965 | - <property name="icon_name">edit-delete</property> |
2966 | - <signal name="clicked" handler="on_delete_task"/> |
2967 | - </object> |
2968 | - <packing> |
2969 | - <property name="expand">False</property> |
2970 | - <property name="homogeneous">True</property> |
2971 | - </packing> |
2972 | - </child> |
2973 | - <child> |
2974 | - <object class="GtkSeparatorToolItem" id="separator_7"> |
2975 | - <property name="visible">True</property> |
2976 | - </object> |
2977 | - <packing> |
2978 | - <property name="expand">False</property> |
2979 | - <property name="homogeneous">True</property> |
2980 | - </packing> |
2981 | - </child> |
2982 | - <child> |
2983 | - <object class="GtkToggleToolButton" id="workview_toggle"> |
2984 | - <property name="visible">True</property> |
2985 | - <property name="is_important">True</property> |
2986 | - <property name="label" translatable="yes">Work View</property> |
2987 | - <property name="stock_id">gtk-index</property> |
2988 | - <signal name="toggled" handler="on_workview_toggled"/> |
2989 | - </object> |
2990 | - <packing> |
2991 | - <property name="expand">False</property> |
2992 | - <property name="homogeneous">True</property> |
2993 | + <object class="GtkToolbar" id="task_toolbar"> |
2994 | + <property name="visible">True</property> |
2995 | + <child> |
2996 | + <object class="GtkToolButton" id="new_task_b"> |
2997 | + <property name="visible">True</property> |
2998 | + <property name="is_important">True</property> |
2999 | + <property name="label" translatable="yes">New Task</property> |
3000 | + <property name="icon_name">gtg-task-new</property> |
3001 | + <signal name="clicked" handler="on_add_task"/> |
3002 | + </object> |
3003 | + <packing> |
3004 | + <property name="expand">False</property> |
3005 | + <property name="homogeneous">True</property> |
3006 | + </packing> |
3007 | + </child> |
3008 | + <child> |
3009 | + <object class="GtkToolButton" id="new_subtask_b"> |
3010 | + <property name="label" translatable="yes">New Subtask</property> |
3011 | + <property name="icon_name">gtg-task-new</property> |
3012 | + <signal name="clicked" handler="on_add_subtask"/> |
3013 | + </object> |
3014 | + <packing> |
3015 | + <property name="expand">False</property> |
3016 | + <property name="homogeneous">True</property> |
3017 | + </packing> |
3018 | + </child> |
3019 | + <child> |
3020 | + <object class="GtkToolButton" id="edit_b"> |
3021 | + <property name="visible">True</property> |
3022 | + <property name="visible_horizontal">False</property> |
3023 | + <property name="visible_vertical">False</property> |
3024 | + <property name="label" translatable="yes">Edit</property> |
3025 | + <property name="stock_id">gtk-edit</property> |
3026 | + <signal name="clicked" handler="on_edit_active_task"/> |
3027 | + </object> |
3028 | + <packing> |
3029 | + <property name="expand">False</property> |
3030 | + <property name="homogeneous">True</property> |
3031 | + </packing> |
3032 | + </child> |
3033 | + <child> |
3034 | + <object class="GtkSeparatorToolItem" id="<separateur>"/> |
3035 | + <packing> |
3036 | + <property name="expand">False</property> |
3037 | + <property name="homogeneous">True</property> |
3038 | + </packing> |
3039 | + </child> |
3040 | + <child> |
3041 | + <object class="GtkToolButton" id="Undo"> |
3042 | + <property name="label" translatable="yes">Undo</property> |
3043 | + <property name="stock_id">gtk-undo</property> |
3044 | + </object> |
3045 | + <packing> |
3046 | + <property name="expand">False</property> |
3047 | + <property name="homogeneous">True</property> |
3048 | + </packing> |
3049 | + </child> |
3050 | + <child> |
3051 | + <object class="GtkToolButton" id="Redo"> |
3052 | + <property name="label" translatable="yes">Redo</property> |
3053 | + <property name="stock_id">gtk-redo</property> |
3054 | + </object> |
3055 | + <packing> |
3056 | + <property name="expand">False</property> |
3057 | + <property name="homogeneous">True</property> |
3058 | + </packing> |
3059 | + </child> |
3060 | + <child> |
3061 | + <object class="GtkSeparatorToolItem" id="separator_6"> |
3062 | + <property name="visible">True</property> |
3063 | + </object> |
3064 | + <packing> |
3065 | + <property name="expand">False</property> |
3066 | + <property name="homogeneous">True</property> |
3067 | + </packing> |
3068 | + </child> |
3069 | + <child> |
3070 | + <object class="GtkToolButton" id="done_b"> |
3071 | + <property name="visible">True</property> |
3072 | + <property name="sensitive">False</property> |
3073 | + <property name="is_important">True</property> |
3074 | + <property name="label" translatable="yes">Mark as Done</property> |
3075 | + <property name="icon_name">gtg-task-done</property> |
3076 | + <signal name="clicked" handler="on_mark_as_done"/> |
3077 | + </object> |
3078 | + <packing> |
3079 | + <property name="expand">False</property> |
3080 | + <property name="homogeneous">True</property> |
3081 | + </packing> |
3082 | + </child> |
3083 | + <child> |
3084 | + <object class="GtkToolButton" id="dismiss_b"> |
3085 | + <property name="visible">True</property> |
3086 | + <property name="sensitive">False</property> |
3087 | + <property name="label" translatable="yes">Dismiss</property> |
3088 | + <property name="icon_name">gtg-task-dismiss</property> |
3089 | + <signal name="clicked" handler="on_dismiss_task"/> |
3090 | + </object> |
3091 | + <packing> |
3092 | + <property name="expand">False</property> |
3093 | + <property name="homogeneous">True</property> |
3094 | + </packing> |
3095 | + </child> |
3096 | + <child> |
3097 | + <object class="GtkToolButton" id="delete_b"> |
3098 | + <property name="sensitive">False</property> |
3099 | + <property name="label" translatable="yes">Delete</property> |
3100 | + <property name="icon_name">edit-delete</property> |
3101 | + <signal name="clicked" handler="on_delete_task"/> |
3102 | + </object> |
3103 | + <packing> |
3104 | + <property name="expand">False</property> |
3105 | + <property name="homogeneous">True</property> |
3106 | + </packing> |
3107 | + </child> |
3108 | + <child> |
3109 | + <object class="GtkSeparatorToolItem" id="separator_7"> |
3110 | + <property name="visible">True</property> |
3111 | + </object> |
3112 | + <packing> |
3113 | + <property name="expand">False</property> |
3114 | + <property name="homogeneous">True</property> |
3115 | + </packing> |
3116 | + </child> |
3117 | + <child> |
3118 | + <object class="GtkToggleToolButton" id="workview_toggle"> |
3119 | + <property name="visible">True</property> |
3120 | + <property name="is_important">True</property> |
3121 | + <property name="label" translatable="yes">Work View</property> |
3122 | + <property name="stock_id">gtk-index</property> |
3123 | + <signal name="toggled" handler="on_workview_toggled"/> |
3124 | + </object> |
3125 | + <packing> |
3126 | + <property name="expand">False</property> |
3127 | + <property name="homogeneous">True</property> |
3128 | + </packing> |
3129 | + </child> |
3130 | + </object> |
3131 | + <packing> |
3132 | + <property name="expand">False</property> |
3133 | + <property name="position">0</property> |
3134 | </packing> |
3135 | </child> |
3136 | </object> |
3137 | <packing> |
3138 | <property name="expand">False</property> |
3139 | + <property name="fill">False</property> |
3140 | <property name="position">1</property> |
3141 | </packing> |
3142 | </child> |
3143 | @@ -429,7 +446,6 @@ |
3144 | <child> |
3145 | <object class="GtkVBox" id="sidebar_vbox"> |
3146 | <property name="width_request">75</property> |
3147 | - <property name="orientation">vertical</property> |
3148 | <child> |
3149 | <object class="GtkHBox" id="hbox4"> |
3150 | <property name="visible">True</property> |
3151 | @@ -484,9 +500,9 @@ |
3152 | <object class="GtkNotebook" id="sidebar_notebook"> |
3153 | <property name="visible">True</property> |
3154 | <property name="can_focus">True</property> |
3155 | + <property name="tab_pos">bottom</property> |
3156 | + <property name="show_tabs">False</property> |
3157 | <property name="group_id">1</property> |
3158 | - <property name="show_tabs">False</property> |
3159 | - <property name="tab_pos">bottom</property> |
3160 | <child> |
3161 | <object class="GtkScrolledWindow" id="sidebar-scroll"> |
3162 | <property name="visible">True</property> |
3163 | @@ -507,7 +523,6 @@ |
3164 | <packing> |
3165 | <property name="tab_fill">False</property> |
3166 | </packing> |
3167 | - |
3168 | </child> |
3169 | </object> |
3170 | <packing> |
3171 | @@ -523,7 +538,6 @@ |
3172 | <child> |
3173 | <object class="GtkVBox" id="main_vbox"> |
3174 | <property name="visible">True</property> |
3175 | - <property name="orientation">vertical</property> |
3176 | <child> |
3177 | <object class="GtkHBox" id="quickadd_pane"> |
3178 | <property name="visible">True</property> |
3179 | @@ -567,14 +581,13 @@ |
3180 | <object class="GtkVPaned" id="vpaned1"> |
3181 | <property name="visible">True</property> |
3182 | <property name="can_focus">True</property> |
3183 | - <property name="orientation">vertical</property> |
3184 | <child> |
3185 | <object class="GtkNotebook" id="main_notebook"> |
3186 | <property name="visible">True</property> |
3187 | <property name="can_focus">True</property> |
3188 | + <property name="tab_pos">bottom</property> |
3189 | + <property name="show_tabs">False</property> |
3190 | <property name="group_id">2</property> |
3191 | - <property name="show_tabs">False</property> |
3192 | - <property name="tab_pos">bottom</property> |
3193 | <child> |
3194 | <object class="GtkScrolledWindow" id="main_pane"> |
3195 | <property name="visible">True</property> |
3196 | @@ -603,11 +616,10 @@ |
3197 | </child> |
3198 | <child> |
3199 | <object class="GtkNotebook" id="accessory_notebook"> |
3200 | - <property name="visible">False</property> |
3201 | <property name="can_focus">True</property> |
3202 | + <property name="tab_pos">bottom</property> |
3203 | + <property name="show_tabs">False</property> |
3204 | <property name="group_id">2</property> |
3205 | - <property name="show_tabs">False</property> |
3206 | - <property name="tab_pos">bottom</property> |
3207 | <child> |
3208 | <placeholder/> |
3209 | </child> |
3210 | @@ -695,7 +707,6 @@ |
3211 | <child internal-child="vbox"> |
3212 | <object class="GtkVBox" id="about_dialog_vbox"> |
3213 | <property name="visible">True</property> |
3214 | - <property name="orientation">vertical</property> |
3215 | <property name="spacing">2</property> |
3216 | <child internal-child="action_area"> |
3217 | <object class="GtkHButtonBox" id="dialog-action_area2"> |
3218 | @@ -959,7 +970,6 @@ |
3219 | <child internal-child="vbox"> |
3220 | <object class="GtkVBox" id="addtag_dialog_vbox"> |
3221 | <property name="visible">True</property> |
3222 | - <property name="orientation">vertical</property> |
3223 | <property name="spacing">2</property> |
3224 | <child> |
3225 | <object class="GtkLabel" id="addtag_label"> |
3226 | @@ -1062,4 +1072,8 @@ |
3227 | <property name="pixel_size">16</property> |
3228 | <property name="icon_name">gtg-tag</property> |
3229 | </object> |
3230 | + <object class="GtkImage" id="image4"> |
3231 | + <property name="visible">True</property> |
3232 | + <property name="stock">gtk-home</property> |
3233 | + </object> |
3234 | </interface> |
3235 | |
3236 | === modified file 'GTG/gtk/colors.py' |
3237 | --- GTG/gtk/colors.py 2010-06-07 21:14:45 +0000 |
3238 | +++ GTG/gtk/colors.py 2010-09-04 17:54:43 +0000 |
3239 | @@ -20,7 +20,7 @@ |
3240 | |
3241 | #Take list of Tags and give the background color that should be applied |
3242 | #The returned color might be None (in which case, the default is used) |
3243 | -def background_color(tags, bgcolor=None): |
3244 | +def background_color(tags, bgcolor = None): |
3245 | if not bgcolor: |
3246 | bgcolor = gtk.gdk.color_parse("#FFFFFF") |
3247 | # Compute color |
3248 | @@ -52,3 +52,29 @@ |
3249 | my_color = gtk.gdk.Color(red, green, blue).to_string() |
3250 | return my_color |
3251 | |
3252 | +def get_colored_tag_markup(req, tag_name): |
3253 | + ''' |
3254 | + Given a tag name, returns a string containing the markup to color the |
3255 | + tag name |
3256 | + ''' |
3257 | + tag = req.get_tag(tag_name) |
3258 | + if tag is None: |
3259 | + #no task loaded with that tag, color cannot be taken |
3260 | + return tag_name |
3261 | + else: |
3262 | + tag_color = tag.get_attribute("color") |
3263 | + if tag_color: |
3264 | + return '<span color="%s">%s</span>' % (tag_color, tag_name) |
3265 | + else: |
3266 | + return tag_name |
3267 | + |
3268 | +def get_colored_tags_markup(req, tag_names): |
3269 | + ''' |
3270 | + Calls get_colored_tag_markup for each tag_name in tag_names |
3271 | + ''' |
3272 | + tag_markups = map(lambda t: get_colored_tag_markup(req, t), tag_names) |
3273 | + tags_txt = "" |
3274 | + if tag_markups: |
3275 | + #reduce crashes if applied to an empty list |
3276 | + tags_txt = reduce(lambda a, b: a + ", " + b, tag_markups) |
3277 | + return tags_txt |
3278 | |
3279 | === modified file 'GTG/gtk/manager.py' |
3280 | --- GTG/gtk/manager.py 2010-08-03 17:07:31 +0000 |
3281 | +++ GTG/gtk/manager.py 2010-09-04 17:54:43 +0000 |
3282 | @@ -39,10 +39,11 @@ |
3283 | from GTG.core.plugins.engine import PluginEngine |
3284 | from GTG.core.plugins.api import PluginAPI |
3285 | from GTG.tools.logger import Log |
3286 | - |
3287 | - |
3288 | - |
3289 | -class Manager: |
3290 | +from GTG.gtk.backends_dialog import BackendsDialog |
3291 | + |
3292 | + |
3293 | + |
3294 | +class Manager(object): |
3295 | |
3296 | |
3297 | ############## init ##################################################### |
3298 | @@ -80,6 +81,7 @@ |
3299 | #Preferences and Backends windows |
3300 | # Initialize dialogs |
3301 | self.preferences_dialog = None |
3302 | + self.edit_backends_dialog = None |
3303 | |
3304 | #DBus |
3305 | DBusTaskWrapper(self.req, self) |
3306 | @@ -196,6 +198,16 @@ |
3307 | |
3308 | ################ Others dialog ############################################ |
3309 | |
3310 | + def open_edit_backends(self, sender = None, backend_id = None): |
3311 | + if not self.edit_backends_dialog: |
3312 | + self.edit_backends_dialog = BackendsDialog(self.req) |
3313 | + self.edit_backends_dialog.activate() |
3314 | + if backend_id != None: |
3315 | + self.edit_backends_dialog.show_config_for_backend(backend_id) |
3316 | + |
3317 | + def configure_backend(self, backend_id): |
3318 | + self.open_edit_backends(None, backend_id) |
3319 | + |
3320 | def open_preferences(self, config_priv, sender=None): |
3321 | if not hasattr(self, "preferences"): |
3322 | self.preferences = PreferencesDialog(self.pengine, self.p_apis, \ |
3323 | @@ -211,7 +223,8 @@ |
3324 | self.close_task(t) |
3325 | |
3326 | ### MAIN ################################################################### |
3327 | - def main(self, once_thru=False): |
3328 | + |
3329 | + def main(self, once_thru = False): |
3330 | gobject.threads_init() |
3331 | if once_thru: |
3332 | gtk.main_iteration() |
3333 | @@ -219,7 +232,6 @@ |
3334 | gtk.main() |
3335 | return 0 |
3336 | |
3337 | - |
3338 | def quit(self,sender=None): |
3339 | gtk.main_quit() |
3340 | #save opened tasks and their positions. |
3341 | |
3342 | === added file 'GTG/tests/test_interruptible.py' |
3343 | --- GTG/tests/test_interruptible.py 1970-01-01 00:00:00 +0000 |
3344 | +++ GTG/tests/test_interruptible.py 2010-09-04 17:54:43 +0000 |
3345 | @@ -0,0 +1,69 @@ |
3346 | +# -*- coding: utf-8 -*- |
3347 | +# ----------------------------------------------------------------------------- |
3348 | +# Gettings Things Gnome! - a personal organizer for the GNOME desktop |
3349 | +# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau |
3350 | +# |
3351 | +# This program is free software: you can redistribute it and/or modify it under |
3352 | +# the terms of the GNU General Public License as published by the Free Software |
3353 | +# Foundation, either version 3 of the License, or (at your option) any later |
3354 | +# version. |
3355 | +# |
3356 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
3357 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
3358 | +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
3359 | +# details. |
3360 | +# |
3361 | +# You should have received a copy of the GNU General Public License along with |
3362 | +# this program. If not, see <http://www.gnu.org/licenses/>. |
3363 | +# ----------------------------------------------------------------------------- |
3364 | + |
3365 | +''' |
3366 | +Tests for interrupting cooperative threads |
3367 | +''' |
3368 | + |
3369 | +import unittest |
3370 | +import time |
3371 | +from threading import Thread, Event |
3372 | + |
3373 | +from GTG.tools.interruptible import interruptible, _cancellation_point |
3374 | + |
3375 | + |
3376 | +class TestInterruptible(unittest.TestCase): |
3377 | + ''' |
3378 | + Tests for interrupting cooperative threads |
3379 | + ''' |
3380 | + |
3381 | + def test_interruptible_decorator(self): |
3382 | + self.quit_condition = False |
3383 | + cancellation_point = lambda: _cancellation_point(\ |
3384 | + lambda: self.quit_condition) |
3385 | + self.thread_started = Event() |
3386 | + @interruptible |
3387 | + def never_ending(cancellation_point): |
3388 | + self.thread_started.set() |
3389 | + while True: |
3390 | + time.sleep(0.1) |
3391 | + cancellation_point() |
3392 | + thread = Thread(target = never_ending, args = (cancellation_point, )) |
3393 | + thread.start() |
3394 | + self.thread_started.wait() |
3395 | + self.quit_condition = True |
3396 | + countdown = 10 |
3397 | + while thread.is_alive() and countdown > 0: |
3398 | + time.sleep(0.1) |
3399 | + countdown -= 1 |
3400 | + self.assertFalse(thread.is_alive()) |
3401 | + |
3402 | + |
3403 | + |
3404 | + |
3405 | + |
3406 | + |
3407 | + |
3408 | + |
3409 | + |
3410 | + |
3411 | + |
3412 | +def test_suite(): |
3413 | + return unittest.TestLoader().loadTestsFromTestCase(TestInterruptible) |
3414 | + |
3415 | |
3416 | === added file 'GTG/tools/networkmanager.py' |
3417 | --- GTG/tools/networkmanager.py 1970-01-01 00:00:00 +0000 |
3418 | +++ GTG/tools/networkmanager.py 2010-09-04 17:54:43 +0000 |
3419 | @@ -0,0 +1,57 @@ |
3420 | +#!/bin/env python |
3421 | +# |
3422 | +# This program is free software; you can redistribute it and/or modify |
3423 | +# it under the terms of the GNU General Public License as published by |
3424 | +# the Free Software Foundation; either version 2 of the License, or |
3425 | +# (at your option) any later version. |
3426 | +# |
3427 | +# This program is distributed in the hope that it will be useful, |
3428 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3429 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3430 | +# GNU General Public License for more details. |
3431 | +# |
3432 | +# You should have received a copy of the GNU General Public License along |
3433 | +# with this program; if not, write to the Free Software Foundation, Inc., |
3434 | +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
3435 | +# |
3436 | +# Copyright (C) 2010 Red Hat, Inc. |
3437 | +# |
3438 | + |
3439 | +import dbus |
3440 | + |
3441 | + |
3442 | +def is_connection_up(): |
3443 | + ''' |
3444 | + Returns True if network-manager reports that at least one connection is up |
3445 | + |
3446 | + @returns bool |
3447 | + ''' |
3448 | + state = False |
3449 | + bus = dbus.SystemBus() |
3450 | + |
3451 | + proxy = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager") |
3452 | + manager = dbus.Interface(proxy, "org.freedesktop.NetworkManager") |
3453 | + |
3454 | + manager_prop_iface = dbus.Interface(proxy, "org.freedesktop.DBus.Properties") |
3455 | + active = manager_prop_iface.Get("org.freedesktop.NetworkManager", "ActiveConnections") |
3456 | + for a in active: |
3457 | + ac_proxy = bus.get_object("org.freedesktop.NetworkManager", a) |
3458 | + prop_iface = dbus.Interface(ac_proxy, "org.freedesktop.DBus.Properties") |
3459 | + state = prop_iface.Get("org.freedesktop.NetworkManager.ActiveConnection", "State") |
3460 | + |
3461 | + # Connections in NM are a collection of settings that describe everything |
3462 | + # needed to connect to a specific network. Lets get those details so we |
3463 | + # can find the user-readable name of the connection. |
3464 | + con_path = prop_iface.Get("org.freedesktop.NetworkManager.ActiveConnection", "Connection") |
3465 | + con_service = prop_iface.Get("org.freedesktop.NetworkManager.ActiveConnection", "ServiceName") |
3466 | + |
3467 | + # ask the provider of the connection for its details |
3468 | + service_proxy = bus.get_object(con_service, con_path) |
3469 | + con_iface = dbus.Interface(service_proxy, "org.freedesktop.NetworkManagerSettings.Connection") |
3470 | + con_details = con_iface.GetSettings() |
3471 | + con_name = con_details['connection']['id'] |
3472 | + |
3473 | + if state == 2: # activated |
3474 | + state = True |
3475 | + return state |
3476 | + |
3477 | |
3478 | === added file 'data/icons/hicolor/scalable/apps/backend_localfile.png' |
3479 | Binary files data/icons/hicolor/scalable/apps/backend_localfile.png 1970-01-01 00:00:00 +0000 and data/icons/hicolor/scalable/apps/backend_localfile.png 2010-09-04 17:54:43 +0000 differ |
Backend window works fine. One existing backend (local file) can be added and deleted.
This warning is printed in console after starting GTG:
/home/marko/ Projects/ gtg/backends- window/ GTG/gtk/ browser/ browser. py:109: GtkWarning: No object called: image4 builder. add_from_ file(GnomeConfi g.GLADE_ FILE)
self.
Should that be fixed?