1 # @todo Add icons to buttons/rows to indicate that the currently playing track is coming from that
12 import util.misc as misc_utils
19 _moduleLogger = logging.getLogger(__name__)
22 class BasicWindow(gobject.GObject):
26 gobject.SIGNAL_RUN_LAST,
31 gobject.SIGNAL_RUN_LAST,
36 gobject.SIGNAL_RUN_LAST,
38 (gobject.TYPE_BOOLEAN, ),
41 gobject.SIGNAL_RUN_LAST,
43 (gobject.TYPE_BOOLEAN, ),
47 def __init__(self, player, store, index):
48 gobject.GObject.__init__(self)
49 self._isDestroyed = False
55 self._clipboard = gtk.clipboard_get()
56 self._windowInFullscreen = False
58 self._errorBanner = banners.StackingBanner()
60 self._layout = gtk.VBox()
61 self._layout.pack_start(self._errorBanner.toplevel, False, True)
63 self._window = gtk.Window()
64 self._window.add(self._layout)
65 self._window = hildonize.hildonize_window(self, self._window)
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)
76 def save_settings(self, config, sectionName):
77 config.add_section(sectionName)
78 config.set(sectionName, "fullscreen", str(self._windowInFullscreen))
80 def load_settings(self, config, sectionName):
82 self._windowInFullscreen = config.getboolean(sectionName, "fullscreen")
83 except ConfigParser.NoSectionError, e:
85 "Settings file %s is missing section %s" % (
86 constants._user_settings_,
91 if self._windowInFullscreen:
92 self._window.fullscreen()
94 self._window.unfullscreen()
96 @misc_utils.log_exception(_moduleLogger)
97 def _on_destroy(self, *args):
98 self._isDestroyed = True
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
105 self._windowInFullscreen = False
106 self.emit("fullscreen", self._windowInFullscreen)
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)
113 event.keyval == gtk.keysyms.F6 or
114 event.keyval in RETURN_TYPES and isCtrl
116 # The "Full screen" hardware key has been pressed
117 if self._windowInFullscreen:
118 self._window.unfullscreen ()
120 self._window.fullscreen ()
123 event.keyval in (gtk.keysyms.w, ) and
124 event.get_state() & gtk.gdk.CONTROL_MASK
126 self._window.destroy()
128 event.keyval in (gtk.keysyms.q, ) and
129 event.get_state() & gtk.gdk.CONTROL_MASK
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))
139 @misc_utils.log_exception(_moduleLogger)
140 def _on_home(self, *args):
142 self._window.destroy()
144 @misc_utils.log_exception(_moduleLogger)
145 def _on_quit(self, *args):
147 self._window.destroy()
150 class SourceSelector(BasicWindow):
152 def __init__(self, player, store, index):
155 BasicWindow.__init__(self, player, store, index)
157 self._loadingBanner = banners.GenericBanner()
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)
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)
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)
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)
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)
186 self._playcontrol = playcontrol.PlayControl(player, store)
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)
192 self._window.set_title(constants.__pretty_app_name__)
193 self._window.show_all()
194 self._errorBanner.toplevel.hide()
195 self._playcontrol.toplevel.hide()
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)
205 def _hide_loading(self):
206 self._loadingBanner.hide()
207 self._buttonLayout.set_sensitive(True)
211 self._index.download(
217 def _create_button(self, icon, message):
218 image = self._store.get_image_from_store(self._store.STORE_LOOKUP[icon])
221 label.set_text(message)
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)
231 @misc_utils.log_exception(_moduleLogger)
232 def _on_languages(self, languages):
234 self._languages = list(languages)
236 @misc_utils.log_exception(_moduleLogger)
237 def _on_error(self, exception):
239 self._errorBanner.push_message(exception)
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)
250 gobject.type_register(SourceSelector)
253 class RadioWindow(BasicWindow):
255 def __init__(self, player, store, index, languageId):
256 BasicWindow.__init__(self, player, store, index)
258 self._player.connect("state-change", self._on_player_state_change)
259 self._player.connect("title-change", self._on_player_title_change)
261 self._loadingBanner = banners.GenericBanner()
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)
270 self._programmingModel = gtk.ListStore(
275 textrenderer = gtk.CellRendererText()
276 timeColumn = gtk.TreeViewColumn("Time")
277 timeColumn.pack_start(textrenderer, expand=True)
278 timeColumn.add_attribute(textrenderer, "text", 0)
280 textrenderer = gtk.CellRendererText()
281 titleColumn = gtk.TreeViewColumn("Program")
282 titleColumn.pack_start(textrenderer, expand=True)
283 titleColumn.add_attribute(textrenderer, "text", 1)
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)
292 self._treeScroller = gtk.ScrolledWindow()
293 self._treeScroller.add(self._treeView)
294 self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
296 self._presenter = presenter.StreamMiniPresenter(self._store)
297 if self._player.state == "play":
298 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
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)
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)
311 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
312 self._layout.pack_start(self._radioLayout, True, True)
314 self._window.set_title("Radio")
315 self._window.show_all()
316 self._errorBanner.toplevel.hide()
317 self._loadingBanner.toplevel.hide()
319 self._dateShown = datetime.datetime.now()
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...")
327 def _hide_loading(self):
328 self._loadingBanner.hide()
332 self._programmingModel.clear()
333 self._index.download(
334 "get_radio_channels",
339 def _get_current_row(self):
340 nowTime = self._dateShown.strftime("%H:%M:%S")
342 for i, row in enumerate(self._programmingModel):
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():
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"])
361 _moduleLogger.info("Unhandled player state %s" % newState)
362 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
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)
369 @misc_utils.log_exception(_moduleLogger)
370 def _on_navigating(self, widget, navState):
371 if navState == "clicking":
372 if self._player.state == "play":
376 elif navState == "down":
378 elif navState == "up":
380 elif navState == "left":
382 elif navState == "right":
385 self._presenter.set_state(self._store.STORE_LOOKUP[imageName])
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"])
392 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
394 if navState == "clicking":
395 if self._player.state == "play":
399 elif navState == "down":
400 self.window.destroy()
401 elif navState == "up":
403 elif navState == "left":
404 self._dateShown += datetime.timedelta(days=1)
406 elif navState == "right":
407 self._dateShown -= datetime.timedelta(days=1)
410 @misc_utils.log_exception(_moduleLogger)
411 def _on_channels(self, channels):
412 if self._isDestroyed:
413 _moduleLogger.info("Download complete but window destroyed")
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",
428 @misc_utils.log_exception(_moduleLogger)
429 def _on_channel(self, programs):
430 if self._isDestroyed:
431 _moduleLogger.info("Download complete but window destroyed")
435 for program in programs:
436 row = program["time"], program["title"]
437 self._programmingModel.append(row)
439 path = (self._get_current_row(), )
440 self._treeView.scroll_to_cell(path)
441 self._treeView.get_selection().select_path(path)
443 @misc_utils.log_exception(_moduleLogger)
444 def _on_load_error(self, exception):
446 self._errorBanner.push_message(exception)
448 @misc_utils.log_exception(_moduleLogger)
449 def _on_row_changed(self, selection):
450 if len(self._programmingModel) == 0:
453 rowIndex = self._get_current_row()
455 if not selection.path_is_selected(path):
456 # Undo the user's changing of the selection
457 selection.select_path(path)
460 gobject.type_register(RadioWindow)
463 class ListWindow(BasicWindow):
465 def __init__(self, player, store, index):
466 BasicWindow.__init__(self, player, store, index)
468 self._loadingBanner = banners.GenericBanner()
470 modelTypes, columns = zip(*self._get_columns())
472 self._model = gtk.ListStore(*modelTypes)
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)
482 self._treeScroller = gtk.ScrolledWindow()
483 self._treeScroller.add(self._treeView)
484 self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
486 self._playcontrol = playcontrol.PlayControl(self._player, self._store)
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)
492 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
493 self._layout.pack_start(self._contentLayout, True, True)
495 self._window.show_all()
496 self._errorBanner.toplevel.hide()
497 self._loadingBanner.toplevel.hide()
500 self._playcontrol.refresh()
503 def _get_columns(cls):
504 raise NotImplementedError("")
506 def _get_current_row(self):
507 raise NotImplementedError("")
509 @misc_utils.log_exception(_moduleLogger)
510 def _on_row_activated(self, view, path, column):
511 raise NotImplementedError("")
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...")
518 def _hide_loading(self):
519 self._loadingBanner.hide()
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)
531 class ConferencesWindow(ListWindow):
533 def __init__(self, player, store, index, languageId):
534 self._languageId = languageId
536 ListWindow.__init__(self, player, store, index)
537 self._window.set_title("Conferences")
540 def _get_columns(cls):
541 yield gobject.TYPE_STRING, None
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
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
555 def _get_current_row(self):
556 # @todo Not implemented yet
560 ListWindow._refresh(self)
561 self._index.download(
563 self._on_conferences,
568 @misc_utils.log_exception(_moduleLogger)
569 def _on_conferences(self, programs):
570 if self._isDestroyed:
571 _moduleLogger.info("Download complete but window destroyed")
575 for program in programs:
576 row = program["id"], program["title"], program["full_title"]
577 self._model.append(row)
579 path = (self._get_current_row(), )
580 self._treeView.scroll_to_cell(path)
581 self._treeView.get_selection().select_path(path)
583 @misc_utils.log_exception(_moduleLogger)
584 def _on_error(self, exception):
586 self._errorBanner.push_message(exception)
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)
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)
601 gobject.type_register(ConferencesWindow)
604 class ConferenceSessionsWindow(ListWindow):
606 def __init__(self, player, store, index, conferenceId):
607 self._conferenceId = conferenceId
609 ListWindow.__init__(self, player, store, index)
610 self._window.set_title("Sessions")
613 def _get_columns(cls):
614 yield gobject.TYPE_STRING, None
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
622 def _get_current_row(self):
623 # @todo Not implemented yet
627 ListWindow._refresh(self)
628 self._index.download(
629 "get_conference_sessions",
630 self._on_conference_sessions,
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")
642 for program in programs:
643 row = program["id"], program["title"]
644 self._model.append(row)
646 path = (self._get_current_row(), )
647 self._treeView.scroll_to_cell(path)
648 self._treeView.get_selection().select_path(path)
650 @misc_utils.log_exception(_moduleLogger)
651 def _on_error(self, exception):
653 self._errorBanner.push_message(exception)
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)
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)
668 gobject.type_register(ConferenceSessionsWindow)
671 class ConferenceTalksWindow(ListWindow):
673 def __init__(self, player, store, index, sessionId):
674 self._sessionId = sessionId
676 ListWindow.__init__(self, player, store, index)
677 self._window.set_title("Talks")
680 def _get_columns(cls):
681 yield gobject.TYPE_STRING, None
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
689 def _get_current_row(self):
690 # @todo Not implemented yet
694 ListWindow._refresh(self)
695 self._index.download(
696 "get_conference_talks",
697 self._on_conference_talks,
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")
709 for program in programs:
710 row = program, "%s\n%s" % (program["title"], program["speaker"])
711 self._model.append(row)
713 path = (self._get_current_row(), )
714 self._treeView.scroll_to_cell(path)
715 self._treeView.get_selection().select_path(path)
717 @misc_utils.log_exception(_moduleLogger)
718 def _on_error(self, exception):
720 self._errorBanner.push_message(exception)
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)
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)
735 gobject.type_register(ConferenceTalksWindow)
738 class ConferenceTalkWindow(BasicWindow):
740 def __init__(self, player, store, index, talkData):
741 BasicWindow.__init__(self, player, store, index)
743 self._player.connect("state-change", self._on_player_state_change)
744 self._player.connect("title-change", self._on_player_title_change)
746 self._loadingBanner = banners.GenericBanner()
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)
754 self._layout.pack_start(self._loadingBanner.toplevel, False, False)
755 self._layout.pack_start(self._presenterNavigation.toplevel, True, True)
757 self._window.set_title("Talk")
758 self._window.show_all()
759 self._errorBanner.toplevel.hide()
760 self._loadingBanner.toplevel.hide()
762 self._presenter.set_context(
763 self._store.STORE_LOOKUP[self._player.background],
765 self._player.subtitle,
767 if self._player.state == "play":
768 self._presenter.set_state(self._store.STORE_LOOKUP["play"])
770 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
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...")
777 def _hide_loading(self):
778 self._loadingBanner.hide()
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():
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"])
790 _moduleLogger.info("Unhandled player state %s" % newState)
791 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
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],
798 self._player.subtitle,
801 @misc_utils.log_exception(_moduleLogger)
802 def _on_navigating(self, widget, navState):
803 if navState == "clicking":
804 if self._player.state == "play":
808 elif navState == "down":
810 elif navState == "up":
812 elif navState == "left":
814 elif navState == "right":
817 self._presenter.set_state(self._store.STORE_LOOKUP[imageName])
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"])
824 self._presenter.set_state(self._store.STORE_LOOKUP["pause"])
826 if navState == "clicking":
827 if self._player.state == "play":
831 elif navState == "down":
833 self._window.destroy()
834 elif navState == "up":
836 elif navState == "left":
838 elif navState == "right":
842 gobject.type_register(ConferenceTalkWindow)