7 import util.go_utils as go_utils
8 import util.misc as misc_utils
16 _moduleLogger = logging.getLogger(__name__)
19 class MagazinesWindow(windows._base.ListWindow):
21 def __init__(self, app, player, store, node):
22 windows._base.ListWindow.__init__(self, app, player, store, node)
23 self._window.set_title(self._node.title)
26 def _get_columns(cls):
27 yield gobject.TYPE_PYOBJECT, None
29 pixrenderer = gtk.CellRendererPixbuf()
30 column = gtk.TreeViewColumn("Covers")
31 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
32 column.set_property("fixed-width", 96)
33 column.pack_start(pixrenderer, expand=True)
34 column.add_attribute(pixrenderer, "pixbuf", 1)
35 yield gobject.TYPE_OBJECT, column
37 textrenderer = gtk.CellRendererText()
38 hildonize.set_cell_thumb_selectable(textrenderer)
39 column = gtk.TreeViewColumn("Magazine")
40 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
41 column.pack_start(textrenderer, expand=True)
42 column.add_attribute(textrenderer, "text", 2)
43 yield gobject.TYPE_STRING, column
46 windows._base.ListWindow._refresh(self)
47 self._node.get_children(
52 @misc_utils.log_exception(_moduleLogger)
53 def _on_magazines(self, programs):
55 _moduleLogger.info("Download complete but window destroyed")
59 for i, programNode in enumerate(programs):
60 program = programNode.get_properties()
61 img = self._store.get_pixbuf_from_store(self._store.STORE_LOOKUP["nomagazineimage"])
62 row = programNode, img, program["title"]
63 self._model.append(row)
65 programNode.get_children(self._create_on_issues(i), self._on_error)
69 def _create_on_issues(self, row):
70 return lambda issues: self._on_issues(row, issues)
72 @misc_utils.log_exception(_moduleLogger)
73 def _on_issues(self, row, issues):
75 self._store.get_pixbuf_from_url(
76 issue.get_properties()["pictureURL"],
77 lambda pix: self._on_image(row, pix),
82 _moduleLogger.info("No issues for magazine %s" % row)
84 @misc_utils.log_exception(_moduleLogger)
85 def _on_image(self, row, pix):
86 treeiter = self._model.iter_nth_child(None, row)
87 self._model.set_value(treeiter, 1, pix)
88 treeiter = self._model.iter_nth_child(None, row)
89 self._model.row_changed((row, ), treeiter)
91 @misc_utils.log_exception(_moduleLogger)
92 def _on_error(self, exception):
94 self._errorBanner.push_message(str(exception))
96 def _window_from_node(self, node):
97 issuesWindow = MagazineIssuesWindow(self._app, self._player, self._store, node)
98 issuesWindow.window.set_modal(True)
99 issuesWindow.window.set_transient_for(self._window)
100 issuesWindow.window.set_default_size(*self._window.get_size())
101 issuesWindow.connect("quit", self._on_quit)
102 issuesWindow.connect("home", self._on_home)
103 issuesWindow.connect("jump-to", self._on_jump)
107 @misc_utils.log_exception(_moduleLogger)
108 def _on_row_activated(self, view, path, column):
109 itr = self._model.get_iter(path)
110 node = self._model.get_value(itr, 0)
111 self._window_from_node(node)
114 gobject.type_register(MagazinesWindow)
117 class MagazineIssuesWindow(windows._base.ListWindow):
119 def __init__(self, app, player, store, node):
120 windows._base.ListWindow.__init__(self, app, player, store, node)
121 self._window.set_title(self._node.title)
124 def _get_columns(cls):
125 yield gobject.TYPE_PYOBJECT, None
127 pixrenderer = gtk.CellRendererPixbuf()
128 column = gtk.TreeViewColumn("Covers")
129 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
130 column.set_property("fixed-width", 96)
131 column.pack_start(pixrenderer, expand=True)
132 column.add_attribute(pixrenderer, "pixbuf", 1)
133 yield gobject.TYPE_OBJECT, column
135 textrenderer = gtk.CellRendererText()
136 hildonize.set_cell_thumb_selectable(textrenderer)
137 column = gtk.TreeViewColumn("Issue")
138 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
139 column.pack_start(textrenderer, expand=True)
140 column.add_attribute(textrenderer, "text", 2)
141 yield gobject.TYPE_STRING, column
144 windows._base.ListWindow._refresh(self)
145 self._node.get_children(
146 self._on_magazine_issues,
150 @misc_utils.log_exception(_moduleLogger)
151 def _on_magazine_issues(self, programs):
152 if self._isDestroyed:
153 _moduleLogger.info("Download complete but window destroyed")
157 for programNode in programs:
158 program = programNode.get_properties()
159 img = self._store.get_pixbuf_from_store(self._store.STORE_LOOKUP["nomagazineimage"])
160 row = programNode, img, program["title"]
161 self._model.append(row)
163 self._store.get_pixbuf_from_url(
164 program["pictureURL"],
165 self._create_on_image(programNode),
171 @misc_utils.log_exception(_moduleLogger)
172 def _on_error(self, exception):
174 self._errorBanner.push_message(str(exception))
176 def _create_on_image(self, programNode):
177 return lambda pix: self._on_image(programNode, pix)
179 @misc_utils.log_exception(_moduleLogger)
180 def _on_image(self, childNode, pix):
181 for i, row in enumerate(self._model):
182 if row[0] is childNode:
185 raise RuntimeError("Could not find %r" % childNode)
186 treeiter = self._model.iter_nth_child(None, i)
187 self._model.set_value(treeiter, 1, pix)
188 treeiter = self._model.iter_nth_child(None, i)
189 self._model.row_changed((i, ), treeiter)
191 def _window_from_node(self, node):
192 issuesWindow = MagazineArticlesWindow(self._app, self._player, self._store, node)
193 issuesWindow.window.set_modal(True)
194 issuesWindow.window.set_transient_for(self._window)
195 issuesWindow.window.set_default_size(*self._window.get_size())
196 issuesWindow.connect("quit", self._on_quit)
197 issuesWindow.connect("home", self._on_home)
198 issuesWindow.connect("jump-to", self._on_jump)
202 @misc_utils.log_exception(_moduleLogger)
203 def _on_row_activated(self, view, path, column):
204 itr = self._model.get_iter(path)
205 node = self._model.get_value(itr, 0)
206 self._window_from_node(node)
209 gobject.type_register(MagazineIssuesWindow)
212 class MagazineArticlesWindow(windows._base.ListWindow):
214 def __init__(self, app, player, store, node):
215 windows._base.ListWindow.__init__(self, app, player, store, node)
216 self._window.set_title(self._node.title)
219 def _get_columns(cls):
220 yield gobject.TYPE_PYOBJECT, None
222 textrenderer = gtk.CellRendererText()
223 hildonize.set_cell_thumb_selectable(textrenderer)
224 column = gtk.TreeViewColumn("Article")
225 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
226 column.pack_start(textrenderer, expand=True)
227 column.add_attribute(textrenderer, "text", 1)
228 yield gobject.TYPE_STRING, column
231 windows._base.ListWindow._refresh(self)
232 self._node.get_children(
233 self._on_magazine_articles,
237 @misc_utils.log_exception(_moduleLogger)
238 def _on_magazine_articles(self, programs):
239 if self._isDestroyed:
240 _moduleLogger.info("Download complete but window destroyed")
244 for programNode in programs:
245 program = programNode.get_properties()
246 row = programNode, "%s\n%s" % (program["title"], program["author"])
247 self._model.append(row)
251 @misc_utils.log_exception(_moduleLogger)
252 def _on_error(self, exception):
254 self._errorBanner.push_message(str(exception))
256 def _window_from_node(self, node):
257 issuesWindow = MagazineArticleWindow(self._app, self._player, self._store, node)
258 issuesWindow.window.set_modal(True)
259 issuesWindow.window.set_transient_for(self._window)
260 issuesWindow.window.set_default_size(*self._window.get_size())
261 issuesWindow.connect("quit", self._on_quit)
262 issuesWindow.connect("home", self._on_home)
263 issuesWindow.connect("jump-to", self._on_jump)
267 @misc_utils.log_exception(_moduleLogger)
268 def _on_row_activated(self, view, path, column):
269 itr = self._model.get_iter(path)
270 node = self._model.get_value(itr, 0)
271 self._window_from_node(node)
274 gobject.type_register(MagazineArticlesWindow)
277 class MagazineArticleWindow(windows._base.BasicWindow):
279 def __init__(self, app, player, store, node):
280 windows._base.BasicWindow.__init__(self, app, player, store)
282 self._playerNode = self._player.node
283 self._nextSearch = None
284 self._updateSeek = None
286 self.connect_auto(self._player, "state-change", self._on_player_state_change)
287 self.connect_auto(self._player, "title-change", self._on_player_title_change)
288 self.connect_auto(self._player, "error", self._on_player_error)
290 self._loadingBanner = banners.GenericBanner()
292 self._presenter = presenter.StreamPresenter(self._store)
293 self._presenter.set_context(
294 self._store.STORE_LOOKUP["magazine_background"],
298 self._presenterNavigation = presenter.NavigationBox()
299 self._presenterNavigation.toplevel.add(self._presenter.toplevel)
300 self._presenterNavigation.connect("action", self._on_nav_action)
301 self._presenterNavigation.connect("navigating", self._on_navigating)
303 self._seekbar = hildonize.create_seekbar()
304 self._seekbar.connect("change-value", self._on_user_seek)
306 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
307 self._layout.pack_start(self._presenterNavigation.toplevel, True, True)
308 self._layout.pack_start(self._seekbar, False, False)
310 self._window.set_title(self._node.title)
313 windows._base.BasicWindow.show(self)
314 self._window.show_all()
315 self._errorBanner.toplevel.hide()
316 self._loadingBanner.toplevel.hide()
317 self._set_context(self._player.state)
320 def jump_to(self, node):
321 assert self._node is node
325 return self._playerNode is self._node
327 def _show_loading(self):
328 animationPath = self._store.STORE_LOOKUP["loading"]
329 animation = self._store.get_pixbuf_animation_from_store(animationPath)
330 self._loadingBanner.show(animation, "Loading...")
332 def _hide_loading(self):
333 self._loadingBanner.hide()
335 def _set_context(self, state):
336 if state == self._player.STATE_PLAY:
338 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
340 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
341 elif state == self._player.STATE_PAUSE:
342 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
343 elif state == self._player.STATE_STOP:
344 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
346 _moduleLogger.info("Unhandled player state %s" % state)
348 @misc_utils.log_exception(_moduleLogger)
349 def _on_user_seek(self, widget, scroll, value):
350 self._player.seek(value / 100.0)
352 @misc_utils.log_exception(_moduleLogger)
353 def _on_player_update_seek(self):
354 if self._isDestroyed:
356 self._seekbar.set_value(self._player.percent_elapsed * 100)
359 @misc_utils.log_exception(_moduleLogger)
360 def _on_player_state_change(self, player, newState):
361 if self._active and self._player.state == self._player.STATE_PLAY:
363 assert self._updateSeek is None
364 self._updateSeek = go_utils.Timeout(self._on_player_update_seek, once=False)
365 self._updateSeek.start(seconds=1)
368 if self._updateSeek is not None:
369 self._updateSeek.cancel()
370 self._updateSeek = None
372 if not self._presenterNavigation.is_active():
373 self._set_context(newState)
375 @misc_utils.log_exception(_moduleLogger)
376 def _on_player_title_change(self, player, node):
377 if not self._active or node in [None, self._node]:
378 self._playerNode = player.node
380 self._playerNode = player.node
381 self.emit("jump-to", node)
382 self._window.destroy()
384 @misc_utils.log_exception(_moduleLogger)
385 def _on_player_error(self, player, err, debug):
386 _moduleLogger.error("%r - %r" % (err, debug))
388 @misc_utils.log_exception(_moduleLogger)
389 def _on_navigating(self, widget, navState):
390 if navState == "clicking":
391 if self._player.state == self._player.STATE_PLAY:
393 imageName = "pause_pressed"
395 imageName = "play_pressed"
396 elif self._player.state == self._player.STATE_PAUSE:
397 imageName = "play_pressed"
398 elif self._player.state == self._player.STATE_STOP:
399 imageName = "play_pressed"
401 _moduleLogger.info("Unhandled player state %s" % self._player.state)
402 elif navState == "down":
404 elif navState == "up":
405 if self._player.state == self._player.STATE_PLAY:
410 elif self._player.state == self._player.STATE_PAUSE:
412 elif self._player.state == self._player.STATE_STOP:
415 _moduleLogger.info("Unhandled player state %s" % self._player.state)
416 elif navState == "left":
418 elif navState == "right":
421 self._presenter.set_state(self._store.STORE_LOOKUP[imageName])
423 @misc_utils.log_exception(_moduleLogger)
424 def _on_nav_action(self, widget, navState):
425 self._set_context(self._player.state)
427 if navState == "clicking":
428 if self._player.state == self._player.STATE_PLAY:
432 self._player.set_piece_by_node(self._node)
434 elif self._player.state == self._player.STATE_PAUSE:
436 elif self._player.state == self._player.STATE_STOP:
437 self._player.set_piece_by_node(self._node)
440 _moduleLogger.info("Unhandled player state %s" % self._player.state)
441 elif navState == "down":
443 self._window.destroy()
444 elif navState == "up":
446 elif navState == "left":
450 assert self._nextSearch is None
451 self._nextSearch = stream_index.AsyncWalker(stream_index.get_next)
452 self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error)
453 elif navState == "right":
457 assert self._nextSearch is None
458 self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous)
459 self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error)
461 @misc_utils.log_exception(_moduleLogger)
462 def _on_next_node(self, node):
463 self._nextSearch = None
464 self.emit("jump-to", node)
465 self._window.destroy()
467 @misc_utils.log_exception(_moduleLogger)
468 def _on_node_search_error(self, e):
469 self._nextSearch = None
470 self._errorBanner.push_message(str(e))
473 gobject.type_register(MagazineArticleWindow)