Package elisa :: Package base_components :: Module controller
[hide private]
[frames] | no frames]

Source Code for Module elisa.base_components.controller

  1  # -*- coding: utf-8 -*- 
  2  # Elisa - Home multimedia server 
  3  # Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com). 
  4  # All rights reserved. 
  5  # 
  6  # This file is available under one of two license agreements. 
  7  # 
  8  # This file is licensed under the GPL version 3. 
  9  # See "LICENSE.GPL" in the root of this distribution including a special 
 10  # exception to use Elisa with Fluendo's plugins. 
 11  # 
 12  # The GPL part of Elisa is also available under a commercial licensing 
 13  # agreement from Fluendo. 
 14  # See "LICENSE.Elisa" in the root directory of this distribution package 
 15  # for details on that license. 
 16   
 17   
 18  __maintainer__ = 'Lionel Martin <lionel@fluendo.com>' 
 19  __maintainer2__ = 'Florian Boucault <florian@fluendo.com>' 
 20   
 21   
 22  from elisa.core.component import Component, ComponentError 
 23  from elisa.base_components.model import Model 
 24  from elisa.core.observers.observer import Observer 
 25  from elisa.core.observers.observable import Observable 
 26  from elisa.core import common 
 27  from elisa.core.input_event import * 
 28  from elisa.core.interface_controller import UndefinedMVCAssociation 
 29   
 30   
31 -class ModelNotSupported(Exception):
32 pass
33 34
35 -class Controller(Component, Observer, Observable):
36 """ 37 Connects to a compatible L{elisa.base_components.model.Model} and 38 holds extra information useful for a L{elisa.base_components.view.View} to 39 render the model. 40 It is notified of all the changes occuring to L{model}. Part of these 41 changes can be treated automatically using the bindings system. 42 A Controller can be part of a hierarchy of Controllers thus potentially 43 having a reference to its L{parent} and to its children defined in 44 L{bindings}. 45 46 DOCME: focus, focused, branch of focused controllers, backend. 47 48 @cvar supported_models: list of models that are compatible with the 49 controller identified by their path: 'plugin:model' 50 @type supported_models: tuple of strings 51 @cvar bindings: associations between L{model}'s attribute and 52 Controller's attributes; if an attribute 53 contained in L{bindings}' keys is changed or 54 created in L{model}, it is replicated in all 55 the corresponding attributes of the 56 controller defined in L{bindings} values 57 @type bindings: dictionary: keys: strings; values: list of 58 strings or strings 59 @ivar model: model to which the controller is connected; 60 setting it to an incompatible model will fail 61 @type model: L{elisa.base_components.model.Model} 62 @ivar parent: parent controller; None if the controller is a root 63 @type parent: L{elisa.base_components.controller.Controller} 64 @ivar focused: DOCME 65 @type focused: boolean 66 @ivar backend: backend to which the controller belongs 67 @type backend: L{elisa.core.backend.Backend} 68 """ 69 70 supported_models = ('base:model',) 71 bindings = {} 72
73 - def __init__(self):
74 Component.__init__(self) 75 Observer.__init__(self) 76 Observable.__init__(self) 77 78 self._model = None 79 self._backend = None 80 self._focused = False 81 self._root = None 82 self._parent = None 83 self._supported_model_classes = ()
84
85 - def initialize(self):
86 plugin_registry = common.application.plugin_registry 87 classes = [ plugin_registry.get_component_class(model_path) for 88 model_path in self.supported_models ] 89 self._supported_model_classes = tuple(classes)
90
91 - def focus(self):
92 """ 93 Grab the focus. The controller previously owning the focus loses it. 94 """ 95 self.backend.focused_controller = self
96
97 - def focused__get(self):
98 return self._focused
99
100 - def focused__set(self, focused):
101 # FIXME: how/why is it different from focus? This is confusing. 102 # Apparently, it is wrong to do controller.focused = True/False since 103 # it does not have the same effect as doing controller.focus 104 105 old_focused = self._focused 106 self._focused = focused 107 self.focused_changed(old_focused, focused)
108
109 - def focused_changed(self, old_focused, new_focused):
110 """ 111 Called when L{focused} is set to a new value. 112 113 Override if you wish to react to that change. Do not forget to call 114 the parent class method. 115 116 @param old_focused: value of focused before 117 @type old_focused: bool 118 @param new_focused: value of focused now 119 @type new_focused: bool 120 """ 121 pass
122
123 - def backend__get(self):
124 return self._backend
125
126 - def backend__set(self, new_backend):
127 old_backend, self._backend = self._backend, new_backend 128 if old_backend != new_backend: 129 self.backend_changed(old_backend, new_backend)
130
131 - def backend_changed(self, old_backend, new_backend):
132 """ 133 Called when L{backend} is set to a new value. 134 135 Override if you wish to react to that change. Do not forget to call 136 the parent class method. 137 138 @param old_backend: value of backend before 139 @type old_backend: L{elisa.core.backend.Backend} 140 @param new_backend: value of backend now 141 @type new_backend: L{elisa.core.backend.Backend} 142 """ 143 pass
144
145 - def parent__set(self, parent):
146 self._parent = parent 147 if parent is not None: 148 self.backend = parent.backend
149
150 - def parent__get(self):
151 return self._parent
152
153 - def model__get(self):
154 return self._model
155
156 - def model__set(self, model):
157 if self._model == model: 158 return 159 160 if model != None and \ 161 not isinstance(model, self._supported_model_classes): 162 msg = "%r does not support %r, but supports %r" % \ 163 (self, model.path, self.supported_models) 164 self.warning(msg) 165 raise ModelNotSupported(msg) 166 167 old_model = self._model 168 self._model = model 169 170 # update bindings 171 for model_attribute in self.bindings.keys(): 172 self._update_bound_attributes(model_attribute) 173 174 # stop observing the old model and start observing the new one 175 if old_model != None: 176 old_model.remove_observer(self) 177 if model != None: 178 model.add_observer(self) 179 180 self.model_changed(old_model, model)
181
182 - def model_changed(self, old_model, new_model):
183 """ 184 Called when L{model} is set to a new value. 185 186 Override if you wish to react to that change. Do not forget to call 187 the parent class method. 188 189 @param old_model: value of model before 190 @type old_model: L{elisa.base_components.model.Model} 191 @param new_model: value of model now 192 @type new_model: L{elisa.base_components.model.Model} 193 """ 194 pass
195
196 - def handle_input(self, input_event):
197 """ 198 Process an input event. 199 It can decide that no further processing should be done by returning 200 True or let its parent controller process it by returning False. 201 202 @param input_event: the input event to translate 203 @type input_event: L{elisa.core.input_event.InputEvent} 204 @rtype: bool 205 """ 206 if input_event.action == EventAction.EXIT: 207 common.application.stop() 208 return True 209 210 return False
211
212 - def attribute_set(self, origin, key, old_value, new_value):
213 """ 214 Called when an attribute of the model to which it is connected changes. 215 216 @param key: attribute changed 217 @type key: string 218 @param old_value: value of the attribute before being set; None if 219 attribute was not existing 220 @type old_value: any 221 @param new_value: value of the attribute after being set 222 @type new_value: any 223 """ 224 # update bindings 225 if key in self.bindings.keys(): 226 self._update_bound_attributes(key)
227
228 - def _delete_bound_attributes(self, model_attribute):
229 """ 230 Delete controller's attributes bound to L{model_attribute}. 231 """ 232 child_attributes = self.bindings[model_attribute] 233 if isinstance(child_attributes, str): 234 child_attributes = [child_attributes] 235 236 for child_attribute in child_attributes: 237 try: 238 delattr(self, child_attribute) 239 except AttributeError: 240 pass
241
242 - def _update_bound_attributes(self, model_attribute):
243 """ 244 Update (create or delete) controller's attribute bound to 245 L{model_attribute}. 246 """ 247 try: 248 model_child_value = getattr(self._model, model_attribute) 249 except AttributeError: 250 self._delete_bound_attributes(model_attribute) 251 return 252 253 child_attributes = self.bindings[model_attribute] 254 if isinstance(child_attributes, str): 255 child_attributes = [child_attributes] 256 257 if isinstance(model_child_value, Model): 258 for attribute in child_attributes: 259 self._update_bound_controller(attribute, model_child_value) 260 else: 261 for attribute in child_attributes: 262 setattr(self, attribute, model_child_value)
263
264 - def _update_bound_controller(self, attribute, model):
265 """ 266 Update (create or delete) a child controller stored as L{attribute} 267 which is connected to L{model}. 268 """ 269 if hasattr(self, attribute): 270 # reconnect the corresponding controller 271 child_controller = getattr(self, attribute) 272 child_controller.model = model 273 else: 274 try: 275 # create the corresponding controller 276 path = self.backend.get_controller_path(model.path) 277 ctrl = self._create_child_controller(attribute, path) 278 child_controller = ctrl 279 280 # connect the corresponding controller 281 child_controller.model = model 282 except UndefinedMVCAssociation, e: 283 self.warning(e)
284
285 - def _create_child_controller(self, attribute, path):
286 """ 287 Create a Controller of type L{path} and make it a child of 288 self stored as an instance variable named after the content of 289 L{attribute}. 290 291 @param attribute: attribute of self where to store the newly 292 created controller 293 @type attribute: string 294 @param path: path to the type of controller which is to be 295 created; syntax: plugin:component 296 @type path: string 297 """ 298 # create the new child controller 299 registry = common.application.plugin_registry 300 new_controller = registry.create_component(path) 301 302 # associate the new child controller 303 self._add_child_controller(attribute, new_controller) 304 305 return new_controller
306
307 - def _add_child_controller(self, attribute, controller):
308 """ 309 Make L{controller} a child of self stored as an instance variable named 310 after the content of L{attribute}. 311 312 @param attribute: attribute of self where to store the controller 313 @type attribute: string 314 @param controller: controller to be made child 315 @type path: L{elisa.base_components.controller.Controller} 316 """ 317 controller.backend = self.backend 318 controller.parent = self 319 setattr(self, attribute, controller)
320