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)
108 gobject.type_register(MagazinesWindow)
111 class MagazineIssuesWindow(windows._base.ListWindow):
113 def __init__(self, app, player, store, node):
114 windows._base.ListWindow.__init__(self, app, player, store, node)
115 self._window.set_title(self._node.title)
118 def _get_columns(cls):
119 yield gobject.TYPE_PYOBJECT, None
121 pixrenderer = gtk.CellRendererPixbuf()
122 column = gtk.TreeViewColumn("Covers")
123 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
124 column.set_property("fixed-width", 96)
125 column.pack_start(pixrenderer, expand=True)
126 column.add_attribute(pixrenderer, "pixbuf", 1)
127 yield gobject.TYPE_OBJECT, column
129 textrenderer = gtk.CellRendererText()
130 hildonize.set_cell_thumb_selectable(textrenderer)
131 column = gtk.TreeViewColumn("Issue")
132 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
133 column.pack_start(textrenderer, expand=True)
134 column.add_attribute(textrenderer, "text", 2)
135 yield gobject.TYPE_STRING, column
138 windows._base.ListWindow._refresh(self)
139 self._node.get_children(
140 self._on_magazine_issues,
144 @misc_utils.log_exception(_moduleLogger)
145 def _on_magazine_issues(self, programs):
146 if self._isDestroyed:
147 _moduleLogger.info("Download complete but window destroyed")
151 for programNode in programs:
152 program = programNode.get_properties()
153 img = self._store.get_pixbuf_from_store(self._store.STORE_LOOKUP["nomagazineimage"])
154 row = programNode, img, program["title"]
155 self._model.append(row)
157 self._store.get_pixbuf_from_url(
158 program["pictureURL"],
159 self._create_on_image(programNode),
165 @misc_utils.log_exception(_moduleLogger)
166 def _on_error(self, exception):
168 self._errorBanner.push_message(str(exception))
170 def _create_on_image(self, programNode):
171 return lambda pix: self._on_image(programNode, pix)
173 @misc_utils.log_exception(_moduleLogger)
174 def _on_image(self, childNode, pix):
175 for i, row in enumerate(self._model):
176 if row[0] is childNode:
179 raise RuntimeError("Could not find %r" % childNode)
180 treeiter = self._model.iter_nth_child(None, i)
181 self._model.set_value(treeiter, 1, pix)
182 treeiter = self._model.iter_nth_child(None, i)
183 self._model.row_changed((i, ), treeiter)
185 def _window_from_node(self, node):
186 issuesWindow = MagazineArticlesWindow(self._app, self._player, self._store, node)
187 issuesWindow.window.set_modal(True)
188 issuesWindow.window.set_transient_for(self._window)
189 issuesWindow.window.set_default_size(*self._window.get_size())
190 issuesWindow.connect("quit", self._on_quit)
191 issuesWindow.connect("home", self._on_home)
192 issuesWindow.connect("jump-to", self._on_jump)
197 gobject.type_register(MagazineIssuesWindow)
200 class MagazineArticlesWindow(windows._base.ListWindow):
202 def __init__(self, app, player, store, node):
203 windows._base.ListWindow.__init__(self, app, player, store, node)
204 self._window.set_title(self._node.title)
207 def _get_columns(cls):
208 yield gobject.TYPE_PYOBJECT, None
210 textrenderer = gtk.CellRendererText()
211 column = gtk.TreeViewColumn("Article")
212 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
213 column.pack_start(textrenderer, expand=True)
214 column.add_attribute(textrenderer, "markup", 1)
215 yield gobject.TYPE_STRING, column
218 windows._base.ListWindow._refresh(self)
219 self._node.get_children(
220 self._on_magazine_articles,
224 @misc_utils.log_exception(_moduleLogger)
225 def _on_magazine_articles(self, programs):
226 if self._isDestroyed:
227 _moduleLogger.info("Download complete but window destroyed")
231 for programNode in programs:
232 program = programNode.get_properties()
233 row = programNode, "%s\n<small>%s</small>" % (programNode.title, programNode.subtitle)
234 self._model.append(row)
238 @misc_utils.log_exception(_moduleLogger)
239 def _on_error(self, exception):
241 self._errorBanner.push_message(str(exception))
243 def _window_from_node(self, node):
244 issuesWindow = MagazineArticleWindow(self._app, self._player, self._store, node)
245 issuesWindow.window.set_modal(True)
246 issuesWindow.window.set_transient_for(self._window)
247 issuesWindow.window.set_default_size(*self._window.get_size())
248 issuesWindow.connect("quit", self._on_quit)
249 issuesWindow.connect("home", self._on_home)
250 issuesWindow.connect("jump-to", self._on_jump)
255 gobject.type_register(MagazineArticlesWindow)
258 class MagazineArticleWindow(windows._base.BasicWindow):
260 def __init__(self, app, player, store, node):
261 windows._base.BasicWindow.__init__(self, app, player, store)
263 self._playerNode = self._player.node
264 self._nextSearch = None
265 self._updateSeek = None
267 self.connect_auto(self._player, "state-change", self._on_player_state_change)
268 self.connect_auto(self._player, "title-change", self._on_player_title_change)
269 self.connect_auto(self._player, "error", self._on_player_error)
271 self._loadingBanner = banners.GenericBanner()
273 self._presenter = presenter.StreamPresenter(self._store)
274 self._presenter.set_context(
275 self._store.STORE_LOOKUP["magazine_background"],
279 self._presenterNavigation = presenter.NavigationBox()
280 self._presenterNavigation.toplevel.add(self._presenter.toplevel)
281 self._presenterNavigation.connect("action", self._on_nav_action)
282 self._presenterNavigation.connect("navigating", self._on_navigating)
284 self._seekbar = hildonize.create_seekbar()
285 self._seekbar.connect("change-value", self._on_user_seek)
287 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
288 self._layout.pack_start(self._presenterNavigation.toplevel, True, True)
289 self._layout.pack_start(self._seekbar, False, False)
291 self._window.set_title(self._node.title)
294 windows._base.BasicWindow.show(self)
295 self._window.show_all()
296 self._errorBanner.toplevel.hide()
297 self._loadingBanner.toplevel.hide()
298 self._set_context(self._player.state)
301 def jump_to(self, node):
302 assert self._node is node
306 return self._playerNode is self._node
308 def _show_loading(self):
309 animationPath = self._store.STORE_LOOKUP["loading"]
310 animation = self._store.get_pixbuf_animation_from_store(animationPath)
311 self._loadingBanner.show(animation, "Loading...")
313 def _hide_loading(self):
314 self._loadingBanner.hide()
316 def _set_context(self, state):
317 if state == self._player.STATE_PLAY:
319 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
321 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
322 elif state == self._player.STATE_PAUSE:
323 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
324 elif state == self._player.STATE_STOP:
325 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
327 _moduleLogger.info("Unhandled player state %s" % state)
329 @misc_utils.log_exception(_moduleLogger)
330 def _on_user_seek(self, widget, scroll, value):
331 self._player.seek(value / 100.0)
333 @misc_utils.log_exception(_moduleLogger)
334 def _on_player_update_seek(self):
335 if self._isDestroyed:
337 self._seekbar.set_value(self._player.percent_elapsed * 100)
340 @misc_utils.log_exception(_moduleLogger)
341 def _on_player_state_change(self, player, newState):
342 if self._active and self._player.state == self._player.STATE_PLAY:
344 assert self._updateSeek is None
345 self._updateSeek = go_utils.Timeout(self._on_player_update_seek, once=False)
346 self._updateSeek.start(seconds=1)
349 if self._updateSeek is not None:
350 self._updateSeek.cancel()
351 self._updateSeek = None
353 if not self._presenterNavigation.is_active():
354 self._set_context(newState)
356 @misc_utils.log_exception(_moduleLogger)
357 def _on_player_title_change(self, player, node):
358 if not self._active or node in [None, self._node]:
359 self._playerNode = player.node
361 self._playerNode = player.node
362 self.emit("jump-to", node)
363 self._window.destroy()
365 @misc_utils.log_exception(_moduleLogger)
366 def _on_player_error(self, player, err, debug):
367 _moduleLogger.error("%r - %r" % (err, debug))
369 @misc_utils.log_exception(_moduleLogger)
370 def _on_navigating(self, widget, navState):
371 if navState == "clicking":
372 if self._player.state == self._player.STATE_PLAY:
374 imageName = "pause_pressed"
376 imageName = "play_pressed"
377 elif self._player.state == self._player.STATE_PAUSE:
378 imageName = "play_pressed"
379 elif self._player.state == self._player.STATE_STOP:
380 imageName = "play_pressed"
382 _moduleLogger.info("Unhandled player state %s" % self._player.state)
383 elif navState == "down":
385 elif navState == "up":
386 if self._player.state == self._player.STATE_PLAY:
391 elif self._player.state == self._player.STATE_PAUSE:
393 elif self._player.state == self._player.STATE_STOP:
396 _moduleLogger.info("Unhandled player state %s" % self._player.state)
397 elif navState == "left":
399 elif navState == "right":
402 self._presenter.set_state(self._store.STORE_LOOKUP[imageName])
404 @misc_utils.log_exception(_moduleLogger)
405 def _on_nav_action(self, widget, navState):
406 self._set_context(self._player.state)
408 if navState == "clicking":
409 if self._player.state == self._player.STATE_PLAY:
413 self._player.set_piece_by_node(self._node)
415 elif self._player.state == self._player.STATE_PAUSE:
417 elif self._player.state == self._player.STATE_STOP:
418 self._player.set_piece_by_node(self._node)
421 _moduleLogger.info("Unhandled player state %s" % self._player.state)
422 elif navState == "down":
424 self._window.destroy()
425 elif navState == "up":
427 elif navState == "left":
431 assert self._nextSearch is None
432 self._nextSearch = stream_index.AsyncWalker(stream_index.get_next)
433 self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error)
434 elif navState == "right":
438 assert self._nextSearch is None
439 self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous)
440 self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error)
442 @misc_utils.log_exception(_moduleLogger)
443 def _on_next_node(self, node):
444 self._nextSearch = None
445 self.emit("jump-to", node)
446 self._window.destroy()
448 @misc_utils.log_exception(_moduleLogger)
449 def _on_node_search_error(self, e):
450 self._nextSearch = None
451 self._errorBanner.push_message(str(e))
454 gobject.type_register(MagazineArticleWindow)