Playback while silent
[watersofshiloah] / src / mormonchannel_gtk.py
index ddcd8f3..188a4e6 100755 (executable)
@@ -1,27 +1,44 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+"""
+@todo backgrounds need some resizing
+@todo Re-use windows for better performance
+@bug For some reason, the back/close button doesn't work when I nest multiple levels
+@todo Need to confirm id's are persistent (not just for todos but broken behavior on transition)
+       @todo Track recent
+       @todo Persisted Pause
+       @todo Favorites
+@todo Sleep timer
+@todo Reverse order option.  Toggle between playing ascending/descending chronological order
+@todo Podcast integration
+"""
+
 from __future__ import with_statement
 
+import os
 import gc
 import logging
 import ConfigParser
 
+import gobject
+import dbus
+import dbus.mainloop.glib
 import gtk
 
 try:
-       import hildon
-except ImportError:
-       hildon = None
-
-try:
        import osso
 except ImportError:
        osso = None
 
 import constants
 import hildonize
-import gtk_toolbox
+import util.misc as misc_utils
+
+import imagestore
+import player
+import stream_index
+import windows
 
 
 _moduleLogger = logging.getLogger(__name__)
@@ -32,127 +49,52 @@ class MormonChannelProgram(hildonize.get_app_class()):
 
        def __init__(self):
                super(MormonChannelProgram, self).__init__()
-               self._clipboard = gtk.clipboard_get()
-
-               self._window_in_fullscreen = False #The window isn't in full screen mode initially.
-
-               #Create GUI main vbox
-               vbox = gtk.VBox(homogeneous = False, spacing = 0)
-
-               if hildonize.GTK_MENU_USED:
-                       #Create Menu and apply it for hildon
-                       filemenu = gtk.Menu()
-
-                       menu_items = gtk.MenuItem("Quit")
-                       filemenu.append(menu_items)
-                       menu_items.connect("activate", self._on_destroy, None)
-
-                       file_menu = gtk.MenuItem("File")
-                       file_menu.show()
-                       file_menu.set_submenu(filemenu)
-
-                       categorymenu = gtk.Menu()
-
-                       menu_items = gtk.MenuItem("Search")
-                       categorymenu.append(menu_items)
-                       menu_items.connect("activate", self._on_toggle_search)
-
-                       helpmenu = gtk.Menu()
-
-                       menu_items = gtk.MenuItem("About")
-                       helpmenu.append(menu_items)
-                       menu_items.connect("activate", self._on_show_about, None)
-
-                       help_menu = gtk.MenuItem("Help")
-                       help_menu.show()
-                       help_menu.set_submenu(helpmenu)
-
-                       menuBar = gtk.MenuBar()
-                       menuBar.show()
-                       menuBar.append (file_menu)
-                       menuBar.append (help_menu)
-
-                       vbox.pack_start(menuBar, False, False, 0)
-               else:
-                       menuBar = gtk.MenuBar()
-                       menuBar.show()
-                       vbox.pack_start(menuBar, False, False, 0)
-
-               #Get the Main Window, and connect the "destroy" event
-               self._window = gtk.Window()
-               self._window.add(vbox)
-
-               self._window = hildonize.hildonize_window(self, self._window)
-               hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
-               menuBar = hildonize.hildonize_menu(
-                       self._window,
-                       menuBar,
-               )
-               if hildonize.IS_FREMANTLE_SUPPORTED:
-                       searchButton= gtk.Button("Search")
-                       searchButton.connect("clicked", self._on_toggle_search)
-                       menuBar.append(searchButton)
-
-                       menuBar.show_all()
-
-               if not hildonize.IS_HILDON_SUPPORTED:
-                       _moduleLogger.info("No hildonization support")
-
-               if osso is not None:
-                       self._osso_c = osso.Context(constants.__app_name__, constants.__version__, False)
-                       self._deviceState = osso.DeviceState(self._osso_c)
-                       self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
-               else:
-                       _moduleLogger.info("No osso support")
-                       self._osso_c = None
-                       self._deviceState = None
-
-               self._window.connect("delete-event", self._on_delete_event)
-               self._window.connect("destroy", self._on_destroy)
-               self._window.connect("key-press-event", self._on_key_press)
-               self._window.connect("window-state-event", self._on_window_state_change)
+               currentPath = os.path.abspath(__file__)
+               storePath = os.path.join(os.path.split(os.path.dirname(currentPath))[0], "data")
+               self._store = imagestore.ImageStore(storePath, constants._cache_path_)
+               self._index = stream_index.AudioIndex()
+               self._player = player.Player(self._index)
+
+               self._store.start()
+               self._index.start()
+               try:
+                       if not hildonize.IS_HILDON_SUPPORTED:
+                               _moduleLogger.info("No hildonization support")
 
-               self._window.show_all()
-               self._load_settings()
+                       if osso is not None:
+                               self._osso_c = osso.Context(constants.__app_name__, constants.__version__, False)
+                               self._deviceState = osso.DeviceState(self._osso_c)
+                               self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
+                       else:
+                               _moduleLogger.info("No osso support")
+                               self._osso_c = None
+                               self._deviceState = None
+
+                       self._sourceSelector = windows.source.SourceSelector(self, self._player, self._store, self._index)
+                       self._sourceSelector.window.connect("destroy", self._on_destroy)
+                       self._sourceSelector.window.set_default_size(400, 800)
+                       self._sourceSelector.show()
+                       self._load_settings()
+               except:
+                       self._index.stop()
+                       self._store.stop()
+                       raise
 
        def _save_settings(self):
                config = ConfigParser.SafeConfigParser()
-               self.save_settings(config)
+
+               self._sourceSelector.save_settings(config, "Windows")
+
                with open(constants._user_settings_, "wb") as configFile:
                        config.write(configFile)
 
-       def save_settings(self, config):
-               config.add_section(constants.__pretty_app_name__)
-               config.set(constants.__pretty_app_name__, "fullscreen", str(self._window_in_fullscreen))
-
        def _load_settings(self):
                config = ConfigParser.SafeConfigParser()
                config.read(constants._user_settings_)
-               self.load_settings(config)
 
-       def load_settings(self, config):
-               try:
-                       self._window_in_fullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen")
-               except ConfigParser.NoSectionError, e:
-                       _moduleLogger.info(
-                               "Settings file %s is missing section %s" % (
-                                       constants._user_settings_,
-                                       e.section,
-                               )
-                       )
-
-               if self._window_in_fullscreen:
-                       self._window.fullscreen()
-               else:
-                       self._window.unfullscreen()
-
-       def _toggle_search(self):
-               if self._search.get_property("visible"):
-                       self._search.hide()
-               else:
-                       self._search.show()
-
-       @gtk_toolbox.log_exception(_moduleLogger)
+               self._sourceSelector.load_settings(config, "Windows")
+
+       @misc_utils.log_exception(_moduleLogger)
        def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
                """
                For system_inactivity, we have no background tasks to pause
@@ -165,67 +107,30 @@ class MormonChannelProgram(hildonize.get_app_class()):
                if save_unsaved_data or shutdown:
                        self._save_settings()
 
-       @gtk_toolbox.log_exception(_moduleLogger)
-       def _on_window_state_change(self, widget, event, *args):
-               if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
-                       self._window_in_fullscreen = True
-               else:
-                       self._window_in_fullscreen = False
-
-       @gtk_toolbox.log_exception(_moduleLogger)
-       def _on_key_press(self, widget, event, *args):
-               RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
-               isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK)
-               if (
-                       event.keyval == gtk.keysyms.F6 or
-                       event.keyval in RETURN_TYPES and isCtrl
-               ):
-                       # The "Full screen" hardware key has been pressed 
-                       if self._window_in_fullscreen:
-                               self._window.unfullscreen ()
-                       else:
-                               self._window.fullscreen ()
-                       return True
-               elif event.keyval == gtk.keysyms.f and isCtrl:
-                       self._toggle_search()
-                       return True
-               elif (
-                       event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
-                       event.get_state() & gtk.gdk.CONTROL_MASK
-               ):
-                       self._window.destroy()
-               elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
-                       with open(constants._user_logpath_, "r") as f:
-                               logLines = f.xreadlines()
-                               log = "".join(logLines)
-                               self._clipboard.set_text(str(log))
-                       return True
-
-       @gtk_toolbox.log_exception(_moduleLogger)
-       def _on_toggle_search(self, *args):
-               self._toggle_search()
-
-       @gtk_toolbox.log_exception(_moduleLogger)
-       def _on_delete_event(self, widget, event, data = None):
-               return False
-
-       @gtk_toolbox.log_exception(_moduleLogger)
+       @misc_utils.log_exception(_moduleLogger)
        def _on_destroy(self, widget = None, data = None):
                try:
-                       self._save_settings()
-
-                       try:
-                               self._deviceState.close()
-                       except AttributeError:
-                               pass # Either None or close was removed (in Fremantle)
-                       try:
-                               self._osso_c.close()
-                       except AttributeError:
-                               pass # Either None or close was removed (in Fremantle)
+                       self.quit()
                finally:
                        gtk.main_quit()
 
-       @gtk_toolbox.log_exception(_moduleLogger)
+       def quit(self):
+               self._save_settings()
+
+               self._player.stop()
+               self._index.stop()
+               self._store.stop()
+
+               try:
+                       self._deviceState.close()
+               except AttributeError:
+                       pass # Either None or close was removed (in Fremantle)
+               try:
+                       self._osso_c.close()
+               except AttributeError:
+                       pass # Either None or close was removed (in Fremantle)
+
+       @misc_utils.log_exception(_moduleLogger)
        def _on_show_about(self, widget = None, data = None):
                dialog = gtk.AboutDialog()
                dialog.set_position(gtk.WIN_POS_CENTER)
@@ -241,11 +146,20 @@ class MormonChannelProgram(hildonize.get_app_class()):
 
 
 def run():
-       if hildonize.IS_HILDON_SUPPORTED:
-               gtk.set_application_name(constants.__pretty_app_name__)
+       gobject.threads_init()
+       gtk.gdk.threads_init()
+       l = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       hildonize.set_application_name("FMRadio") # Playback while silent on Maemo 5
        app = MormonChannelProgram()
        if not PROFILE_STARTUP:
-               gtk.main()
+               try:
+                       gtk.main()
+               except KeyboardInterrupt:
+                       app.quit()
+                       raise
+       else:
+               app.quit()
 
 
 if __name__ == "__main__":