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 column = gtk.TreeViewColumn("Magazine")
39 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
40 column.pack_start(textrenderer, expand=True)
41 column.add_attribute(textrenderer, "text", 2)
42 yield gobject.TYPE_STRING, column
45 windows._base.ListWindow._refresh(self)
46 self._node.get_children(
51 @misc_utils.log_exception(_moduleLogger)
52 def _on_magazines(self, programs):
54 _moduleLogger.info("Download complete but window destroyed")
58 for i, programNode in enumerate(programs):
59 program = programNode.get_properties()
60 img = self._store.get_pixbuf_from_store(self._store.STORE_LOOKUP["nomagazineimage"])
61 row = programNode, img, program["title"]
62 self._model.append(row)
64 programNode.get_children(self._create_on_issues(i), self._on_error)
68 def _create_on_issues(self, row):
69 return lambda issues: self._on_issues(row, issues)
71 @misc_utils.log_exception(_moduleLogger)
72 def _on_issues(self, row, issues):
74 self._store.get_pixbuf_from_url(
75 issue.get_properties()["pictureURL"],
76 lambda pix: self._on_image(row, pix),
81 _moduleLogger.info("No issues for magazine %s" % row)
83 @misc_utils.log_exception(_moduleLogger)
84 def _on_image(self, row, pix):
85 treeiter = self._model.iter_nth_child(None, row)
86 self._model.set_value(treeiter, 1, pix)
87 treeiter = self._model.iter_nth_child(None, row)
88 self._model.row_changed((row, ), treeiter)
90 @misc_utils.log_exception(_moduleLogger)
91 def _on_error(self, exception):
93 self._errorBanner.push_message(str(exception))
95 def _window_from_node(self, node):
96 issuesWindow = MagazineIssuesWindow(self._app, self._player, self._store, node)
97 issuesWindow.window.set_modal(True)
98 issuesWindow.window.set_transient_for(self._window)
99 issuesWindow.window.set_default_size(*self._window.get_size())
100 issuesWindow.connect("quit", self._on_quit)
101 issuesWindow.connect("home", self._on_home)
102 issuesWindow.connect("jump-to", self._on_jump)
106 @misc_utils.log_exception(_moduleLogger)
107 def _on_row_activated(self, view, path, column):
108 itr = self._model.get_iter(path)
109 node = self._model.get_value(itr, 0)
110 self._window_from_node(node)
113 gobject.type_register(MagazinesWindow)
116 class MagazineIssuesWindow(windows._base.ListWindow):
118 def __init__(self, app, player, store, node):
119 windows._base.ListWindow.__init__(self, app, player, store, node)
120 self._window.set_title(self._node.title)
123 def _get_columns(cls):
124 yield gobject.TYPE_PYOBJECT, None
126 pixrenderer = gtk.CellRendererPixbuf()
127 column = gtk.TreeViewColumn("Covers")
128 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
129 column.set_property("fixed-width", 96)
130 column.pack_start(pixrenderer, expand=True)
131 column.add_attribute(pixrenderer, "pixbuf", 1)
132 yield gobject.TYPE_OBJECT, column
134 textrenderer = gtk.CellRendererText()
135 column = gtk.TreeViewColumn("Issue")
136 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
137 column.pack_start(textrenderer, expand=True)
138 column.add_attribute(textrenderer, "text", 2)
139 yield gobject.TYPE_STRING, column
142 windows._base.ListWindow._refresh(self)
143 self._node.get_children(
144 self._on_magazine_issues,
148 @misc_utils.log_exception(_moduleLogger)
149 def _on_magazine_issues(self, programs):
150 if self._isDestroyed:
151 _moduleLogger.info("Download complete but window destroyed")
155 for programNode in programs:
156 program = programNode.get_properties()
157 img = self._store.get_pixbuf_from_store(self._store.STORE_LOOKUP["nomagazineimage"])
158 row = programNode, img, program["title"]
159 self._model.append(row)
161 self._store.get_pixbuf_from_url(
162 program["pictureURL"],
163 self._create_on_image(programNode),
169 @misc_utils.log_exception(_moduleLogger)
170 def _on_error(self, exception):
172 self._errorBanner.push_message(str(exception))
174 def _create_on_image(self, programNode):
175 return lambda pix: self._on_image(programNode, pix)
177 @misc_utils.log_exception(_moduleLogger)
178 def _on_image(self, childNode, pix):
179 for i, row in enumerate(self._model):
180 if row[0] is childNode:
183 raise RuntimeError("Could not find %r" % childNode)
184 treeiter = self._model.iter_nth_child(None, i)
185 self._model.set_value(treeiter, 1, pix)
186 treeiter = self._model.iter_nth_child(None, i)
187 self._model.row_changed((i, ), treeiter)
189 def _window_from_node(self, node):
190 issuesWindow = MagazineArticlesWindow(self._app, self._player, self._store, node)
191 issuesWindow.window.set_modal(True)
192 issuesWindow.window.set_transient_for(self._window)
193 issuesWindow.window.set_default_size(*self._window.get_size())
194 issuesWindow.connect("quit", self._on_quit)
195 issuesWindow.connect("home", self._on_home)
196 issuesWindow.connect("jump-to", self._on_jump)
200 @misc_utils.log_exception(_moduleLogger)
201 def _on_row_activated(self, view, path, column):
202 itr = self._model.get_iter(path)
203 node = self._model.get_value(itr, 0)
204 self._window_from_node(node)
207 gobject.type_register(MagazineIssuesWindow)
210 class MagazineArticlesWindow(windows._base.ListWindow):
212 def __init__(self, app, player, store, node):
213 windows._base.ListWindow.__init__(self, app, player, store, node)
214 self._window.set_title(self._node.title)
217 def _get_columns(cls):
218 yield gobject.TYPE_PYOBJECT, None
220 textrenderer = gtk.CellRendererText()
221 column = gtk.TreeViewColumn("Article")
222 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
223 column.pack_start(textrenderer, expand=True)
224 column.add_attribute(textrenderer, "text", 1)
225 yield gobject.TYPE_STRING, column
228 windows._base.ListWindow._refresh(self)
229 self._node.get_children(
230 self._on_magazine_articles,
234 @misc_utils.log_exception(_moduleLogger)
235 def _on_magazine_articles(self, programs):
236 if self._isDestroyed:
237 _moduleLogger.info("Download complete but window destroyed")
241 for programNode in programs:
242 program = programNode.get_properties()
243 row = programNode, "%s\n%s" % (program["title"], program["author"])
244 self._model.append(row)
248 @misc_utils.log_exception(_moduleLogger)
249 def _on_error(self, exception):
251 self._errorBanner.push_message(str(exception))
253 def _window_from_node(self, node):
254 issuesWindow = MagazineArticleWindow(self._app, self._player, self._store, node)
255 issuesWindow.window.set_modal(True)
256 issuesWindow.window.set_transient_for(self._window)
257 issuesWindow.window.set_default_size(*self._window.get_size())
258 issuesWindow.connect("quit", self._on_quit)
259 issuesWindow.connect("home", self._on_home)
260 issuesWindow.connect("jump-to", self._on_jump)
264 @misc_utils.log_exception(_moduleLogger)
265 def _on_row_activated(self, view, path, column):
266 itr = self._model.get_iter(path)
267 node = self._model.get_value(itr, 0)
268 self._window_from_node(node)
271 gobject.type_register(MagazineArticlesWindow)
274 class MagazineArticleWindow(windows._base.BasicWindow):
276 def __init__(self, app, player, store, node):
277 windows._base.BasicWindow.__init__(self, app, player, store)
279 self._playerNode = self._player.node
280 self._nextSearch = None
281 self._updateSeek = None
283 self.connect_auto(self._player, "state-change", self._on_player_state_change)
284 self.connect_auto(self._player, "title-change", self._on_player_title_change)
285 self.connect_auto(self._player, "error", self._on_player_error)
287 self._loadingBanner = banners.GenericBanner()
289 self._presenter = presenter.StreamPresenter(self._store)
290 self._presenter.set_context(
291 self._store.STORE_LOOKUP["magazine_background"],
295 self._presenterNavigation = presenter.NavigationBox()
296 self._presenterNavigation.toplevel.add(self._presenter.toplevel)
297 self._presenterNavigation.connect("action", self._on_nav_action)
298 self._presenterNavigation.connect("navigating", self._on_navigating)
300 self._seekbar = hildonize.create_seekbar()
301 self._seekbar.connect("change-value", self._on_user_seek)
303 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
304 self._layout.pack_start(self._presenterNavigation.toplevel, True, True)
305 self._layout.pack_start(self._seekbar, False, False)
307 self._window.set_title(self._node.title)
310 windows._base.BasicWindow.show(self)
311 self._window.show_all()
312 self._errorBanner.toplevel.hide()
313 self._loadingBanner.toplevel.hide()
314 self._set_context(self._player.state)
317 def jump_to(self, node):
318 assert self._node is node
322 return self._playerNode is self._node
324 def _show_loading(self):
325 animationPath = self._store.STORE_LOOKUP["loading"]
326 animation = self._store.get_pixbuf_animation_from_store(animationPath)
327 self._loadingBanner.show(animation, "Loading...")
329 def _hide_loading(self):
330 self._loadingBanner.hide()
332 def _set_context(self, state):
333 if state == self._player.STATE_PLAY:
335 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
337 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
338 elif state == self._player.STATE_PAUSE:
339 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
340 elif state == self._player.STATE_STOP:
341 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
343 _moduleLogger.info("Unhandled player state %s" % state)
345 @misc_utils.log_exception(_moduleLogger)
346 def _on_user_seek(self, widget, scroll, value):
347 self._player.seek(value / 100.0)
349 @misc_utils.log_exception(_moduleLogger)
350 def _on_player_update_seek(self):
351 if self._isDestroyed:
353 self._seekbar.set_value(self._player.percent_elapsed * 100)
356 @misc_utils.log_exception(_moduleLogger)
357 def _on_player_state_change(self, player, newState):
358 if self._active and self._player.state == self._player.STATE_PLAY:
360 assert self._updateSeek is None
361 self._updateSeek = go_utils.Timeout(self._on_player_update_seek, once=False)
362 self._updateSeek.start(seconds=1)
365 if self._updateSeek is not None:
366 self._updateSeek.cancel()
367 self._updateSeek = None
369 if not self._presenterNavigation.is_active():
370 self._set_context(newState)
372 @misc_utils.log_exception(_moduleLogger)
373 def _on_player_title_change(self, player, node):
374 if not self._active or node in [None, self._node]:
375 self._playerNode = player.node
377 self._playerNode = player.node
378 self.emit("jump-to", node)
379 self._window.destroy()
381 @misc_utils.log_exception(_moduleLogger)
382 def _on_player_error(self, player, err, debug):
383 _moduleLogger.error("%r - %r" % (err, debug))
385 @misc_utils.log_exception(_moduleLogger)
386 def _on_navigating(self, widget, navState):
387 if navState == "clicking":
388 if self._player.state == self._player.STATE_PLAY:
390 imageName = "pause_pressed"
392 imageName = "play_pressed"
393 elif self._player.state == self._player.STATE_PAUSE:
394 imageName = "play_pressed"
395 elif self._player.state == self._player.STATE_STOP:
396 imageName = "play_pressed"
398 _moduleLogger.info("Unhandled player state %s" % self._player.state)
399 elif navState == "down":
401 elif navState == "up":
402 if self._player.state == self._player.STATE_PLAY:
407 elif self._player.state == self._player.STATE_PAUSE:
409 elif self._player.state == self._player.STATE_STOP:
412 _moduleLogger.info("Unhandled player state %s" % self._player.state)
413 elif navState == "left":
415 elif navState == "right":
418 self._presenter.set_state(self._store.STORE_LOOKUP[imageName])
420 @misc_utils.log_exception(_moduleLogger)
421 def _on_nav_action(self, widget, navState):
422 self._set_context(self._player.state)
424 if navState == "clicking":
425 if self._player.state == self._player.STATE_PLAY:
429 self._player.set_piece_by_node(self._node)
431 elif self._player.state == self._player.STATE_PAUSE:
433 elif self._player.state == self._player.STATE_STOP:
434 self._player.set_piece_by_node(self._node)
437 _moduleLogger.info("Unhandled player state %s" % self._player.state)
438 elif navState == "down":
440 self._window.destroy()
441 elif navState == "up":
443 elif navState == "left":
447 assert self._nextSearch is None
448 self._nextSearch = stream_index.AsyncWalker(stream_index.get_next)
449 self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error)
450 elif navState == "right":
454 assert self._nextSearch is None
455 self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous)
456 self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error)
458 @misc_utils.log_exception(_moduleLogger)
459 def _on_next_node(self, node):
460 self._nextSearch = None
461 self.emit("jump-to", node)
462 self._window.destroy()
464 @misc_utils.log_exception(_moduleLogger)
465 def _on_node_search_error(self, e):
466 self._nextSearch = None
467 self._errorBanner.push_message(str(e))
470 gobject.type_register(MagazineArticleWindow)