1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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
56
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
77 self._pendingUIState = None
78
79
80
81 self._gladefilepath = None
82
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
95
99
103
104 - def callRemote(self, methodName, *args, **kwargs):
107
108
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
135
136 gladeFile = gladeFile.replace(os.path.sep, '/')
137
138
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
152
167
176
182
192
194 "Override me"
195 pass
196
198 "Override me"
199 pass
200
202 "Override me"
203 pass
204
206 "Override me"
207 pass
208
210 "Override me"
211 pass
212
214 """
215 Render the GTK+ admin view for this component.
216
217 Returns: a deferred returning the main widget for embedding
218 """
219
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
228
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
240
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
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