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