~ubuntuone-control-tower/ubuntuone-client/trunk

« back to all changes in this revision

Viewing changes to bin/ubuntuone-client-applet

  • Committer: Rodney Dawes
  • Date: 2009-05-12 13:36:05 UTC
  • Revision ID: rodney.dawes@canonical.com-20090512133605-6aqs6e8xnnmp5u1p
        Import the code
        Hook up lint/trial tests in setup.py
        Use icontool now instead of including the render script
        Add missing python-gnome2-desktop to package dependencies
        Update debian/rules to fix the icon cache issue

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
# ubuntuone-client-applet - Tray icon applet for managing Ubuntu One
 
4
#
 
5
# Author: Rodney Dawes <rodney.dawes@canonical.com>
 
6
#
 
7
# Copyright 2009 Canonical Ltd.
 
8
#
 
9
# This program is free software: you can redistribute it and/or modify it
 
10
# under the terms of the GNU General Public License version 3, as published
 
11
# by the Free Software Foundation.
 
12
#
 
13
# This program is distributed in the hope that it will be useful, but
 
14
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
15
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
16
# PURPOSE.  See the GNU General Public License for more details.
 
17
#
 
18
# You should have received a copy of the GNU General Public License along
 
19
# with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
 
 
21
from __future__ import with_statement
 
22
 
 
23
import pygtk
 
24
pygtk.require('2.0')
 
25
import gobject
 
26
import gtk
 
27
import os
 
28
import subprocess
 
29
import sys
 
30
 
 
31
import dbus.service
 
32
 
 
33
# pylint: disable-msg=F0401
 
34
import pynotify
 
35
 
 
36
from dbus.exceptions import DBusException
 
37
from dbus.mainloop.glib import DBusGMainLoop
 
38
from canonical.ubuntuone.oauthdesktop.main import Login
 
39
from gobject.option import OptionGroup, OptionParser, make_option
 
40
from xdg.BaseDirectory import xdg_data_home, xdg_config_home
 
41
from urllib import quote
 
42
 
 
43
from canonical.ubuntuone.oauthdesktop.logger import setupLogging
 
44
setupLogging()
 
45
import logging
 
46
logger = logging.getLogger("UbuntuOne.Client.Applet")
 
47
 
 
48
DBusGMainLoop(set_as_default=True)
 
49
 
 
50
# Wait for 30 seconds to get a proper login request
 
51
LOGIN_TIMEOUT = 30
 
52
 
 
53
# Prepend tooltip labels with this label
 
54
TOOLTIP_TITLE = "Ubuntu One: "
 
55
 
 
56
DBUS_IFACE_NAME = "com.ubuntuone.SyncDaemon"
 
57
DBUS_IFACE_SYNC_NAME = "com.ubuntuone.SyncDaemon.SyncDaemon"
 
58
DBUS_IFACE_STATUS_NAME = "com.ubuntuone.SyncDaemon.Status"
 
59
 
 
60
class AppletMain(object):
 
61
      """Main applet process class."""
 
62
 
 
63
      def __init__(self, signup=False, *args, **kw):
 
64
            """Initializes the child threads and dbus monitor."""
 
65
            from twisted.internet import gtk2reactor
 
66
            gtk2reactor.install()
 
67
            login = Login(dbus.service.BusName("com.ubuntuone.Authentication",
 
68
                                               bus=dbus.SessionBus()))
 
69
 
 
70
            self.__signup = signup
 
71
            self.__icon = None
 
72
            self.__check_id = 0
 
73
 
 
74
            self.__bus = dbus.SessionBus()
 
75
            self.__bus.add_signal_receiver(
 
76
                  handler_function=self.__new_credentials,
 
77
                  signal_name="NewCredentials",
 
78
                  dbus_interface="com.ubuntuone.Authentication")
 
79
            self.__bus.add_signal_receiver(
 
80
                  handler_function=self.__auth_denied,
 
81
                  signal_name="AuthorizationDenied",
 
82
                  dbus_interface="com.ubuntuone.Authentication")
 
83
 
 
84
      def __new_credentials(self, realm=None, consumer_key=None, sender=None):
 
85
            """Signal callback for when we get new credentials."""
 
86
            self.__start_storage_daemon()
 
87
 
 
88
            if not self.__icon:
 
89
                  self.__icon = AppletIcon()
 
90
            if self.__check_id > 0:
 
91
                  gobject.source_remove(self.__check_id)
 
92
            if self.__signup:
 
93
                  self.add_to_autostart()
 
94
 
 
95
      def __auth_denied(self):
 
96
            """Signal callback for when auth was denied by user."""
 
97
            def timeout_exit():
 
98
                  """Do the quit on a timeout"""
 
99
                  from twisted.internet import reactor
 
100
                  reactor.stop()
 
101
                  return False
 
102
 
 
103
            self.remove_from_autostart()
 
104
            if not self.__signup:
 
105
                  self.__check_id = gobject.timeout_add_seconds(LOGIN_TIMEOUT,
 
106
                                                                timeout_exit)
 
107
 
 
108
      def __check_for_token(self):
 
109
            """Method to check for an existing token."""
 
110
            try:
 
111
                  client = self.__bus.get_object("com.ubuntuone.Authentication",
 
112
                                                 "/")
 
113
            except DBusException:
 
114
                  return False
 
115
 
 
116
            iface = dbus.Interface(client, "com.ubuntuone.Authentication")
 
117
            def handler():
 
118
                  """Simple handler to make dbus do stuff async."""
 
119
                  pass
 
120
 
 
121
            iface.maybe_login("https://ubuntuone.com", "ubuntuone",
 
122
                              self.__signup,
 
123
                              reply_handler=handler, error_handler=handler)
 
124
            return False
 
125
 
 
126
      def __start_storage_daemon_maybe(self):
 
127
            """Start the storage daemon."""
 
128
            try:
 
129
                  client = self.__bus.get_object(DBUS_IFACE_NAME, "/")
 
130
            except DBusException:
 
131
                  return False
 
132
 
 
133
            iface = dbus.Interface(client, DBUS_IFACE_SYNC_NAME)
 
134
            def handler(data):
 
135
                  """Simple handler to make dbus to stuff async."""
 
136
                  pass
 
137
 
 
138
            iface.get_rootdir(reply_handler=handler, error_handler=handler)
 
139
            return False
 
140
 
 
141
      def __start_storage_daemon(self):
 
142
            """Need to call dbus from a idle callback"""
 
143
            gobject.idle_add(self.__start_storage_daemon_maybe)
 
144
 
 
145
      def add_to_autostart(self):
 
146
            """Add ourself to the autostart config."""
 
147
            autostart_entry = """[Desktop Entry]
 
148
Name=Ubuntu One
 
149
Comment=Control applet for Ubuntu One
 
150
Exec=ubuntuone-client-applet
 
151
Icon=ubuntuone-client
 
152
Terminal=false
 
153
Type=Application
 
154
X-Ubuntu-Gettext-Domain=ubuntuone-client
 
155
X-KDE-autostart-after=panel
 
156
X-GNOME-Autostart-enabled=true
 
157
"""
 
158
            with open(os.path.join(xdg_config_home, "autostart",
 
159
                                   "ubuntuone-client-applet.desktop"),
 
160
                      "w+") as f:
 
161
                  f.write(autostart_entry)
 
162
 
 
163
      def remove_from_autostart(self):
 
164
            """Remove ourself from the autostart config."""
 
165
            path = os.path.join(xdg_config_home, "autostart",
 
166
                                "ubuntuone-client-applet.desktop")
 
167
            try:
 
168
                  os.unlink(path)
 
169
            except OSError:
 
170
                  pass
 
171
 
 
172
      def main(self):
 
173
            """Starts the gtk main loop."""
 
174
            from twisted.internet import reactor
 
175
            gobject.idle_add(self.__check_for_token)
 
176
 
 
177
            reactor.run()
 
178
 
 
179
 
 
180
def do_xdg_open(path_or_url):
 
181
      """Utility method to run xdg-open with path_or_url."""
 
182
      ret = subprocess.call(["xdg-open", path_or_url])
 
183
      if ret != 0:
 
184
            logger.debug("Failed to run 'xdg-open %s'" % path_or_url)
 
185
 
 
186
class AppletIcon(gtk.StatusIcon):
 
187
      """
 
188
      Custom StatusIcon derived from gtk.StatusIcon which supports
 
189
      animated icons and a few other nice things.
 
190
      """
 
191
 
 
192
      def __init__(self, *args, **kw):
 
193
            """Initializes our custom StatusIcon based widget."""
 
194
            super(AppletIcon, self).__init__(*args, **kw)
 
195
 
 
196
            self.__managed_dir = None
 
197
 
 
198
            self.__theme = gtk.icon_theme_get_default()
 
199
            iconpath = os.path.abspath(os.path.join(
 
200
                        os.path.split(os.path.dirname(__file__))[0],
 
201
                        "data"))
 
202
            self.__theme.append_search_path(iconpath)
 
203
 
 
204
            self.__theme.append_search_path(os.path.sep + os.path.join(
 
205
                        "usr", "share", "ubuntuone-client", "icons"))
 
206
            self.__theme.append_search_path(os.path.sep + os.path.join(
 
207
                        "usr", "local", "share", "ubuntuone-client", "icons"))
 
208
 
 
209
            self.set_from_icon_name('ubuntuone-client')
 
210
            self.connect("popup-menu", self.__popup_menu)
 
211
            self.connect("activate", self.__open_folder)
 
212
 
 
213
            self.__busy = False
 
214
            self.__busy_id = 0
 
215
 
 
216
            self.__size = 24
 
217
            self.__pixbuf = None
 
218
 
 
219
            self.__vframes = 0
 
220
            self.__hframes = 0
 
221
            self.__vpos = 0
 
222
            self.__hpos = 0
 
223
 
 
224
            self.__size_changed(self, self.__size)
 
225
 
 
226
            self.__items = {}
 
227
            self.__menu = self.__build_menus()
 
228
 
 
229
            self.__connected = False
 
230
            self.__need_update = False
 
231
 
 
232
            pynotify.init("Ubuntu One")
 
233
 
 
234
            self.__bus = dbus.SessionBus()
 
235
 
 
236
            self.__bus.add_signal_receiver(
 
237
                  handler_function=self.__status_changed,
 
238
                  signal_name="StatusChanged",
 
239
                  dbus_interface=DBUS_IFACE_STATUS_NAME)
 
240
 
 
241
            gobject.idle_add(self.__get_root)
 
242
 
 
243
      def __status_changed(self, status):
 
244
            """The sync daemon status changed."""
 
245
            if self.__managed_dir is None:
 
246
                gobject.idle_add(self.__get_root)
 
247
            if str(status) == "OFFLINE" or str(status).startswith("INIT") or \
 
248
                      str(status).startswith("READY"):
 
249
                  self.set_busy(False)
 
250
                  self.set_from_icon_name("ubuntuone-client-offline")
 
251
                  self.set_tooltip(TOOLTIP_TITLE + "Disconnected")
 
252
                  self.__connected = False
 
253
            elif str(status).startswith("IDLE"):
 
254
                  self.set_busy(False)
 
255
                  self.set_from_icon_name("ubuntuone-client")
 
256
                  self.set_tooltip(TOOLTIP_TITLE + "Idle")
 
257
                  self.__connected = True
 
258
            elif str(status) == "BAD_VERSION":
 
259
                  self.set_busy(False)
 
260
                  self.set_from_icon_name("ubuntuone-client-offline")
 
261
                  self.set_tooltip(TOOLTIP_TITLE + "Update Required")
 
262
                  self.__connected = False
 
263
                  self.__need_update = True
 
264
                  self.__items["connect"].set_sensitive(False)
 
265
                  n = pynotify.Notification(
 
266
                        "Ubuntu One",
 
267
                        "A new client version is required to continue " +
 
268
                        "using the service. Please click the Ubuntu One" +
 
269
                        "applet icon to upgrade.",
 
270
                        "ubuntuone-client-offline")
 
271
                  n.set_urgency(pynotify.URGENCY_CRITICAL)
 
272
                  n.show()
 
273
                  logger.debug("Invalid protocol version. Update needed.")
 
274
            elif str(status) == "AUTH_FAILED":
 
275
                  self.set_busy(False)
 
276
                  self.set_from_icon_name("ubuntuone-client-offline")
 
277
                  self.set_tooltip(TOOLTIP_TITLE + "Authentication failed")
 
278
                  self.__connected = False
 
279
                  try:
 
280
                        client = self.__bus.get_object(
 
281
                              "com.ubuntuone.Authentication",
 
282
                              "/")
 
283
                        iface = dbus.Interface(client,
 
284
                                               "com.ubuntuone.Authentication")
 
285
                        def handler():
 
286
                              """Simple handler to make dbus do stuff async."""
 
287
                              return
 
288
 
 
289
                        iface.maybe_login("https://ubuntuone.com",
 
290
                                          "ubuntuone",
 
291
                                          True,
 
292
                                          reply_handler=handler,
 
293
                                          error_handler=handler)
 
294
 
 
295
                  except DBusException, e:
 
296
                        raise e
 
297
 
 
298
            else:
 
299
                  self.set_busy()
 
300
                  self.__connected = True
 
301
                  if str(status).startswith("CONNECTING") or \
 
302
                            str(status).startswith("AUTHENTICATING") or \
 
303
                            str(status).startswith("CONNECTED"):
 
304
                        self.set_tooltip(TOOLTIP_TITLE + "Connecting")
 
305
                  elif str(status).startswith("SCANNING") or \
 
306
                            str(status).startswith("READING"):
 
307
                        self.set_tooltip(TOOLTIP_TITLE + "Scanning")
 
308
                  elif str(status).startswith("WORKING"):
 
309
                        self.set_tooltip(TOOLTIP_TITLE + "Synchronizing")
 
310
                  elif str(status).startswith("UNKNOWN_ERROR"):
 
311
                        pass
 
312
                  else:
 
313
                        self.set_tooltip(TOOLTIP_TITLE + "Working")
 
314
 
 
315
            if self.__connected:
 
316
                  self.__items["connect"].hide()
 
317
                  self.__items["disconnect"].show()
 
318
            else:
 
319
                  self.__items["connect"].show()
 
320
                  self.__items["disconnect"].hide()
 
321
 
 
322
      def __get_root(self):
 
323
            """Method to get the rootdir from the sync daemon."""
 
324
            # Get the managed root directory
 
325
            try:
 
326
                  client = self.__bus.get_object(DBUS_IFACE_NAME, "/")
 
327
            except DBusException:
 
328
                  return False
 
329
 
 
330
            iface = dbus.Interface(client, DBUS_IFACE_SYNC_NAME)
 
331
            def got_root(root):
 
332
                  """We got the root dir."""
 
333
                  if self.__managed_dir:
 
334
                        self.__remove_from_places(self.__managed_dir)
 
335
                  self.__managed_dir = root
 
336
                  if os.path.isdir(self.__managed_dir) and \
 
337
                            os.access(self.__managed_dir,
 
338
                                      os.F_OK | os.R_OK | os.W_OK):
 
339
                        self.__items["open"].set_sensitive(True)
 
340
                        self.__add_to_places()
 
341
                  else:
 
342
                        self.__items["open"].set_sensitive(False)
 
343
 
 
344
            def got_err(error):
 
345
                  """Handle error from the dbus callback."""
 
346
                  if self.__managed_dir:
 
347
                        self.__remove_from_places(self.__managed_dir)
 
348
                  self.__managed_dir = None
 
349
                  self.__items["open"].set_sensitive(False)
 
350
 
 
351
            iface.get_rootdir(reply_handler=got_root, error_handler=got_err)
 
352
 
 
353
            # Now get the current status
 
354
            try:
 
355
                  client = self.__bus.get_object(DBUS_IFACE_NAME, "/status")
 
356
            except DBusException:
 
357
                  return False
 
358
 
 
359
            iface = dbus.Interface(client, DBUS_IFACE_STATUS_NAME)
 
360
            def status_error(error):
 
361
                  """Handle status error."""
 
362
                  pass
 
363
 
 
364
            iface.current_status(reply_handler=self.__status_changed,
 
365
                                 error_handler=status_error)
 
366
 
 
367
            return False
 
368
 
 
369
      def __build_menus(self):
 
370
            """Create the pop-up menu items."""
 
371
            menu = gtk.Menu()
 
372
 
 
373
            self.__items["connect"] = gtk.ImageMenuItem(stock_id=gtk.STOCK_CONNECT)
 
374
            menu.append(self.__items["connect"])
 
375
            self.__items["connect"].connect("activate", self.__toggle_state)
 
376
            self.__items["connect"].show()
 
377
 
 
378
            self.__items["disconnect"] = gtk.ImageMenuItem(stock_id=gtk.STOCK_DISCONNECT)
 
379
            menu.append(self.__items["disconnect"])
 
380
            self.__items["disconnect"].connect("activate", self.__toggle_state)
 
381
 
 
382
            sep = gtk.SeparatorMenuItem()
 
383
            menu.append(sep)
 
384
            sep.show()
 
385
 
 
386
            self.__items["bug"] = gtk.MenuItem(label="_Report a Problem")
 
387
            menu.append(self.__items["bug"])
 
388
            self.__items["bug"].connect("activate", self.__open_website,
 
389
                    "https://bugs.launchpad.net/ubuntuone-client/+filebug")
 
390
            self.__items["bug"].show()
 
391
 
 
392
            self.__items["open"] = gtk.MenuItem(label="_Open Folder")
 
393
            menu.append(self.__items["open"])
 
394
            self.__items["open"].connect("activate", self.__open_folder)
 
395
            self.__items["open"].set_sensitive(False)
 
396
            self.__items["open"].show()
 
397
 
 
398
            self.__items["web"] = gtk.MenuItem(label="_Go to Web")
 
399
            menu.append(self.__items["web"])
 
400
            self.__items["web"].connect("activate", self.__open_website)
 
401
            self.__items["web"].show()
 
402
 
 
403
            sep = gtk.SeparatorMenuItem()
 
404
            menu.append(sep)
 
405
            sep.show()
 
406
 
 
407
            self.__items["quit"] = gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
 
408
            menu.append(self.__items["quit"])
 
409
            self.__items["quit"].connect("activate", self.__quit_applet)
 
410
            self.__items["quit"].show()
 
411
 
 
412
            menu.show()
 
413
 
 
414
            return menu
 
415
 
 
416
      def __size_changed(self, icon, size, data=None):
 
417
            """Callback for when the size changes."""
 
418
            if size < 24:
 
419
                  self.__size = 16
 
420
            elif size >= 24 and size < 32:
 
421
                  self.__size = 24
 
422
            elif size >= 32 and size < 48:
 
423
                  self.__size = 32
 
424
            elif size >= 48 and size < 64:
 
425
                  self.__size = 48
 
426
            else:
 
427
                  self.__size = size
 
428
 
 
429
            self.__pixbuf = self.__theme.load_icon(
 
430
                  'ubuntuone-client-working',
 
431
                  self.__size,
 
432
                  gtk.ICON_LOOKUP_GENERIC_FALLBACK)
 
433
 
 
434
            self.__hframes = self.__pixbuf.get_width() / self.__size
 
435
            self.__vframes = self.__pixbuf.get_height() / self.__size
 
436
 
 
437
            return True
 
438
 
 
439
 
 
440
      def __spinner_timeout(self):
 
441
            """Timeout callback that spins the spinner."""
 
442
            # This is wrong
 
443
            if not self.__busy:
 
444
                  return False
 
445
 
 
446
            # Skip the first (resting) frame to avoid flicker
 
447
            if self.__hpos == 0 and self.__vpos == 0:
 
448
                  self.__hpos = 1
 
449
 
 
450
            x = self.__size * self.__hpos
 
451
            y = self.__size * self.__vpos
 
452
            pixbuf = self.__pixbuf.subpixbuf(x, y, self.__size, self.__size)
 
453
            self.set_from_pixbuf(pixbuf)
 
454
 
 
455
            self.__hpos += 1
 
456
            if self.__hpos == self.__hframes:
 
457
                  self.__hpos = 0
 
458
                  self.__vpos += 1
 
459
            if self.__vpos >= self.__vframes:
 
460
                  self.__vpos = 0
 
461
 
 
462
            return True
 
463
 
 
464
      def set_busy(self, busy=True):
 
465
            if self.__busy and busy:
 
466
                  return
 
467
 
 
468
            if not busy and self.__busy_id:
 
469
                  gobject.source_remove(self.__busy_id)
 
470
 
 
471
            self.__busy = busy
 
472
            self.__busy_id = gobject.timeout_add(100,
 
473
                                                 self.__spinner_timeout)
 
474
 
 
475
      def __popup_menu(self, icon, button, timestamp, data=None):
 
476
            """Pops up the context menu for the tray icon."""
 
477
            self.__menu.popup(None, None, gtk.status_icon_position_menu,
 
478
                              button, timestamp, icon)
 
479
 
 
480
      def __quit_applet(self, menuitem, data=None):
 
481
            """Disconnects the daemon and closes the applet."""
 
482
            def handler():
 
483
                  """Simple handler to make dbus do stuff async."""
 
484
                  pass
 
485
 
 
486
            try:
 
487
                  client = self.__bus.get_object(DBUS_IFACE_NAME, "/")
 
488
                  iface = dbus.Interface(client, DBUS_IFACE_SYNC_NAME)
 
489
                  iface.disconnect(reply_handler=handler, error_handler=handler)
 
490
            except DBusException:
 
491
                  pass
 
492
 
 
493
            from twisted.internet import reactor
 
494
            reactor.stop()
 
495
 
 
496
      def __toggle_state(self, menuitem, data=None):
 
497
            """Connects or disconnects the storage sync process."""
 
498
            def handler():
 
499
                  """Simple handler to make dbus do stuff async."""
 
500
                  pass
 
501
 
 
502
            try:
 
503
                  client = self.__bus.get_object(DBUS_IFACE_NAME, "/")
 
504
            except DBusException:
 
505
                  return
 
506
 
 
507
            iface = dbus.Interface(client, DBUS_IFACE_SYNC_NAME)
 
508
            if self.__connected:
 
509
                  iface.disconnect(reply_handler=handler, error_handler=handler)
 
510
            else:
 
511
                  iface.connect(reply_handler=handler, error_handler=handler)
 
512
 
 
513
      def __open_folder(self, data=None):
 
514
            """Opens the storage folder in the file manager."""
 
515
            if self.__need_update:
 
516
                  do_xdg_open("apt:ubuntuone-storage-protocol?refresh=yes")
 
517
                  return
 
518
 
 
519
            if not self.__managed_dir or not os.path.isdir(self.__managed_dir):
 
520
                  return
 
521
 
 
522
            folder = "file://%s" % quote(self.__managed_dir)
 
523
            do_xdg_open(folder)
 
524
 
 
525
      def __open_website(self, data=None, url=None):
 
526
            """Opens the ubuntuone.com web site."""
 
527
            if url:
 
528
                  do_xdg_open(url)
 
529
            else:
 
530
                  do_xdg_open("https://ubuntuone.com/")
 
531
 
 
532
      def __add_to_places(self):
 
533
            """Add the managed directory to the .gtk-bookmarks file."""
 
534
            path = os.path.join(os.path.expanduser("~"), ".gtk-bookmarks")
 
535
            with open(path, "a+") as f:
 
536
                  bookmarks_entry = "file://%s %s\n" % (
 
537
                        quote(self.__managed_dir),
 
538
                        os.path.basename(self.__managed_dir))
 
539
                  in_file = False
 
540
                  for line in f:
 
541
                        if line == bookmarks_entry:
 
542
                              in_file = True
 
543
                  if not in_file:
 
544
                        f.write(bookmarks_entry)
 
545
 
 
546
      def __remove_from_places(self, dir):
 
547
            """Remove the old path from the .gtk-bookmarks file."""
 
548
            path = os.path.join(os.path.expanduser("~"), ".gtk-bookmarks")
 
549
            with open(path, "a+") as f:
 
550
                  entry = "file://%s %s\n" % (quote(dir), os.path.basename(dir))
 
551
                  lines = []
 
552
                  for line in f:
 
553
                        if line != entry:
 
554
                              lines.append(line)
 
555
                  f.truncate(0)
 
556
                  f.seek(0)
 
557
                  output = "".join(lines)
 
558
                  f.write(output)
 
559
 
 
560
 
 
561
if __name__ == "__main__":
 
562
      parser = OptionParser("",
 
563
                            description="Ubuntu One status applet",
 
564
                            option_list = [
 
565
                  make_option("--signup", "-s",
 
566
                              action="store_true",
 
567
                              dest="signup",
 
568
                              help="Log in or Sign Up for Ubuntu One"),
 
569
                  ])
 
570
      options, args = parser.parse_args()
 
571
 
 
572
      # Register DBus service for making sure we run only one instance
 
573
      bus = dbus.SessionBus()
 
574
      if bus.request_name("com.ubuntuone.ClientApplet", dbus.bus.NAME_FLAG_DO_NOT_QUEUE) == dbus.bus.REQUEST_NAME_REPLY_EXISTS:
 
575
            print "Ubuntu One client applet already running, quitting"
 
576
            sys.exit(-1)
 
577
 
 
578
      icon = AppletMain(signup=options.signup)
 
579
      icon.main()