Merge lp://staging/~dobey/ubuntuone-client/updown-visibility into lp://staging/ubuntuone-client
- updown-visibility
- Merge into trunk
Proposed by
dobey
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Joshua Blount | ||||||||
Approved revision: | 172 | ||||||||
Merged at revision: | not available | ||||||||
Proposed branch: | lp://staging/~dobey/ubuntuone-client/updown-visibility | ||||||||
Merge into: | lp://staging/ubuntuone-client | ||||||||
Diff against target: | None lines | ||||||||
To merge this branch: | bzr merge lp://staging/~dobey/ubuntuone-client/updown-visibility | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Joshua Blount (community) | Approve | ||
John O'Brien (community) | Approve | ||
Review via email: mp+10587@code.staging.launchpad.net |
Commit message
Handle the new ContentQueue signal
Maintain state for transfer status information
Change the left click behavior to open a menu by default
Update a menu item in the left click menu with transfer status
Pop up notifications to signal start and end of transfers
Description of the change
To post a comment you must log in.
Revision history for this message
John O'Brien (jdobrien) wrote : | # |
Revision history for this message
John O'Brien (jdobrien) wrote : | # |
...except that the reviewer select "Approve"
review:
Approve
Revision history for this message
Joshua Blount (jblount) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/ubuntuone-client-applet' |
2 | --- bin/ubuntuone-client-applet 2009-08-18 19:47:56 +0000 |
3 | +++ bin/ubuntuone-client-applet 2009-08-24 01:12:37 +0000 |
4 | @@ -40,6 +40,7 @@ |
5 | from dbus.mainloop.glib import DBusGMainLoop |
6 | from ubuntuone.oauthdesktop.main import Login |
7 | from xdg.BaseDirectory import xdg_config_home |
8 | +from threading import Lock |
9 | from urllib import quote |
10 | |
11 | from ubuntuone.oauthdesktop.logger import setupLogging |
12 | @@ -311,8 +312,9 @@ |
13 | self.__size = 24 |
14 | self.__size_changed(self, self.__size) |
15 | |
16 | - self.__items = {} |
17 | - self.__menu = self.__build_menus() |
18 | + self.__litems = {} |
19 | + self.__ritems = {} |
20 | + self.__status_menu, self.__config_menu = self.__build_menus() |
21 | |
22 | self.__connected = False |
23 | self.__need_update = False |
24 | @@ -321,10 +323,15 @@ |
25 | pynotify.init("Ubuntu One") |
26 | |
27 | # Managing applet visibility |
28 | - self.__persist = True |
29 | self.__visible = True |
30 | self.__visible_id = 0 |
31 | |
32 | + # Up/Dn status |
33 | + self.__lock = Lock() |
34 | + self.__updating = 0 |
35 | + self.__total = 0 |
36 | + self.__last_id = 0 |
37 | + |
38 | self.__bus = dbus.SessionBus() |
39 | |
40 | self.__bus.add_signal_receiver( |
41 | @@ -332,28 +339,140 @@ |
42 | signal_name="StatusChanged", |
43 | dbus_interface=DBUS_IFACE_STATUS_NAME) |
44 | |
45 | + self.__bus.add_signal_receiver( |
46 | + handler_function=self.__queue_changed, |
47 | + signal_name="ContentQueueChanged", |
48 | + dbus_interface=DBUS_IFACE_STATUS_NAME) |
49 | + self.__bus.add_signal_receiver( |
50 | + handler_function=self.__transfer_started, |
51 | + signal_name="UploadStarted", |
52 | + dbus_interface=DBUS_IFACE_STATUS_NAME) |
53 | + self.__bus.add_signal_receiver( |
54 | + handler_function=self.__transfer_started, |
55 | + signal_name="DownloadStarted", |
56 | + dbus_interface=DBUS_IFACE_STATUS_NAME) |
57 | + |
58 | gobject.idle_add(self.__get_root) |
59 | |
60 | + def __update_transfer_status(self, done=False): |
61 | + """Update the status display.""" |
62 | + with self.__lock: |
63 | + text = "Updating %(transfers)d of %(total)d files..." % ( |
64 | + { 'transfers' : self.__updating, |
65 | + 'total' : self.__total}) |
66 | + label = self.__litems["status"].get_child() |
67 | + if done: |
68 | + self.set_tooltip(_("Files updated.")) |
69 | + label.set_text(_("Your files are up to date.")) |
70 | + else: |
71 | + label.set_markup("<i>%s</i>" % text) |
72 | + |
73 | + def __queue_changed(self, queue): |
74 | + """Handle ContentQueueChanged.""" |
75 | + total = 0 |
76 | + d = queue.get('Download', None) |
77 | + if d is not None: |
78 | + total += int(d.get('count', 0)) |
79 | + d = queue.get('Upload', None) |
80 | + if d is not None: |
81 | + total += int(d.get('count', 0)) |
82 | + first = False |
83 | + last = False |
84 | + self.__visible = True |
85 | + self.set_tooltip(_("Updating files...")) |
86 | + self.__update_visibility() |
87 | + with self.__lock: |
88 | + if self.__total == 0: |
89 | + first = True |
90 | + last = False |
91 | + if self.__total != 0 and total == 0: |
92 | + first = False |
93 | + last = True |
94 | + self.__total = total + self.__updating |
95 | + if first: |
96 | + n = pynotify.Notification( |
97 | + "Updating Files...", |
98 | + _("Ubuntu One is now updating your files.")) |
99 | + pixbuf = self.__theme.load_icon( |
100 | + "ubuntuone-client", 24, |
101 | + gtk.ICON_LOOKUP_GENERIC_FALLBACK) |
102 | + n.set_icon_from_pixbuf(pixbuf) |
103 | + n.show() |
104 | + if last: |
105 | + if self.__last_id != 0: |
106 | + gobject.source_remove(self.__last_id) |
107 | + self.__last_id = 0 |
108 | + self.__last_id = gobject.timeout_add_seconds( |
109 | + 15, self.__updating_completed) |
110 | + |
111 | + def __updating_completed(self): |
112 | + """Timeout to avoid multiple started/finished notifications.""" |
113 | + really_last = False |
114 | + n = None |
115 | + with self.__lock: |
116 | + done = self.__total - self.__updating |
117 | + if done == 0: |
118 | + really_last = True |
119 | + if not really_last: |
120 | + return False |
121 | + |
122 | + with self.__lock: |
123 | + n = pynotify.Notification( |
124 | + "Updating Finished", |
125 | + _("Ubuntu One finished updating %(total)d files.") % |
126 | + { 'total' : self.__total }) |
127 | + self.__total = 0 |
128 | + self.__updating = 0 |
129 | + pixbuf = self.__theme.load_icon( |
130 | + "ubuntuone-client", 24, |
131 | + gtk.ICON_LOOKUP_GENERIC_FALLBACK) |
132 | + n.set_icon_from_pixbuf(pixbuf) |
133 | + n.show() |
134 | + self.__update_transfer_status(True) |
135 | + return False |
136 | + |
137 | + def __transfer_started(self, path): |
138 | + """Handle the started signals.""" |
139 | + with self.__lock: |
140 | + self.__updating += 1 |
141 | + self.__update_transfer_status() |
142 | + |
143 | + def __update_visibility(self): |
144 | + """Update the icon's visibility.""" |
145 | + if self.__visible_id != 0: |
146 | + gobject.source_remove(self.__visible_id) |
147 | + self.__visible_id = 0 |
148 | + |
149 | + if self.__visible: |
150 | + self.set_visible(True) |
151 | + return |
152 | + |
153 | + # If the icon is shown, set up a timeout to hide it |
154 | + if self.get_visible(): |
155 | + self.__visible_id = gobject.timeout_add_seconds( |
156 | + 30, self.__hide_icon) |
157 | + |
158 | def __status_changed(self, status): |
159 | """The sync daemon status changed.""" |
160 | if self.__managed_dir is None: |
161 | gobject.idle_add(self.__get_root) |
162 | |
163 | - self.__persist = False |
164 | - self.__visible = True |
165 | + self.__visible = False |
166 | + state = status['name'] |
167 | |
168 | - if status['name'] == "OFFLINE" or \ |
169 | - status['name'].startswith("INIT") or \ |
170 | - status['name'].startswith("READY"): |
171 | + if state == "OFFLINE" or state.startswith("INIT") or \ |
172 | + state.startswith("READY"): |
173 | self.set_from_icon_name("ubuntuone-client-offline") |
174 | self.__connected = False |
175 | - self.__persist = True |
176 | self.__visible = True |
177 | - elif status['name'] == "AUTH_FAILED": |
178 | + elif state == "IDLE" or state.startswith("READING") or \ |
179 | + state.startswith("SCANNING"): |
180 | + self.__connected = True |
181 | + self.__visible = False |
182 | + elif state == "AUTH_FAILED": |
183 | self.set_from_icon_name("ubuntuone-client-error") |
184 | self.set_tooltip(_("Authentication failed")) |
185 | self.__connected = False |
186 | - self.__persist = True |
187 | self.__visible = True |
188 | try: |
189 | def reauthorize_error(error): |
190 | @@ -374,17 +493,16 @@ |
191 | error_handler=reauthorize_error) |
192 | except DBusException: |
193 | pass |
194 | - elif status['name'] == "UNKNOWN_ERROR": |
195 | + elif state == "UNKNOWN_ERROR": |
196 | # Disable some menu items |
197 | - self.__items["connect"].set_sensitive(False) |
198 | - self.__items["disconnect"].set_sensitive(False) |
199 | + self.__litems["connect"].set_sensitive(False) |
200 | + self.__litems["disconnect"].set_sensitive(False) |
201 | # Change the behavior to file a bug |
202 | if self.__fatal_error: |
203 | return |
204 | |
205 | self.__fatal_error = True |
206 | - self.__persist = True |
207 | - self.__visibile = True |
208 | + self.__visible = True |
209 | |
210 | # Pop up a notification |
211 | n = pynotify.Notification( |
212 | @@ -402,45 +520,28 @@ |
213 | # Set the tooltip and icon on the applet |
214 | self.set_tooltip(_("Fatal Error")) |
215 | self.set_from_icon_name("ubuntuone-client-error") |
216 | - if self.__visible_id != 0: |
217 | - gobject.source_remove(self.__visible_id) |
218 | - self.__visible_id = 0 |
219 | - self.set_visible(True) |
220 | - return |
221 | else: |
222 | self.__connected = True |
223 | self.set_from_icon_name("ubuntuone-client") |
224 | - self.__visible = True |
225 | - if status['name'].startswith("CONNECTING") or \ |
226 | - status['name'].startswith("START_CONNECTING") or \ |
227 | - status['name'].startswith("AUTHENTICATING") or \ |
228 | - status['name'].startswith("CONNECTED"): |
229 | - self.set_from_icon_name("ubuntuone-client-offline") |
230 | + if state.startswith("CONNECTING") or \ |
231 | + state.startswith("START_CONNECTING") or \ |
232 | + state.startswith("AUTHENTICATING") or \ |
233 | + state.startswith("CONNECTED") or \ |
234 | + state.startswith("START_CONNECTED"): |
235 | + self.set_from_icon_name("ubuntuone-client") |
236 | self.set_tooltip(_("Connecting")) |
237 | - else: |
238 | - self.set_tooltip("Ubuntu One") |
239 | - self.__visible = False |
240 | + self.__visible = True |
241 | |
242 | - # If we're visible, set up a reasonable timeout to hide ourself, |
243 | - # and if not shown yet, show ourself |
244 | - if self.__visible: |
245 | - if not self.get_visible(): |
246 | - self.set_visible(True) |
247 | - if self.__visible_id != 0: |
248 | - gobject.source_remove(self.__visible_id) |
249 | - self.__visible_id = 0 |
250 | - if not self.__persist: |
251 | - self.__visible_id = gobject.timeout_add_seconds( |
252 | - 30, self.__toggle_visible) |
253 | + self.__update_visibility() |
254 | |
255 | if self.__connected: |
256 | - self.__items["connect"].hide() |
257 | - self.__items["disconnect"].show() |
258 | + self.__litems["connect"].hide() |
259 | + self.__litems["disconnect"].show() |
260 | else: |
261 | - self.__items["connect"].show() |
262 | - self.__items["disconnect"].hide() |
263 | + self.__litems["connect"].show() |
264 | + self.__litems["disconnect"].hide() |
265 | |
266 | - def __toggle_visible(self): |
267 | + def __hide_icon(self): |
268 | """Timeout to hide tray icon after a period of inactivity.""" |
269 | self.__visible = False |
270 | self.__visible_id = 0 |
271 | @@ -464,16 +565,16 @@ |
272 | if os.path.isdir(self.__managed_dir) and \ |
273 | os.access(self.__managed_dir, |
274 | os.F_OK | os.R_OK): |
275 | - self.__items["open"].set_sensitive(True) |
276 | + self.__ritems["open"].set_sensitive(True) |
277 | self.__add_to_places() |
278 | else: |
279 | - self.__items["open"].set_sensitive(False) |
280 | + self.__ritems["open"].set_sensitive(False) |
281 | |
282 | def got_err(error): |
283 | """Handle error from the dbus callback.""" |
284 | self.dbus_error(error) |
285 | self.__managed_dir = None |
286 | - self.__items["open"].set_sensitive(False) |
287 | + self.__ritems["open"].set_sensitive(False) |
288 | |
289 | iface.get_rootdir(reply_handler=got_root, error_handler=got_err) |
290 | |
291 | @@ -491,49 +592,63 @@ |
292 | |
293 | def __build_menus(self): |
294 | """Create the pop-up menu items.""" |
295 | - menu = gtk.Menu() |
296 | - |
297 | - self.__items["connect"] = gtk.ImageMenuItem(stock_id=gtk.STOCK_CONNECT) |
298 | - menu.append(self.__items["connect"]) |
299 | - self.__items["connect"].connect("activate", self.__toggle_state) |
300 | - self.__items["connect"].show() |
301 | - |
302 | - self.__items["disconnect"] = gtk.ImageMenuItem(stock_id=gtk.STOCK_DISCONNECT) |
303 | - menu.append(self.__items["disconnect"]) |
304 | - self.__items["disconnect"].connect("activate", self.__toggle_state) |
305 | - |
306 | - sep = gtk.SeparatorMenuItem() |
307 | - menu.append(sep) |
308 | - sep.show() |
309 | - |
310 | - self.__items["bug"] = gtk.MenuItem(label=_("_Report a Problem")) |
311 | - menu.append(self.__items["bug"]) |
312 | - self.__items["bug"].connect("activate", self.__report_problem) |
313 | - self.__items["bug"].show() |
314 | - |
315 | - self.__items["open"] = gtk.MenuItem(label=_("_Open Folder")) |
316 | - menu.append(self.__items["open"]) |
317 | - self.__items["open"].connect("activate", self.__open_folder) |
318 | - self.__items["open"].set_sensitive(False) |
319 | - self.__items["open"].show() |
320 | - |
321 | - self.__items["web"] = gtk.MenuItem(label=_("_Go to Web")) |
322 | - menu.append(self.__items["web"]) |
323 | - self.__items["web"].connect("activate", self.__open_website) |
324 | - self.__items["web"].show() |
325 | - |
326 | - sep = gtk.SeparatorMenuItem() |
327 | - menu.append(sep) |
328 | - sep.show() |
329 | - |
330 | - self.__items["quit"] = gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT) |
331 | - menu.append(self.__items["quit"]) |
332 | - self.__items["quit"].connect("activate", self.__quit_applet) |
333 | - self.__items["quit"].show() |
334 | - |
335 | - menu.show() |
336 | - |
337 | - return menu |
338 | + # Create the left-click menu |
339 | + lmenu = gtk.Menu() |
340 | + |
341 | + self.__litems["status"] = gtk.MenuItem( |
342 | + label=_("Your files are up to date.")) |
343 | + lmenu.append(self.__litems["status"]) |
344 | + self.__litems["status"].set_sensitive(False) |
345 | + self.__litems["status"].show() |
346 | + |
347 | + sep = gtk.SeparatorMenuItem() |
348 | + lmenu.append(sep) |
349 | + sep.show() |
350 | + |
351 | + self.__litems["connect"] = gtk.ImageMenuItem( |
352 | + stock_id=gtk.STOCK_CONNECT) |
353 | + lmenu.append(self.__litems["connect"]) |
354 | + self.__litems["connect"].connect("activate", self.__toggle_state) |
355 | + self.__litems["connect"].show() |
356 | + |
357 | + self.__litems["disconnect"] = gtk.ImageMenuItem( |
358 | + stock_id=gtk.STOCK_DISCONNECT) |
359 | + lmenu.append(self.__litems["disconnect"]) |
360 | + self.__litems["disconnect"].connect("activate", self.__toggle_state) |
361 | + |
362 | + lmenu.show() |
363 | + |
364 | + # Create the right-click menu |
365 | + rmenu = gtk.Menu() |
366 | + |
367 | + self.__ritems["bug"] = gtk.MenuItem(label=_("_Report a Problem")) |
368 | + rmenu.append(self.__ritems["bug"]) |
369 | + self.__ritems["bug"].connect("activate", self.__report_problem) |
370 | + self.__ritems["bug"].show() |
371 | + |
372 | + self.__ritems["open"] = gtk.MenuItem(label=_("_Open Folder")) |
373 | + rmenu.append(self.__ritems["open"]) |
374 | + self.__ritems["open"].connect("activate", self.__open_folder) |
375 | + self.__ritems["open"].set_sensitive(False) |
376 | + self.__ritems["open"].show() |
377 | + |
378 | + self.__ritems["web"] = gtk.MenuItem(label=_("_Go to Web")) |
379 | + rmenu.append(self.__ritems["web"]) |
380 | + self.__ritems["web"].connect("activate", self.__open_website) |
381 | + self.__ritems["web"].show() |
382 | + |
383 | + sep = gtk.SeparatorMenuItem() |
384 | + rmenu.append(sep) |
385 | + sep.show() |
386 | + |
387 | + self.__ritems["quit"] = gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT) |
388 | + rmenu.append(self.__ritems["quit"]) |
389 | + self.__ritems["quit"].connect("activate", self.__quit_applet) |
390 | + self.__ritems["quit"].show() |
391 | + |
392 | + rmenu.show() |
393 | + |
394 | + return lmenu, rmenu |
395 | |
396 | def __size_changed(self, icon, size, data=None): |
397 | """Callback for when the size changes.""" |
398 | @@ -550,8 +665,14 @@ |
399 | |
400 | def __popup_menu(self, icon, button, timestamp, data=None): |
401 | """Pops up the context menu for the tray icon.""" |
402 | - self.__menu.popup(None, None, gtk.status_icon_position_menu, |
403 | - button, timestamp, icon) |
404 | + if button == 0: |
405 | + self.__status_menu.popup(None, None, |
406 | + gtk.status_icon_position_menu, |
407 | + button, timestamp, icon) |
408 | + else: |
409 | + self.__config_menu.popup(None, None, |
410 | + gtk.status_icon_position_menu, |
411 | + button, timestamp, icon) |
412 | |
413 | def __quit_applet(self, menuitem, data=None): |
414 | """Quit the daemon and closes the applet.""" |
415 | @@ -601,7 +722,8 @@ |
416 | do_xdg_open("apt:ubuntuone-storage-protocol?refresh=yes") |
417 | return |
418 | |
419 | - self.__open_folder() |
420 | + # Popup the status menu |
421 | + self.emit("popup-menu", 0, gtk.get_current_event_time()) |
422 | |
423 | def __report_problem(self, data=None): |
424 | """Runs apport to report a problem against our code.""" |
changes look good, tests run, and it doesn't break ubunet....who could ask for anything more!