Package elisa :: Package plugins :: Package bad :: Package raval_frontend :: Module list_view
[hide private]
[frames] | no frames]

Source Code for Module elisa.plugins.bad.raval_frontend.list_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  from elisa.core.observers.list import ListObserver 
 22  from elisa.core.media_manager import MediaProviderNotFound 
 23  from elisa.core import common 
 24   
 25  from twisted.internet import reactor, defer 
 26   
 27  plugin_registry = common.application.plugin_registry 
 28  PigmentView = plugin_registry.get_component_class('pigment:pigment_view') 
 29   
 30  # for image stack support 
 31  import os, cairo, math, array, pgm 
 32   
33 -class ListView(PigmentView, ListObserver):
34 35 supported_controllers = ('raval:list_controller',) 36 bindings = {"current_index": "selected_item", 37 "focused": "selector_visible", 38 "action_called" : "action_called"} 39 40 # duration between two updates of the preview 41 update_delay = 0.500 42 update_delayed_call = None 43 update_needed = True 44 images_per_stack = 3 45
46 - def selected_item__set(self, index):
47 super(ListView, self).selected_item__set(index) 48 # if it's been more than self.update_delay since the 49 # last update, update now and schedule a potential update 50 # in the future 51 if self.update_delayed_call == None or \ 52 not self.update_delayed_call.active(): 53 self.update_details() 54 self.update_needed = False 55 56 def update_if_needed(): 57 if self.update_needed: 58 self.update_details() 59 self.update_needed = False 60 call = reactor.callLater(self.update_delay, 61 update_if_needed) 62 self.update_delayed_call = call
63 64 call = reactor.callLater(self.update_delay, update_if_needed) 65 self.update_delayed_call = call 66 else: 67 self.update_needed = True
68
69 - def selected_item__get(self):
70 return super(ListView, self).selected_item__get()
71
72 - def update_details(self):
73 pass
74
75 - def create_widgets(self):
76 self.debug("creating widgets") 77 78 # NOTE: this is a bit evil: since ListView is a PigmentView, it 79 # already has a context_handle which is a Group. 80 # self.context_handle.add(self) would be more logical but less 81 # efficient. 82 self.context_handle = self 83 self.context_handle.visible = True
84
85 - def create_item(self, model):
86 raise NotImplementedError
87
88 - def inserted(self, elements, position):
89 self.debug("inserted %r elements at %r: %r", len(elements), position, 90 elements) 91 index = position 92 for model in elements: 93 drawable = self.create_item(model) 94 # FIXME: need to catch IndexError, list_ng is not threadsafe 95 try: 96 self.insert(index, drawable) 97 except IndexError: 98 self.append(drawable) 99 else: 100 index += 1
101
102 - def removed(self, elements, position):
103 self.debug("removed %r elements from %r: %r", len(elements), position, 104 elements) 105 for i in xrange(len(elements)): 106 try: 107 self.pop(position) 108 except IndexError: 109 break
110
111 - def modified(self, position, value):
112 self.debug("element at %r modified: %r", position, value)
113
114 - def dirtied(self):
115 self.debug("all the elements changed")
116
117 - def element_attribute_set(self, position, key, old_value, new_value):
118 self.debug("attribute %r of element at %r set to %r", key, position, 119 new_value)
120
121 - def controller_changed(self, old_controller, new_controller):
122 super(ListView, self).controller_changed(old_controller, new_controller) 123 124 if new_controller == None: 125 if self.update_delayed_call != None and \ 126 self.update_delayed_call.active(): 127 self.update_delayed_call.cancel() 128 if new_controller != None: 129 for i in xrange(len(self)): 130 self.pop(0) 131 if new_controller.model: 132 self.inserted(new_controller.model, 0) 133 134 # update the selected item of the list widget after all the elements 135 # got inserted; it was already done by the bindings but too early 136 self.selected_item = self.controller.current_index
137
138 - def _thumbnail_source_to_image(self, uri, image):
139 # TODO: move that to PigmentView 140 141 def display(result): 142 image_path = result[0] 143 image.set_from_file(image_path) 144 image.set_name(image_path) 145 return image_path
146 147 def error(error): 148 # No thumbnail, we want to go on with setting the unknown_icon 149 self.info(error) 150 if self.frontend: 151 return self.frontend.theme.get_media('unknown_icon') 152 153 dfr = self._get_path_from_thumbnailer(uri) 154 dfr.addErrback(error) 155 dfr.addCallback(display) 156 return dfr 157
158 - def _get_path_from_thumbnailer(self, uri):
159 160 dfr = common.application.media_manager.get_media_type(uri) 161 162 def got_media_type(media_type): 163 file_type = media_type["file_type"] 164 dfr2 = common.application.thumbnailer.get_thumbnail(uri, 165 256, 166 file_type) 167 return dfr2
168 169 dfr.addCallback(got_media_type) 170 return dfr 171 172 # image stack support
173 - def _create_image_stack(self, model, image, reflection = None, 174 gradient = 0.5):
175 if hasattr(model, 'uri'): 176 uri = model.uri 177 178 if uri is None: 179 return 180 181 media_manager = common.application.media_manager 182 dfr = media_manager.is_directory(uri) 183 dfr.addCallback(self._model_is_directory, 184 uri, 185 image, 186 reflection, 187 gradient)
188
189 - def _model_is_directory(self, it_is, uri, image, reflection, stop_gradient):
190 191 if not it_is: 192 # No directory, nothing to do :( 193 return 194 195 smalling = 0.7 196 canvas = self.frontend.context.viewport_handle.get_canvas() 197 media_manager = common.application.media_manager 198 list_of_images = [] 199 200 def create_stack(): 201 if len(list_of_images) == 0: 202 return 203 204 size = 450 205 data = array.array('c', chr(0) * size * size * 4) 206 dest_surface = cairo.ImageSurface.create_for_data(data, cairo.FORMAT_ARGB32, 207 size, 208 size, 209 size * 4) 210 211 ctx = cairo.Context(dest_surface) 212 213 degrees = math.pi / 180.0 * 15.0 214 215 list_of_images.reverse() 216 217 # turn it back the amount, we need 218 ctx.translate(size/2, size/2) 219 ctx.scale(1.3, 1.3) 220 ctx.rotate(-degrees * len(list_of_images)) 221 ctx.translate(-size/2, -size/2) 222 223 for path in list_of_images: 224 path = path[0] 225 226 image_surface = cairo.ImageSurface.create_from_png(path) 227 width = image_surface.get_width() 228 height = image_surface.get_height() 229 230 # move to the correct offset 231 left_offset = ((size - width) / 2) 232 top_offset = ((size - height) / 2) 233 ctx.translate(left_offset, top_offset) 234 235 # turn the image 236 ctx.translate(width / 2, height / 2) 237 ctx.rotate(degrees) 238 ctx.translate(-width / 2, -height / 2) 239 ctx.set_source_surface(image_surface) 240 ctx.paint() 241 242 # and the same for the borders 243 ctx.rectangle(0, 0, width, height) 244 ctx.set_line_width(5.0) 245 ctx.set_source_rgb(255, 255, 255) 246 ctx.stroke() 247 248 # move back the offset 249 ctx.translate(-left_offset, -top_offset) 250 251 ctx.save() 252 253 # and set the new image 254 image.set_from_buffer(pgm.IMAGE_BGRA, size, size, size*4, 255 size*size*4, data.tostring()) 256 257 image.opacity = 255 258 image.bg_color = (0, 0, 0, 0) 259 image.visible = True 260 261 if reflection is not None: 262 # We should also create the reflection 263 ref_data = array.array('c', chr(0) * size * size * 4) 264 dest_surface = cairo.ImageSurface.create_for_data(ref_data, 265 cairo.FORMAT_ARGB32, 266 size, 267 size, 268 size * 4) 269 source = cairo.ImageSurface.create_for_data(data, 270 cairo.FORMAT_ARGB32, 271 size, 272 size, 273 size * 4) 274 275 276 context = cairo.Context(dest_surface) 277 context.set_source_surface(source) 278 279 context.scale(size, size) 280 281 gradient = cairo.LinearGradient(0.0, 0.0, 0.0, 1.0) 282 gradient.add_color_stop_rgba(stop_gradient, 0, 0, 0, 0) 283 gradient.add_color_stop_rgba(1, 0, 0, 0, 1) 284 285 context.mask(gradient) 286 287 reflection.set_from_buffer(pgm.IMAGE_BGRA, size, size, size*4, 288 size*size*4, ref_data.tostring())
289 290 291 def image_created(image_path, list_of_children): 292 if image_path is None: 293 process_next_child(list_of_children) 294 return 295 296 if not os.path.isfile(image_path[0]): 297 process_next_child(list_of_children) 298 return 299 300 list_of_images.append(image_path) 301 if len(list_of_images) == self.images_per_stack \ 302 or len(list_of_children) == 0: 303 self.debug("creating stack") 304 create_stack() 305 else: 306 process_next_child(list_of_children) 307 308 def error(failure, pending_children): 309 self.info("Got an error:%s" % failure) 310 process_next_child(pending_children) 311 312 def process_next_child(pending_children): 313 if len(pending_children) == 0: 314 return 315 316 uri, metadata = pending_children.pop(0) 317 self.info("Looking for thumbnail for %s" % uri) 318 319 dfr = self._get_path_from_thumbnailer(uri) 320 dfr.addCallback(image_created, pending_children) 321 dfr.addErrback(error, pending_children) 322 323 lst = [] 324 dfr = media_manager.get_direct_children(uri, lst) 325 dfr.addCallback(process_next_child) 326