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