Package conduit :: Package gtkui :: Module UI
[hide private]

Source Code for Module conduit.gtkui.UI

  1  """ 
  2  Draws the applications main window 
  3   
  4  Also manages the callbacks from menu and GUI items 
  5   
  6  Copyright: John Stowers, 2006 
  7  License: GPLv2 
  8  """ 
  9  import gobject 
 10  import gtk, gtk.glade 
 11  import gnome.ui 
 12  import os.path 
 13  import gettext 
 14  import threading 
 15  from gettext import gettext as _ 
 16  import logging 
 17  log = logging.getLogger("gtkui.UI") 
 18   
 19  import conduit 
 20  import conduit.Web as Web 
 21  import conduit.Conduit as Conduit 
 22  import conduit.gtkui.Canvas as Canvas 
 23  import conduit.gtkui.MsgArea as MsgArea 
 24  import conduit.gtkui.Tree as Tree 
 25  import conduit.gtkui.ConflictResolver as ConflictResolver 
 26  import conduit.gtkui.Database as Database 
 27   
 28   
 29  DEFAULT_CONDUIT_BROWSER = "gtkmozembed" 
 30  DEVELOPER_WEB_LINKS = ( 
 31  #name,                      #url 
 32  ("Introduction",            "http://www.conduit-project.org/wiki/Development"), 
 33  ("Writing a Data Provider", "http://www.conduit-project.org/wiki/WritingADataProvider"), 
 34  ("API Documentation",       "http://doc.conduit-project.org/conduit/"), 
 35  ("Test Results",            "http://tests.conduit-project.org/") 
 36  ) 
 37   
 38  #set up the gettext system and locales 
 39  for module in gtk.glade, gettext: 
 40      module.bindtextdomain('conduit', conduit.LOCALE_DIR) 
 41      module.textdomain('conduit') 
 42       
43 -class MainWindow:
44 """ 45 The main conduit window. 46 """
47 - def __init__(self, conduitApplication, moduleManager, typeConverter, syncManager):
48 """ 49 Constructs the mainwindow. Throws up a splash screen to cover 50 the most time consuming pieces 51 """ 52 gnome.ui.authentication_manager_init() 53 54 self.conduitApplication = conduitApplication 55 56 #add some additional dirs to the icon theme search path so that 57 #modules can provider their own icons 58 icon_dirs = [ 59 conduit.SHARED_DATA_DIR, 60 conduit.SHARED_MODULE_DIR, 61 os.path.join(conduit.SHARED_DATA_DIR,"icons"), 62 os.path.join(conduit.USER_DIR, "modules") 63 ] 64 for i in icon_dirs: 65 gtk.icon_theme_get_default().prepend_search_path(i) 66 67 self.gladeFile = os.path.join(conduit.SHARED_DATA_DIR, "conduit.glade") 68 self.widgets = gtk.glade.XML(self.gladeFile, "MainWindow") 69 70 dic = { "on_mainwindow_delete" : self.on_window_closed, 71 "on_mainwindow_state_event" : self.on_window_state_event, 72 "on_synchronize_activate" : self.on_synchronize_all_clicked, 73 "on_cancel_activate" : self.on_cancel_all_clicked, 74 "on_quit_activate" : self.on_window_closed, 75 "on_clear_canvas_activate" : self.on_clear_canvas, 76 "on_preferences_activate" : self.on_conduit_preferences, 77 "on_about_activate" : self.on_about_conduit, 78 "on_contents_activate" : self.on_help, 79 "on_save1_activate" : self.save_settings, 80 None : None 81 } 82 self.widgets.signal_autoconnect(dic) 83 84 #type converter and sync manager 85 self.type_converter = typeConverter 86 self.sync_manager = syncManager 87 88 #Initialize the mainWindow 89 self.mainWindow = self.widgets.get_widget("MainWindow") 90 #Enable RGBA colormap 91 if conduit.GLOBALS.settings.get("gui_use_rgba_colormap") == True: 92 screen = self.mainWindow.get_screen() 93 colormap = screen.get_rgba_colormap() 94 if colormap: 95 gtk.widget_set_default_colormap(colormap) 96 self.mainWindow.set_position(gtk.WIN_POS_CENTER) 97 self.mainWindow.set_icon_name("conduit") 98 title = "Conduit" 99 if conduit.IS_DEVELOPMENT_VERSION: 100 title = title + " - %s (Development Version)" % conduit.VERSION 101 if not conduit.IS_INSTALLED: 102 title = title + " - Running Uninstalled" 103 self.mainWindow.set_title(title) 104 105 #Configure canvas 106 self.canvasSW = self.widgets.get_widget("canvasScrolledWindow") 107 self.hpane = self.widgets.get_widget("hpaned1") 108 109 #start up the canvas 110 msg = MsgArea.MsgAreaController() 111 self.widgets.get_widget("mainVbox").pack_start(msg, False, False) 112 self.canvas = Canvas.Canvas( 113 parentWindow=self.mainWindow, 114 typeConverter=self.type_converter, 115 syncManager=self.sync_manager, 116 dataproviderMenu=gtk.glade.XML(self.gladeFile, "DataProviderMenu"), 117 conduitMenu=gtk.glade.XML(self.gladeFile, "ConduitMenu"), 118 msg=msg 119 ) 120 self.canvasSW.add(self.canvas) 121 self.canvas.connect('drag-drop', self.drop_cb) 122 self.canvas.connect("drag-data-received", self.drag_data_received_data) 123 124 # Populate the tree model 125 self.dataproviderTreeModel = Tree.DataProviderTreeModel() 126 dataproviderScrolledWindow = self.widgets.get_widget("scrolledwindow2") 127 self.dataproviderTreeView = Tree.DataProviderTreeView(self.dataproviderTreeModel) 128 dataproviderScrolledWindow.add(self.dataproviderTreeView) 129 130 #Set up the expander used for resolving sync conflicts 131 self.conflictResolver = ConflictResolver.ConflictResolver(self.widgets) 132 133 #final GUI setup 134 self.cancelSyncButton = self.widgets.get_widget('cancel') 135 self.hpane.set_position(conduit.GLOBALS.settings.get("gui_hpane_postion")) 136 self.dataproviderTreeView.set_expand_rows() 137 self.window_state = 0 138 139 #if running a development version, add some developer specific links 140 #to the help menu 141 if conduit.IS_DEVELOPMENT_VERSION: 142 helpMenu = self.widgets.get_widget("help_menu") 143 developersMenuItem = gtk.ImageMenuItem("Developers") 144 developersMenuItem.set_image( 145 gtk.image_new_from_icon_name( 146 "applications-development", 147 gtk.ICON_SIZE_MENU)) 148 developersMenu = gtk.Menu() 149 developersMenuItem.set_submenu(developersMenu) 150 helpMenu.prepend(developersMenuItem) 151 for name,url in DEVELOPER_WEB_LINKS: 152 item = gtk.ImageMenuItem(name) 153 item.set_image( 154 gtk.image_new_from_icon_name( 155 "applications-internet", 156 gtk.ICON_SIZE_MENU)) 157 item.connect("activate",self.on_developer_menu_item_clicked,name,url) 158 developersMenu.append(item)
159 160
161 - def on_developer_menu_item_clicked(self, menuitem, name, url):
162 threading.Thread( 163 target=Web.LoginMagic, 164 args=(name, url), 165 kwargs={"login_function":lambda: True} 166 ).start()
167
168 - def on_conduit_added(self, syncset, cond):
169 cond.connect("sync-started", self.on_sync_started) 170 cond.connect("sync-completed", self.on_sync_completed) 171 cond.connect("sync-conflict", self.conflictResolver.on_conflict)
172
173 - def set_model(self, syncSet):
174 self.syncSet = syncSet 175 self.syncSet.connect("conduit-added", self.on_conduit_added) 176 self.canvas.set_sync_set(syncSet)
177
178 - def present(self):
179 """ 180 Present the main window. Enjoy your window 181 """ 182 log.debug("Presenting GUI") 183 self.mainWindow.show_all() 184 self.mainWindow.present()
185
186 - def minimize_to_tray(self):
187 """ 188 Iconifies the main window 189 """ 190 log.debug("Iconifying GUI") 191 self.mainWindow.hide()
192
193 - def is_visible(self):
194 """ 195 Returns True if mainWindow is visible 196 (not minimized or withdrawn) 197 """ 198 minimized = self.window_state & gtk.gdk.WINDOW_STATE_ICONIFIED 199 return (not minimized) and self.mainWindow.get_property('visible')
200
201 - def on_sync_started(self, thread):
202 self.cancelSyncButton.set_property("sensitive", True)
203
204 - def on_sync_completed(self, thread, aborted, error, conflict):
205 self.cancelSyncButton.set_property( 206 "sensitive", 207 conduit.GLOBALS.syncManager.is_busy() 208 )
209
210 - def on_synchronize_all_clicked(self, widget):
211 """ 212 Synchronize all valid conduits on the canvas 213 """ 214 self.conduitApplication.Synchronize()
215
216 - def on_cancel_all_clicked(self, widget):
217 """ 218 Cancels all currently runnings syncs 219 """ 220 self.conduitApplication.Cancel()
221
222 - def on_clear_canvas(self, widget):
223 """ 224 Clear the canvas and start a new sync set 225 """ 226 self.canvas.clear_canvas()
227
228 - def on_conduit_preferences(self, widget):
229 """ 230 Show the properties of the current sync set (status, conflicts, etc 231 Edit the sync specific properties 232 """ 233 def on_clear_button_clicked(sender, treeview, sqliteListStore): 234 treeview.set_model(None) 235 conduit.GLOBALS.mappingDB.delete() 236 treeview.set_model(sqliteListStore)
237 238 #Build some liststores to display 239 CONVERT_FROM_MESSAGE = _("Convert from") 240 CONVERT_INTO_MESSAGE = _("into") 241 242 convertables = self.type_converter.get_convertables_list() 243 converterListStore = gtk.ListStore( str ) 244 for froms,tos in convertables: 245 string = "%s %s %s %s" % (CONVERT_FROM_MESSAGE, froms, CONVERT_INTO_MESSAGE, tos) 246 converterListStore.append( (string,) ) 247 dataProviderListStore = gtk.ListStore( str, bool ) 248 #get all dataproviders 249 for i in conduit.GLOBALS.moduleManager.get_modules_by_type("sink","source","twoway"): 250 dataProviderListStore.append(("Name: %s\nDescription: %s)" % (i.name, i.description), True)) 251 #include files that could not be loaded 252 for f in conduit.GLOBALS.moduleManager.invalidFiles: 253 dataProviderListStore.append(("Error loading file: %s" % f, False)) 254 255 #construct the dialog 256 tree = gtk.glade.XML(self.gladeFile, "PreferencesDialog") 257 notebook = tree.get_widget("prop_notebook") 258 259 #Show the DB contents to help debugging 260 if conduit.IS_DEVELOPMENT_VERSION: 261 vbox = gtk.VBox(False,5) 262 263 #build the treeview to show all column fields. For performance 264 #reasons it is fixed_height and fixed_FIXE 265 treeview = gtk.TreeView() 266 treeview.set_headers_visible(True) 267 treeview.set_fixed_height_mode(True) 268 index = 1 269 db = conduit.GLOBALS.mappingDB._db 270 for name in db.get_fields("mappings"): 271 column = gtk.TreeViewColumn( 272 name, 273 gtk.CellRendererText(), 274 text=index) 275 column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) 276 column.set_fixed_width(250) 277 treeview.append_column(column) 278 index = index + 1 279 280 store = Database.GenericDBListStore("mappings", db) 281 treeview.set_model(store) 282 283 sw = gtk.ScrolledWindow() 284 sw.add(treeview) 285 vbox.pack_start(sw,True,True) 286 287 clear = gtk.Button(None,gtk.STOCK_CLEAR) 288 clear.connect("clicked", on_clear_button_clicked, treeview, store) 289 vbox.pack_start(clear, False, False) 290 291 notebook.append_page(vbox,gtk.Label(_('Relationship Database'))) 292 293 converterTreeView = tree.get_widget("dataConversionsTreeView") 294 converterTreeView.set_model(converterListStore) 295 converterTreeView.append_column(gtk.TreeViewColumn(_("Conversions Available"), 296 gtk.CellRendererText(), 297 text=0) 298 ) 299 dataproviderTreeView = tree.get_widget("dataProvidersTreeView") 300 dataproviderTreeView.set_model(dataProviderListStore) 301 dataproviderTreeView.append_column(gtk.TreeViewColumn(_("Name"), 302 gtk.CellRendererText(), 303 text=0) 304 ) 305 dataproviderTreeView.append_column(gtk.TreeViewColumn(_("Loaded"), 306 gtk.CellRendererToggle(), 307 active=1) 308 ) 309 310 #fill out the configuration tab 311 save_settings_check = tree.get_widget("save_settings_check") 312 save_settings_check.set_active(conduit.GLOBALS.settings.get("save_on_exit")) 313 status_icon_check = tree.get_widget("status_icon_check") 314 status_icon_check.set_active(conduit.GLOBALS.settings.get("show_status_icon")) 315 minimize_to_tray_check = tree.get_widget("minimize_to_tray_check") 316 minimize_to_tray_check.set_active(conduit.GLOBALS.settings.get("gui_minimize_to_tray")) 317 web_browser_check = tree.get_widget("web_check") 318 web_browser_check.set_active(conduit.GLOBALS.settings.get("web_login_browser") != "system") 319 show_hints_check = tree.get_widget("show_hints_check") 320 show_hints_check.set_active(conduit.GLOBALS.settings.get("gui_show_hints")) 321 322 323 #restore the current policy 324 for policyName in Conduit.CONFLICT_POLICY_NAMES: 325 currentValue = conduit.GLOBALS.settings.get("default_policy_%s" % policyName) 326 for policyValue in Conduit.CONFLICT_POLICY_VALUES: 327 name = "%s_%s" % (policyName,policyValue) 328 widget = tree.get_widget(name+"_radio") 329 widget.set_image( 330 gtk.image_new_from_icon_name( 331 Conduit.CONFLICT_POLICY_VALUE_ICONS[name], 332 gtk.ICON_SIZE_MENU)) 333 if currentValue == policyValue: 334 widget.set_active(True) 335 336 #The dataprovider factories can provide a configuration widget which is 337 #packed into the notebook 338 for i in conduit.GLOBALS.moduleManager.dataproviderFactories:#get_modules_by_type("dataprovider-factory"): 339 widget = i.setup_configuration_widget() 340 if widget: 341 notebook.append_page( 342 widget, 343 gtk.Label(i.get_name())) 344 345 notebook.show_all() 346 347 #Show the dialog 348 dialog = tree.get_widget("PreferencesDialog") 349 dialog.set_transient_for(self.mainWindow) 350 response = dialog.run() 351 if response == gtk.RESPONSE_OK: 352 conduit.GLOBALS.settings.set("save_on_exit", save_settings_check.get_active()) 353 conduit.GLOBALS.settings.set("show_status_icon", status_icon_check.get_active()) 354 conduit.GLOBALS.settings.set("gui_minimize_to_tray", minimize_to_tray_check.get_active()) 355 if web_browser_check.get_active(): 356 conduit.GLOBALS.settings.set("web_login_browser", DEFAULT_CONDUIT_BROWSER) 357 else: 358 conduit.GLOBALS.settings.set("web_login_browser", "system") 359 conduit.GLOBALS.settings.set("gui_show_hints", show_hints_check.get_active()) 360 #save the current policy 361 for policyName in Conduit.CONFLICT_POLICY_NAMES: 362 for policyValue in Conduit.CONFLICT_POLICY_VALUES: 363 name = "%s_%s" % (policyName,policyValue) 364 if tree.get_widget(name+"_radio").get_active() == True: 365 conduit.GLOBALS.settings.set( 366 "default_policy_%s" % policyName, 367 policyValue) 368 369 #give the dataprovider factories to ability to save themselves 370 for factory in conduit.GLOBALS.moduleManager.dataproviderFactories: 371 factory.save_configuration(response == gtk.RESPONSE_OK) 372 373 dialog.destroy()
374 375
376 - def on_about_conduit(self, widget):
377 """ 378 Display about dialog 379 """ 380 dialog = AboutDialog() 381 dialog.set_transient_for(self.mainWindow) 382 dialog.run() 383 dialog.destroy()
384
385 - def on_help(self, widget):
386 """ 387 Display help 388 """ 389 if conduit.IS_INSTALLED: 390 uri = "ghelp:conduit" 391 else: 392 #if we are not installed then launch the ghelp uri with a full path 393 uri = "ghelp:%s" % os.path.join(conduit.DIRECTORY,"help","C","conduit.xml") 394 log.debug("Launching help: %s" % uri) 395 gobject.spawn_async( 396 argv=("xdg-open",uri), 397 flags=gobject.SPAWN_SEARCH_PATH | gobject.SPAWN_STDOUT_TO_DEV_NULL | gobject.SPAWN_STDERR_TO_DEV_NULL 398 )
399
400 - def on_window_state_event(self, widget, event):
401 visible = self.is_visible() 402 self.window_state = event.new_window_state 403 if self.window_state & gtk.gdk.WINDOW_STATE_ICONIFIED and visible: 404 if conduit.GLOBALS.settings.get("gui_minimize_to_tray"): 405 self.minimize_to_tray()
406
407 - def on_window_closed(self, widget, event=None):
408 """ 409 Check if there are any synchronizations currently in progress and 410 ask the user if they wish to cancel them 411 """ 412 busy = False 413 quit = False 414 for c in self.syncSet.get_all_conduits(): 415 if c.is_busy(): 416 busy = True 417 418 if busy: 419 dialog = gtk.MessageDialog( 420 self.mainWindow, 421 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 422 gtk.MESSAGE_QUESTION, 423 gtk.BUTTONS_YES_NO,_("Synchronization in progress. Do you want to cancel it?") 424 ) 425 response = dialog.run() 426 if response == gtk.RESPONSE_YES: 427 quit = True 428 else: 429 #Dont exit 430 dialog.destroy() 431 return True 432 else: 433 quit = True 434 435 #OK, if we have decided to quit then call quit on the 436 #DBus interface which will tidy up any pending running 437 #non gui tasks 438 if quit: 439 self.conduitApplication.Quit()
440 441
442 - def drop_cb(self, wid, context, x, y, time):
443 """ 444 drop cb 445 """ 446 self.canvas.drag_get_data(context, context.targets[0], time) 447 return True
448
449 - def drag_data_received_data(self, treeview, context, x, y, selection, info, etime):
450 """ 451 DND 452 """ 453 dataproviderKey = selection.data 454 #FIXME: DnD should be cancelled in the Treeview on the drag-begin 455 #signal and NOT here 456 if dataproviderKey != "": 457 #adjust for scrolled window offset 458 scroll = self.canvasSW.get_vadjustment().get_value() 459 460 #Add a new instance if the dataprovider to the canvas. 461 new = conduit.GLOBALS.moduleManager.get_module_wrapper_with_instance(dataproviderKey) 462 self.canvas.add_dataprovider_to_canvas( 463 dataproviderKey, 464 new, 465 x, 466 int(scroll) + y 467 ) 468 469 context.finish(True, True, etime) 470 return
471
472 - def save_settings(self, widget):
473 """ 474 Saves the application settings to an XML document. 475 Saves the GUI settings (window state, position, etc to gconf) 476 """ 477 #save the canvas 478 self.syncSet.save_to_xml() 479 480 #GUI settings 481 conduit.GLOBALS.settings.set( 482 "gui_hpane_postion", 483 self.hpane.get_position()) 484 conduit.GLOBALS.settings.set( 485 "gui_window_size", 486 self.mainWindow.get_size()) 487 conduit.GLOBALS.settings.set( 488 "gui_expanded_rows", 489 self.dataproviderTreeView.get_expanded_rows())
490
491 -class SplashScreen:
492 """ 493 Simple splash screen class which shows an image for a predetermined period 494 of time or until L{SplashScreen.destroy} is called. 495 496 Code adapted from banshee 497 """ 498 DELAY = 1500 #msec
499 - def __init__(self):
500 """ 501 Constructor 502 """ 503 #If false the main window should call destroy() to remove the splash 504 self.destroyed = True
505
506 - def show(self):
507 """ 508 Builds the splashscreen and connects the splash window to be destroyed 509 via a timeout callback in L{SplashScreen.DELAY}msec time. 510 511 The splash can also be destroyed manually by the application 512 """ 513 self.wSplash = gtk.Window(gtk.WINDOW_POPUP) 514 self.wSplash.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_SPLASHSCREEN) 515 self.wSplash.set_decorated(False) 516 517 wSplashScreen = gtk.Image() 518 wSplashScreen.set_from_file(os.path.join(conduit.SHARED_DATA_DIR,"conduit-splash.png")) 519 520 # Make a pretty frame 521 wSplashFrame = gtk.Frame() 522 wSplashFrame.set_shadow_type(gtk.SHADOW_OUT) 523 wSplashFrame.add(wSplashScreen) 524 self.wSplash.add(wSplashFrame) 525 526 # OK throw up the splashscreen 527 self.wSplash.set_position(gtk.WIN_POS_CENTER) 528 529 #The splash screen is destroyed automatically (via timeout) 530 #or when the application is finished loading 531 self.destroyed = False 532 533 self.wSplash.show_all() 534 # ensure it is rendered immediately 535 while gtk.events_pending(): 536 gtk.main_iteration() 537 # The idle timeout handler to destroy the splashscreen 538 gobject.timeout_add(SplashScreen.DELAY,self.destroy)
539
540 - def destroy(self):
541 """ 542 Destroys the splashscreen. Can be safely called manually (prior to) 543 or via the timer callback 544 """ 545 if not self.destroyed: 546 self.wSplash.destroy() 547 self.destroyed = True
548
549 -class AboutDialog(gtk.AboutDialog):
550 - def __init__(self):
551 gtk.AboutDialog.__init__(self) 552 self.set_name("Conduit") 553 self.set_version(conduit.VERSION) 554 self.set_comments("Synchronisation for GNOME") 555 self.set_website("http://www.conduit-project.org") 556 self.set_authors([ 557 "John Stowers", 558 "John Carr", 559 "Thomas Van Machelen", 560 "Jonny Lamb"]) 561 self.set_artists([ 562 "John Stowers", 563 "mejogid", 564 "The Tango Project (http://tango.freedesktop.org)"]) 565 self.set_logo_icon_name("conduit")
566
567 -class StatusIcon(gtk.StatusIcon):
568 - def __init__(self, conduitApplication):
569 gtk.StatusIcon.__init__(self) 570 571 #we need some custom icons 572 gtk.icon_theme_get_default().prepend_search_path(conduit.SHARED_DATA_DIR) 573 574 self.conduitApplication = conduitApplication 575 menu = ''' 576 <ui> 577 <menubar name="Menubar"> 578 <menu action="Menu"> 579 <menuitem action="Sync"/> 580 <menuitem action="Cancel"/> 581 <menuitem action="Quit"/> 582 <separator/> 583 <menuitem action="About"/> 584 </menu> 585 </menubar> 586 </ui> 587 ''' 588 actions = [ 589 ('Menu', None, 'Menu'), 590 ('Sync', gtk.STOCK_EXECUTE, _("_Synchronize All"), None, _("Synchronizes All Groups"), self.on_synchronize), 591 ('Cancel', gtk.STOCK_CANCEL, _("_Cancel Synchronization"), None, _("Cancels Currently Synchronizing Groups"), self.on_cancel), 592 ('Quit', gtk.STOCK_QUIT, _("_Quit"), None, _("Close Conduit"), self.on_quit), 593 ('About', gtk.STOCK_ABOUT, _("_About"), None, _("About Conduit"), self.on_about)] 594 ag = gtk.ActionGroup('Actions') 595 ag.add_actions(actions) 596 self.manager = gtk.UIManager() 597 self.manager.insert_action_group(ag, 0) 598 self.manager.add_ui_from_string(menu) 599 self.menu = self.manager.get_widget('/Menubar/Menu/About').props.parent 600 self.cancelButton = self.manager.get_widget('/Menubar/Menu/Cancel') 601 self.connect('popup-menu', self.on_popup_menu) 602 self.connect('activate', self.on_click) 603 604 self.animating = False 605 self.check_animate = False 606 self.conflict = False 607 self.animated_idx = 0 608 self.animated_icons = range(1,8) 609 610 #start with the application icon 611 self.cancelButton.set_property("sensitive", False) 612 self.set_from_icon_name("conduit") 613 self.set_tooltip("Conduit") 614 self.set_visible(True)
615
616 - def _animate_icon_timeout(self):
617 #FIXME: When will gtk support animated gtk status icons? 618 if self.animating: 619 if self.animated_idx == self.animated_icons[-1]: 620 self.animated_idx = 1 621 else: 622 self.animated_idx += 1 623 self.set_from_icon_name("conduit-progress-%d" % self.animated_idx) 624 # re-check animation? 625 if self.check_animate: 626 self.animating = conduit.GLOBALS.syncManager.is_busy() 627 self.check_animate = False 628 return True 629 630 else: 631 if self.conflict: 632 self.set_from_icon_name("dialog-error") 633 self.set_tooltip(_("Synchronization Error")) 634 else: 635 self.set_from_icon_name("conduit") 636 self.set_tooltip(_("Synchronization Complete")) 637 self.conflict = False 638 self.cancelButton.set_property("sensitive", False) 639 return False
640
641 - def on_conduit_added(self, syncset, cond):
642 cond.connect("sync-started", self._on_sync_started) 643 cond.connect("sync-completed", self._on_sync_completed) 644 cond.connect("sync-conflict", self._on_sync_conflict)
645
646 - def on_conduit_removed(self, syncset, cond):
647 pass
648
649 - def _on_sync_started(self, cond):
650 if not self.animating: 651 self.animating = True 652 self.set_tooltip(_("Synchronizing")) 653 self.cancelButton.set_property("sensitive", True) 654 gobject.timeout_add(100, self._animate_icon_timeout)
655
656 - def _on_sync_completed(self, cond, aborted, error, conflict):
657 # check need for animation on next iteration 658 self.check_animate = True
659
660 - def _on_sync_conflict(self, cond, conflict):
661 self.conflict = True
662
663 - def on_synchronize(self, data):
664 self.conduitApplication.Synchronize()
665
666 - def on_cancel(self, data):
667 self.conduitApplication.Cancel()
668
669 - def on_popup_menu(self, status, button, time):
670 self.menu.popup(None, None, None, button, time)
671
672 - def on_quit(self, data):
673 self.conduitApplication.Quit()
674
675 - def on_about(self, data):
676 dialog = AboutDialog() 677 dialog.run() 678 dialog.destroy()
679
680 - def on_click(self, status):
681 if self.conduitApplication.HasGUI(): 682 if self.conduitApplication.gui.is_visible(): 683 self.conduitApplication.gui.minimize_to_tray() 684 else: 685 self.conduitApplication.gui.present() 686 else: 687 self.conduitApplication.BuildGUI() 688 self.conduitApplication.ShowGUI()
689
690 -def main_loop():
691 gtk.main()
692
693 -def main_quit():
694 gtk.main_quit()
695