Switching over to the nav box plus a bug fix
[watersofshiloah] / src / windows.py
1 # @todo Add icons to buttons/rows to indicate that the currently playing track is coming from that
2
3 import ConfigParser
4 import datetime
5 import logging
6
7 import gobject
8 import gtk
9
10 import constants
11 import hildonize
12 import util.misc as misc_utils
13
14 import banners
15 import playcontrol
16 import presenter
17
18
19 _moduleLogger = logging.getLogger(__name__)
20
21
22 class BasicWindow(gobject.GObject):
23
24         __gsignals__ = {
25                 'quit' : (
26                         gobject.SIGNAL_RUN_LAST,
27                         gobject.TYPE_NONE,
28                         (),
29                 ),
30                 'home' : (
31                         gobject.SIGNAL_RUN_LAST,
32                         gobject.TYPE_NONE,
33                         (),
34                 ),
35                 'jump-to' : (
36                         gobject.SIGNAL_RUN_LAST,
37                         gobject.TYPE_NONE,
38                         (gobject.TYPE_PYOBJECT, ),
39                 ),
40                 'rotate' : (
41                         gobject.SIGNAL_RUN_LAST,
42                         gobject.TYPE_NONE,
43                         (gobject.TYPE_BOOLEAN, ),
44                 ),
45                 'fullscreen' : (
46                         gobject.SIGNAL_RUN_LAST,
47                         gobject.TYPE_NONE,
48                         (gobject.TYPE_BOOLEAN, ),
49                 ),
50         }
51
52         def __init__(self, player, store):
53                 gobject.GObject.__init__(self)
54                 self._isDestroyed = False
55
56                 self._player = player
57                 self._store = store
58
59                 self._clipboard = gtk.clipboard_get()
60                 self._windowInFullscreen = False
61
62                 self._errorBanner = banners.StackingBanner()
63
64                 self._layout = gtk.VBox()
65                 self._layout.pack_start(self._errorBanner.toplevel, False, True)
66
67                 self._window = gtk.Window()
68                 self._window.add(self._layout)
69                 self._window = hildonize.hildonize_window(self, self._window)
70
71                 self._window.set_icon(self._store.get_pixbuf_from_store(self._store.STORE_LOOKUP["icon"]))
72                 self._window.connect("key-press-event", self._on_key_press)
73                 self._window.connect("window-state-event", self._on_window_state_change)
74                 self._window.connect("destroy", self._on_destroy)
75
76         @property
77         def window(self):
78                 return self._window
79
80         def show(self):
81                 self._window.show_all()
82
83         def save_settings(self, config, sectionName):
84                 config.add_section(sectionName)
85                 config.set(sectionName, "fullscreen", str(self._windowInFullscreen))
86
87         def load_settings(self, config, sectionName):
88                 try:
89                         self._windowInFullscreen = config.getboolean(sectionName, "fullscreen")
90                 except ConfigParser.NoSectionError, e:
91                         _moduleLogger.info(
92                                 "Settings file %s is missing section %s" % (
93                                         constants._user_settings_,
94                                         e.section,
95                                 )
96                         )
97
98                 if self._windowInFullscreen:
99                         self._window.fullscreen()
100                 else:
101                         self._window.unfullscreen()
102
103         @misc_utils.log_exception(_moduleLogger)
104         def _on_destroy(self, *args):
105                 self._isDestroyed = True
106
107         @misc_utils.log_exception(_moduleLogger)
108         def _on_window_state_change(self, widget, event, *args):
109                 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
110                         self._windowInFullscreen = True
111                 else:
112                         self._windowInFullscreen = False
113                 self.emit("fullscreen", self._windowInFullscreen)
114
115         @misc_utils.log_exception(_moduleLogger)
116         def _on_key_press(self, widget, event, *args):
117                 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
118                 isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
119                 if (
120                         event.keyval == gtk.keysyms.F6 or
121                         event.keyval in RETURN_TYPES and isCtrl
122                 ):
123                         # The "Full screen" hardware key has been pressed
124                         if self._windowInFullscreen:
125                                 self._window.unfullscreen ()
126                         else:
127                                 self._window.fullscreen ()
128                         return True
129                 elif (
130                         event.keyval in (gtk.keysyms.w, ) and
131                         event.get_state() & gtk.gdk.CONTROL_MASK
132                 ):
133                         self._window.destroy()
134                 elif (
135                         event.keyval in (gtk.keysyms.q, ) and
136                         event.get_state() & gtk.gdk.CONTROL_MASK
137                 ):
138                         self.emit("quit")
139                 elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
140                         with open(constants._user_logpath_, "r") as f:
141                                 logLines = f.xreadlines()
142                                 log = "".join(logLines)
143                                 self._clipboard.set_text(str(log))
144                         return True
145
146         @misc_utils.log_exception(_moduleLogger)
147         def _on_home(self, *args):
148                 self.emit("home")
149                 self._window.destroy()
150
151         @misc_utils.log_exception(_moduleLogger)
152         def _on_jump(self, source, node):
153                 _moduleLogger.error("Jump is not implemented")
154                 self.emit("jump-to", node)
155                 self._window.destroy()
156
157         @misc_utils.log_exception(_moduleLogger)
158         def _on_quit(self, *args):
159                 self.emit("quit")
160                 self._window.destroy()
161
162
163 class SourceSelector(BasicWindow):
164
165         def __init__(self, player, store, index):
166                 BasicWindow.__init__(self, player, store)
167                 self._languages = []
168                 self._index = index
169
170                 self._loadingBanner = banners.GenericBanner()
171
172                 self._radioButton = self._create_button("radio", "Radio")
173                 self._radioButton.connect("clicked", self._on_source_selected, RadioWindow, "radio")
174                 self._radioWrapper = gtk.VBox()
175                 self._radioWrapper.pack_start(self._radioButton, False, True)
176
177                 self._conferenceButton = self._create_button("conferences", "Conferences")
178                 self._conferenceButton.connect("clicked", self._on_source_selected, ConferencesWindow, "conferences")
179                 self._conferenceWrapper = gtk.VBox()
180                 self._conferenceWrapper.pack_start(self._conferenceButton, False, True)
181
182                 self._magazineButton = self._create_button("magazines", "Magazines")
183                 #self._magazineButton.connect("clicked", self._on_source_selected)
184                 self._magazineWrapper = gtk.VBox()
185                 self._magazineWrapper.pack_start(self._magazineButton, False, True)
186
187                 self._scriptureButton = self._create_button("scriptures", "Scriptures")
188                 #self._scriptureButton.connect("clicked", self._on_source_selected)
189                 self._scriptureWrapper = gtk.VBox()
190                 self._scriptureWrapper.pack_start(self._scriptureButton, False, True)
191
192                 self._buttonLayout = gtk.VButtonBox()
193                 self._buttonLayout.set_layout(gtk.BUTTONBOX_SPREAD)
194                 self._buttonLayout.pack_start(self._radioWrapper, True, True)
195                 self._buttonLayout.pack_start(self._conferenceWrapper, True, True)
196                 self._buttonLayout.pack_start(self._magazineWrapper, True, True)
197                 self._buttonLayout.pack_start(self._scriptureWrapper, True, True)
198
199                 self._playcontrol = playcontrol.NavControl(player, store)
200
201                 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
202                 self._layout.pack_start(self._buttonLayout, True, True)
203                 self._layout.pack_start(self._playcontrol.toplevel, False, True)
204
205                 self._window.set_title(constants.__pretty_app_name__)
206
207         def show(self):
208                 BasicWindow.show(self)
209
210                 self._errorBanner.toplevel.hide()
211                 self._playcontrol.toplevel.hide()
212
213                 self._refresh()
214
215         def _show_loading(self):
216                 animationPath = self._store.STORE_LOOKUP["loading"]
217                 animation = self._store.get_pixbuf_animation_from_store(animationPath)
218                 self._loadingBanner.show(animation, "Loading...")
219                 self._buttonLayout.set_sensitive(False)
220
221         def _hide_loading(self):
222                 self._loadingBanner.hide()
223                 self._buttonLayout.set_sensitive(True)
224
225         def _refresh(self):
226                 self._show_loading()
227                 self._index.get_languages(self._on_languages, self._on_error)
228
229         def _create_button(self, icon, message):
230                 image = self._store.get_image_from_store(self._store.STORE_LOOKUP[icon])
231
232                 label = gtk.Label()
233                 label.set_text(message)
234
235                 buttonLayout = gtk.HBox(False, 5)
236                 buttonLayout.pack_start(image, False, False)
237                 buttonLayout.pack_start(label, False, True)
238                 button = gtk.Button()
239                 button.add(buttonLayout)
240
241                 return button
242
243         @misc_utils.log_exception(_moduleLogger)
244         def _on_languages(self, languages):
245                 self._hide_loading()
246                 self._languages = list(languages)
247
248         @misc_utils.log_exception(_moduleLogger)
249         def _on_error(self, exception):
250                 self._hide_loading()
251                 self._errorBanner.push_message(str(exception))
252
253         @misc_utils.log_exception(_moduleLogger)
254         def _on_source_selected(self, widget, Source, nodeName):
255                 node = self._index.get_source(nodeName, self._languages[0]["id"])
256                 sourceWindow = Source(self._player, self._store, node)
257                 sourceWindow.window.set_modal(True)
258                 sourceWindow.window.set_transient_for(self._window)
259                 sourceWindow.window.set_default_size(*self._window.get_size())
260                 sourceWindow.connect("quit", self._on_quit)
261                 sourceWindow.show()
262
263
264 gobject.type_register(SourceSelector)
265
266
267 class RadioWindow(BasicWindow):
268
269         def __init__(self, player, store, node):
270                 BasicWindow.__init__(self, player, store)
271                 self._node = node
272                 self._childNode = None
273
274                 self._player.connect("state-change", self._on_player_state_change)
275                 self._player.connect("title-change", self._on_player_title_change)
276
277                 self._loadingBanner = banners.GenericBanner()
278
279                 headerPath = self._store.STORE_LOOKUP["radio_header"]
280                 self._header = self._store.get_image_from_store(headerPath)
281                 self._headerNavigation = presenter.NavigationBox()
282                 self._headerNavigation.toplevel.add(self._header)
283                 self._headerNavigation.connect("action", self._on_nav_action)
284                 self._headerNavigation.connect("navigating", self._on_navigating)
285
286                 self._programmingModel = gtk.ListStore(
287                         gobject.TYPE_STRING,
288                         gobject.TYPE_STRING,
289                 )
290
291                 textrenderer = gtk.CellRendererText()
292                 timeColumn = gtk.TreeViewColumn("Time")
293                 timeColumn.pack_start(textrenderer, expand=True)
294                 timeColumn.add_attribute(textrenderer, "text", 0)
295
296                 textrenderer = gtk.CellRendererText()
297                 titleColumn = gtk.TreeViewColumn("Program")
298                 titleColumn.pack_start(textrenderer, expand=True)
299                 titleColumn.add_attribute(textrenderer, "text", 1)
300
301                 self._treeView = gtk.TreeView()
302                 self._treeView.set_headers_visible(False)
303                 self._treeView.set_model(self._programmingModel)
304                 self._treeView.append_column(timeColumn)
305                 self._treeView.append_column(titleColumn)
306                 self._treeView.get_selection().connect("changed", self._on_row_changed)
307
308                 self._treeScroller = gtk.ScrolledWindow()
309                 self._treeScroller.add(self._treeView)
310                 self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
311
312                 self._presenter = presenter.StreamMiniPresenter(self._store)
313                 if self._player.state == "play":
314                         self._presenter.set_state(self._store.STORE_LOOKUP["play"])
315                 else:
316                         self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
317                 self._presenterNavigation = presenter.NavigationBox()
318                 self._presenterNavigation.toplevel.add(self._presenter.toplevel)
319                 self._presenterNavigation.connect("action", self._on_nav_action)
320                 self._presenterNavigation.connect("navigating", self._on_navigating)
321
322                 self._radioLayout = gtk.VBox(False)
323                 self._radioLayout.pack_start(self._headerNavigation.toplevel, False, False)
324                 self._radioLayout.pack_start(self._treeScroller, True, True)
325                 self._radioLayout.pack_start(self._presenterNavigation.toplevel, False, True)
326
327                 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
328                 self._layout.pack_start(self._radioLayout, True, True)
329
330                 self._window.set_title("Radio")
331                 self._dateShown = datetime.datetime.now()
332
333         def show(self):
334                 BasicWindow.show(self)
335
336                 self._errorBanner.toplevel.hide()
337                 self._loadingBanner.toplevel.hide()
338
339                 self._refresh()
340
341         def _show_loading(self):
342                 animationPath = self._store.STORE_LOOKUP["loading"]
343                 animation = self._store.get_pixbuf_animation_from_store(animationPath)
344                 self._loadingBanner.show(animation, "Loading...")
345
346         def _hide_loading(self):
347                 self._loadingBanner.hide()
348
349         def _refresh(self):
350                 self._show_loading()
351                 self._programmingModel.clear()
352                 self._node.get_children(
353                         self._on_channels,
354                         self._on_load_error,
355                 )
356
357         def _get_current_row(self):
358                 nowTime = self._dateShown.strftime("%H:%M:%S")
359                 i = 0
360                 for i, row in enumerate(self._programmingModel):
361                         if nowTime < row[0]:
362                                 if i == 0:
363                                         return 0
364                                 else:
365                                         return i - 1
366                 else:
367                         return i
368
369         @misc_utils.log_exception(_moduleLogger)
370         def _on_player_state_change(self, player, newState):
371                 if self._headerNavigation.is_active() or self._presenterNavigation.is_active():
372                         return
373
374                 if newState == "play":
375                         self._presenter.set_state(self._store.STORE_LOOKUP["play"])
376                 elif newState == "pause":
377                         self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
378                 else:
379                         _moduleLogger.info("Unhandled player state %s" % newState)
380                         self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
381
382         @misc_utils.log_exception(_moduleLogger)
383         def _on_player_title_change(self, player, newState):
384                 _moduleLogger.info("Player title magically changed to %s" % player.title)
385                 self._destroy()
386
387         @misc_utils.log_exception(_moduleLogger)
388         def _on_navigating(self, widget, navState):
389                 if navState == "clicking":
390                         if self._player.state == "play":
391                                 imageName = "pause"
392                         else:
393                                 imageName = "play"
394                 elif navState == "down":
395                         imageName = "home"
396                 elif navState == "up":
397                         imageName = "play"
398                 elif navState == "left":
399                         imageName = "play"
400                 elif navState == "right":
401                         imageName = "play"
402
403                 self._presenter.set_state(self._store.STORE_LOOKUP[imageName])
404
405         @misc_utils.log_exception(_moduleLogger)
406         def _on_nav_action(self, widget, navState):
407                 if self._player.state == "play":
408                         self._presenter.set_state(self._store.STORE_LOOKUP["play"])
409                 else:
410                         self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
411
412                 if navState == "clicking":
413                         if self._player.state == "play":
414                                 self._player.pause()
415                         else:
416                                 self._player.play()
417                 elif navState == "down":
418                         self.window.destroy()
419                 elif navState == "up":
420                         pass
421                 elif navState == "left":
422                         self._dateShown += datetime.timedelta(days=1)
423                         self._refresh()
424                 elif navState == "right":
425                         self._dateShown -= datetime.timedelta(days=1)
426                         self._refresh()
427
428         @misc_utils.log_exception(_moduleLogger)
429         def _on_channels(self, channels):
430                 if self._isDestroyed:
431                         _moduleLogger.info("Download complete but window destroyed")
432                         return
433
434                 channels = channels
435                 if 1 < len(channels):
436                         _moduleLogger.warning("More channels now available!")
437                 self._childNode = channels[0]
438                 self._childNode.get_programming(
439                         self._dateShown,
440                         self._on_channel,
441                         self._on_load_error,
442                 )
443
444         @misc_utils.log_exception(_moduleLogger)
445         def _on_channel(self, programs):
446                 if self._isDestroyed:
447                         _moduleLogger.info("Download complete but window destroyed")
448                         return
449
450                 self._hide_loading()
451                 for program in programs:
452                         row = program["time"], program["title"]
453                         self._programmingModel.append(row)
454
455                 path = (self._get_current_row(), )
456                 self._treeView.scroll_to_cell(path)
457                 self._treeView.get_selection().select_path(path)
458
459         @misc_utils.log_exception(_moduleLogger)
460         def _on_load_error(self, exception):
461                 self._hide_loading()
462                 self._errorBanner.push_message(str(exception))
463
464         @misc_utils.log_exception(_moduleLogger)
465         def _on_row_changed(self, selection):
466                 if len(self._programmingModel) == 0:
467                         return
468
469                 rowIndex = self._get_current_row()
470                 path = (rowIndex, )
471                 if not selection.path_is_selected(path):
472                         # Undo the user's changing of the selection
473                         selection.select_path(path)
474
475
476 gobject.type_register(RadioWindow)
477
478
479 class ListWindow(BasicWindow):
480
481         def __init__(self, player, store, node):
482                 BasicWindow.__init__(self, player, store)
483                 self._node = node
484
485                 self._loadingBanner = banners.GenericBanner()
486
487                 modelTypes, columns = zip(*self._get_columns())
488
489                 self._model = gtk.ListStore(*modelTypes)
490
491                 self._treeView = gtk.TreeView()
492                 self._treeView.connect("row-activated", self._on_row_activated)
493                 self._treeView.set_headers_visible(False)
494                 self._treeView.set_model(self._model)
495                 for column in columns:
496                         if column is not None:
497                                 self._treeView.append_column(column)
498
499                 self._treeScroller = gtk.ScrolledWindow()
500                 self._treeScroller.add(self._treeView)
501                 self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
502
503                 self._playcontrol = playcontrol.NavControl(self._player, self._store)
504                 self._playcontrol.connect("home", self._on_home)
505                 self._playcontrol.connect("jump-to", self._on_jump)
506
507                 self._contentLayout = gtk.VBox(False)
508                 self._contentLayout.pack_start(self._treeScroller, True, True)
509                 self._contentLayout.pack_start(self._playcontrol.toplevel, False, True)
510
511                 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
512                 self._layout.pack_start(self._contentLayout, True, True)
513
514         def show(self):
515                 BasicWindow.show(self)
516
517                 self._errorBanner.toplevel.hide()
518                 self._loadingBanner.toplevel.hide()
519
520                 self._refresh()
521                 self._playcontrol.refresh()
522
523         @classmethod
524         def _get_columns(cls):
525                 raise NotImplementedError("")
526
527         def _get_current_row(self):
528                 raise NotImplementedError("")
529
530         @misc_utils.log_exception(_moduleLogger)
531         def _on_row_activated(self, view, path, column):
532                 raise NotImplementedError("")
533
534         def _show_loading(self):
535                 animationPath = self._store.STORE_LOOKUP["loading"]
536                 animation = self._store.get_pixbuf_animation_from_store(animationPath)
537                 self._loadingBanner.show(animation, "Loading...")
538
539         def _hide_loading(self):
540                 self._loadingBanner.hide()
541
542         def _refresh(self):
543                 self._show_loading()
544                 self._model.clear()
545
546         def _select_row(self):
547                 path = (self._get_current_row(), )
548                 self._treeView.scroll_to_cell(path)
549                 self._treeView.get_selection().select_path(path)
550
551
552 class ConferencesWindow(ListWindow):
553
554         def __init__(self, player, store, node):
555                 ListWindow.__init__(self, player, store, node)
556                 self._window.set_title("Conferences")
557
558         @classmethod
559         def _get_columns(cls):
560                 yield gobject.TYPE_PYOBJECT, None
561
562                 textrenderer = gtk.CellRendererText()
563                 column = gtk.TreeViewColumn("Date")
564                 column.pack_start(textrenderer, expand=True)
565                 column.add_attribute(textrenderer, "text", 1)
566                 yield gobject.TYPE_STRING, column
567
568                 textrenderer = gtk.CellRendererText()
569                 column = gtk.TreeViewColumn("Conference")
570                 column.pack_start(textrenderer, expand=True)
571                 column.add_attribute(textrenderer, "text", 2)
572                 yield gobject.TYPE_STRING, column
573
574         def _get_current_row(self):
575                 # @todo Not implemented yet
576                 return 0
577
578         def _refresh(self):
579                 ListWindow._refresh(self)
580                 self._node.get_children(
581                         self._on_conferences,
582                         self._on_error,
583                 )
584
585         @misc_utils.log_exception(_moduleLogger)
586         def _on_conferences(self, programs):
587                 if self._isDestroyed:
588                         _moduleLogger.info("Download complete but window destroyed")
589                         return
590
591                 self._hide_loading()
592                 for programNode in programs:
593                         program = programNode.get_properties()
594                         row = programNode, program["title"], program["full_title"]
595                         self._model.append(row)
596
597                 path = (self._get_current_row(), )
598                 self._treeView.scroll_to_cell(path)
599                 self._treeView.get_selection().select_path(path)
600
601         @misc_utils.log_exception(_moduleLogger)
602         def _on_error(self, exception):
603                 self._hide_loading()
604                 self._errorBanner.push_message(str(exception))
605
606         @misc_utils.log_exception(_moduleLogger)
607         def _on_row_activated(self, view, path, column):
608                 itr = self._model.get_iter(path)
609                 node = self._model.get_value(itr, 0)
610
611                 sessionsWindow = ConferenceSessionsWindow(self._player, self._store, node)
612                 sessionsWindow.window.set_modal(True)
613                 sessionsWindow.window.set_transient_for(self._window)
614                 sessionsWindow.window.set_default_size(*self._window.get_size())
615                 sessionsWindow.connect("quit", self._on_quit)
616                 sessionsWindow.connect("home", self._on_home)
617                 sessionsWindow.show()
618
619
620 gobject.type_register(ConferencesWindow)
621
622
623 class ConferenceSessionsWindow(ListWindow):
624
625         def __init__(self, player, store, node):
626                 ListWindow.__init__(self, player, store, node)
627                 self._window.set_title("Sessions")
628
629         @classmethod
630         def _get_columns(cls):
631                 yield gobject.TYPE_PYOBJECT, None
632
633                 textrenderer = gtk.CellRendererText()
634                 column = gtk.TreeViewColumn("Session")
635                 column.pack_start(textrenderer, expand=True)
636                 column.add_attribute(textrenderer, "text", 1)
637                 yield gobject.TYPE_STRING, column
638
639         def _get_current_row(self):
640                 # @todo Not implemented yet
641                 return 0
642
643         def _refresh(self):
644                 ListWindow._refresh(self)
645                 self._node.get_children(
646                         self._on_conference_sessions,
647                         self._on_error,
648                 )
649
650         @misc_utils.log_exception(_moduleLogger)
651         def _on_conference_sessions(self, programs):
652                 if self._isDestroyed:
653                         _moduleLogger.info("Download complete but window destroyed")
654                         return
655
656                 self._hide_loading()
657                 for programNode in programs:
658                         program = programNode.get_properties()
659                         row = programNode, program["title"]
660                         self._model.append(row)
661
662                 path = (self._get_current_row(), )
663                 self._treeView.scroll_to_cell(path)
664                 self._treeView.get_selection().select_path(path)
665
666         @misc_utils.log_exception(_moduleLogger)
667         def _on_error(self, exception):
668                 self._hide_loading()
669                 self._errorBanner.push_message(str(exception))
670
671         @misc_utils.log_exception(_moduleLogger)
672         def _on_row_activated(self, view, path, column):
673                 itr = self._model.get_iter(path)
674                 node = self._model.get_value(itr, 0)
675
676                 sessionsWindow = ConferenceTalksWindow(self._player, self._store, node)
677                 sessionsWindow.window.set_modal(True)
678                 sessionsWindow.window.set_transient_for(self._window)
679                 sessionsWindow.window.set_default_size(*self._window.get_size())
680                 sessionsWindow.connect("quit", self._on_quit)
681                 sessionsWindow.connect("home", self._on_home)
682                 sessionsWindow.show()
683
684
685 gobject.type_register(ConferenceSessionsWindow)
686
687
688 class ConferenceTalksWindow(ListWindow):
689
690         def __init__(self, player, store, node):
691                 ListWindow.__init__(self, player, store, node)
692                 self._window.set_title("Talks")
693
694         @classmethod
695         def _get_columns(cls):
696                 yield gobject.TYPE_PYOBJECT, None
697
698                 textrenderer = gtk.CellRendererText()
699                 column = gtk.TreeViewColumn("Talk")
700                 column.pack_start(textrenderer, expand=True)
701                 column.add_attribute(textrenderer, "text", 1)
702                 yield gobject.TYPE_STRING, column
703
704         def _get_current_row(self):
705                 # @todo Not implemented yet
706                 return 0
707
708         def _refresh(self):
709                 ListWindow._refresh(self)
710                 self._node.get_children(
711                         self._on_conference_talks,
712                         self._on_error,
713                 )
714
715         @misc_utils.log_exception(_moduleLogger)
716         def _on_conference_talks(self, programs):
717                 if self._isDestroyed:
718                         _moduleLogger.info("Download complete but window destroyed")
719                         return
720
721                 self._hide_loading()
722                 for programNode in programs:
723                         program = programNode.get_properties()
724                         row = programNode, "%s\n%s" % (program["title"], program["speaker"])
725                         self._model.append(row)
726
727                 path = (self._get_current_row(), )
728                 self._treeView.scroll_to_cell(path)
729                 self._treeView.get_selection().select_path(path)
730
731         @misc_utils.log_exception(_moduleLogger)
732         def _on_error(self, exception):
733                 self._hide_loading()
734                 self._errorBanner.push_message(str(exception))
735
736         @misc_utils.log_exception(_moduleLogger)
737         def _on_row_activated(self, view, path, column):
738                 itr = self._model.get_iter(path)
739                 node = self._model.get_value(itr, 0)
740
741                 sessionsWindow = ConferenceTalkWindow(self._player, self._store, node)
742                 sessionsWindow.window.set_modal(True)
743                 sessionsWindow.window.set_transient_for(self._window)
744                 sessionsWindow.window.set_default_size(*self._window.get_size())
745                 sessionsWindow.connect("quit", self._on_quit)
746                 sessionsWindow.connect("home", self._on_home)
747                 sessionsWindow.show()
748
749
750 gobject.type_register(ConferenceTalksWindow)
751
752
753 class ConferenceTalkWindow(BasicWindow):
754
755         def __init__(self, player, store, node):
756                 BasicWindow.__init__(self, player, store)
757
758                 self._player.connect("state-change", self._on_player_state_change)
759                 self._player.connect("title-change", self._on_player_title_change)
760
761                 self._loadingBanner = banners.GenericBanner()
762
763                 self._presenter = presenter.StreamPresenter(self._store)
764                 self._presenterNavigation = presenter.NavigationBox()
765                 self._presenterNavigation.toplevel.add(self._presenter.toplevel)
766                 self._presenterNavigation.connect("action", self._on_nav_action)
767                 self._presenterNavigation.connect("navigating", self._on_navigating)
768
769                 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
770                 self._layout.pack_start(self._presenterNavigation.toplevel, True, True)
771
772                 self._window.set_title("Talk")
773
774         def show(self):
775                 BasicWindow.show(self)
776                 self._window.show_all()
777                 self._errorBanner.toplevel.hide()
778                 self._loadingBanner.toplevel.hide()
779
780                 self._presenter.set_context(
781                         self._store.STORE_LOOKUP["conference_background"],
782                         self._player.title,
783                         self._player.subtitle,
784                 )
785                 if self._player.state == "play":
786                         self._presenter.set_state(self._store.STORE_LOOKUP["play"])
787                 else:
788                         self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
789
790         def _show_loading(self):
791                 animationPath = self._store.STORE_LOOKUP["loading"]
792                 animation = self._store.get_pixbuf_animation_from_store(animationPath)
793                 self._loadingBanner.show(animation, "Loading...")
794
795         def _hide_loading(self):
796                 self._loadingBanner.hide()
797
798         @misc_utils.log_exception(_moduleLogger)
799         def _on_player_state_change(self, player, newState):
800                 if self._presenterNavigation.is_active():
801                         return
802
803                 if newState == "play":
804                         self._presenter.set_state(self._store.STORE_LOOKUP["play"])
805                 elif newState == "pause":
806                         self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
807                 else:
808                         _moduleLogger.info("Unhandled player state %s" % newState)
809                         self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
810
811         @misc_utils.log_exception(_moduleLogger)
812         def _on_player_title_change(self, player, newState):
813                 self._presenter.set_context(
814                         self._store.STORE_LOOKUP["conference_background"],
815                         self._player.title,
816                         self._player.subtitle,
817                 )
818
819         @misc_utils.log_exception(_moduleLogger)
820         def _on_navigating(self, widget, navState):
821                 if navState == "clicking":
822                         if self._player.state == "play":
823                                 imageName = "pause"
824                         else:
825                                 imageName = "play"
826                 elif navState == "down":
827                         imageName = "home"
828                 elif navState == "up":
829                         imageName = "play"
830                 elif navState == "left":
831                         imageName = "next"
832                 elif navState == "right":
833                         imageName = "prev"
834
835                 self._presenter.set_state(self._store.STORE_LOOKUP[imageName])
836
837         @misc_utils.log_exception(_moduleLogger)
838         def _on_nav_action(self, widget, navState):
839                 if self._player.state == "play":
840                         self._presenter.set_state(self._store.STORE_LOOKUP["play"])
841                 else:
842                         self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
843
844                 if navState == "clicking":
845                         if self._player.state == "play":
846                                 self._player.pause()
847                         else:
848                                 self._player.play()
849                 elif navState == "down":
850                         self.emit("home")
851                         self._window.destroy()
852                 elif navState == "up":
853                         pass
854                 elif navState == "left":
855                         self._player.next()
856                 elif navState == "right":
857                         self._player.back()
858
859
860 gobject.type_register(ConferenceTalkWindow)