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