58fe5ee4675548b80f51b42b4e7dfecd2e8b5153
[watersofshiloah] / src / windows / scriptures.py
1 import logging
2
3 import gobject
4 import gtk
5
6 import hildonize
7 import util.go_utils as go_utils
8 import util.misc as misc_utils
9 import banners
10 import presenter
11 import stream_index
12
13 import windows
14
15
16 _moduleLogger = logging.getLogger(__name__)
17
18
19 class ScripturesWindow(windows._base.ListWindow):
20
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)
24
25         @classmethod
26         def _get_columns(cls):
27                 yield gobject.TYPE_PYOBJECT, None
28
29                 textrenderer = gtk.CellRendererText()
30                 hildonize.set_cell_thumb_selectable(textrenderer)
31                 column = gtk.TreeViewColumn("Scripture")
32                 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
33                 column.pack_start(textrenderer, expand=True)
34                 column.add_attribute(textrenderer, "text", 1)
35                 yield gobject.TYPE_STRING, column
36
37         def _refresh(self):
38                 windows._base.ListWindow._refresh(self)
39                 self._node.get_children(
40                         self._on_scriptures,
41                         self._on_error,
42                 )
43
44         @misc_utils.log_exception(_moduleLogger)
45         def _on_scriptures(self, programs):
46                 if self._isDestroyed:
47                         _moduleLogger.info("Download complete but window destroyed")
48                         return
49
50                 self._hide_loading()
51                 for programNode in programs:
52                         program = programNode.get_properties()
53                         row = programNode, program["title"]
54                         self._model.append(row)
55
56                 self._select_row()
57
58         @misc_utils.log_exception(_moduleLogger)
59         def _on_error(self, exception):
60                 self._hide_loading()
61                 self._errorBanner.push_message(str(exception))
62
63         def _window_from_node(self, node):
64                 booksWindow = ScriptureBooksWindow(self._app, self._player, self._store, node)
65                 booksWindow.window.set_modal(True)
66                 booksWindow.window.set_transient_for(self._window)
67                 booksWindow.window.set_default_size(*self._window.get_size())
68                 booksWindow.connect("quit", self._on_quit)
69                 booksWindow.connect("home", self._on_home)
70                 booksWindow.connect("jump-to", self._on_jump)
71                 booksWindow.show()
72                 return booksWindow
73
74         @misc_utils.log_exception(_moduleLogger)
75         def _on_row_activated(self, view, path, column):
76                 itr = self._model.get_iter(path)
77                 node = self._model.get_value(itr, 0)
78                 self._window_from_node(node)
79
80
81 gobject.type_register(ScripturesWindow)
82
83
84 class ScriptureBooksWindow(windows._base.ListWindow):
85
86         def __init__(self, app, player, store, node):
87                 windows._base.ListWindow.__init__(self, app, player, store, node)
88                 self._window.set_title(self._node.title)
89
90         @classmethod
91         def _get_columns(cls):
92                 yield gobject.TYPE_PYOBJECT, None
93
94                 textrenderer = gtk.CellRendererText()
95                 hildonize.set_cell_thumb_selectable(textrenderer)
96                 column = gtk.TreeViewColumn("Book")
97                 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
98                 column.pack_start(textrenderer, expand=True)
99                 column.add_attribute(textrenderer, "text", 1)
100                 yield gobject.TYPE_STRING, column
101
102         def _refresh(self):
103                 windows._base.ListWindow._refresh(self)
104                 self._node.get_children(
105                         self._on_scripture_books,
106                         self._on_error,
107                 )
108
109         @misc_utils.log_exception(_moduleLogger)
110         def _on_scripture_books(self, programs):
111                 if self._isDestroyed:
112                         _moduleLogger.info("Download complete but window destroyed")
113                         return
114
115                 self._hide_loading()
116                 for programNode in programs:
117                         program = programNode.get_properties()
118                         row = programNode, program["title"]
119                         self._model.append(row)
120
121                 self._select_row()
122
123         @misc_utils.log_exception(_moduleLogger)
124         def _on_error(self, exception):
125                 self._hide_loading()
126                 self._errorBanner.push_message(str(exception))
127
128         def _window_from_node(self, node):
129                 booksWindow = ScriptureChaptersWindow(self._app, self._player, self._store, node)
130                 booksWindow.window.set_modal(True)
131                 booksWindow.window.set_transient_for(self._window)
132                 booksWindow.window.set_default_size(*self._window.get_size())
133                 booksWindow.connect("quit", self._on_quit)
134                 booksWindow.connect("home", self._on_home)
135                 booksWindow.connect("jump-to", self._on_jump)
136                 booksWindow.show()
137                 return booksWindow
138
139         @misc_utils.log_exception(_moduleLogger)
140         def _on_row_activated(self, view, path, column):
141                 itr = self._model.get_iter(path)
142                 node = self._model.get_value(itr, 0)
143                 self._window_from_node(node)
144
145
146 gobject.type_register(ScriptureBooksWindow)
147
148
149 class ScriptureChaptersWindow(windows._base.ListWindow):
150
151         def __init__(self, app, player, store, node):
152                 windows._base.ListWindow.__init__(self, app, player, store, node)
153                 self._window.set_title(self._node.title)
154
155         @classmethod
156         def _get_columns(cls):
157                 yield gobject.TYPE_PYOBJECT, None
158
159                 textrenderer = gtk.CellRendererText()
160                 hildonize.set_cell_thumb_selectable(textrenderer)
161                 column = gtk.TreeViewColumn("Chapter")
162                 column.set_property("sizing", gtk.TREE_VIEW_COLUMN_FIXED)
163                 column.pack_start(textrenderer, expand=True)
164                 column.add_attribute(textrenderer, "text", 1)
165                 yield gobject.TYPE_STRING, column
166
167         def _refresh(self):
168                 windows._base.ListWindow._refresh(self)
169                 self._node.get_children(
170                         self._on_scripture_chapters,
171                         self._on_error,
172                 )
173
174         @misc_utils.log_exception(_moduleLogger)
175         def _on_scripture_chapters(self, programs):
176                 if self._isDestroyed:
177                         _moduleLogger.info("Download complete but window destroyed")
178                         return
179
180                 self._hide_loading()
181                 for programNode in programs:
182                         program = programNode.get_properties()
183                         row = programNode, program["title"]
184                         self._model.append(row)
185
186                 self._select_row()
187
188         @misc_utils.log_exception(_moduleLogger)
189         def _on_error(self, exception):
190                 self._hide_loading()
191                 self._errorBanner.push_message(str(exception))
192
193         def _window_from_node(self, node):
194                 booksWindow = ScriptureChapterWindow(self._app, self._player, self._store, node)
195                 booksWindow.window.set_modal(True)
196                 booksWindow.window.set_transient_for(self._window)
197                 booksWindow.window.set_default_size(*self._window.get_size())
198                 booksWindow.connect("quit", self._on_quit)
199                 booksWindow.connect("home", self._on_home)
200                 booksWindow.connect("jump-to", self._on_jump)
201                 booksWindow.show()
202                 return booksWindow
203
204         @misc_utils.log_exception(_moduleLogger)
205         def _on_row_activated(self, view, path, column):
206                 itr = self._model.get_iter(path)
207                 node = self._model.get_value(itr, 0)
208                 self._window_from_node(node)
209
210
211 gobject.type_register(ScriptureChaptersWindow)
212
213
214 class ScriptureChapterWindow(windows._base.BasicWindow):
215
216         def __init__(self, app, player, store, node):
217                 windows._base.BasicWindow.__init__(self, app, player, store)
218                 self._node = node
219                 self._playerNode = self._player.node
220                 self._nextSearch = None
221                 self._updateSeek = None
222
223                 self.connect_auto(self._player, "state-change", self._on_player_state_change)
224                 self.connect_auto(self._player, "title-change", self._on_player_title_change)
225                 self.connect_auto(self._player, "error", self._on_player_error)
226
227                 self._loadingBanner = banners.GenericBanner()
228
229                 self._presenter = presenter.StreamPresenter(self._store)
230                 self._presenter.set_context(
231                         self._store.STORE_LOOKUP["scripture_background"],
232                         self._node.title,
233                         self._node.subtitle,
234                 )
235                 self._presenterNavigation = presenter.NavigationBox()
236                 self._presenterNavigation.toplevel.add(self._presenter.toplevel)
237                 self._presenterNavigation.connect("action", self._on_nav_action)
238                 self._presenterNavigation.connect("navigating", self._on_navigating)
239
240                 self._seekbar = hildonize.create_seekbar()
241                 self._seekbar.connect("change-value", self._on_user_seek)
242
243                 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
244                 self._layout.pack_start(self._presenterNavigation.toplevel, True, True)
245                 self._layout.pack_start(self._seekbar, False, False)
246
247                 self._window.set_title(self._node.title)
248
249         def show(self):
250                 windows._base.BasicWindow.show(self)
251                 self._window.show_all()
252                 self._errorBanner.toplevel.hide()
253                 self._loadingBanner.toplevel.hide()
254                 self._set_context(self._player.state)
255                 self._seekbar.hide()
256
257         def jump_to(self, node):
258                 assert self._node is node
259
260         @property
261         def _active(self):
262                 return self._playerNode is self._node
263
264         def _show_loading(self):
265                 animationPath = self._store.STORE_LOOKUP["loading"]
266                 animation = self._store.get_pixbuf_animation_from_store(animationPath)
267                 self._loadingBanner.show(animation, "Loading...")
268
269         def _hide_loading(self):
270                 self._loadingBanner.hide()
271
272         def _set_context(self, state):
273                 if state == self._player.STATE_PLAY:
274                         if self._active:
275                                 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
276                         else:
277                                 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
278                 elif state == self._player.STATE_PAUSE:
279                         self._presenter.set_state(self._store.STORE_LOOKUP["play"])
280                 elif state == self._player.STATE_STOP:
281                         self._presenter.set_state(self._store.STORE_LOOKUP["play"])
282                 else:
283                         _moduleLogger.info("Unhandled player state %s" % state)
284
285         @misc_utils.log_exception(_moduleLogger)
286         def _on_user_seek(self, widget, scroll, value):
287                 self._player.seek(value / 100.0)
288
289         @misc_utils.log_exception(_moduleLogger)
290         def _on_player_update_seek(self):
291                 if self._isDestroyed:
292                         return False
293                 self._seekbar.set_value(self._player.percent_elapsed * 100)
294                 return True
295
296         @misc_utils.log_exception(_moduleLogger)
297         def _on_player_state_change(self, player, newState):
298                 if self._active and self._player.state == self._player.STATE_PLAY:
299                         self._seekbar.show()
300                         assert self._updateSeek is None
301                         self._updateSeek = go_utils.Timeout(self._on_player_update_seek, once=False)
302                         self._updateSeek.start(seconds=1)
303                 else:
304                         self._seekbar.hide()
305                         if self._updateSeek is not None:
306                                 self._updateSeek.cancel()
307                                 self._updateSeek = None
308
309                 if not self._presenterNavigation.is_active():
310                         self._set_context(newState)
311
312         @misc_utils.log_exception(_moduleLogger)
313         def _on_player_title_change(self, player, node):
314                 if not self._active or node in [None, self._node]:
315                         self._playerNode = player.node
316                         return
317                 self._playerNode = player.node
318                 self.emit("jump-to", node)
319                 self._window.destroy()
320
321         @misc_utils.log_exception(_moduleLogger)
322         def _on_player_error(self, player, err, debug):
323                 _moduleLogger.error("%r - %r" % (err, debug))
324
325         @misc_utils.log_exception(_moduleLogger)
326         def _on_navigating(self, widget, navState):
327                 if navState == "clicking":
328                         if self._player.state == self._player.STATE_PLAY:
329                                 if self._active:
330                                         imageName = "pause_pressed"
331                                 else:
332                                         imageName = "play_pressed"
333                         elif self._player.state == self._player.STATE_PAUSE:
334                                 imageName = "play_pressed"
335                         elif self._player.state == self._player.STATE_STOP:
336                                 imageName = "play_pressed"
337                         else:
338                                 _moduleLogger.info("Unhandled player state %s" % self._player.state)
339                 elif navState == "down":
340                         imageName = "home"
341                 elif navState == "up":
342                         if self._player.state == self._player.STATE_PLAY:
343                                 if self._active:
344                                         imageName = "pause"
345                                 else:
346                                         imageName = "play"
347                         elif self._player.state == self._player.STATE_PAUSE:
348                                 imageName = "play"
349                         elif self._player.state == self._player.STATE_STOP:
350                                 imageName = "play"
351                         else:
352                                 _moduleLogger.info("Unhandled player state %s" % self._player.state)
353                 elif navState == "left":
354                         imageName = "next"
355                 elif navState == "right":
356                         imageName = "prev"
357
358                 self._presenter.set_state(self._store.STORE_LOOKUP[imageName])
359
360         @misc_utils.log_exception(_moduleLogger)
361         def _on_nav_action(self, widget, navState):
362                 self._set_context(self._player.state)
363
364                 if navState == "clicking":
365                         if self._player.state == self._player.STATE_PLAY:
366                                 if self._active:
367                                         self._player.pause()
368                                 else:
369                                         self._player.set_piece_by_node(self._node)
370                                         self._player.play()
371                         elif self._player.state == self._player.STATE_PAUSE:
372                                 self._player.play()
373                         elif self._player.state == self._player.STATE_STOP:
374                                 self._player.set_piece_by_node(self._node)
375                                 self._player.play()
376                         else:
377                                 _moduleLogger.info("Unhandled player state %s" % self._player.state)
378                 elif navState == "down":
379                         self.emit("home")
380                         self._window.destroy()
381                 elif navState == "up":
382                         pass
383                 elif navState == "left":
384                         if self._active:
385                                 self._player.next()
386                         else:
387                                 assert self._nextSearch is None
388                                 self._nextSearch = stream_index.AsyncWalker(stream_index.get_next)
389                                 self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error)
390                 elif navState == "right":
391                         if self._active:
392                                 self._player.back()
393                         else:
394                                 assert self._nextSearch is None
395                                 self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous)
396                                 self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error)
397
398         @misc_utils.log_exception(_moduleLogger)
399         def _on_next_node(self, node):
400                 self._nextSearch = None
401                 self.emit("jump-to", node)
402                 self._window.destroy()
403
404         @misc_utils.log_exception(_moduleLogger)
405         def _on_node_search_error(self, e):
406                 self._nextSearch = None
407                 self._errorBanner.push_message(str(e))
408
409
410 gobject.type_register(ScriptureChapterWindow)