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