Package elisa :: Package plugins :: Package bad :: Package poblenou_frontend :: Module node_view
[hide private]
[frames] | no frames]

Source Code for Module elisa.plugins.bad.poblenou_frontend.node_view

  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__ = 'Florian Boucault <florian@fluendo.com>' 
 19   
 20   
 21  import os, math 
 22  import weakref 
 23   
 24  from twisted.internet import reactor 
 25   
 26  import pgm 
 27   
 28  from pgm.timing import implicit 
 29  from pgm.graph.image import Image 
 30   
 31  from poblenou_widgets.circular_list import * 
 32   
 33  from elisa.core import common 
 34  from elisa.core.media_manager import MediaProviderNotFound 
 35  from elisa.core import plugin_registry 
 36   
 37  from list_cache import CacheList 
 38   
 39  BaseNodeView = plugin_registry.get_component_class('base:node_view') 
 40   
41 -class NodeView(BaseNodeView):
42 43 supported_controllers = ('poblenou:node_controller',) 44
45 - def __init__(self):
46 super(NodeView, self).__init__() 47 self.context_path = 'pigment:pigment_context' 48 self.context_handle = None 49 50 # duration between two updates of the current selection UI 51 # wise: description label and arrow 52 self._update_delay = 0.400 53 self._update_delayed_call = None 54 self._update_needed = True 55 56 self._representation = None 57 58 # menu widget 59 self._menu = None 60 self._animated_menu = None 61 62 # number of preloaded children in terms of graphical ressources 63 self._preloaded_children = 25 64 65 self._opacity = [0, 5, 25]
66
67 - def frontend_changed(self, previous_frontend, new_frontend):
68 if new_frontend != previous_frontend: 69 if previous_frontend != None: 70 previous_frontend.theme_changed.disconnect(self.theme_changed) 71 72 if new_frontend == None: 73 return 74 75 # FIXME: re-bind the widgets to the new canvas 76 77 # connects to the theme_changed signal of the new context 78 new_frontend.theme_changed.connect(self.theme_changed)
79
80 - def theme_changed(self, new_theme):
81 if self.controller.model.thumbnail_source == None and \ 82 self._representation != None: 83 widget = self._representation() 84 if widget != None: 85 icon = self.controller.model.theme_icon 86 image_path = self.frontend.theme.get_media(icon) 87 widget.set_from_file(image_path)
88
89 - def controller_changed(self, old_controller, new_controller):
90 # FIXME: should deal with non-empty controller 91 super(NodeView, self).controller_changed(old_controller, new_controller)
92
93 - def _create_list_menu(self):
94 canvas = self.frontend.context.canvas 95 self._menu = CircularList(canvas, pgm.DRAWABLE_MIDDLE, 96 orientation = HORIZONTAL) 97 self._cache_menu = CacheList(self._preloaded_children, 98 self._menu)
99
100 - def _create_menu(self):
101 canvas = self.frontend.context.canvas 102 103 old_menu_widget = self._menu 104 105 self._create_list_menu() 106 107 if old_menu_widget != None: 108 old_widgets = old_menu_widget.widgets 109 old_animated_widgets = old_menu_widget.animated_widgets 110 old_selection = old_menu_widget.selected_item 111 self.root.root_group.remove(old_menu_widget) 112 113 for animated_widget in old_animated_widgets: 114 animated_widget.stop_animations() 115 116 for widget in old_widgets: 117 self._menu.append(widget) 118 119 self._menu.selected_item = old_selection 120 121 122 self._menu.fg_color = (255, 255, 255, 255) 123 self._menu.bg_color = (0, 0, 0, 0) 124 125 self.root.root_group.add(self._menu) 126 127 self._animated_menu = implicit.AnimatedObject(self._menu) 128 menu = self._animated_menu 129 menu.mode = implicit.REPLACE 130 menu.setup_next_animations(duration = 350, 131 transformation = implicit.DECELERATE) 132 self.context_handle = self._animated_menu 133 134 self._scale_menu() 135 self._menu.visible = True
136
137 - def _scale_menu(self):
138 # set canvas size dependent properties of menu (size, position) 139 canvas = self.frontend.context.canvas 140 141 x = 0.0 142 y = canvas.height * (1.0 - 0.560) 143 z_step = 500.0 144 self._hidden_position = (x, y, 500.0) 145 self._position = [(x, y, z) for z in range(-z_step*3, 0.0, z_step)] 146 self._selected_position = (x, y, 0.0) 147 148 149 self._menu.width = canvas.width 150 self._menu.height = canvas.height/3.0 151 self._menu.visible_items = 11 152 153 if self.controller.selected: 154 self._menu.opacity = 255 155 self._menu.position = self._selected_position 156 self.hide_parents(2) 157 self.hide_children() 158 else: 159 self._animated_menu.position = self._hidden_position 160 self._animated_menu.opacity = 0
161
162 - def _create_thumbnail_widget(self, controller):
163 widget = Image() 164 widget.bg_color = (0, 0, 0, 0) 165 widget.layout = pgm.IMAGE_ZOOMED 166 widget.alignment = pgm.IMAGE_CENTER 167 widget.opacity = 255 168 169 widget.connect("clicked", self._drawable_clicked, controller) 170 171 icon = controller.model.theme_icon 172 # FIXME: cloning deactivated because set_image_from_image is not ready 173 # in Pigment yet; before reactivating it, be sure to use Python's 174 # WeakValueDictionary for the caching 175 # (see http://docs.python.org/lib/module-weakref.html) 176 #widget.set_from_image(self.root.get_icon_drawable(icon)) 177 image_path = self.frontend.theme.get_media(icon) 178 widget.set_from_file(image_path) 179 widget.set_name(icon) 180 181 widget.visible = True 182 183 uri = controller.model.thumbnail_source 184 if uri != None: 185 self._thumbnail_source_to_image(uri, widget) 186 187 return widget
188
189 - def _thumbnail_source_to_image(self, uri, image):
190 try: 191 dfr = common.application.media_manager.get_media_type(uri) 192 except MediaProviderNotFound, e: 193 self.info("Could not make a thumbnail for %s: no MediaProvider" 194 " found for this kind of URI (scheme: %s)" % (uri, 195 uri.scheme)) 196 # If there is no provider for this uri existing, we can't go on 197 return 198 199 def got_media_type(media_type): 200 file_type = media_type["file_type"] 201 dfr2 = common.application.thumbnailer.get_thumbnail(uri, 202 256, 203 file_type) 204 def display(result): 205 image.set_from_file(result[0]) 206 image.set_name(result[0])
207 208 def error(error): 209 self.warning(error) 210 image_path = self.frontend.theme.get_media('unknown_icon') 211 image.set_from_file(image_path) 212 image.set_name(image_path)
213 214 dfr2.addCallback(display).addErrback(error) 215 216 def error(error): 217 self.warning(error) 218 image_path = self.frontend.theme.get_media('unknown_icon') 219 image.set_from_file(image_path) 220 image.set_name(image_path) 221 222 dfr.addCallback(got_media_type).addErrback(error) 223 224
225 - def create_representation(self, controller):
226 representation = self._create_thumbnail_widget(controller) 227 self._representation = weakref.ref(representation) 228 return self._representation()
229
230 - def attribute_set(self, origin, key, old_value, new_value):
231 super(NodeView, self).attribute_set(origin, key, old_value, new_value) 232 if key == 'current_index': 233 self.debug("new index for %s: %s" % (self, new_value)) 234 235 if self._menu != None: 236 if new_value == old_value: 237 return 238 239 self._cache_menu.current_index = new_value 240 if self.controller.selected: 241 # if it's been more than self._update_delay since the 242 # last update, update now and schedule a potential update 243 # in the future 244 if self._update_delayed_call == None or \ 245 not self._update_delayed_call.active(): 246 self._update_selection() 247 self._update_needed = False 248 249 def update_if_needed(): 250 if self._update_needed: 251 self._update_selection() 252 self._update_needed = False 253 call = reactor.callLater(self._update_delay, 254 update_if_needed) 255 self._update_delayed_call = call
256 257 call = reactor.callLater(self._update_delay, 258 update_if_needed) 259 self._update_delayed_call = call 260 else: 261 self._update_needed = True 262 263 elif key == 'selected': 264 self.debug("%s (un)selected: %s" % (self, new_value)) 265 if new_value: 266 if self._animated_menu != None: 267 self._animated_menu.opacity = 255 268 self._animated_menu.position = self._selected_position 269 270 self._update_selection() 271 272 self.hide_parents(2) 273 self.hide_children() 274 275 self.root.display_loading(self.controller.model.loading) 276 277 elif key == 'loading': 278 self.debug("%s loading: %s" % (self, new_value)) 279 if new_value == old_value or not self.controller.selected: 280 return 281 282 if new_value == True: 283 self.root.display_loading(True) 284 else: 285 if self.controller.model.has_children: 286 self.root.display_loading(False) 287 else: 288 self.root.display_empty(True) 289 290 elif key == 'thumbnail_source': 291 if self._representation != None: 292 widget = self._representation() 293 if widget != None: 294 self._thumbnail_source_to_image(new_value, widget) 295 # FIXME: should there really be both? 296 elif key == 'theme_icon': 297 widget = None 298 if self._representation != None: 299 widget = self._representation() 300 301 if self.controller.model.thumbnail_source == None and \ 302 widget != None: 303 icon = self.controller.model.theme_icon 304 image_path = self.frontend.theme.get_media(icon) 305 widget.set_from_file(image_path) 306 307 elif key == 'visualisation_mode': 308 self.info("Switching to %s mode" % new_value) 309 self._create_menu() 310 311 elif key == 'has_children': 312 if isinstance(self.parent, NodeView): 313 self.parent._display_arrow() 314 315 if new_value: 316 self.root.display_loading(False) 317
318 - def hide_parents(self, depth):
319 parent = self.parent 320 if isinstance(parent, NodeView): 321 # FIXME: avoid private variable access like that 322 parent._animated_menu.opacity = parent._opacity[max(depth, 0)] 323 parent._animated_menu.position = parent._position[max(depth, 0)] 324 parent.hide_parents(depth-1)
325
326 - def hide_children(self):
327 child_index = self.controller.current_index 328 if child_index >= 0 and child_index < len(self): 329 self[child_index].hide() 330 self[child_index].hide_children()
331
332 - def hide(self):
333 if self._menu != None: 334 self._animated_menu.opacity = 0 335 self._animated_menu.position = self._hidden_position
336
337 - def child_view_created(self, view, position):
338 super(NodeView, self).child_view_created(view, position) 339 if position == self.controller.current_index and self.controller.selected: 340 self._update_selection() 341 342 if self._menu == None: 343 self._create_menu() 344 345 view_creator = lambda: view.create_representation(view.controller) 346 self._cache_menu.insert(position, view_creator)
347
348 - def removed(self, elements, position):
349 self.debug("%s got a REMOVED at %s for %s elements" % \ 350 (self, position, len(elements))) 351 352 # FIXME: only handles removal of 1 element 353 if len(elements) > 1: 354 self.warning("Support for multiple elements removal not yet \ 355 implemented") 356 return 357 358 self._cache_menu.pop(position) 359 super(NodeView, self).removed(elements, position) 360 361 if len(self) == 0: 362 self.debug("Menu of %s is empty: setting it to None" % self) 363 self._menu = None 364 elif self.controller.selected: 365 self.debug("%s is updating the menu" % self) 366 self._update_selection()
367
368 - def _update_selection(self):
369 self._display_description() 370 self._display_arrow()
371
372 - def _display_description(self):
373 # display the description of currently selected item 374 if self.controller.current_index < len(self) and \ 375 self.controller.selected: 376 current_index = self.controller.current_index 377 description = self[current_index].controller.model.text 378 else: 379 description = "" 380 381 self.root.display_description(description)
382
383 - def _display_arrow(self):
384 # display an animated arrow if the currently selected item has 385 # children 386 controller = self.controller 387 if controller.current_index < len(self) and \ 388 self[controller.current_index].controller.model.has_children and \ 389 controller.selected: 390 self.root.display_arrow(True) 391 else: 392 self.root.display_arrow(False)
393
394 - def _drawable_clicked(self, drawable, x, y, z, button, time, controller):
395 canvas = self.frontend.context.canvas 396 self.debug("%s clicked" % controller.model.text) 397 398 if drawable.opacity == 0: 399 return False 400 401 if self._root._drag_started: 402 # do not process the click if there is vertical dragging 403 # currently happening 404 dy = self._root._drag_start_position[1] - y 405 if dy > 0.05: 406 return True 407 408 # do not process the click if there is horizontal dragging 409 # happening in the current menu level 410 if self._root._drag_start_index != self.controller.parent.current_index: 411 return True 412 413 if self.controller.root.selected_controller == self.controller.parent: 414 # this level is currently selected 415 current_index = self.controller.parent.current_index 416 clicked_index = self.controller.parent.index(self.controller) 417 if current_index != clicked_index: 418 # scroll to the clicked entry of this level 419 self.controller.parent.current_index = clicked_index 420 else: 421 # enter the clicked entry of the menu 422 self.controller.parent.activate_node() 423 424 return True 425 426 return False
427