Package flumotion :: Package admin :: Package gtk :: Module adminwindow
[hide private]

Source Code for Module flumotion.admin.gtk.adminwindow

   1  # -*- Mode: Python -*- 
   2  # vi:si:et:sw=4:sts=4:ts=4 
   3  # 
   4  # Flumotion - a streaming media server 
   5  # Copyright (C) 2004,2005,2006,2007,2008 Fluendo, S.L. (www.fluendo.com). 
   6  # All rights reserved. 
   7   
   8  # This file may be distributed and/or modified under the terms of 
   9  # the GNU General Public License version 2 as published by 
  10  # the Free Software Foundation. 
  11  # This file is distributed without any warranty; without even the implied 
  12  # warranty of merchantability or fitness for a particular purpose. 
  13  # See "LICENSE.GPL" in the source distribution for more information. 
  14   
  15  # Licensees having purchased or holding a valid Flumotion Advanced 
  16  # Streaming Server license may use this file in accordance with the 
  17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
  18  # See "LICENSE.Flumotion" in the source distribution for more information. 
  19   
  20  # Headers in this file shall remain intact. 
  21   
  22  """admin window interface, the main interface of flumotion-admin. 
  23   
  24  Here is an overview of the different parts of the admin interface:: 
  25   
  26   +--------------[ AdminWindow ]-------------+ 
  27   | Menubar                                  | 
  28   +------------------------------------------+ 
  29   | Toolbar                                  | 
  30   +--------------------+---------------------+ 
  31   |                    |                     | 
  32   |                    |                     | 
  33   |                    |                     | 
  34   |                    |                     | 
  35   |  ComponentList     |   ComponentView     | 
  36   |                    |                     | 
  37   |                    |                     | 
  38   |                    |                     | 
  39   |                    |                     | 
  40   |                    |                     | 
  41   +--------------------+---------------------+ 
  42   | AdminStatusbar                           | 
  43   +------------------------------------------- 
  44   
  45  The main class which builds everything together is a L{AdminWindow}, 
  46  which is defined in this file: 
  47   
  48    - L{AdminWindow} creates the other UI parts internally, see the 
  49      L{AdminWindow._createUI}. 
  50    - Menubar and Toolbar are created by a GtkUIManager, see 
  51      L{AdminWindow._createUI} and L{MAIN_UI}. 
  52    - L{ComponentList<flumotion.admin.gtk.componentlist.ComponentList>} 
  53      is a list of all components, and is created in the 
  54      L{flumotion.admin.gtk.componentlist} module. 
  55    - L{ComponentView<flumotion.admin.gtk.componentview.ComponentView>} 
  56      contains a component specific view, usually a set of tabs, it is 
  57      created in the L{flumotion.admin.gtk.componentview} module. 
  58    - L{AdminStatus<flumotion.admin.gtk.statusbar.AdminStatus>} is a 
  59      statusbar displaying context specific hints and is defined in the 
  60      L{flumotion.admin.gtk.statusbar} module. 
  61   
  62  """ 
  63   
  64  import gettext 
  65  import os 
  66  import sys 
  67   
  68  import gobject 
  69  import gtk 
  70  from kiwi.ui.delegates import GladeDelegate 
  71  from kiwi.ui.dialogs import yesno 
  72  from twisted.internet import defer, reactor 
  73  from zope.interface import implements 
  74   
  75  from flumotion.admin.admin import AdminModel 
  76  from flumotion.admin.connections import getRecentConnections, \ 
  77       hasRecentConnections 
  78  from flumotion.admin.gtk.dialogs import AboutDialog, ErrorDialog, \ 
  79       ProgressDialog, showConnectionErrorDialog 
  80  from flumotion.admin.gtk.connections import ConnectionsDialog 
  81  from flumotion.admin.gtk.componentlist import getComponentLabel, ComponentList 
  82  from flumotion.admin.gtk.debugmarkerview import DebugMarkerDialog 
  83  from flumotion.admin.gtk.statusbar import AdminStatusbar 
  84  from flumotion.common.common import componentId 
  85  from flumotion.common.connection import PBConnectionInfo 
  86  from flumotion.common.errors import ConnectionRefusedError, \ 
  87       ConnectionFailedError, BusyComponentError 
  88  from flumotion.common.i18n import N_, gettexter 
  89  from flumotion.common.log import Loggable 
  90  from flumotion.common.planet import AdminComponentState, moods 
  91  from flumotion.common.pygobject import gsignal 
  92  from flumotion.configure import configure 
  93  from flumotion.manager import admin # Register types 
  94  from flumotion.twisted.flavors import IStateListener 
  95  from flumotion.ui.trayicon import FluTrayIcon 
  96  from flumotion.wizard.models import AudioProducer, Porter, VideoProducer 
  97   
  98  admin # pyflakes 
  99   
 100  __version__ = "$Rev: 7070 $" 
 101  _ = gettext.gettext 
 102  T_ = gettexter() 
 103   
 104  MAIN_UI = """ 
 105  <ui> 
 106    <menubar name="Menubar"> 
 107      <menu action="Connection"> 
 108        <menuitem action="OpenRecent"/> 
 109        <menuitem action="OpenExisting"/> 
 110        <menuitem action="ImportConfig"/> 
 111        <menuitem action="ExportConfig"/> 
 112        <separator name="sep-conn1"/> 
 113        <placeholder name="Recent"/> 
 114        <separator name="sep-conn2"/> 
 115        <menuitem action="Quit"/> 
 116      </menu> 
 117      <menu action="Manage"> 
 118        <menuitem action="StartComponent"/> 
 119        <menuitem action="StopComponent"/> 
 120        <menuitem action="DeleteComponent"/> 
 121        <separator name="sep-manage1"/> 
 122        <menuitem action="StartAll"/> 
 123        <menuitem action="StopAll"/> 
 124        <menuitem action="ClearAll"/> 
 125        <separator name="sep-manage2"/> 
 126        <menuitem action="AddFormat"/> 
 127        <separator name="sep-manage3"/> 
 128        <menuitem action="RunConfigurationWizard"/> 
 129      </menu> 
 130      <menu action="Debug"> 
 131        <menuitem action="EnableDebugging"/> 
 132        <separator name="sep-debug1"/> 
 133        <menuitem action="StartShell"/> 
 134        <menuitem action="DumpConfiguration"/> 
 135        <menuitem action="WriteDebugMarker"/> 
 136      </menu> 
 137      <menu action="Help"> 
 138        <menuitem action="About"/> 
 139      </menu> 
 140    </menubar> 
 141    <toolbar name="Toolbar"> 
 142      <toolitem action="OpenRecent"/> 
 143      <separator name="sep-toolbar1"/> 
 144      <toolitem action="StartComponent"/> 
 145      <toolitem action="StopComponent"/> 
 146      <toolitem action="DeleteComponent"/> 
 147      <separator name="sep-toolbar2"/> 
 148      <toolitem action="RunConfigurationWizard"/> 
 149    </toolbar> 
 150    <popup name="ComponentContextMenu"> 
 151      <menuitem action="StartComponent"/> 
 152      <menuitem action="StopComponent"/> 
 153      <menuitem action="DeleteComponent"/> 
 154      <menuitem action="KillComponent"/> 
 155    </popup> 
 156  </ui> 
 157  """ 
 158   
 159  RECENT_UI_TEMPLATE = '''<ui> 
 160    <menubar name="Menubar"> 
 161      <menu action="Connection"> 
 162        <placeholder name="Recent"> 
 163        %s 
 164        </placeholder> 
 165      </menu> 
 166    </menubar> 
 167  </ui>''' 
 168   
 169  MAX_RECENT_ITEMS = 4 
 170   
 171   
172 -class AdminWindow(Loggable, GladeDelegate):
173 '''Creates the GtkWindow for the user interface. 174 Also connects to the manager on the given host and port. 175 ''' 176 177 # GladeDelegate 178 gladefile = 'admin.glade' 179 toplevel_name = 'main_window' 180 181 # Loggable 182 logCategory = 'adminwindow' 183 184 # Interfaces we implement 185 implements(IStateListener) 186 187 # Signals 188 gsignal('connected') 189
190 - def __init__(self):
191 GladeDelegate.__init__(self) 192 193 self._adminModel = None 194 self._currentComponentStates = None 195 self._componentContextMenu = None 196 self._componentList = None # ComponentList 197 self._componentStates = None # name -> planet.AdminComponentState 198 self._componentView = None 199 self._debugEnabled = False 200 self._debugActions = None 201 self._debugEnableAction = None 202 self._disconnectedDialog = None # set to a dialog when disconnected 203 self._planetState = None 204 self._recentMenuID = None 205 self._trayicon = None 206 self._configurationWizardIsRunning = False 207 208 self._createUI() 209 self._appendRecentConnections() 210 self.setDebugEnabled(False)
211 212 # Public API 213 214 #FIXME: This function may not be called ever. 215 # It has not been properly tested 216 # with the multiselection (ticket #795). 217 # A ticket for reviewing that has been opened #961 218
219 - def stateSet(self, state, key, value):
220 # called by model when state of something changes 221 if not isinstance(state, AdminComponentState): 222 return 223 224 if key == 'message': 225 self.statusbar.set('main', value) 226 elif key == 'mood': 227 self._updateComponentActions() 228 current = self.components_view.getSelectedNames() 229 if value == moods.sleeping.value: 230 if state.get('name') in current: 231 self._messageView.clearMessage(value.id)
232 233 #FIXME: This function may not be called ever. 234 # It has not been properly tested 235 # with the multiselection (ticket #795). 236 # A ticket for reviewing that has been opened #961 237
238 - def componentCallRemoteStatus(self, state, pre, post, fail, 239 methodName, *args, **kwargs):
240 241 def cb(result, self, mid): 242 if mid: 243 self.statusbar.remove('main', mid) 244 if post: 245 self.statusbar.push('main', post % label)
246 247 def eb(failure, self, mid): 248 if mid: 249 self.statusbar.remove('main', mid) 250 self.warning("Failed to execute %s on component %s: %s" 251 % (methodName, label, failure)) 252 if fail: 253 self.statusbar.push('main', fail % label)
254 if not state: 255 states = self.components_view.getSelectedStates() 256 if not states: 257 return 258 for state in states: 259 self.componentCallRemoteStatus(state, pre, post, fail, 260 methodName, args, kwargs) 261 else: 262 label = getComponentLabel(state) 263 if not label: 264 return 265 266 mid = None 267 if pre: 268 mid = self.statusbar.push('main', pre % label) 269 d = self._adminModel.componentCallRemote( 270 state, methodName, *args, **kwargs) 271 d.addCallback(cb, self, mid) 272 d.addErrback(eb, self, mid) 273
274 - def componentCallRemote(self, state, methodName, *args, **kwargs):
275 self.componentCallRemoteStatus(None, None, None, None, 276 methodName, *args, **kwargs)
277
278 - def whsAppend(self, state, key, value):
279 if key == 'names': 280 self.statusbar.set( 281 'main', _('Worker %s logged in.') % value)
282
283 - def whsRemove(self, state, key, value):
284 if key == 'names': 285 self.statusbar.set( 286 'main', _('Worker %s logged out.') % value)
287
288 - def show(self):
289 self._window.show()
290
291 - def setDebugEnabled(self, enabled):
292 """Set if debug should be enabled for the admin client window 293 @param enable: if debug should be enabled 294 """ 295 self._debugEnabled = enabled 296 self._debugActions.set_sensitive(enabled) 297 self._debugEnableAction.set_active(enabled) 298 self._componentView.setDebugEnabled(enabled) 299 self._killComponentAction.set_property('visible', enabled)
300
301 - def getWindow(self):
302 """Get the gtk window for the admin interface 303 @returns: window 304 @rtype: gtk.Window 305 """ 306 return self._window
307
308 - def openConnection(self, info):
309 """Connects to a manager given a connection info 310 @param info: connection info 311 @type info: L{PBConnectionInfo} 312 """ 313 assert isinstance(info, PBConnectionInfo), info 314 return self._openConnection(info)
315 316 # Private 317
318 - def _createUI(self):
319 self.debug('creating UI') 320 321 # Widgets created in admin.glade 322 self._window = self.toplevel 323 self._componentList = ComponentList(self.component_list) 324 del self.component_list 325 self._componentView = self.component_view 326 del self.component_view 327 self._statusbar = AdminStatusbar(self.statusbar) 328 del self.statusbar 329 self._messageView = self.messages_view 330 del self.messages_view 331 332 self._window.set_name("AdminWindow") 333 self._window.connect('delete-event', self._window_delete_event_cb) 334 335 uimgr = gtk.UIManager() 336 uimgr.connect('connect-proxy', 337 self._on_uimanager__connect_proxy) 338 uimgr.connect('disconnect-proxy', 339 self._on_uimanager__disconnect_proxy) 340 341 # Normal actions 342 group = gtk.ActionGroup('Actions') 343 group.add_actions([ 344 # Connection 345 ('Connection', None, _("_Connection")), 346 ('OpenRecent', gtk.STOCK_OPEN, _('_Open Recent Connection...'), 347 None, _('Connect to a recently used connection'), 348 self._connection_open_recent_cb), 349 ('OpenExisting', None, _('Connect to _running manager...'), None, 350 _('Connect to an previously used connection'), 351 self._connection_open_existing_cb), 352 ('ImportConfig', None, _('_Import Configuration...'), None, 353 _('Import configuration from a file'), 354 self._connection_import_configuration_cb), 355 ('ExportConfig', None, _('_Export Configuration...'), None, 356 _('Export current configuration to a file'), 357 self._connection_export_configuration_cb), 358 ('Quit', gtk.STOCK_QUIT, _('_Quit'), None, 359 _('Quit the application and disconnect from the manager'), 360 self._connection_quit_cb), 361 362 # Manage 363 ('Manage', None, _('_Manage')), 364 ('StartComponent', 'flumotion-play', _('_Start Component(s)'), 365 None, _('Start the selected component(s)'), 366 self._manage_start_component_cb), 367 ('StopComponent', 'flumotion-stop', _('St_op Component(s)'), 368 None, _('Stop the selected component(s)'), 369 self._manage_stop_component_cb), 370 ('DeleteComponent', gtk.STOCK_DELETE, _('_Delete Component(s)'), 371 None, _('Delete the selected component(s)'), 372 self._manage_delete_component_cb), 373 ('StartAll', None, _('Start _All'), None, 374 _('Start all components'), 375 self._manage_start_all_cb), 376 ('StopAll', None, _('Stop A_ll'), None, 377 _('Stop all components'), 378 self._manage_stop_all_cb), 379 ('ClearAll', gtk.STOCK_CLEAR, _('_Clear All'), None, 380 _('Remove all components'), 381 self._manage_clear_all_cb), 382 ('AddFormat', gtk.STOCK_ADD, _('Add new encoding _format...'), None, 383 _('Add a new format to the current stream'), 384 self._manage_add_format_cb), 385 ('RunConfigurationWizard', 'flumotion-wizard', _('Run _Wizard'), None, 386 _('Run the configuration wizard'), 387 self._manage_run_wizard_cb), 388 389 # Debug 390 ('Debug', None, _('_Debug')), 391 392 # Help 393 ('Help', None, _('_Help')), 394 ('About', gtk.STOCK_ABOUT, _('_About'), None, 395 _('Displays an about dialog'), 396 self._help_about_cb), 397 398 # Only in context menu 399 ('KillComponent', None, _('_Kill Component'), None, 400 _('Kills the currently selected component'), 401 self._kill_component_cb), 402 403 ]) 404 group.add_toggle_actions([ 405 ('EnableDebugging', None, _('Enable _Debugging'), None, 406 _('Enable debugging in the admin interface'), 407 self._debug_enable_cb), 408 ]) 409 self._debugEnableAction = group.get_action('EnableDebugging') 410 uimgr.insert_action_group(group, 0) 411 412 # Debug actions 413 self._debugActions = gtk.ActionGroup('Actions') 414 self._debugActions.add_actions([ 415 # Debug 416 ('StartShell', gtk.STOCK_EXECUTE, _('Start _Shell'), None, 417 _('Start an interactive debugging shell'), 418 self._debug_start_shell_cb), 419 ('DumpConfiguration', gtk.STOCK_EXECUTE, 420 _('Dump configuration'), None, 421 _('Dumps the current manager configuration'), 422 self._debug_dump_configuration_cb), 423 ('WriteDebugMarker', gtk.STOCK_EXECUTE, 424 _('Write debug marker...'), None, 425 _('Writes a debug marker to all the logs'), 426 self._debug_write_debug_marker_cb) 427 ]) 428 uimgr.insert_action_group(self._debugActions, 0) 429 self._debugActions.set_sensitive(False) 430 431 uimgr.add_ui_from_string(MAIN_UI) 432 self._window.add_accel_group(uimgr.get_accel_group()) 433 434 menubar = uimgr.get_widget('/Menubar') 435 self.main_vbox.pack_start(menubar, expand=False) 436 self.main_vbox.reorder_child(menubar, 0) 437 438 toolbar = uimgr.get_widget('/Toolbar') 439 toolbar.set_icon_size(gtk.ICON_SIZE_SMALL_TOOLBAR) 440 toolbar.set_style(gtk.TOOLBAR_ICONS) 441 self.main_vbox.pack_start(toolbar, expand=False) 442 self.main_vbox.reorder_child(toolbar, 1) 443 444 self._componentContextMenu = uimgr.get_widget('/ComponentContextMenu') 445 self._componentContextMenu.show() 446 447 menubar.show_all() 448 449 self._actiongroup = group 450 self._uimgr = uimgr 451 self._openRecentAction = group.get_action("OpenRecent") 452 self._startComponentAction = group.get_action("StartComponent") 453 self._stopComponentAction = group.get_action("StopComponent") 454 self._deleteComponentAction = group.get_action("DeleteComponent") 455 self._stopAllAction = group.get_action("StopAll") 456 assert self._stopAllAction 457 self._startAllAction = group.get_action("StartAll") 458 assert self._startAllAction 459 self._clearAllAction = group.get_action("ClearAll") 460 assert self._clearAllAction 461 self._addFormatAction = group.get_action("AddFormat") 462 assert self._addFormatAction 463 self._killComponentAction = group.get_action("KillComponent") 464 assert self._killComponentAction 465 466 self._trayicon = FluTrayIcon(self._window) 467 self._trayicon.connect("quit", self._trayicon_quit_cb) 468 self._trayicon.set_tooltip(_('Not connected')) 469 470 self._componentList.connect('selection_changed', 471 self._components_selection_changed_cb) 472 self._componentList.connect('show-popup-menu', 473 self._components_show_popup_menu_cb) 474 475 self._updateComponentActions() 476 self._componentList.connect( 477 'notify::can-start-any', 478 self._components_start_stop_notify_cb) 479 self._componentList.connect( 480 'notify::can-stop-any', 481 self._components_start_stop_notify_cb) 482 self._updateComponentActions() 483 484 self._messageView.hide()
485
486 - def _connectActionProxy(self, action, widget):
487 tooltip = action.get_property('tooltip') 488 if not tooltip: 489 return 490 491 if isinstance(widget, gtk.MenuItem): 492 cid = widget.connect('select', self._on_menu_item__select, 493 tooltip) 494 cid2 = widget.connect('deselect', self._on_menu_item__deselect) 495 widget.set_data('pygtk-app::proxy-signal-ids', (cid, cid2)) 496 elif isinstance(widget, gtk.ToolButton): 497 cid = widget.child.connect('enter', self._on_tool_button__enter, 498 tooltip) 499 cid2 = widget.child.connect('leave', self._on_tool_button__leave) 500 widget.set_data('pygtk-app::proxy-signal-ids', (cid, cid2))
501
502 - def _disconnectActionProxy(self, action, widget):
503 cids = widget.get_data('pygtk-app::proxy-signal-ids') 504 if not cids: 505 return 506 507 if isinstance(widget, gtk.ToolButton): 508 widget = widget.child 509 510 for name, cid in cids: 511 widget.disconnect(cid)
512
513 - def _setAdminModel(self, model):
514 'set the model to which we are a view/controller' 515 # it's ok if we've already been connected 516 self.debug('setting model') 517 518 if self._adminModel: 519 self.debug('Connecting to new model %r' % model) 520 521 self._adminModel = model 522 523 # window gets created after model connects initially, so check 524 # here 525 if self._adminModel.isConnected(): 526 self._connectionOpened(model) 527 528 self._adminModel.connect('connected', 529 self._admin_connected_cb) 530 self._adminModel.connect('disconnected', 531 self._admin_disconnected_cb) 532 self._adminModel.connect('connection-refused', 533 self._admin_connection_refused_cb) 534 self._adminModel.connect('connection-failed', 535 self._admin_connection_failed_cb) 536 self._adminModel.connect('update', self._admin_update_cb)
537
538 - def _openConnection(self, info):
539 self._trayicon.set_tooltip(_("Connecting to %s:%s") % ( 540 info.host, info.port)) 541 542 def connected(model): 543 self._setAdminModel(model) 544 self._appendRecentConnections()
545 546 model = AdminModel() 547 d = model.connectToManager(info) 548 d.addCallback(connected) 549 return d 550
551 - def _openConnectionInternal(self, info):
552 d = self._openConnection(info) 553 554 def errorMessageDisplayed(unused): 555 self._window.set_sensitive(True)
556 557 def connected(model): 558 self._window.set_sensitive(True) 559 560 def errbackConnectionRefusedError(failure): 561 failure.trap(ConnectionRefusedError) 562 d = showConnectionErrorDialog(failure, info, parent=self._window) 563 d.addCallback(errorMessageDisplayed) 564 565 def errbackConnectionFailedError(failure): 566 failure.trap(ConnectionFailedError) 567 d = showConnectionErrorDialog(failure, info, parent=self._window) 568 d.addCallback(errorMessageDisplayed) 569 return d 570 571 d.addCallback(connected) 572 d.addErrback(errbackConnectionRefusedError) 573 d.addErrback(errbackConnectionFailedError) 574 self._window.set_sensitive(False) 575 return d 576
577 - def _appendRecentConnections(self):
578 if self._recentMenuID: 579 self._uimgr.remove_ui(self._recentMenuID) 580 self._uimgr.ensure_update() 581 582 ui = "" 583 connections = getRecentConnections()[:MAX_RECENT_ITEMS] 584 for conn in connections: 585 name = conn.host 586 ui += '<menuitem action="%s"/>' % name 587 action = gtk.Action(name, name, 588 _('Connect to the manager on %s') % conn.host, 589 '') 590 action.connect('activate', self._recent_action_activate_cb, conn) 591 self._actiongroup.add_action(action) 592 593 self._recentMenuID = self._uimgr.add_ui_from_string( 594 RECENT_UI_TEMPLATE % ui) 595 self._openRecentAction.set_sensitive(len(connections))
596
597 - def _quit(self):
598 """Quitting the application in a controlled manner""" 599 self._clearAdmin() 600 self._close()
601
602 - def _close(self, *args):
603 reactor.stop()
604
605 - def _dumpConfig(self, configation):
606 import pprint 607 import cStringIO 608 fd = cStringIO.StringIO() 609 pprint.pprint(configation, fd) 610 fd.seek(0) 611 self.debug('Configuration=%s' % fd.read())
612
613 - def _error(self, message):
614 errorDialog = ErrorDialog(message, self._window, 615 close_on_response=True) 616 errorDialog.show()
617
618 - def _fatalError(self, message, tray=None):
619 if tray: 620 self._trayicon.set_tooltip(tray) 621 622 self.info(message) 623 errorDialog = ErrorDialog(message, self._window) 624 errorDialog.show() 625 errorDialog.connect('response', self._close)
626
627 - def _setStatusbarText(self, text):
628 return self._statusbar.push('main', text)
629
630 - def _clearLastStatusbarText(self):
631 self._statusbar.pop('main')
632
633 - def _wizardFinshed(self, wizard, configuration):
634 wizard.destroy() 635 self._configurationWizardIsRunning = False 636 self._dumpConfig(configuration) 637 self._adminModel.loadConfiguration(configuration) 638 self._clearMessages() 639 self._statusbar.clear(None) 640 self._updateComponentActions() 641 self.show()
642
643 - def _getComponentBy(self, componentType):
644 if componentType is None: 645 raise ValueError 646 componentStates = [] 647 648 for state in self._componentStates.values(): 649 config = state.get('config') 650 if componentType and config['type'] == componentType: 651 componentStates.append(state) 652 653 if not componentStates: 654 return None 655 elif len(componentStates) == 1: 656 return componentStates[0] 657 else: 658 raise AssertionError( 659 "Attempted to fetch a component state by type %r, " 660 "expected one, but got %r" % ( 661 componentType, componentStates))
662
663 - def _getHTTPPorter(self):
664 porterState = self._getComponentBy(componentType='porter') 665 if porterState is None: 666 return None 667 properties = porterState.get('config')['properties'] 668 porter = Porter(worker=None, 669 port=properties['port'], 670 username=properties['username'], 671 password=properties['password'], 672 socketPath=properties['socket-path']) 673 porter.exists = True 674 return porter
675
676 - def _createComponentsByWizardType(self, componentClass, entries):
677 def _getComponents(): 678 for componentState in self._componentStates.values(): 679 componentType = componentState.get('config')['type'] 680 for entry in entries: 681 if entry.componentType == componentType: 682 yield (componentState, entry)
683 684 685 for componentState, entry in _getComponents(): 686 component = componentClass() 687 component.componentType = entry.componentType 688 component.description = entry.description 689 component.exists = True 690 config = componentState.get('config') 691 for key, value in config['properties'].items(): 692 component.properties[key] = value 693 yield component 694
695 - def _runAddNewFormatWizard(self):
696 from flumotion.admin.gtk.addformatwizard import AddFormatWizard 697 addFormatWizard = AddFormatWizard(self._window) 698 def cb(entries): 699 entryDict = {} 700 for entry in entries: 701 entryDict.setdefault(entry.type, []).append(entry) 702 703 audioProducers = self._createComponentsByWizardType( 704 AudioProducer, entryDict['audio-producer'], ) 705 videoProducers = self._createComponentsByWizardType( 706 VideoProducer, entryDict['video-producer']) 707 addFormatWizard.setAudioProducers(audioProducers) 708 addFormatWizard.setVideoProducers(videoProducers) 709 self._runWizard(addFormatWizard)
710 711 d = self._adminModel.getWizardEntries( 712 wizardTypes=['audio-producer', 'video-producer']) 713 d.addCallback(cb) 714
715 - def _runConfigurationWizard(self):
716 from flumotion.wizard.configurationwizard import ConfigurationWizard 717 718 def runWizard(): 719 configurationWizard = ConfigurationWizard(self._window) 720 self._runWizard(configurationWizard) 721 self._configurationWizardIsRunning = True
722 723 if not self._componentStates: 724 runWizard() 725 return 726 727 if yesno(_("Running the Configuration Wizard again will remove " 728 "all components from the current stream and create " 729 "a new one."), 730 parent=self._window, 731 buttons=((_("Keep the current stream"), gtk.RESPONSE_NO), 732 (_("Run the Wizard anyway"), gtk.RESPONSE_YES)) 733 ) != gtk.RESPONSE_YES: 734 return 735 736 d = self._clearAllComponents() 737 d.addCallback(lambda unused: runWizard()) 738
739 - def _runWizard(self, wizard):
740 workerHeavenState = self._adminModel.getWorkerHeavenState() 741 if not workerHeavenState.get('names'): 742 self._error( 743 _('The wizard cannot be run because no workers are ' 744 'logged in.')) 745 return 746 747 wizard.setExistingComponentNames( 748 self._componentList.getComponentNames()) 749 wizard.setAdminModel(self._adminModel) 750 wizard.setWorkerHeavenState(workerHeavenState) 751 httpPorter = self._getHTTPPorter() 752 if httpPorter: 753 wizard.setHTTPPorter(httpPorter) 754 wizard.connect('finished', self._wizard_finished_cb) 755 wizard.run(main=False)
756
757 - def _clearAdmin(self):
758 if not self._adminModel: 759 return 760 761 self._adminModel.disconnect_by_func(self._admin_connected_cb) 762 self._adminModel.disconnect_by_func(self._admin_disconnected_cb) 763 self._adminModel.disconnect_by_func(self._admin_connection_refused_cb) 764 self._adminModel.disconnect_by_func(self._admin_connection_failed_cb) 765 self._adminModel.disconnect_by_func(self._admin_update_cb) 766 self._adminModel = None
767
768 - def _updateConnectionActions(self):
769 self._openRecentAction.set_sensitive(hasRecentConnections())
770
771 - def _updateComponentActions(self):
772 canStart = self._componentList.canStart() 773 canStop = self._componentList.canStop() 774 canDelete = bool(self._currentComponentStates and canStart) 775 self._startComponentAction.set_sensitive(canStart) 776 self._stopComponentAction.set_sensitive(canStop) 777 self._deleteComponentAction.set_sensitive(canDelete) 778 self.debug('can start %r, can stop %r' % (canStart, canStop)) 779 canStartAll = self._componentList.get_property('can-start-any') 780 canStopAll = self._componentList.get_property('can-stop-any') 781 782 # they're all in sleeping or lost 783 canClearAll = canStartAll and not canStopAll 784 self._stopAllAction.set_sensitive(canStopAll) 785 self._startAllAction.set_sensitive(canStartAll) 786 self._clearAllAction.set_sensitive(canClearAll) 787 self._killComponentAction.set_sensitive(canStop) 788 789 hasProducer = self._hasProducerComponent() 790 self._addFormatAction.set_sensitive(hasProducer)
791
792 - def _updateComponents(self):
793 self._componentList.clearAndRebuild(self._componentStates) 794 self._trayicon.update(self._componentStates)
795
796 - def _hasProducerComponent(self):
797 for state in self._componentList.getComponentStates(): 798 if state is None: 799 continue 800 # FIXME: Not correct, should expose wizard state from 801 # the registry. 802 name = state.get('name') 803 if 'producer' in name: 804 return True 805 return False
806
807 - def _clearMessages(self):
808 self._messageView.clear() 809 pstate = self._planetState 810 if pstate and pstate.hasKey('messages'): 811 for message in pstate.get('messages').values(): 812 self._messageView.addMessage(message)
813
814 - def _setPlanetState(self, planetState):
815 816 def flowStateAppend(state, key, value): 817 self.debug('flow state append: key %s, value %r' % (key, value)) 818 if key == 'components': 819 self._componentStates[value.get('name')] = value 820 # FIXME: would be nicer to do this incrementally instead 821 self._updateComponents()
822 823 def flowStateRemove(state, key, value): 824 if key == 'components': 825 self._removeComponent(value) 826 827 def atmosphereStateAppend(state, key, value): 828 if key == 'components': 829 self._componentStates[value.get('name')] = value 830 # FIXME: would be nicer to do this incrementally instead 831 self._updateComponents() 832 833 def atmosphereStateRemove(state, key, value): 834 if key == 'components': 835 self._removeComponent(value) 836 837 def planetStateAppend(state, key, value): 838 if key == 'flows': 839 if value != state.get('flows')[0]: 840 self.warning('flumotion-admin can only handle one ' 841 'flow, ignoring /%s', value.get('name')) 842 return 843 self.debug('%s flow started', value.get('name')) 844 value.addListener(self, append=flowStateAppend, 845 remove=flowStateRemove) 846 for c in value.get('components'): 847 flowStateAppend(value, 'components', c) 848 self._updateComponents() 849 850 def planetStateRemove(state, key, value): 851 self.debug('something got removed from the planet') 852 853 def planetStateSetitem(state, key, subkey, value): 854 if key == 'messages': 855 self._messageView.addMessage(value) 856 857 def planetStateDelitem(state, key, subkey, value): 858 if key == 'messages': 859 self._messageView.clearMessage(value.id) 860 861 self.debug('parsing planetState %r' % planetState) 862 self._planetState = planetState 863 864 # clear and rebuild list of components that interests us 865 self._componentStates = {} 866 867 planetState.addListener(self, append=planetStateAppend, 868 remove=planetStateRemove, 869 setitem=planetStateSetitem, 870 delitem=planetStateDelitem) 871 872 self._clearMessages() 873 874 a = planetState.get('atmosphere') 875 a.addListener(self, append=atmosphereStateAppend, 876 remove=atmosphereStateRemove) 877 for c in a.get('components'): 878 atmosphereStateAppend(a, 'components', c) 879 880 for f in planetState.get('flows'): 881 planetStateAppend(planetState, 'flows', f) 882
883 - def _clearAllComponents(self):
884 d = self._adminModel.cleanComponents() 885 def busyComponentError(failure): 886 failure.trap(BusyComponentError) 887 self._error( 888 _("Some component(s) are still busy and cannot be removed.\n" 889 "Try again later."))
890 d.addErrback(busyComponentError) 891 return d 892 893 # component view activation functions 894
895 - def _removeComponent(self, state):
896 name = state.get('name') 897 self.debug('removing component %s' % name) 898 del self._componentStates[name] 899 900 # if this component was selected, clear selection 901 if self._currentComponentStates and state \ 902 in self._currentComponentStates: 903 self._currentComponentStates.remove(state) 904 # FIXME: would be nicer to do this incrementally instead 905 self._updateComponents() 906 # a component being removed means our selected component could 907 # have gone away 908 self._updateComponentActions()
909
910 - def _componentStop(self, state):
911 """ 912 @returns: a L{twisted.internet.defer.Deferred} 913 """ 914 return self._componentDo(state, 'componentStop', 915 'Stop', 'Stopping', 'Stopped')
916
917 - def _componentStart(self, state):
918 """ 919 @returns: a L{twisted.internet.defer.Deferred} 920 """ 921 return self._componentDo(state, 'componentStart', 922 'Start', 'Starting', 'Started')
923
924 - def _componentDelete(self, state):
925 """ 926 @returns: a L{twisted.internet.defer.Deferred} 927 """ 928 return self._componentDo(state, 'deleteComponent', 929 'Delete', 'Deleting', 'Deleted')
930
931 - def _componentDo(self, state, methodName, action, doing, done):
932 """Do something with a component and update the statusbar 933 @param state: componentState 934 @type state: L{AdminComponentState} 935 @param methodName: name of the method to call 936 @type methodName: str 937 @param action: string used to explain that to do 938 @type action: str 939 @param doing: string used to explain that the action started 940 @type doing: str 941 @param done: string used to explain that the action was completed 942 @type done: str 943 """ 944 if state is None: 945 states = self._componentList.getSelectedStates() 946 else: 947 states = [state] 948 949 if not states: 950 return 951 952 def callbackSingle(result, self, mid, name): 953 self._statusbar.remove('main', mid) 954 self._setStatusbarText( 955 _("%s component %s") % (done, name))
956 957 def errbackSingle(failure, self, mid, name): 958 self._statusbar.remove('main', mid) 959 self.warning("Failed to %s component %s: %s" % ( 960 action.lower(), name, failure)) 961 self._setStatusbarText( 962 _("Failed to %(action)s component %(name)s.") % { 963 'action': action.lower(), 964 'name': name, 965 }) 966 967 def callbackMultiple(results, self, mid): 968 self._statusbar.remove('main', mid) 969 self._setStatusbarText( 970 _("%s components.") % (done,)) 971 972 def errbackMultiple(failure, self, mid): 973 self._statusbar.remove('main', mid) 974 self.warning("Failed to %s some components: %s." % ( 975 action.lower(), failure)) 976 self._setStatusbarText( 977 _("Failed to %s some components.") % (action,)) 978 979 f = gettext.dngettext( 980 configure.PACKAGE, 981 # first %s is one of Stopping/Starting/Deleting 982 # second %s is a component name like "audio-producer" 983 N_("%s component %s"), 984 # first %s is one of Stopping/Starting/Deleting 985 # second %s is a list of component names, like 986 # "audio-producer, video-producer" 987 N_("%s components %s"), len(states)) 988 statusText = f % (doing, 989 ', '.join([getComponentLabel(s) for s in states])) 990 mid = self._setStatusbarText(statusText) 991 992 if len(states) == 1: 993 state = states[0] 994 name = getComponentLabel(state) 995 d = self._adminModel.callRemote(methodName, state) 996 d.addCallback(callbackSingle, self, mid, name) 997 d.addErrback(errbackSingle, self, mid, name) 998 else: 999 deferreds = [] 1000 for state in states: 1001 d = self._adminModel.callRemote(methodName, state) 1002 deferreds.append(d) 1003 d = defer.DeferredList(deferreds) 1004 d.addCallback(callbackMultiple, self, mid) 1005 d.addErrback(errbackMultiple, self, mid) 1006 return d 1007
1008 - def _killSelectedComponents(self):
1009 for state in self._componentList.getSelectedStates(): 1010 workerName = state.get('workerRequested') 1011 avatarId = componentId(state.get('parent').get('name'), 1012 state.get('name')) 1013 self._adminModel.callRemote( 1014 'workerCallRemote', workerName, 'killJob', avatarId)
1015
1016 - def _componentSelectionChanged(self, states):
1017 self.debug('component %s has selection', states) 1018 1019 def compSet(state, key, value): 1020 if key == 'mood': 1021 self._updateComponentActions()
1022 1023 def compAppend(state, key, value): 1024 name = state.get('name') 1025 self.debug('stateAppend on component state of %s' % name) 1026 if key == 'messages': 1027 current = self._componentList.getSelectedNames() 1028 if name in current: 1029 self._messageView.addMessage(value) 1030 1031 def compRemove(state, key, value): 1032 name = state.get('name') 1033 self.debug('stateRemove on component state of %s' % name) 1034 if key == 'messages': 1035 current = self._componentList.getSelectedNames() 1036 if name in current: 1037 self._messageView.clearMessage(value.id) 1038 1039 if self._currentComponentStates: 1040 for currentComponentState in self._currentComponentStates: 1041 currentComponentState.removeListener(self) 1042 self._currentComponentStates = states 1043 if self._currentComponentStates: 1044 for currentComponentState in self._currentComponentStates: 1045 currentComponentState.addListener( 1046 self, set=compSet, append=compAppend, remove=compRemove) 1047 1048 self._updateComponentActions() 1049 self._clearMessages() 1050 state = None 1051 if states: 1052 if len(states) == 1: 1053 self.debug( 1054 "only one component is selected on the components view") 1055 state = states[0] 1056 elif states: 1057 self.debug("more than one components are selected in the " 1058 "components view") 1059 self._componentView.activateComponent(state) 1060 1061 statusbarMessage = " " 1062 for state in states: 1063 name = getComponentLabel(state) 1064 messages = state.get('messages') 1065 if messages: 1066 for m in messages: 1067 self.debug('have message %r' % m) 1068 self.debug('message id %s' % m.id) 1069 self._messageView.addMessage(m) 1070 1071 if state.get('mood') == moods.sad.value: 1072 self.debug('component %s is sad' % name) 1073 statusbarMessage = statusbarMessage + \ 1074 _("Component %s is sad. ") % name 1075 if statusbarMessage != " ": 1076 self._setStatusbarText(statusbarMessage) 1077 1078 1079 # FIXME: show statusbar things 1080 # self._statusbar.set('main', _('Showing UI for %s') % name) 1081 # self._statusbar.set('main', 1082 # _("Component %s is still sleeping") % name) 1083 # self._statusbar.set('main', _("Requesting UI for %s ...") % name) 1084 # self._statusbar.set('main', _("Loading UI for %s ...") % name) 1085 # self._statusbar.clear('main') 1086 # mid = self._statusbar.push('notebook', 1087 # _("Loading tab %s for %s ...") % (node.title, name)) 1088 # node.statusbar = self._statusbar # hack 1089
1090 - def _componentShowPopupMenu(self, event_button, event_time):
1091 self._componentContextMenu.popup(None, None, None, 1092 event_button, event_time)
1093
1094 - def _connectionOpened(self, admin):
1095 self.info('Connected to manager') 1096 if self._disconnectedDialog: 1097 self._disconnectedDialog.destroy() 1098 self._disconnectedDialog = None 1099 1100 # FIXME: have a method for this 1101 self._window.set_title(_('%s - Flumotion Administration') % 1102 self._adminModel.adminInfoStr()) 1103 self._trayicon.set_tooltip(self._adminModel.adminInfoStr()) 1104 1105 self.emit('connected') 1106 1107 self._componentView.setSingleAdmin(admin) 1108 1109 self._setPlanetState(admin.planet) 1110 self._updateConnectionActions() 1111 self._updateComponentActions() 1112 1113 if not self._componentStates and not self._configurationWizardIsRunning: 1114 self.debug('no components detected, running wizard') 1115 # ensure our window is shown 1116 self.show() 1117 self._configurationWizardIsRunning = True 1118 self._runConfigurationWizard() 1119 else: 1120 self.show()
1121
1122 - def _showConnectionLostDialog(self):
1123 RESPONSE_REFRESH = 1 1124 1125 def response(dialog, response_id): 1126 if response_id == RESPONSE_REFRESH: 1127 self._adminModel.reconnect() 1128 else: 1129 # FIXME: notify admin of cancel 1130 dialog.stop() 1131 dialog.destroy() 1132 return
1133 1134 dialog = ProgressDialog( 1135 _("Reconnecting ..."), 1136 _("Lost connection to manager %s, reconnecting ...") 1137 % (self._adminModel.adminInfoStr(), ), self._window) 1138 1139 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) 1140 dialog.add_button(gtk.STOCK_REFRESH, RESPONSE_REFRESH) 1141 dialog.connect("response", response) 1142 dialog.start() 1143 self._disconnectedDialog = dialog 1144
1145 - def _connectionLost(self):
1146 self._componentStates = {} 1147 self._updateComponents() 1148 self._clearMessages() 1149 if self._planetState: 1150 self._planetState.removeListener(self) 1151 self._planetState = None 1152 1153 self._showConnectionLostDialog()
1154
1155 - def _connectionRefused(self):
1156 def refusedLater(): 1157 self._fatalError( 1158 _("Connection to manager on %s was refused.") % ( 1159 self._adminModel.connectionInfoStr()), 1160 _("Connection to %s was refused") % 1161 (self._adminModel.adminInfoStr(),))
1162 1163 self.debug("handling connection-refused") 1164 reactor.callLater(0, refusedLater) 1165 self.debug("handled connection-refused") 1166
1167 - def _connectionFailed(self, reason):
1168 return self._fatalError( 1169 _("Connection to manager on %s failed (%s).") % ( 1170 self._adminModel.connectionInfoStr(), reason), 1171 _("Connection to %s failed") % 1172 (self._adminModel.adminInfoStr(),))
1173
1174 - def _openRecentConnection(self):
1175 d = ConnectionsDialog(parent=self._window) 1176 1177 def on_have_connection(d, connectionInfo): 1178 d.destroy() 1179 if connectionInfo: 1180 self._openConnectionInternal(connectionInfo.info) 1181 connectionInfo.updateTimestamp() 1182 self._updateConnectionActions()
1183 1184 d.connect('have-connection', on_have_connection) 1185 d.show() 1186
1187 - def _openExistingConnection(self):
1188 from flumotion.admin.gtk.greeter import ConnectExisting 1189 from flumotion.ui.simplewizard import WizardCancelled 1190 wiz = ConnectExisting(parent=self._window) 1191 1192 def got_state(state, g): 1193 g.set_sensitive(False) 1194 g.destroy() 1195 self._openConnectionInternal(state['connectionInfo'])
1196 1197 def cancel(failure): 1198 failure.trap(WizardCancelled) 1199 wiz.stop() 1200 1201 d = wiz.runAsync() 1202 d.addCallback(got_state, wiz) 1203 d.addErrback(cancel) 1204
1205 - def _importConfiguration(self):
1206 dialog = gtk.FileChooserDialog( 1207 _("Import Configuration..."), self._window, 1208 gtk.FILE_CHOOSER_ACTION_OPEN, 1209 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, 1210 _('Import'), gtk.RESPONSE_ACCEPT)) 1211 dialog.set_modal(True) 1212 dialog.set_default_response(gtk.RESPONSE_ACCEPT) 1213 ffilter = gtk.FileFilter() 1214 ffilter.set_name(_("Flumotion XML Configuration files")) 1215 ffilter.add_pattern("*.xml") 1216 dialog.add_filter(ffilter) 1217 ffilter = gtk.FileFilter() 1218 ffilter.set_name(_("All files")) 1219 ffilter.add_pattern("*") 1220 dialog.add_filter(ffilter) 1221 1222 def response(dialog, response): 1223 if response == gtk.RESPONSE_ACCEPT: 1224 name = dialog.get_filename() 1225 conf_xml = open(name, 'r').read() 1226 self._adminModel.loadConfiguration(conf_xml) 1227 dialog.destroy()
1228 1229 dialog.connect('response', response) 1230 dialog.show() 1231
1232 - def _exportConfiguration(self):
1233 d = gtk.FileChooserDialog( 1234 _("Export Configuration..."), self._window, 1235 gtk.FILE_CHOOSER_ACTION_SAVE, 1236 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, 1237 _('Export'), gtk.RESPONSE_ACCEPT)) 1238 d.set_modal(True) 1239 d.set_default_response(gtk.RESPONSE_ACCEPT) 1240 d.set_current_name("configuration.xml") 1241 1242 def getConfiguration(conf_xml, name, chooser): 1243 if not name.endswith('.xml'): 1244 name += '.xml' 1245 1246 file_exists = True 1247 if os.path.exists(name): 1248 d = gtk.MessageDialog( 1249 self._window, gtk.DIALOG_MODAL, 1250 gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, 1251 _("File already exists.\nOverwrite?")) 1252 d.connect("response", lambda self, response: d.hide()) 1253 if d.run() == gtk.RESPONSE_YES: 1254 file_exists = False 1255 else: 1256 file_exists = False 1257 1258 if not file_exists: 1259 f = open(name, 'w') 1260 f.write(conf_xml) 1261 f.close() 1262 chooser.destroy()
1263 1264 def response(d, response): 1265 if response == gtk.RESPONSE_ACCEPT: 1266 deferred = self._adminModel.getConfiguration() 1267 name = d.get_filename() 1268 deferred.addCallback(getConfiguration, name, d) 1269 else: 1270 d.destroy() 1271 1272 d.connect('response', response) 1273 d.show() 1274
1275 - def _startShell(self):
1276 if sys.version_info >= (2, 4): 1277 from flumotion.extern import code 1278 code # pyflakes 1279 else: 1280 import code 1281 1282 ns = { "admin": self._adminModel, 1283 "components": self._componentStates } 1284 message = """Flumotion Admin Debug Shell 1285 1286 Local variables are: 1287 admin (flumotion.admin.admin.AdminModel) 1288 components (dict: name -> flumotion.common.planet.AdminComponentState) 1289 1290 You can do remote component calls using: 1291 admin.componentCallRemote(components['component-name'], 1292 'methodName', arg1, arg2) 1293 1294 """ 1295 code.interact(local=ns, banner=message)
1296
1297 - def _dumpConfiguration(self):
1298 def gotConfiguration(xml): 1299 print xml
1300 d = self._adminModel.getConfiguration() 1301 d.addCallback(gotConfiguration) 1302
1303 - def _setDebugMarker(self):
1304 def setMarker(_, marker, level): 1305 self._adminModel.callRemote('writeFluDebugMarker', level, marker)
1306 debugMarkerDialog = DebugMarkerDialog() 1307 debugMarkerDialog.connect('set-marker', setMarker) 1308 debugMarkerDialog.show() 1309
1310 - def _about(self):
1311 about = AboutDialog(self._window) 1312 about.run() 1313 about.destroy()
1314 1315 ### admin model callbacks 1316
1317 - def _admin_connected_cb(self, admin):
1318 self._connectionOpened(admin)
1319
1320 - def _admin_disconnected_cb(self, admin):
1321 self._connectionLost()
1322
1323 - def _admin_connection_refused_cb(self, admin):
1324 self._connectionRefused()
1325
1326 - def _admin_connection_failed_cb(self, admin, reason):
1327 self._connectionFailed(reason)
1328
1329 - def _admin_update_cb(self, admin):
1330 self._updateComponents()
1331 1332 ### ui callbacks 1333
1334 - def _on_uimanager__connect_proxy(self, uimgr, action, widget):
1335 self._connectActionProxy(action, widget)
1336
1337 - def _on_uimanager__disconnect_proxy(self, uimgr, action, widget):
1338 self._disconnectActionProxy(action, widget)
1339
1340 - def _on_menu_item__select(self, menuitem, tooltip):
1341 self._setStatusbarText(tooltip)
1342
1343 - def _on_menu_item__deselect(self, menuitem):
1344 self._clearLastStatusbarText()
1345
1346 - def _on_tool_button__enter(self, toolbutton, tooltip):
1347 self._setStatusbarText(tooltip)
1348
1349 - def _on_tool_button__leave(self, toolbutton):
1350 self._clearLastStatusbarText()
1351
1352 - def _wizard_finished_cb(self, wizard, configuration):
1353 self._wizardFinshed(wizard, configuration)
1354
1355 - def _window_delete_event_cb(self, window, event):
1356 self._quit()
1357
1358 - def _trayicon_quit_cb(self, trayicon):
1359 self._quit()
1360
1361 - def _recent_action_activate_cb(self, action, conn):
1362 self._openConnectionInternal(conn.info)
1363
1364 - def _components_show_popup_menu_cb(self, clist, event_button, event_time):
1365 self._componentShowPopupMenu(event_button, event_time)
1366
1367 - def _components_selection_changed_cb(self, clist, state):
1368 self._componentSelectionChanged(state)
1369
1370 - def _components_start_stop_notify_cb(self, clist, pspec):
1371 self._updateComponentActions()
1372 1373 ### action callbacks 1374
1375 - def _debug_write_debug_marker_cb(self, action):
1376 self._setDebugMarker()
1377
1378 - def _connection_open_recent_cb(self, action):
1379 self._openRecentConnection()
1380
1381 - def _connection_open_existing_cb(self, action):
1382 self._openExistingConnection()
1383
1384 - def _connection_import_configuration_cb(self, action):
1385 self._importConfiguration()
1386
1387 - def _connection_export_configuration_cb(self, action):
1388 self._exportConfiguration()
1389
1390 - def _connection_quit_cb(self, action):
1391 self._quit()
1392
1393 - def _manage_start_component_cb(self, action):
1394 self._componentStart(None)
1395
1396 - def _manage_stop_component_cb(self, action):
1397 self._componentStop(None)
1398
1399 - def _manage_delete_component_cb(self, action):
1400 self._componentDelete(None)
1401
1402 - def _manage_start_all_cb(self, action):
1403 for c in self._componentStates.values(): 1404 self._componentStart(c)
1405
1406 - def _manage_stop_all_cb(self, action):
1407 for c in self._componentStates.values(): 1408 self._componentStop(c)
1409
1410 - def _manage_clear_all_cb(self, action):
1411 self._clearAllComponents()
1412
1413 - def _manage_add_format_cb(self, action):
1414 self._runAddNewFormatWizard()
1415
1416 - def _manage_run_wizard_cb(self, action):
1417 self._runConfigurationWizard()
1418
1419 - def _debug_enable_cb(self, action):
1420 self.setDebugEnabled(action.get_active())
1421
1422 - def _debug_start_shell_cb(self, action):
1423 self._startShell()
1424
1425 - def _debug_dump_configuration_cb(self, action):
1426 self._dumpConfiguration()
1427
1428 - def _help_about_cb(self, action):
1429 self._about()
1430
1431 - def _kill_component_cb(self, action):
1432 self._killSelectedComponents()
1433 1434 gobject.type_register(AdminWindow) 1435