X-Git-Url: http://git.maemo.org/git/?p=watersofshiloah;a=blobdiff_plain;f=src%2Fwindows.py;h=0820900446eac4dfa3c895add19ea61e0b844a0c;hp=c10c9c6baba4424f636e028df19aef35e82bbd56;hb=48391603fc793625843346917737373b891212a9;hpb=058ae92e155b255df2320e1a2299ec119f41b432 diff --git a/src/windows.py b/src/windows.py index c10c9c6..0820900 100644 --- a/src/windows.py +++ b/src/windows.py @@ -10,7 +10,9 @@ import gtk import constants import hildonize import util.misc as misc_utils +import util.go_utils as go_utils +import stream_index import banners import playcontrol import presenter @@ -19,7 +21,7 @@ import presenter _moduleLogger = logging.getLogger(__name__) -class BasicWindow(gobject.GObject): +class BasicWindow(gobject.GObject, go_utils.AutoSignal): __gsignals__ = { 'quit' : ( @@ -27,20 +29,34 @@ class BasicWindow(gobject.GObject): gobject.TYPE_NONE, (), ), - 'fullscreen' : ( + 'home' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (), + ), + 'jump-to' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, ), ), + 'rotate' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_BOOLEAN, ), + ), + 'fullscreen' : ( + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_BOOLEAN, ), + ), } - def __init__(self, player, store, index): + def __init__(self, player, store): gobject.GObject.__init__(self) self._isDestroyed = False self._player = player self._store = store - self._index = index self._clipboard = gtk.clipboard_get() self._windowInFullscreen = False @@ -51,6 +67,7 @@ class BasicWindow(gobject.GObject): self._layout.pack_start(self._errorBanner.toplevel, False, True) self._window = gtk.Window() + go_utils.AutoSignal.__init__(self, self.window) self._window.add(self._layout) self._window = hildonize.hildonize_window(self, self._window) @@ -63,6 +80,9 @@ class BasicWindow(gobject.GObject): def window(self): return self._window + def show(self): + self._window.show_all() + def save_settings(self, config, sectionName): config.add_section(sectionName) config.set(sectionName, "fullscreen", str(self._windowInFullscreen)) @@ -83,6 +103,9 @@ class BasicWindow(gobject.GObject): else: self._window.unfullscreen() + def jump_to(self, node): + raise NotImplementedError("On %s" % self) + @misc_utils.log_exception(_moduleLogger) def _on_destroy(self, *args): self._isDestroyed = True @@ -126,33 +149,47 @@ class BasicWindow(gobject.GObject): self._clipboard.set_text(str(log)) return True + @misc_utils.log_exception(_moduleLogger) + def _on_home(self, *args): + self.emit("home") + self._window.destroy() + + @misc_utils.log_exception(_moduleLogger) + def _on_jump(self, source, node): + raise NotImplementedError("On %s" % self) + + @misc_utils.log_exception(_moduleLogger) + def _on_quit(self, *args): + self.emit("quit") + self._window.destroy() + class SourceSelector(BasicWindow): def __init__(self, player, store, index): + BasicWindow.__init__(self, player, store) self._languages = [] - - BasicWindow.__init__(self, player, store, index) + self._index = index self._loadingBanner = banners.GenericBanner() self._radioButton = self._create_button("radio", "Radio") - self._radioButton.connect("clicked", self._on_source_selected, RadioWindow) + self._radioButton.connect("clicked", self._on_source_selected, stream_index.SOURCE_RADIO) self._radioWrapper = gtk.VBox() self._radioWrapper.pack_start(self._radioButton, False, True) self._conferenceButton = self._create_button("conferences", "Conferences") - self._conferenceButton.connect("clicked", self._on_source_selected, ConferencesWindow) + self._conferenceButton.connect("clicked", self._on_source_selected, stream_index.SOURCE_CONFERENCES) self._conferenceWrapper = gtk.VBox() self._conferenceWrapper.pack_start(self._conferenceButton, False, True) self._magazineButton = self._create_button("magazines", "Magazines") - #self._magazineButton.connect("clicked", self._on_source_selected) + self._magazineButton.connect("clicked", self._on_source_selected, stream_index.SOURCE_MAGAZINES) self._magazineWrapper = gtk.VBox() self._magazineWrapper.pack_start(self._magazineButton, False, True) self._scriptureButton = self._create_button("scriptures", "Scriptures") - #self._scriptureButton.connect("clicked", self._on_source_selected) + self._scriptureButton.connect("clicked", self._on_source_selected, stream_index.SOURCE_SCRIPTURES) self._scriptureWrapper = gtk.VBox() self._scriptureWrapper.pack_start(self._scriptureButton, False, True) @@ -163,14 +200,20 @@ class SourceSelector(BasicWindow): self._buttonLayout.pack_start(self._magazineWrapper, True, True) self._buttonLayout.pack_start(self._scriptureWrapper, True, True) - self._playcontrol = playcontrol.PlayControl(player, store) + self._separator = gtk.HSeparator() + self._playcontrol = playcontrol.NavControl(player, store) + self._playcontrol.connect("jump-to", self._on_jump) self._layout.pack_start(self._loadingBanner.toplevel, False, False) self._layout.pack_start(self._buttonLayout, True, True) + self._layout.pack_start(self._separator, False, True) self._layout.pack_start(self._playcontrol.toplevel, False, True) self._window.set_title(constants.__pretty_app_name__) - self._window.show_all() + + def show(self): + BasicWindow.show(self) + self._errorBanner.toplevel.hide() self._playcontrol.toplevel.hide() @@ -188,11 +231,7 @@ class SourceSelector(BasicWindow): def _refresh(self): self._show_loading() - self._index.download( - "get_languages", - self._on_languages, - self._on_error, - ) + self._index.get_languages(self._on_languages, self._on_error) def _create_button(self, icon, message): image = self._store.get_image_from_store(self._store.STORE_LOOKUP[icon]) @@ -216,14 +255,37 @@ class SourceSelector(BasicWindow): @misc_utils.log_exception(_moduleLogger) def _on_error(self, exception): self._hide_loading() - self._errorBanner.push_message(exception) - - @misc_utils.log_exception(_moduleLogger) - def _on_source_selected(self, widget, Source): - sourceWindow = Source(self._player, self._store, self._index, self._languages[0]["id"]) + self._errorBanner.push_message(str(exception)) + + def _window_from_node(self, node): + if node.id == stream_index.SOURCE_RADIO: + Source = RadioWindow + elif node.id == stream_index.SOURCE_CONFERENCES: + Source = ConferencesWindow + elif node.id == stream_index.SOURCE_MAGAZINES: + pass + elif node.id == stream_index.SOURCE_SCRIPTURES: + pass + sourceWindow = Source(self._player, self._store, node) sourceWindow.window.set_modal(True) sourceWindow.window.set_transient_for(self._window) sourceWindow.window.set_default_size(*self._window.get_size()) + sourceWindow.connect("quit", self._on_quit) + sourceWindow.connect("jump-to", self._on_jump) + sourceWindow.show() + return sourceWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_jump(self, source, node): + targetNodePath = list(reversed(list(stream_index.walk_ancestors(node)))) + ancestor = targetNodePath[0] + window = self._window_from_node(ancestor) + window.jump_to(node) + + @misc_utils.log_exception(_moduleLogger) + def _on_source_selected(self, widget, nodeName): + node = self._index.get_source(nodeName, self._languages[0]["id"]) + self._window_from_node(node) gobject.type_register(SourceSelector) @@ -231,11 +293,13 @@ gobject.type_register(SourceSelector) class RadioWindow(BasicWindow): - def __init__(self, player, store, index, languageId): - BasicWindow.__init__(self, player, store, index) + def __init__(self, player, store, node): + BasicWindow.__init__(self, player, store) + self._node = node + self._childNode = None - self._player.connect("state-change", self._on_player_state_change) - self._player.connect("title-change", self._on_player_title_change) + self.connect_auto(self._player, "state-change", self._on_player_state_change) + self.connect_auto(self._player, "title-change", self._on_player_title_change) self._loadingBanner = banners.GenericBanner() @@ -273,10 +337,6 @@ class RadioWindow(BasicWindow): self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) self._presenter = presenter.StreamMiniPresenter(self._store) - if self._player.state == "play": - self._presenter.set_state(self._store.STORE_LOOKUP["play"]) - else: - self._presenter.set_state(self._store.STORE_LOOKUP["pause"]) self._presenterNavigation = presenter.NavigationBox() self._presenterNavigation.toplevel.add(self._presenter.toplevel) self._presenterNavigation.connect("action", self._on_nav_action) @@ -290,14 +350,41 @@ class RadioWindow(BasicWindow): self._layout.pack_start(self._loadingBanner.toplevel, False, False) self._layout.pack_start(self._radioLayout, True, True) - self._window.set_title("Radio") - self._window.show_all() + self._dateShown = datetime.datetime.now() + self._update_title() + + def show(self): + BasicWindow.show(self) + self._errorBanner.toplevel.hide() self._loadingBanner.toplevel.hide() - self._dateShown = datetime.datetime.now() self._refresh() + def jump_to(self, node): + _moduleLogger.info("Only 1 channel, nothing to jump to") + + def _update_title(self): + self._window.set_title("%s - %s" % (self._node.title, self._dateShown.strftime("%m/%d"))) + + @property + def _active(self): + return self._player.node is self._childNode + + def _set_context(self, state): + if state == self._player.STATE_PLAY: + if self._active: + self._presenter.set_state(self._store.STORE_LOOKUP["pause"]) + else: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + elif state == self._player.STATE_PAUSE: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + elif state == self._player.STATE_STOP: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + else: + _moduleLogger.info("Unhandled player state %s" % state) + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + def _show_loading(self): animationPath = self._store.STORE_LOOKUP["loading"] animation = self._store.get_pixbuf_animation_from_store(animationPath) @@ -309,11 +396,11 @@ class RadioWindow(BasicWindow): def _refresh(self): self._show_loading() self._programmingModel.clear() - self._index.download( - "get_radio_channels", + self._node.get_children( self._on_channels, self._on_load_error, ) + self._set_context(self._player.state) def _get_current_row(self): nowTime = self._dateShown.strftime("%H:%M:%S") @@ -332,58 +419,68 @@ class RadioWindow(BasicWindow): if self._headerNavigation.is_active() or self._presenterNavigation.is_active(): return - if newState == "play": - self._presenter.set_state(self._store.STORE_LOOKUP["play"]) - elif newState == "pause": - self._presenter.set_state(self._store.STORE_LOOKUP["pause"]) - else: - _moduleLogger.info("Unhandled player state %s" % newState) - self._presenter.set_state(self._store.STORE_LOOKUP["pause"]) + self._set_context(newState) @misc_utils.log_exception(_moduleLogger) - def _on_player_title_change(self, player, newState): - _moduleLogger.info("Player title magically changed to %s" % player.title) - self._destroy() + def _on_player_title_change(self, player, node): + if node is not self._childNode or node is None: + _moduleLogger.info("Player title magically changed to %s" % player.title) + return @misc_utils.log_exception(_moduleLogger) def _on_navigating(self, widget, navState): if navState == "clicking": - if self._player.state == "play": - imageName = "pause" + if self._player.state == self._player.STATE_PLAY: + if self._active: + imageName = "pause_pressed" + else: + imageName = "play_pressed" + elif self._player.state == self._player.STATE_PAUSE: + imageName = "play_pressed" + elif self._player.state == self._player.STATE_STOP: + imageName = "play_pressed" else: - imageName = "play" + imageName = "play_pressed" + _moduleLogger.info("Unhandled player state %s" % self._player.state) elif navState == "down": imageName = "home" - elif navState == "up": - imageName = "play" - elif navState == "left": - imageName = "play" - elif navState == "right": - imageName = "play" + else: + if self._player.state == self._player.STATE_PLAY: + imageName = "pause" + else: + imageName = "play" self._presenter.set_state(self._store.STORE_LOOKUP[imageName]) @misc_utils.log_exception(_moduleLogger) def _on_nav_action(self, widget, navState): - if self._player.state == "play": - self._presenter.set_state(self._store.STORE_LOOKUP["play"]) - else: - self._presenter.set_state(self._store.STORE_LOOKUP["pause"]) + self._set_context(self._player.state) if navState == "clicking": - if self._player.state == "play": - self._player.pause() - else: + if self._player.state == self._player.STATE_PLAY: + if self._active: + self._player.pause() + else: + self._player.set_piece_by_node(self._childNode) + self._player.play() + elif self._player.state == self._player.STATE_PAUSE: + self._player.play() + elif self._player.state == self._player.STATE_STOP: + self._player.set_piece_by_node(self._childNode) self._player.play() + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) elif navState == "down": self.window.destroy() elif navState == "up": pass elif navState == "left": self._dateShown += datetime.timedelta(days=1) + self._update_title() self._refresh() elif navState == "right": self._dateShown -= datetime.timedelta(days=1) + self._update_title() self._refresh() @misc_utils.log_exception(_moduleLogger) @@ -392,16 +489,14 @@ class RadioWindow(BasicWindow): _moduleLogger.info("Download complete but window destroyed") return - channels = list(channels) + channels = channels if 1 < len(channels): _moduleLogger.warning("More channels now available!") - channel = channels[0] - self._index.download( - "get_radio_channel_programming", + self._childNode = channels[0] + self._childNode.get_programming( + self._dateShown, self._on_channel, self._on_load_error, - channel["id"], - self._dateShown, ) @misc_utils.log_exception(_moduleLogger) @@ -415,14 +510,19 @@ class RadioWindow(BasicWindow): row = program["time"], program["title"] self._programmingModel.append(row) - path = (self._get_current_row(), ) - self._treeView.scroll_to_cell(path) - self._treeView.get_selection().select_path(path) + currentDate = datetime.datetime.now() + if currentDate.date() != self._dateShown.date(): + self._treeView.get_selection().set_mode(gtk.SELECTION_NONE) + else: + self._treeView.get_selection().set_mode(gtk.SELECTION_SINGLE) + path = (self._get_current_row(), ) + self._treeView.scroll_to_cell(path) + self._treeView.get_selection().select_path(path) @misc_utils.log_exception(_moduleLogger) def _on_load_error(self, exception): self._hide_loading() - self._errorBanner.push_message(exception) + self._errorBanner.push_message(str(exception)) @misc_utils.log_exception(_moduleLogger) def _on_row_changed(self, selection): @@ -441,8 +541,11 @@ gobject.type_register(RadioWindow) class ListWindow(BasicWindow): - def __init__(self, player, store, index): - BasicWindow.__init__(self, player, store, index) + def __init__(self, player, store, node): + BasicWindow.__init__(self, player, store) + self._node = node + + self.connect_auto(self._player, "title-change", self._on_player_title_change) self._loadingBanner = banners.GenericBanner() @@ -462,16 +565,22 @@ class ListWindow(BasicWindow): self._treeScroller.add(self._treeView) self._treeScroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) - self._playcontrol = playcontrol.PlayControl(self._player, self._store) + self._separator = gtk.HSeparator() + self._playcontrol = playcontrol.NavControl(self._player, self._store) + self._playcontrol.connect("home", self._on_home) + self._playcontrol.connect("jump-to", self._on_jump) self._contentLayout = gtk.VBox(False) self._contentLayout.pack_start(self._treeScroller, True, True) + self._contentLayout.pack_start(self._separator, False, True) self._contentLayout.pack_start(self._playcontrol.toplevel, False, True) self._layout.pack_start(self._loadingBanner.toplevel, False, False) self._layout.pack_start(self._contentLayout, True, True) - self._window.show_all() + def show(self): + BasicWindow.show(self) + self._errorBanner.toplevel.hide() self._loadingBanner.toplevel.hide() @@ -483,12 +592,55 @@ class ListWindow(BasicWindow): raise NotImplementedError("") def _get_current_row(self): + if self._player.node is None: + return -1 + ancestors, current, descendants = stream_index.common_paths(self._player.node, self._node) + if not descendants: + return -1 + activeChild = descendants[0] + for i, row in enumerate(self._model): + if activeChild is row[0]: + return i + else: + return -1 + + def jump_to(self, node): + ancestors, current, descendants = stream_index.common_paths(node, self._node) + if current is None: + raise RuntimeError("Cannot jump to node %s" % node) + if not descendants: + _moduleLogger.info("Current node is the target") + return + child = descendants[0] + window = self._window_from_node(child) + window.jump_to(node) + + def _window_from_node(self, node): raise NotImplementedError("") @misc_utils.log_exception(_moduleLogger) def _on_row_activated(self, view, path, column): raise NotImplementedError("") + @misc_utils.log_exception(_moduleLogger) + def _on_player_title_change(self, player, node): + self._select_row() + + @misc_utils.log_exception(_moduleLogger) + def _on_jump(self, source, node): + ancestors, current, descendants = stream_index.common_paths(node, self._node) + if current is None: + _moduleLogger.info("%s is not the target, moving up" % self._node) + self.emit("jump-to", node) + self._window.destroy() + return + if not descendants: + _moduleLogger.info("Current node is the target") + return + child = descendants[0] + window = self._window_from_node(child) + window.jump_to(node) + def _show_loading(self): animationPath = self._store.STORE_LOOKUP["loading"] animation = self._store.get_pixbuf_animation_from_store(animationPath) @@ -502,22 +654,23 @@ class ListWindow(BasicWindow): self._model.clear() def _select_row(self): - path = (self._get_current_row(), ) + rowIndex = self._get_current_row() + if rowIndex < 0: + return + path = (rowIndex, ) self._treeView.scroll_to_cell(path) self._treeView.get_selection().select_path(path) class ConferencesWindow(ListWindow): - def __init__(self, player, store, index, languageId): - self._languageId = languageId - - ListWindow.__init__(self, player, store, index) - self._window.set_title("Conferences") + def __init__(self, player, store, node): + ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) @classmethod def _get_columns(cls): - yield gobject.TYPE_STRING, None + yield gobject.TYPE_PYOBJECT, None textrenderer = gtk.CellRendererText() column = gtk.TreeViewColumn("Date") @@ -531,17 +684,11 @@ class ConferencesWindow(ListWindow): column.add_attribute(textrenderer, "text", 2) yield gobject.TYPE_STRING, column - def _get_current_row(self): - # @todo Not implemented yet - return 0 - def _refresh(self): ListWindow._refresh(self) - self._index.download( - "get_conferences", + self._node.get_children( self._on_conferences, self._on_error, - self._languageId, ) @misc_utils.log_exception(_moduleLogger) @@ -551,28 +698,34 @@ class ConferencesWindow(ListWindow): return self._hide_loading() - for program in programs: - row = program["id"], program["title"], program["full_title"] + for programNode in programs: + program = programNode.get_properties() + row = programNode, program["title"], program["full_title"] self._model.append(row) - path = (self._get_current_row(), ) - self._treeView.scroll_to_cell(path) - self._treeView.get_selection().select_path(path) + self._select_row() @misc_utils.log_exception(_moduleLogger) def _on_error(self, exception): self._hide_loading() - self._errorBanner.push_message(exception) - - @misc_utils.log_exception(_moduleLogger) - def _on_row_activated(self, view, path, column): - itr = self._model.get_iter(path) - conferenceId = self._model.get_value(itr, 0) + self._errorBanner.push_message(str(exception)) - sessionsWindow = ConferenceSessionsWindow(self._player, self._store, self._index, conferenceId) + def _window_from_node(self, node): + sessionsWindow = ConferenceSessionsWindow(self._player, self._store, node) sessionsWindow.window.set_modal(True) sessionsWindow.window.set_transient_for(self._window) sessionsWindow.window.set_default_size(*self._window.get_size()) + sessionsWindow.connect("quit", self._on_quit) + sessionsWindow.connect("home", self._on_home) + sessionsWindow.connect("jump-to", self._on_jump) + sessionsWindow.show() + return sessionsWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_row_activated(self, view, path, column): + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) gobject.type_register(ConferencesWindow) @@ -580,15 +733,13 @@ gobject.type_register(ConferencesWindow) class ConferenceSessionsWindow(ListWindow): - def __init__(self, player, store, index, conferenceId): - self._conferenceId = conferenceId - - ListWindow.__init__(self, player, store, index) - self._window.set_title("Sessions") + def __init__(self, player, store, node): + ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) @classmethod def _get_columns(cls): - yield gobject.TYPE_STRING, None + yield gobject.TYPE_PYOBJECT, None textrenderer = gtk.CellRendererText() column = gtk.TreeViewColumn("Session") @@ -596,17 +747,11 @@ class ConferenceSessionsWindow(ListWindow): column.add_attribute(textrenderer, "text", 1) yield gobject.TYPE_STRING, column - def _get_current_row(self): - # @todo Not implemented yet - return 0 - def _refresh(self): ListWindow._refresh(self) - self._index.download( - "get_conference_sessions", + self._node.get_children( self._on_conference_sessions, self._on_error, - self._conferenceId, ) @misc_utils.log_exception(_moduleLogger) @@ -616,28 +761,34 @@ class ConferenceSessionsWindow(ListWindow): return self._hide_loading() - for program in programs: - row = program["id"], program["title"] + for programNode in programs: + program = programNode.get_properties() + row = programNode, program["title"] self._model.append(row) - path = (self._get_current_row(), ) - self._treeView.scroll_to_cell(path) - self._treeView.get_selection().select_path(path) + self._select_row() @misc_utils.log_exception(_moduleLogger) def _on_error(self, exception): self._hide_loading() - self._errorBanner.push_message(exception) - - @misc_utils.log_exception(_moduleLogger) - def _on_row_activated(self, view, path, column): - itr = self._model.get_iter(path) - sessionId = self._model.get_value(itr, 0) + self._errorBanner.push_message(str(exception)) - sessionsWindow = ConferenceTalksWindow(self._player, self._store, self._index, sessionId) + def _window_from_node(self, node): + sessionsWindow = ConferenceTalksWindow(self._player, self._store, node) sessionsWindow.window.set_modal(True) sessionsWindow.window.set_transient_for(self._window) sessionsWindow.window.set_default_size(*self._window.get_size()) + sessionsWindow.connect("quit", self._on_quit) + sessionsWindow.connect("home", self._on_home) + sessionsWindow.connect("jump-to", self._on_jump) + sessionsWindow.show() + return sessionsWindow + + @misc_utils.log_exception(_moduleLogger) + def _on_row_activated(self, view, path, column): + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) gobject.type_register(ConferenceSessionsWindow) @@ -645,15 +796,13 @@ gobject.type_register(ConferenceSessionsWindow) class ConferenceTalksWindow(ListWindow): - def __init__(self, player, store, index, sessionId): - self._sessionId = sessionId - - ListWindow.__init__(self, player, store, index) - self._window.set_title("Talks") + def __init__(self, player, store, node): + ListWindow.__init__(self, player, store, node) + self._window.set_title(self._node.title) @classmethod def _get_columns(cls): - yield gobject.TYPE_STRING, None + yield gobject.TYPE_PYOBJECT, None textrenderer = gtk.CellRendererText() column = gtk.TreeViewColumn("Talk") @@ -661,17 +810,11 @@ class ConferenceTalksWindow(ListWindow): column.add_attribute(textrenderer, "text", 1) yield gobject.TYPE_STRING, column - def _get_current_row(self): - # @todo Not implemented yet - return 0 - def _refresh(self): ListWindow._refresh(self) - self._index.download( - "get_conference_talks", + self._node.get_children( self._on_conference_talks, self._on_error, - self._sessionId, ) @misc_utils.log_exception(_moduleLogger) @@ -681,22 +824,230 @@ class ConferenceTalksWindow(ListWindow): return self._hide_loading() - for program in programs: - row = program["id"], "%s\n%s" % (program["title"], program["speaker"]) + for programNode in programs: + program = programNode.get_properties() + row = programNode, "%s\n%s" % (program["title"], program["speaker"]) self._model.append(row) - path = (self._get_current_row(), ) - self._treeView.scroll_to_cell(path) - self._treeView.get_selection().select_path(path) + self._select_row() @misc_utils.log_exception(_moduleLogger) def _on_error(self, exception): self._hide_loading() - self._errorBanner.push_message(exception) + self._errorBanner.push_message(str(exception)) + + def _window_from_node(self, node): + sessionsWindow = ConferenceTalkWindow(self._player, self._store, node) + sessionsWindow.window.set_modal(True) + sessionsWindow.window.set_transient_for(self._window) + sessionsWindow.window.set_default_size(*self._window.get_size()) + sessionsWindow.connect("quit", self._on_quit) + sessionsWindow.connect("home", self._on_home) + sessionsWindow.connect("jump-to", self._on_jump) + sessionsWindow.show() + return sessionsWindow @misc_utils.log_exception(_moduleLogger) def _on_row_activated(self, view, path, column): - raise NotImplementedError("") + itr = self._model.get_iter(path) + node = self._model.get_value(itr, 0) + self._window_from_node(node) gobject.type_register(ConferenceTalksWindow) + + +class ConferenceTalkWindow(BasicWindow): + + def __init__(self, player, store, node): + BasicWindow.__init__(self, player, store) + self._node = node + self._playerNode = self._player.node + self._nextSearch = None + self._updateSeek = None + + self.connect_auto(self._player, "state-change", self._on_player_state_change) + self.connect_auto(self._player, "title-change", self._on_player_title_change) + self.connect_auto(self._player, "error", self._on_player_error) + + self._loadingBanner = banners.GenericBanner() + + self._presenter = presenter.StreamPresenter(self._store) + self._presenter.set_context( + self._store.STORE_LOOKUP["conference_background"], + self._node.title, + self._node.subtitle, + ) + self._presenterNavigation = presenter.NavigationBox() + self._presenterNavigation.toplevel.add(self._presenter.toplevel) + self._presenterNavigation.connect("action", self._on_nav_action) + self._presenterNavigation.connect("navigating", self._on_navigating) + + self._seekbar = hildonize.create_seekbar() + self._seekbar.connect("change-value", self._on_user_seek) + + self._layout.pack_start(self._loadingBanner.toplevel, False, False) + self._layout.pack_start(self._presenterNavigation.toplevel, True, True) + self._layout.pack_start(self._seekbar, False, False) + + self._window.set_title(self._node.title) + + def show(self): + BasicWindow.show(self) + self._window.show_all() + self._errorBanner.toplevel.hide() + self._loadingBanner.toplevel.hide() + self._set_context(self._player.state) + self._seekbar.hide() + + def jump_to(self, node): + assert self._node is node + + @property + def _active(self): + return self._playerNode is self._node + + def _show_loading(self): + animationPath = self._store.STORE_LOOKUP["loading"] + animation = self._store.get_pixbuf_animation_from_store(animationPath) + self._loadingBanner.show(animation, "Loading...") + + def _hide_loading(self): + self._loadingBanner.hide() + + def _set_context(self, state): + if state == self._player.STATE_PLAY: + if self._active: + self._presenter.set_state(self._store.STORE_LOOKUP["pause"]) + else: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + elif state == self._player.STATE_PAUSE: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + elif state == self._player.STATE_STOP: + self._presenter.set_state(self._store.STORE_LOOKUP["play"]) + else: + _moduleLogger.info("Unhandled player state %s" % state) + + @misc_utils.log_exception(_moduleLogger) + def _on_user_seek(self, widget, scroll, value): + self._player.seek(value / 100.0) + + @misc_utils.log_exception(_moduleLogger) + def _on_player_update_seek(self): + self._seekbar.set_value(self._player.percent_elapsed * 100) + return True if not self._isDestroyed else False + + @misc_utils.log_exception(_moduleLogger) + def _on_player_state_change(self, player, newState): + if self._active and self._player.state == self._player.STATE_PLAY: + self._seekbar.show() + assert self._updateSeek is None + self._updateSeek = go_utils.Timeout(self._updateSeek, once=False) + self._updateSeek.start(seconds=30) + else: + self._seekbar.hide() + self._updateSeek.cancel() + self._updateSeek = None + + if not self._presenterNavigation.is_active(): + self._set_context(newState) + + @misc_utils.log_exception(_moduleLogger) + def _on_player_title_change(self, player, node): + if not self._active or node in [None, self._node]: + self._playerNode = player.node + return + self._playerNode = player.node + self.emit("jump-to", node) + self._window.destroy() + + @misc_utils.log_exception(_moduleLogger) + def _on_player_error(self, player, err, debug): + _moduleLogger.error("%r - %r" % (err, debug)) + + @misc_utils.log_exception(_moduleLogger) + def _on_navigating(self, widget, navState): + if navState == "clicking": + if self._player.state == self._player.STATE_PLAY: + if self._active: + imageName = "pause_pressed" + else: + imageName = "play_pressed" + elif self._player.state == self._player.STATE_PAUSE: + imageName = "play_pressed" + elif self._player.state == self._player.STATE_STOP: + imageName = "play_pressed" + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "down": + imageName = "home" + elif navState == "up": + if self._player.state == self._player.STATE_PLAY: + if self._active: + imageName = "pause" + else: + imageName = "play" + elif self._player.state == self._player.STATE_PAUSE: + imageName = "play" + elif self._player.state == self._player.STATE_STOP: + imageName = "play" + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "left": + imageName = "next" + elif navState == "right": + imageName = "prev" + + self._presenter.set_state(self._store.STORE_LOOKUP[imageName]) + + @misc_utils.log_exception(_moduleLogger) + def _on_nav_action(self, widget, navState): + self._set_context(self._player.state) + + if navState == "clicking": + if self._player.state == self._player.STATE_PLAY: + if self._active: + self._player.pause() + else: + self._player.set_piece_by_node(self._node) + self._player.play() + elif self._player.state == self._player.STATE_PAUSE: + self._player.play() + elif self._player.state == self._player.STATE_STOP: + self._player.set_piece_by_node(self._node) + self._player.play() + else: + _moduleLogger.info("Unhandled player state %s" % self._player.state) + elif navState == "down": + self.emit("home") + self._window.destroy() + elif navState == "up": + pass + elif navState == "left": + if self._active: + self._player.next() + else: + assert self._nextSearch is None + self._nextSearch = stream_index.AsyncWalker(stream_index.get_next) + self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error) + elif navState == "right": + if self._active: + self._player.back() + else: + assert self._nextSearch is None + self._nextSearch = stream_index.AsyncWalker(stream_index.get_previous) + self._nextSearch.start(self._node, self._on_next_node, self._on_node_search_error) + + @misc_utils.log_exception(_moduleLogger) + def _on_next_node(self, node): + self._nextSearch = None + self.emit("jump-to", node) + self._window.destroy() + + @misc_utils.log_exception(_moduleLogger) + def _on_node_search_error(self, e): + self._nextSearch = None + self._errorBanner.push_message(str(e)) + + +gobject.type_register(ConferenceTalkWindow)