Package flumotion :: Package component :: Package base :: Module baseadminnode
[hide private]

Source Code for Module flumotion.component.base.baseadminnode

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_feedcomponent010 -*- 
  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  import gettext 
 23  import os 
 24   
 25  import gtk 
 26  import gtk.glade 
 27  from twisted.python import util 
 28  from twisted.internet import defer 
 29  from zope.interface import implements 
 30   
 31  from flumotion.common import errors, log, messages 
 32  from flumotion.common.i18n import N_, gettexter 
 33  from flumotion.configure import configure 
 34  from flumotion.twisted import flavors 
 35  from flumotion.ui.fgtk import ProxyWidgetMapping 
 36   
 37  _ = gettext.gettext 
 38  __version__ = "$Rev: 6990 $" 
 39  T_ = gettexter() 
 40   
 41   
42 -class BaseAdminGtkNode(log.Loggable):
43 """ 44 I am a base class for all GTK+-based Admin UI nodes. 45 I am a view on a set of properties for a component. 46 47 @ivar widget: the main widget representing this node 48 @type widget: L{gtk.Widget} 49 @ivar wtree: the widget tree representation for this node 50 """ 51 52 implements(flavors.IStateListener) 53 54 logCategory = "admingtk" 55 gladeFile = None ## Relative path of the glade file. 56 ## e.g. "flumotion/ui.glade" 57 gettextDomain = configure.PACKAGE 58
59 - def __init__(self, state, admin, title=None):
60 """ 61 @param state: state of component this is a UI node for 62 @type state: L{flumotion.common.planet.AdminComponentState} 63 @param admin: the admin model that interfaces with the manager for us 64 @type admin: L{flumotion.admin.admin.AdminModel} 65 @param title: the (translated) title to show this node with 66 @type title: str 67 """ 68 self._debugEnabled = False 69 self.state = state 70 self.admin = admin 71 self.statusbar = None 72 self.title = title 73 self.nodes = util.OrderedDict() 74 self.wtree = None 75 self.widget = None 76 self.uiState = None # set if we are listening 77 self._pendingUIState = None # set if we are waiting for the ui 78 # to load 79 ## Absolute path to the glade file. 80 ## e.g. "/home/flu/.flumotion/cache/test/80...df7/flumotion/ui.glade 81 self._gladefilepath = None
82
83 - def setDebugEnabled(self, enabled):
84 """Set if debug should be enabled. 85 Not all pages are visible unless debugging is set to true 86 87 @param enabled: whether debug should be enabled 88 @type enabled: bool 89 """ 90 self._debugEnabled = enabled
91
92 - def cleanup(self):
93 if self.uiState: 94 self.uiState.removeListener(self)
95
96 - def status_push(self, str):
97 if self.statusbar: 98 return self.statusbar.push('notebook', str)
99
100 - def status_pop(self, mid):
101 if self.statusbar: 102 return self.statusbar.remove('notebook', mid)
103
104 - def callRemote(self, methodName, *args, **kwargs):
105 return self.admin.componentCallRemote(self.state, methodName, 106 *args, **kwargs)
107 108 # FIXME: do this automatically if there is a gladeFile class attr set
109 - def loadGladeFile(self, gladeFile, domain=configure.PACKAGE):
110 """ 111 Returns: a deferred returning the widget tree from the glade file. 112 """ 113 def _getBundledFileCallback(result, gladeFile): 114 path = result 115 if not os.path.exists(path): 116 self.warning("Glade file %s not found in path %s" % ( 117 gladeFile, path)) 118 self.debug("loading widget tree from %s" % path) 119 120 old = gtk.glade.textdomain() 121 self.debug("Switching glade text domain from %s to %s" % ( 122 old, domain)) 123 self._gladefilepath = path 124 gtk.glade.textdomain(domain) 125 126 self.wtree = gtk.glade.XML(path, 127 typedict=ProxyWidgetMapping()) 128 129 self.debug("Switching glade text domain back from %s to %s" % ( 130 domain, old)) 131 gtk.glade.textdomain(old) 132 return self.wtree
133 134 # The manager is always using / as a path separator, to avoid 135 # confusion, convert os.path.sep -> / here. 136 gladeFile = gladeFile.replace(os.path.sep, '/') 137 # FIXME: this does needless roundtrips; should instead be 138 # loading from the already-downloaded paths 139 self.debug("requesting bundle for glade file %s" % gladeFile) 140 d = self.admin.bundleLoader.getFile(gladeFile) 141 d.addCallback(_getBundledFileCallback, gladeFile) 142 return d
143
144 - def getWidget(self, name):
145 if not self.wtree: 146 raise IndexError 147 widget = self.wtree.get_widget(name) 148 if not widget: 149 self.warning('Could not get widget %s' % name) 150 151 return widget
152
153 - def createWidget(self, name):
154 """ 155 Create a new widget instance from the glade file. 156 Can be used to make multiple instances of the same widget. 157 """ 158 if not self._gladefilepath: 159 raise IndexError 160 wtree = gtk.glade.XML(self._gladefilepath, name, 161 typedict=ProxyWidgetMapping()) 162 widget = wtree.get_widget(name) 163 if not widget: 164 self.warning('Could not create widget %s' % name) 165 166 return widget
167
168 - def haveWidgetTree(self):
169 """ 170 I am called when the widget tree has been gotten from the glade 171 file. Responsible for setting self.widget. 172 173 Override me to act on it. 174 """ 175 pass
176
177 - def gotUIState(self, state):
178 if self.widget: 179 self.setUIState(state) 180 else: 181 self._pendingUIState = state
182
183 - def setUIState(self, state):
184 """ 185 Called by the BaseAdminGtk when it gets the UI state and the GUI 186 is ready. Chain up if you provide your own implementation. 187 """ 188 self.uiState = state 189 state.addListener(self, set_=self.stateSet, append=self.stateAppend, 190 remove=self.stateRemove, setitem=self.stateSetitem, 191 delitem=self.stateDelitem)
192
193 - def stateSet(self, state, key, value):
194 "Override me" 195 pass
196
197 - def stateAppend(self, state, key, value):
198 "Override me" 199 pass
200
201 - def stateRemove(self, state, key, value):
202 "Override me" 203 pass
204
205 - def stateSetitem(self, state, key, subkey, value):
206 "Override me" 207 pass
208
209 - def stateDelitem(self, state, key, subkey, value):
210 "Override me" 211 pass
212
213 - def render(self):
214 """ 215 Render the GTK+ admin view for this component. 216 217 Returns: a deferred returning the main widget for embedding 218 """ 219 # clear up previous error messages 220 allmessages = self.state.get('messages', []) 221 for message in allmessages: 222 if message.id == 'render': 223 self.debug('Removing previous messages %r' % message) 224 self.state.observe_remove('messages', message) 225 226 def error(debug): 227 # add an error message to the component and return 228 # an error label, given a debug string 229 self.warning("error rendering component UI; debug %s", debug) 230 m = messages.Error(T_(N_( 231 "Internal error in component UI. " 232 "Please file a bug against the component.")), 233 debug=debug, mid="render") 234 self.addMessage(m) 235 236 label = gtk.Label(_("Internal error.\nSee component error " 237 "message\nfor more details.")) 238 239 # if we don't set this error as our label, we will raise 240 # a TypeError below and obscure this more meaningful error 241 self.widget = label 242 243 return label
244 245 def loadGladeFile(): 246 if not self.gladeFile: 247 return defer.succeed(None) 248 249 def haveWtree(wtree): 250 self.wtree = wtree 251 self.debug('render: calling haveWidgetTree') 252 try: 253 self.haveWidgetTree() 254 except Exception, e: 255 return error(log.getExceptionMessage(e)) 256 257 self.debug('render: loading glade file %s in text domain %s', 258 self.gladeFile, self.gettextDomain) 259 260 d = self.loadGladeFile(self.gladeFile, self.gettextDomain) 261 d.addCallback(haveWtree) 262 return d 263 264 def loadGladeFileErrback(failure): 265 if failure.check(RuntimeError): 266 return error( 267 'Could not load glade file %s.' % self.gladeFile) 268 if failure.check(errors.NoBundleError): 269 return error( 270 'No bundle found containing %s.' % self.gladeFile) 271 272 return failure 273 274 def renderFinished(_): 275 if not self.widget: 276 self.debug('render: no self.widget, failing') 277 raise TypeError('no self.widget') 278 279 if self._pendingUIState: 280 self.debug('render: calling setUIState on the node') 281 self.setUIState(self._pendingUIState) 282 283 self.debug('renderFinished: returning widget %s', self.widget) 284 return self.widget 285 286 def renderFinishedErrback(failure): 287 return error(log.getFailureMessage(failure)) 288 289 d = loadGladeFile() 290 d.addErrback(loadGladeFileErrback) 291 d.addCallback(renderFinished) 292 d.addErrback(renderFinishedErrback) 293 return d 294
295 - def addMessage(self, message):
296 """ 297 Add a message to the component. 298 Since this is called in a component view and only relevant to the 299 component view, the message only exists in the view, and is not 300 replicated to the manager state. 301 302 The message will be displayed in the usual message view. 303 304 @type message: L{flumotion.common.messages.Message} 305 """ 306 self.state.observe_append('messages', message)
307