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