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