PROJECT_NAME=nqaap
SOURCE_PATH=src
SOURCE=$(shell find $(SOURCE_PATH) -iname "*.py")
-PROGRAM=$(SOURCE_PATH)/opt/Nqa-Audiobook-player/$(PROJECT_NAME)_gtk.py
+PROGRAM=$(SOURCE_PATH)/$(PROJECT_NAME)_gtk.py
OBJ=$(SOURCE:.py=.pyc)
BUILD_PATH=./build
TAG_FILE=~/.ctags/$(PROJECT_NAME).tags
--- /dev/null
+from __future__ import with_statement
+
+import os
+
+import logging
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class Audiobook(object):
+
+ def __init__(self, path, current_chapter = 0):
+ self.title = ""
+ self._coverPath = ""
+ self._chapterPaths = []
+ self.current_chapter = current_chapter
+
+ if is_playlist_book(path):
+ self._scan_index(path)
+ elif is_dir_book(path):
+ self._scan_dir(path)
+ elif is_single_chapter(path):
+ self._scan_chapter(path)
+ else:
+ _moduleLogger.info("Audiobook not found in path: " + path)
+ raise IOError("Audiobook directory not found")
+
+ if len(self._chapterPaths) <= self.current_chapter:
+ _moduleLogger.warning(
+ "Audiobook chapter out of range (%s/%s)" % (
+ self.current_chapter, len(self._chapterPaths)
+ )
+ )
+ self.current_chapter = 0
+
+ @property
+ def chapters(self):
+ return self._chapterPaths
+
+ def get_current_chapter(self):
+ return self._chapterPaths[self.current_chapter]
+
+ def set_chapter(self, chapter):
+ if chapter in self.chapters:
+ self.current_chapter = self.chapters.index(chapter)
+ else:
+ raise Exception("Unknown chapter set")
+
+ def get_previous_chapter(self):
+ """
+ @returns the file name for the next chapter, without path
+ """
+ if 0 == self.current_chapter:
+ return False
+ else:
+ self.current_chapter -= 1
+ return self._chapterPaths[self.current_chapter]
+
+ def get_next_chapter(self):
+ """
+ @returns the file name for the next chapter, without path
+ """
+ if len(self._chapterPaths) == self.current_chapter:
+ return False
+ else:
+ self.current_chapter += 1
+ return self._chapterPaths[self.current_chapter]
+
+ def get_cover_img(self):
+ if self._coverPath:
+ return self._coverPath
+ else:
+ return "%s/NoCover.png" % os.path.dirname(__file__)
+
+ def _scan_dir(self, root):
+ self.title = os.path.split(root)[-1]
+ dirContent = (
+ os.path.join(root, f)
+ for f in os.listdir(root)
+ if not f.startswith(".")
+ )
+
+ files = [
+ path
+ for path in dirContent
+ if os.path.isfile(os.path.join(root, path))
+ ]
+
+ images = [
+ path
+ for path in files
+ if path.rsplit(".", 1)[-1] in ["png", "gif", "jpg", "jpeg"]
+ ]
+ if 0 < len(images):
+ self._coverPath = images[0]
+
+ self._chapterPaths = [
+ path
+ for path in files
+ if is_single_chapter(path)
+ ]
+ self._chapterPaths.sort()
+
+ def _scan_chapter(self, file):
+ self._chapterPaths = [file]
+
+ def _scan_playlist(self, file):
+ root = os.path.dirname(file)
+ self.title = os.path.basename(file).rsplit(".")[0]
+
+ with open(file, 'r') as f:
+ for line in f:
+ if line.startswith("#"):
+ continue
+ path = line
+ if not os.path.isabs(path):
+ path = os.path.normpath(os.path.join(root, path))
+ self._chapterPaths.append(path)
+ # Not sorting, assuming the file is in the desired order
+
+ def _scan_index(self, file):
+ import unicodedata
+
+ # Reading file
+ looking_for_title = False
+ looking_for_cover = False
+ looking_for_chapters = False
+
+ with open(file, 'r') as f:
+ for line in f:
+ # title
+ ascii = unicodedata.normalize('NFKD', unicode(line, "latin-1")).encode('ascii', 'ignore')
+ print line[:-1], "PIC\n" in line, line in "#PIC"
+ if "#BOOK" in line:
+ looking_for_title = True
+ continue
+ if looking_for_title:
+ self.title = line[:-1]
+ looking_for_title = False
+ if "#PIC" in line:
+ looking_for_cover = True
+ continue
+ if looking_for_cover:
+ self.cover = line[:-1]
+ looking_for_cover = False
+ if "#TRACKS" in line:
+ looking_for_chapters = True
+ continue
+ if looking_for_chapters:
+ if "#CHAPTERS" in line:
+ break # no further information needed
+ self.chapters.append(line.split(':')[0])
+
+
+def is_dir_book(path):
+ return os.path.isdir(path)
+
+
+def is_playlist_book(path):
+ return path.rsplit(".", 1)[-1] in ["m3u"]
+
+
+def is_single_chapter(path):
+ return path.rsplit(".", 1)[-1] in ["awb", "mp3", "spx", "ogg", "ac3", "wav"]
+
+
+def is_book(path):
+ if is_dir_book(path):
+ return True
+ elif is_playlist_book(path):
+ return True
+ elif is_single_chapter(path):
+ return True
+ else:
+ return False
--- /dev/null
+import os\r
+\r
+\r
+def open(url):\r
+ os.system('dbus-send --system --type=method_call --dest="com.nokia.osso_browser" /com/nokia/osso_browser/request com.nokia.osso_browser.load_url string:"%s"' % url)\r
--- /dev/null
+import logging
+
+import gobject
+import dbus
+import telepathy
+
+import gtk_toolbox
+
+
+_moduleLogger = logging.getLogger(__name__)
+DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
+
+
+class NewChannelSignaller(object):
+
+ def __init__(self, on_new_channel):
+ self._sessionBus = dbus.SessionBus()
+ self._on_user_new_channel = on_new_channel
+
+ def start(self):
+ self._sessionBus.add_signal_receiver(
+ self._on_new_channel,
+ "NewChannel",
+ "org.freedesktop.Telepathy.Connection",
+ None,
+ None
+ )
+
+ def stop(self):
+ self._sessionBus.remove_signal_receiver(
+ self._on_new_channel,
+ "NewChannel",
+ "org.freedesktop.Telepathy.Connection",
+ None,
+ None
+ )
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_new_channel(
+ self, channelObjectPath, channelType, handleType, handle, supressHandler
+ ):
+ connObjectPath = channel_path_to_conn_path(channelObjectPath)
+ serviceName = path_to_service_name(channelObjectPath)
+ try:
+ self._on_user_new_channel(
+ self._sessionBus, serviceName, connObjectPath, channelObjectPath, channelType
+ )
+ except Exception:
+ _moduleLogger.exception("Blocking exception from being passed up")
+
+
+class ChannelClosed(object):
+
+ def __init__(self, bus, conn, chan, on_closed):
+ self.__on_closed = on_closed
+
+ chan[telepathy.interfaces.CHANNEL].connect_to_signal(
+ "Closed",
+ self._on_closed,
+ )
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_closed(self):
+ self.__on_closed(self)
+
+
+class CallMonitor(gobject.GObject):
+
+ __gsignals__ = {
+ 'call_start' : (
+ gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE,
+ (),
+ ),
+ 'call_end' : (
+ gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE,
+ (),
+ ),
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+ self._isActive = False
+ self._newChannelMonitor = NewChannelSignaller(self._on_new_channel)
+ self._channelClosedMonitors = []
+
+ def start(self):
+ self._isActive = True
+ self._newChannelMonitor.start()
+
+ def stop(self):
+ self._isActive = False
+ self._newChannelMonitor.stop()
+
+ def _on_new_channel(self, sessionBus, serviceName, connObjectPath, channelObjectPath, channelType):
+ if not self._isActive:
+ return
+
+ if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA:
+ return
+
+ cmName = cm_from_path(connObjectPath)
+ conn = telepathy.client.Connection(serviceName, connObjectPath)
+ try:
+ chan = telepathy.client.Channel(serviceName, channelObjectPath)
+ except dbus.exceptions.UnknownMethodException:
+ _moduleLogger.exception("Client might not have implemented a deprecated method")
+ return
+
+ missDetection = ChannelClosed(
+ sessionBus, conn, chan, self._on_close
+ )
+ self._outstandingRequests.append(missDetection)
+ if len(self._outstandingRequests) == 1:
+ self.emit("call_start")
+
+ def _on_close(self, channelCloseMonitor):
+ self._outstandingRequests.remove(channelCloseMonitor)
+ if not self._outstandingRequests:
+ self.emit("call_stop")
+
+
+def channel_path_to_conn_path(channelObjectPath):
+ """
+ >>> channel_path_to_conn_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
+ '/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME'
+ """
+ return channelObjectPath.rsplit("/", 1)[0]
+
+
+def path_to_service_name(path):
+ """
+ >>> path_to_service_name("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
+ 'org.freedesktop.Telepathy.ConnectionManager.theonering.gv.USERNAME'
+ """
+ return ".".join(path[1:].split("/")[0:7])
+
+
+def cm_from_path(path):
+ """
+ >>> cm_from_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
+ 'theonering'
+ """
+ return path[1:].split("/")[4]
--- /dev/null
+from __future__ import with_statement # enable with
+
+import os
+import simplejson
+import logging
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+# @todo Add bookmarks
+
+
+class FileStorage(object):
+
+ def __init__(self, path="~/.SornPlayer/"):
+ # Setup dir
+ _moduleLogger.info("init filestorage")
+ self.path = path
+ self.books_path = os.path.join(self.path, "books.json")
+ self.selected = None
+ self._books = {}
+
+ def load(self):
+ if not os.path.isdir(self.path):
+ os.makedirs(self.path)
+
+ try:
+ with open(self.books_path, "r") as settingsFile:
+ settings = simplejson.load(settingsFile)
+ except IOError, e:
+ _moduleLogger.info("No settings")
+ settings = {}
+ except ValueError:
+ _moduleLogger.info("Settings were corrupt")
+ settings = {}
+
+ if settings:
+ self._books = settings["books"]
+ self.selected = settings["selected"]
+ else:
+ _moduleLogger.info("Falling back to old settings format")
+ self._load_old_settings()
+
+ def save(self):
+ settings = {
+ "selected": self.selected,
+ "books": self._books,
+ }
+ with open(self.books_path, "w") as settingsFile:
+ simplejson.dump(settings, settingsFile)
+
+ def get_selected(self):
+ """returns the currently selected book"""
+ return self.selected
+
+ def select_book(self, bookName):
+ """ Sets the book as the currently playing, and adds it to the
+ database if it is not already there"""
+ book_file = os.path.join(self.books_path, bookName)
+ if bookName not in self._books:
+ self._books[bookName] = {
+ "chapter": 0,
+ "position": 0,
+ }
+
+ self.selected = bookName
+
+ def set_time(self, chapter, position):
+ """ Sets the current time for the book that is currently selected"""
+ bookInfo = self._books[self.selected]
+ bookInfo["chapter"] = chapter
+ bookInfo["position"] = position
+
+ def get_time(self):
+ """Returns the current saved time for the current selected book"""
+ bookInfo = self._books[self.selected]
+ return bookInfo["chapter"], bookInfo["position"]
+
+ def _load_old_settings(self):
+ conf = os.path.join(self.path, "current")
+
+ try:
+ with open(conf) as f:
+ self.selected = f.readline()
+
+ books_path = os.path.join(self.path, "books/")
+ for book in os.listdir(books_path):
+ book_file = os.path.join(books_path, book)
+ with open(book_file, 'r') as f:
+ chapter = int(f.readline())
+ position = int(f.readline())
+ self._books[book] = {
+ "chapter": chapter,
+ "position": position,
+ }
+ except IOError, e:
+ if e.errno == 2:
+ pass
+ else:
+ raise
+ except OSError, e:
+ if e.errno == 2:
+ pass
+ else:
+ raise
--- /dev/null
+from __future__ import with_statement
+
+import os
+import ConfigParser
+import logging
+
+import gobject
+import gtk
+
+import constants
+import hildonize
+import gtk_toolbox
+import Browser
+import CallMonitor
+import settings
+
+if hildonize.IS_FREMANTLE_SUPPORTED:
+ # I don't normally do this but I want to error as loudly as possibly when an issue arises
+ import hildon
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class Gui(object):
+
+ # @todo Jump straight to book selection on first launch?
+ # @todo Absolute increments (+/-5 seconds)
+ # @todo Show elapsed time / time lef
+ # @todo Volume control when screen is off
+ # @todo Variable speed
+ # http://scaletempo.sourceforge.net/0/task-list.html
+ # http://www.gstreamer.net/data/doc/gstreamer/head/gst-plugins-bad-plugins/html/gst-plugins-bad-plugins-scaletempo.html
+
+ def __init__(self):
+ _moduleLogger.info("Starting GUI")
+ self._clipboard = gtk.clipboard_get()
+ self._callMonitor = CallMonitor.CallMonitor()
+ self.__settingsWindow = None
+ self.__settingsManager = None
+ self._bookSelection = []
+ self._bookSelectionIndex = -1
+ self._chapterSelection = []
+ self._chapterSelectionIndex = -1
+ self._sleepSelection = ["0", "1", "10", "20", "30", "60"]
+ self._sleepSelectionIndex = 0
+
+ self.__window_in_fullscreen = False #The window isn't in full screen mode initially.
+ self.__isPortrait = False
+
+ self.controller = None
+ self.sleep_timer = None
+
+ # set up gui
+ self.setup()
+ self._callMonitor.connect("call_start", self.__on_call_started)
+ self._callMonitor.start()
+
+ def setup(self):
+ self._app = hildonize.get_app_class()()
+ self.win = gtk.Window()
+ self.win = hildonize.hildonize_window(self._app, self.win)
+ self.win.set_title(constants.__pretty_app_name__)
+
+ # Cover image
+ self.cover = gtk.Image()
+
+ # Controls:
+
+ # Label that hold the title of the book,and maybe the chapter
+ self.title = gtk.Label()
+ self.title.set_justify(gtk.JUSTIFY_CENTER)
+ self._set_display_title("Select a book to start listening")
+
+ self.chapter = gtk.Label()
+ self.chapter.set_justify(gtk.JUSTIFY_CENTER)
+
+ # Seekbar
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ self.seek = hildon.Seekbar()
+ self.seek.set_range(0.0, 100)
+ self.seek.set_draw_value(False)
+ self.seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.seek.connect('change-value', self.seek_changed) # event
+ # self.seek.connect('value-changed',self.seek_changed) # event
+ else:
+ adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1)
+ self.seek = gtk.HScale(adjustment)
+ self.seek.set_draw_value(False)
+ self.seek.connect('change-value', self.seek_changed) # event
+
+ # Pause button
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ self.backButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PREVIOUS, gtk.HILDON_SIZE_FINGER_HEIGHT)
+ self.backButton.set_image(image)
+
+ self.button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+
+ self.forwardButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_NEXT, gtk.HILDON_SIZE_FINGER_HEIGHT)
+ self.forwardButton.set_image(image)
+ else:
+ self.backButton = gtk.Button(stock=gtk.STOCK_MEDIA_PREVIOUS)
+ self.button = gtk.Button()
+ self.forwardButton = gtk.Button(stock=gtk.STOCK_MEDIA_NEXT)
+ self.set_button_text("Play", "Start playing the audiobook")
+ self.backButton.connect('clicked', self._on_previous_chapter)
+ self.button.connect('clicked', self.play_pressed) # event
+ self.forwardButton.connect('clicked', self._on_next_chapter)
+
+ self._toolbar = gtk.HBox()
+ self._toolbar.pack_start(self.backButton, False, False, 0)
+ self._toolbar.pack_start(self.button, True, True, 0)
+ self._toolbar.pack_start(self.forwardButton, False, False, 0)
+
+ # Box to hold the controls:
+ self._controlLayout = gtk.VBox()
+ self._controlLayout.pack_start(gtk.Label(), True, True, 0)
+ self._controlLayout.pack_start(self.title, False, True, 0)
+ self._controlLayout.pack_start(self.chapter, False, True, 0)
+ self._controlLayout.pack_start(gtk.Label(), True, True, 0)
+ self._controlLayout.pack_start(self.seek, False, True, 0)
+ self._controlLayout.pack_start(self._toolbar, False, True, 0)
+
+ #Box that divides the layout in two: cover on the lefta
+ #and controls on the right
+ self._viewLayout = gtk.HBox()
+ self._viewLayout.pack_start(self.cover, True, True, 0)
+ self._viewLayout.add(self._controlLayout)
+
+ self._menuBar = gtk.MenuBar()
+ self._menuBar.show()
+
+ self._mainLayout = gtk.VBox()
+ self._mainLayout.pack_start(self._menuBar, False, False, 0)
+ self._mainLayout.pack_start(self._viewLayout)
+
+ # Add hbox to the window
+ self.win.add(self._mainLayout)
+
+ #Menu:
+ # Create menu
+ self._populate_menu()
+
+ self.win.connect("delete_event", self.quit) # Add shutdown event
+ self.win.connect("key-press-event", self.on_key_press)
+ self.win.connect("window-state-event", self._on_window_state_change)
+
+ self.win.show_all()
+
+ # Run update timer
+ self.setup_timers()
+
+ def _populate_menu(self):
+ self._menuBar = hildonize.hildonize_menu(
+ self.win,
+ self._menuBar,
+ )
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ # Create a picker button
+ self.book_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ self.book_button.set_title("Audiobook") # Set a title to the button
+ self.book_button.connect("clicked", self._on_select_audiobook)
+
+ # Create a picker button
+ self.chapter_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ self.chapter_button.set_title("Chapter") # Set a title to the button
+ self.chapter_button.connect("clicked", self._on_select_chapter)
+
+ # Create a picker button
+ self.sleeptime_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
+ hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ self.sleeptime_button.set_title("Sleeptimer") # Set a title to the button
+ self.sleeptime_button.connect("clicked", self._on_select_sleep)
+
+ settings_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ settings_button.set_label("Settings")
+ settings_button.connect("clicked", self._on_settings)
+
+ about_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ about_button.set_label("About")
+ about_button.connect("clicked", self._on_about_activate)
+
+ help_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ help_button.set_label("Help")
+ help_button.connect("clicked", self.get_help)
+
+ self._menuBar.append(self.book_button) # Add the button to menu
+ self._menuBar.append(self.chapter_button) # Add the button to menu
+ self._menuBar.append(self.sleeptime_button) # Add the button to menu
+ self._menuBar.append(settings_button)
+ self._menuBar.append(help_button)
+ self._menuBar.append(about_button)
+ self._menuBar.show_all()
+ else:
+ self._audiobookMenuItem = gtk.MenuItem("Audiobook: ")
+ self._audiobookMenuItem.connect("activate", self._on_select_audiobook)
+
+ self._chapterMenuItem = gtk.MenuItem("Chapter: ")
+ self._chapterMenuItem.connect("activate", self._on_select_chapter)
+
+ self._sleepMenuItem = gtk.MenuItem("Sleeptimer: 0")
+ self._sleepMenuItem.connect("activate", self._on_select_sleep)
+
+ settingsMenuItem = gtk.MenuItem("Settings")
+ settingsMenuItem.connect("activate", self._on_settings)
+
+ aboutMenuItem = gtk.MenuItem("About")
+ aboutMenuItem.connect("activate", self._on_about_activate)
+
+ helpMenuItem = gtk.MenuItem("Help")
+ helpMenuItem.connect("activate", self.get_help)
+
+ booksMenu = gtk.Menu()
+ booksMenu.append(self._audiobookMenuItem)
+ booksMenu.append(self._chapterMenuItem)
+ booksMenu.append(self._sleepMenuItem)
+ booksMenu.append(settingsMenuItem)
+ booksMenu.append(helpMenuItem)
+ booksMenu.append(aboutMenuItem)
+
+ booksMenuItem = gtk.MenuItem("Books")
+ booksMenuItem.show()
+ booksMenuItem.set_submenu(booksMenu)
+ self._menuBar.append(booksMenuItem)
+ self._menuBar.show_all()
+
+ def setup_timers(self):
+ self.seek_timer = timeout_add_seconds(3, self.update_seek)
+
+ def save_settings(self):
+ config = ConfigParser.SafeConfigParser()
+ self._save_settings(config)
+ with open(constants._user_settings_, "wb") as configFile:
+ config.write(configFile)
+ self.controller.save()
+
+ def _save_settings(self, config):
+ config.add_section(constants.__pretty_app_name__)
+ config.set(constants.__pretty_app_name__, "portrait", str(self.__isPortrait))
+ config.set(constants.__pretty_app_name__, "fullscreen", str(self.__window_in_fullscreen))
+ config.set(constants.__pretty_app_name__, "audiopath", self.controller.get_books_path())
+
+ def load_settings(self):
+ config = ConfigParser.SafeConfigParser()
+ config.read(constants._user_settings_)
+ self._load_settings(config)
+
+ def _load_settings(self, config):
+ isPortrait = False
+ window_in_fullscreen = False
+ booksPath = constants._default_book_path_
+ try:
+ isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait")
+ window_in_fullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen")
+ booksPath = config.get(constants.__pretty_app_name__, "audiopath")
+ except ConfigParser.NoSectionError, e:
+ _moduleLogger.info(
+ "Settings file %s is missing section %s" % (
+ constants._user_settings_,
+ e.section,
+ )
+ )
+
+ if isPortrait ^ self.__isPortrait:
+ if isPortrait:
+ orientation = gtk.ORIENTATION_VERTICAL
+ else:
+ orientation = gtk.ORIENTATION_HORIZONTAL
+ self.set_orientation(orientation)
+
+ self.__window_in_fullscreen = window_in_fullscreen
+ if self.__window_in_fullscreen:
+ self.win.fullscreen()
+ else:
+ self.win.unfullscreen()
+
+ self.controller.load(booksPath)
+
+ @staticmethod
+ def __format_name(path):
+ if os.path.isfile(path):
+ return os.path.basename(path).rsplit(".", 1)[0]
+ else:
+ return os.path.basename(path)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_select_audiobook(self, *args):
+ if not self._bookSelection:
+ return
+ index = hildonize.touch_selector(
+ self.win,
+ "Audiobook",
+ (self.__format_name(bookPath) for bookPath in self._bookSelection),
+ self._bookSelectionIndex if 0 <= self._bookSelectionIndex else 0,
+ )
+ self._bookSelectionIndex = index
+ bookName = self._bookSelection[index]
+ self.controller.set_book(bookName)
+ self.set_button_text("Play", "Start playing the audiobook") # reset button
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_select_chapter(self, *args):
+ if not self._chapterSelection:
+ return
+ index = hildonize.touch_selector(
+ self.win,
+ "Chapter",
+ (self.__format_name(chapterPath) for chapterPath in self._chapterSelection),
+ self._chapterSelectionIndex if 0 <= self._chapterSelectionIndex else 0,
+ )
+ self._chapterSelectionIndex = index
+ chapterName = self._chapterSelection[index]
+ self.controller.set_chapter(chapterName)
+ self.set_button_text("Play", "Start playing the audiobook") # reset button
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_select_sleep(self, *args):
+ if self.sleep_timer is not None:
+ gobject.source_remove(self.sleep_timer)
+
+ try:
+ index = hildonize.touch_selector(
+ self.win,
+ "Sleeptimer",
+ self._sleepSelection,
+ self._sleepSelectionIndex if 0 <= self._sleepSelectionIndex else 0,
+ )
+ except RuntimeError:
+ _moduleLogger.exception("Handling as if user cancelled")
+ hildonize.show_information_banner(self.win, "Sleep timer canceled")
+ index = 0
+
+ self._sleepSelectionIndex = index
+ sleepName = self._sleepSelection[index]
+
+ time_out = int(sleepName)
+ if 0 < time_out:
+ timeout_add_seconds(time_out * 60, self.sleep)
+
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ self.sleeptime_button.set_text("Sleeptimer", sleepName)
+ else:
+ self._sleepMenuItem.get_child().set_text("Sleeptimer: %s" % (sleepName, ))
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def __on_call_started(self, callMonitor):
+ self.pause()
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_settings(self, *args):
+ if self.__settingsWindow is None:
+ vbox = gtk.VBox()
+ self.__settingsManager = settings.SettingsDialog(vbox)
+
+ self.__settingsWindow = gtk.Window()
+ self.__settingsWindow.add(vbox)
+ self.__settingsWindow = hildonize.hildonize_window(self._app, self.__settingsWindow)
+ self.__settingsManager.window = self.__settingsWindow
+
+ self.__settingsWindow.set_title("Settings")
+ self.__settingsWindow.set_transient_for(self.win)
+ self.__settingsWindow.set_default_size(*self.win.get_size())
+ self.__settingsWindow.connect("delete-event", self._on_settings_delete)
+ self.__settingsManager.set_portrait_state(self.__isPortrait)
+ self.__settingsManager.set_audiobook_path(self.controller.get_books_path())
+ self.__settingsWindow.set_modal(True)
+ self.__settingsWindow.show_all()
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_settings_delete(self, *args):
+ self.__settingsWindow.emit_stop_by_name("delete-event")
+ self.__settingsWindow.hide()
+ self.__settingsWindow.set_modal(False)
+
+ isPortrait = self.__settingsManager.is_portrait()
+ if isPortrait ^ self.__isPortrait:
+ if isPortrait:
+ orientation = gtk.ORIENTATION_VERTICAL
+ else:
+ orientation = gtk.ORIENTATION_HORIZONTAL
+ self.set_orientation(orientation)
+ if self.__settingsManager.get_audiobook_path() != self.controller.get_books_path():
+ self.controller.reload(self.__settingsManager.get_audiobook_path())
+
+ return True
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def update_seek(self):
+ #print self.controller.get_percentage()
+ if self.controller.is_playing():
+ gtk.gdk.threads_enter()
+ self.seek.set_value(self.controller.get_percentage() * 100)
+ gtk.gdk.threads_leave()
+ #self.controller.get_percentage()
+ return True # run again
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def sleep(self):
+ _moduleLogger.info("sleep time timeout")
+ hildonize.show_information_banner(self.win, "Sleep timer")
+ self.controller.stop()
+ self.set_button_text("Resume", "Resume playing the audiobook")
+ return False # do not repeat
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def get_help(self, button):
+ Browser.open("file:///opt/Nqa-Audiobook-player/Help/nqaap.html")
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def seek_changed(self, seek, scroll , value):
+ # print "sok", scroll
+ self.controller.seek_percent(seek.get_value() / 100.0)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_next_chapter(self, *args):
+ self.controller.next_chapter()
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_previous_chapter(self, *args):
+ self.controller.previous_chapter()
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def play_pressed(self, button):
+ if self.controller.is_playing():
+ self.pause()
+ else:
+ self.play()
+
+ @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.win.unfullscreen ()
+ else:
+ self.win.fullscreen ()
+ return True
+ elif event.keyval == gtk.keysyms.o and isCtrl:
+ self._toggle_rotate()
+ return True
+ elif (
+ event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
+ event.get_state() & gtk.gdk.CONTROL_MASK
+ ):
+ self.quit()
+ 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
+ elif event.keyval in RETURN_TYPES:
+ if self.controller.is_playing():
+ self.pause()
+ else:
+ self.play()
+ return True
+ elif event.keyval == gtk.keysyms.Left:
+ self.controller.previous_chapter()
+ return True
+ elif event.keyval == gtk.keysyms.Right:
+ self.controller.next_chapter()
+ return True
+
+ @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 quit(self, *args): # what are the arguments?
+ _moduleLogger.info("Shutting down")
+ try:
+ self.save_settings()
+ self.controller.stop() # to save the state
+ finally:
+ gtk.main_quit()
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_about_activate(self, *args):
+ dlg = gtk.AboutDialog()
+ dlg.set_name(constants.__pretty_app_name__)
+ dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
+ dlg.set_copyright("Copyright 2010")
+ dlg.set_comments("")
+ dlg.set_website("http://nqaap.garage.maemo.org/")
+ dlg.set_authors(["Pengman <pengmeister@gmail.com>", "Ed Page <eopage@byu.net>"])
+ dlg.run()
+ dlg.destroy()
+
+ # Actions:
+
+ def play(self):
+ self.set_button_text("Stop", "Stop playing the audiobook")
+ self.controller.play()
+
+ def pause(self):
+ self.set_button_text("Resume", "Resume playing the audiobook")
+ self.controller.stop()
+
+ def set_orientation(self, orientation):
+ if orientation == gtk.ORIENTATION_VERTICAL:
+ if self.__isPortrait:
+ return
+ hildonize.window_to_portrait(self.win)
+ self.__isPortrait = True
+
+ self._viewLayout.remove(self._controlLayout)
+ self._mainLayout.add(self._controlLayout)
+ elif orientation == gtk.ORIENTATION_HORIZONTAL:
+ if not self.__isPortrait:
+ return
+ hildonize.window_to_landscape(self.win)
+ self.__isPortrait = False
+
+ self._mainLayout.remove(self._controlLayout)
+ self._viewLayout.add(self._controlLayout)
+ else:
+ raise NotImplementedError(orientation)
+
+ def get_orientation(self):
+ return gtk.ORIENTATION_VERTICAL if self.__isPortrait else gtk.ORIENTATION_HORIZONTAL
+
+ def _toggle_rotate(self):
+ if self.__isPortrait:
+ self.set_orientation(gtk.ORIENTATION_HORIZONTAL)
+ else:
+ self.set_orientation(gtk.ORIENTATION_VERTICAL)
+
+ def change_chapter(self, chapterName):
+ if chapterName is None:
+ _moduleLogger.debug("chapter selection canceled.")
+ return True # this should end the function and indicate it has been handled
+
+ _moduleLogger.debug("chapter changed (by controller) to: %s" % chapterName)
+
+ def set_button_text(self, title, text):
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ self.button.set_text(title, text)
+ else:
+ self.button.set_label("%s" % (title, ))
+
+ def set_books(self, books):
+ _moduleLogger.debug("new books")
+ del self._bookSelection[:]
+ self._bookSelection.extend(books)
+ if len(books) == 0 and self.controller is not None:
+ hildonize.show_information_banner(self.win, "No audiobooks found. \nPlease place your audiobooks in the directory %s" % self.controller.get_books_path())
+
+ def set_book(self, bookPath, cover):
+ bookName = self.__format_name(bookPath)
+
+ self.set_button_text("Play", "Start playing the audiobook") # reset button
+ self._set_display_title(bookName)
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ self.book_button.set_text("Audiobook", bookName)
+ else:
+ self._audiobookMenuItem.get_child().set_text("Audiobook: %s" % (bookName, ))
+ if cover != "":
+ self.cover.set_from_file(cover)
+
+ def set_chapter(self, chapterIndex):
+ '''
+ Called from controller whenever a new chapter is started
+
+ chapter parameter is supposed to be the index for the chapter, not the name
+ '''
+ self.auto_chapter_selected = True
+ self._set_display_chapter(str(chapterIndex + 1))
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ self.chapter_button.set_text("Chapter", str(chapterIndex))
+ else:
+ self._chapterMenuItem.get_child().set_text("Chapter: %s" % (chapterIndex+1, ))
+
+ def set_chapters(self, chapters):
+ _moduleLogger.debug("setting chapters" )
+ del self._chapterSelection[:]
+ self._chapterSelection.extend(chapters)
+
+ def set_sleep_timer(self, mins):
+ pass
+
+ # Utils
+ def set_selected_value(self, button, value):
+ i = button.get_selector().get_model(0).index[value] # get index of value from list
+ button.set_active(i) # set active index to that index
+
+ def _set_display_title(self, title):
+ self.title.set_markup("<b><big>%s</big></b>" % title)
+
+ def _set_display_chapter(self, chapter):
+ self.chapter.set_markup("<b><big>Chapter %s</big></b>" % chapter)
+
+
+def _old_timeout_add_seconds(timeout, callback):
+ return gobject.timeout_add(timeout * 1000, callback)
+
+
+def _timeout_add_seconds(timeout, callback):
+ return gobject.timeout_add_seconds(timeout, callback)
+
+
+try:
+ gobject.timeout_add_seconds
+ timeout_add_seconds = _timeout_add_seconds
+except AttributeError:
+ timeout_add_seconds = _old_timeout_add_seconds
+
+
+if __name__ == "__main__":
+ g = Gui(None)
--- /dev/null
+import os
+import threading
+import time
+import logging
+
+import constants
+import hildonize
+import Audiobook
+import FileStorage
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class Player(object):
+
+ def __init__(self, ui):
+ self.storage = FileStorage.FileStorage(path = constants._data_path_)
+ if hildonize.IS_HILDON_SUPPORTED and not hildonize.IS_FREMANTLE_SUPPORTED:
+ import SimpleOSSOPlayer as _SimplePlayer
+ SimplePlayer = _SimplePlayer # silence PyFlakes
+ else:
+ import SimpleGStreamer as SimplePlayer
+ self.player = SimplePlayer.SimplePlayer(self.next_chapter)
+ self.ui = ui
+ self.audiobook = None
+ self._bookDir = None
+ self._bookPaths = {}
+
+ def get_books_path(self):
+ return self._bookDir
+
+ def reload(self, booksPath):
+ if self.audiobook is not None:
+ position = self.player.elapsed()
+ self.storage.set_time(self.audiobook.current_chapter, position)
+ self.save()
+ self.load(booksPath)
+
+ def load(self, booksPath):
+ _moduleLogger.info("Loading books from %s" % booksPath)
+ self.storage.load()
+ self._bookDir = booksPath
+
+ self._bookPaths = dict(
+ (self.__format_name(bookPath), bookPath)
+ for bookPath in self._find_books()
+ )
+ if self.ui is not None:
+ bookPaths = self._bookPaths.values()
+ bookPaths.sort()
+ self.ui.set_books(bookPaths)
+
+ lastBookName = self.storage.get_selected()
+ if lastBookName is not None:
+ _moduleLogger.info("continuing book: %s" % lastBookName)
+ try:
+ bookPath = self._bookPaths[lastBookName]
+ self.set_book(bookPath)
+ except KeyError:
+ _moduleLogger.exception("Audiobook was not found")
+ except IndexError:
+ _moduleLogger.exception("Chapter was not found")
+ except IOError:
+ _moduleLogger.exception("Audiobook could not be loaded")
+ except Exception:
+ _moduleLogger.exception("Can you say 'confusion'?")
+
+ def save(self):
+ position = self.player.elapsed()
+ if self.audiobook is not None:
+ self.storage.set_time(self.audiobook.current_chapter, position)
+ self.storage.save()
+
+ @staticmethod
+ def __format_name(path):
+ if os.path.isfile(path):
+ return os.path.basename(path).rsplit(".", 1)[0]
+ else:
+ return os.path.basename(path)
+
+ def set_book(self, bookPath):
+ oldBookName = self.storage.get_selected()
+ try:
+ bookName = self.__format_name(bookPath)
+ self.storage.select_book(bookName)
+ chapter_num, _ = self.storage.get_time()
+ self.audiobook = Audiobook.Audiobook(
+ bookPath,
+ chapter_num
+ )
+ except:
+ self.storage.select_book(oldBookName)
+ raise
+
+ # self.player.set_file(self.audiobook.get_current_chapter())
+ # self.player.seek_time(time)
+
+ if self.ui is not None:
+ self.ui.set_book(bookPath, self.audiobook.get_cover_img())
+ self.ui.set_chapters(self.audiobook.chapters)
+
+ chapter_title = self.audiobook.chapters[self.audiobook.current_chapter]
+ self.set_chapter(chapter_title, True)
+
+ def set_chapter(self, chapter, continuing = False):
+ _moduleLogger.info("set chapter:" + chapter + " : Continuing: " + str(continuing))
+ self.audiobook.set_chapter(chapter)
+ self.player.set_file(self.audiobook.get_current_chapter())
+ if not continuing:
+ self.storage.set_time(self.audiobook.current_chapter, 0)
+
+ if self.ui is not None:
+ self.ui.set_chapter(self.audiobook.current_chapter)
+
+ def previous_chapter(self, *args):
+ _moduleLogger.info("Going back a chapter")
+ self.player.stop()
+ next_file = self.audiobook.get_previous_chapter()
+ if next_file is not False:
+ self.set_chapter(next_file)
+ self.player.play()
+ else: # the book is over
+ self.storage.set_time(0, 0)
+
+ def next_chapter(self, *args):
+ _moduleLogger.info("Advancing a chapter")
+ self.player.stop()
+ next_file = self.audiobook.get_next_chapter()
+ if next_file is not False:
+ self.set_chapter(next_file)
+ self.player.play()
+ else: # the book is over
+ self.storage.set_time(0, 0)
+
+ def play(self):
+ if self.audiobook is not None:
+ self.player.play()
+ _, target_time = self.storage.get_time()
+ if 0 < target_time:
+ time.sleep(1)
+ self.player.seek_time(target_time)
+ #print self.player.elapsed()
+ else:
+ print "No book selected, find one in ", self._bookDir
+
+ def stop(self):
+ position = self.player.elapsed()
+ self.player.stop()
+
+ if self.audiobook is not None:
+ self.storage.set_time(self.audiobook.current_chapter, position)
+
+ def is_playing(self):
+ return self.player.playing
+
+ def sleeptimer(self, secs):
+ #print "sleeper", secs
+ time.sleep(secs)
+ #print "now its time to sleep"
+ self.stop()
+
+ def start_sleeptimer(self, secs):
+ #print "startin sleep"
+ sleep_thread = threading.Thread(target=self.sleeptimer, args=(secs, ))
+ sleep_thread.start()
+ #print "started sleep"
+
+ def get_percentage(self):
+ try:
+ return float(self.player.elapsed()) / float(self.player.duration())
+ except ZeroDivisionError:
+ return 0.0
+
+ def seek_percent(self, ratio):
+ try:
+ target = int(self.player.duration() * ratio) # Calculate where to seek
+ self.player.seek_time(target) # seek
+
+ position = self.player.elapsed()
+ self.storage.set_time(self.audiobook.current_chapter, target) # save position
+ return True
+ except:
+ _moduleLogger.exception("Seek failed")
+ return False
+
+ def _find_books(self):
+ try:
+ paths = (
+ os.path.join(self._bookDir, f)
+ for f in os.listdir(self._bookDir)
+ )
+ return (
+ path
+ for path in paths
+ if Audiobook.is_book(path)
+ )
+ except OSError:
+ return ()
--- /dev/null
+import os
+import logging
+
+import gst
+
+import gtk_toolbox
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class SimplePlayer(object):
+
+ # @todo Add pitch/speed control
+ # http://github.com/jwagner/playitslowly/blob/master/playitslowly/pipeline.py
+
+ def __init__(self, on_playing_done = None):
+ #Fields
+ self.playing = False
+ self.__filename = ""
+ self.__elapsed = 0
+ self.__duration = 0
+
+ #Event callbacks
+ self.on_playing_done = on_playing_done
+
+ #Set up GStreamer
+ self.player = gst.element_factory_make("playbin2", "player")
+ fakesink = gst.element_factory_make("fakesink", "fakesink")
+ self.player.set_property("video-sink", fakesink)
+ bus = self.player.get_bus()
+ bus.add_signal_watch()
+ bus.connect("message", self.on_message)
+
+ #Constants
+ self.time_format = gst.Format(gst.FORMAT_TIME)
+ self.seek_flag = gst.SEEK_FLAG_FLUSH
+
+ @property
+ def has_file(self):
+ return 0 < len(self.__filename)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def on_message(self, bus, message):
+ t = message.type
+ if t == gst.MESSAGE_EOS: # End-Of-Stream
+ self.player.set_state(gst.STATE_NULL)
+ self.playing = False
+ if self.on_playing_done is not None: # event callback
+ self.on_playing_done(self)
+ elif t == gst.MESSAGE_ERROR:
+ self.player.set_state(gst.STATE_NULL)
+ err, debug = message.parse_error()
+ #print "Error: %s" % err, debug
+ _moduleLogger.error("Error: %s, (%s)" % (err, debug))
+ self.playing = False
+
+ def set_file(self, file):
+ _moduleLogger.info("set file: %s", file)
+ if os.path.isfile(file):
+ if self.__filename != file:
+ self._invalidate_cache()
+ if self.playing:
+ self.stop()
+
+ file = os.path.abspath(file) # ensure absolute path
+ _moduleLogger.debug("set file (absolute path): %s "%file)
+ self.player.set_property("uri", "file://" + file)
+ self.__filename = file
+ else:
+ _moduleLogger.error("File: %s not found" % file)
+
+ def play(self):
+ _moduleLogger.info("Started playing")
+ self.player.set_state(gst.STATE_PLAYING)
+ self.playing = True
+
+ def stop(self):
+ self.player.set_state(gst.STATE_NULL)
+ self.playing = False
+ _moduleLogger.info("Stopped playing")
+
+ def elapsed(self):
+ try:
+ self.__elapsed = self.player.query_position(self.time_format, None)[0]
+ except:
+ pass
+ return self.__elapsed
+
+ def duration(self):
+ try:
+ self.__duration = self.player.query_duration(self.time_format, None)[0]
+ except:
+ _moduleLogger.exception("Query failed")
+ pass
+ return self.__duration
+
+ def seek_time(self, ns):
+ _moduleLogger.debug("Seeking to: %s", ns)
+ self.player.seek_simple(self.time_format, self.seek_flag, ns)
+
+ def _invalidate_cache(self):
+ self.__elapsed = 0
+ self.__duration = 0
+
+ def __seek_percent(self, percent):
+ format = gst.Format(gst.FORMAT_PERCENT)
+ self.player.seek_simple(format, self.seek_flag, percent)
--- /dev/null
+import os
+import logging
+
+import dbus
+
+import gtk_toolbox
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class SimplePlayer(object):
+
+ SERVICE_NAME = "com.nokia.osso_media_server"
+ OBJECT_PATH = "/com/nokia/osso_media_server"
+ AUDIO_INTERFACE_NAME = "com.nokia.osso_media_server.music"
+
+ def __init__(self, on_playing_done = None):
+ #Fields
+ self.has_file = False
+ self.playing = False
+ self.__elapsed = 0
+
+ #Event callbacks
+ self.on_playing_done = on_playing_done
+
+ session_bus = dbus.SessionBus()
+
+ # Get the osso-media-player proxy object
+ oms_object = session_bus.get_object(
+ self.SERVICE_NAME,
+ self.OBJECT_PATH,
+ introspect=False,
+ follow_name_owner_changes=True,
+ )
+ # Use the audio interface
+ oms_audio_interface = dbus.Interface(
+ oms_object,
+ self.AUDIO_INTERFACE_NAME,
+ )
+ self._audioProxy = oms_audio_interface
+
+ self._audioProxy.connect_to_signal("state_changed", self._on_state_changed)
+ self._audioProxy.connect_to_signal("end_of_stream", self._on_end_of_stream)
+
+ error_signals = [
+ "no_media_selected",
+ "file_not_found",
+ "type_not_found",
+ "unsupported_type",
+ "gstreamer",
+ "dsp",
+ "device_unavailable",
+ "corrupted_file",
+ "out_of_memory",
+ "audio_codec_not_supported",
+ ]
+ for error in error_signals:
+ self._audioProxy.connect_to_signal(error, self._on_error)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_error(self, *args):
+ self.playing = False
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_end_of_stream(self, *args):
+ self.playing = False
+ if self.on_playing_done is not None: # event callback
+ self.on_playing_done(self)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_state_changed(self, state):
+ _moduleLogger.info("State: %s", state)
+
+ def set_file(self, file):
+ _moduleLogger.info("set file: %s", file)
+ if os.path.isfile(file):
+ if self.playing:
+ self.stop()
+
+ uri = "file://" + file
+ self._audioProxy.set_media_location(uri)
+ self.has_file = True
+ else:
+ _moduleLogger.error("File: %s not found" % file)
+
+ def play(self):
+ _moduleLogger.info("Started playing")
+ self._audioProxy.play()
+ self.playing = True
+
+ def stop(self):
+ self._audioProxy.stop()
+ self.playing = False
+ _moduleLogger.info("Stopped playing")
+
+ def elapsed(self):
+ pos_info = self._audioProxy.get_position()
+ if isinstance(pos_info, tuple):
+ pos, _ = pos_info
+ return pos
+ else:
+ return 0
+
+ def duration(self):
+ pos_info = self._audioProxy.get_position()
+ if isinstance(pos_info, tuple):
+ _, dur = pos_info
+ return dur
+ else:
+ return 0
+
+ def seek_time(self, ns):
+ _moduleLogger.debug("Seeking to: %s", ns)
+ self._audioProxy.seek( dbus.Int32(1), dbus.Int32(ns) )
--- /dev/null
+#!/usr/bin/env python
--- /dev/null
+import os
+
+__pretty_app_name__ = "nQaap"
+__app_name__ = "nqaap"
+__version__ = "0.8.6"
+__build__ = 0
+__app_magic__ = 0xdeadbeef
+_data_path_ = os.path.join(os.path.expanduser("~"), ".%s" % __app_name__)
+_user_settings_ = "%s/settings.ini" % _data_path_
+_user_logpath_ = "%s/%s.log" % (_data_path_, __app_name__)
+_default_book_path_ = os.path.join(os.path.expanduser("~"), "MyDocs/Audiobooks/")
--- /dev/null
+#!/usr/bin/python
+
+from __future__ import with_statement
+
+import os
+import errno
+import sys
+import time
+import itertools
+import functools
+import contextlib
+import logging
+import threading
+import Queue
+
+import gobject
+import gtk
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+def get_screen_orientation():
+ width, height = gtk.gdk.get_default_root_window().get_size()
+ if width < height:
+ return gtk.ORIENTATION_VERTICAL
+ else:
+ return gtk.ORIENTATION_HORIZONTAL
+
+
+def orientation_change_connect(handler, *args):
+ """
+ @param handler(orientation, *args) -> None(?)
+ """
+ initialScreenOrientation = get_screen_orientation()
+ orientationAndArgs = list(itertools.chain((initialScreenOrientation, ), args))
+
+ def _on_screen_size_changed(screen):
+ newScreenOrientation = get_screen_orientation()
+ if newScreenOrientation != orientationAndArgs[0]:
+ orientationAndArgs[0] = newScreenOrientation
+ handler(*orientationAndArgs)
+
+ rootScreen = gtk.gdk.get_default_root_window()
+ return gtk.connect(rootScreen, "size-changed", _on_screen_size_changed)
+
+
+@contextlib.contextmanager
+def flock(path, timeout=-1):
+ WAIT_FOREVER = -1
+ DELAY = 0.1
+ timeSpent = 0
+
+ acquired = False
+
+ while timeSpent <= timeout or timeout == WAIT_FOREVER:
+ try:
+ fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
+ acquired = True
+ break
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+ time.sleep(DELAY)
+ timeSpent += DELAY
+
+ assert acquired, "Failed to grab file-lock %s within timeout %d" % (path, timeout)
+
+ try:
+ yield fd
+ finally:
+ os.unlink(path)
+
+
+@contextlib.contextmanager
+def gtk_lock():
+ gtk.gdk.threads_enter()
+ try:
+ yield
+ finally:
+ gtk.gdk.threads_leave()
+
+
+def find_parent_window(widget):
+ while True:
+ parent = widget.get_parent()
+ if isinstance(parent, gtk.Window):
+ return parent
+ widget = parent
+
+
+def make_idler(func):
+ """
+ Decorator that makes a generator-function into a function that will continue execution on next call
+ """
+ a = []
+
+ @functools.wraps(func)
+ def decorated_func(*args, **kwds):
+ if not a:
+ a.append(func(*args, **kwds))
+ try:
+ a[0].next()
+ return True
+ except StopIteration:
+ del a[:]
+ return False
+
+ return decorated_func
+
+
+def asynchronous_gtk_message(original_func):
+ """
+ @note Idea came from http://www.aclevername.com/articles/python-webgui/
+ """
+
+ def execute(allArgs):
+ args, kwargs = allArgs
+ with gtk_lock():
+ original_func(*args, **kwargs)
+ return False
+
+ @functools.wraps(original_func)
+ def delayed_func(*args, **kwargs):
+ gobject.idle_add(execute, (args, kwargs))
+
+ return delayed_func
+
+
+def synchronous_gtk_message(original_func):
+ """
+ @note Idea came from http://www.aclevername.com/articles/python-webgui/
+ """
+
+ @functools.wraps(original_func)
+ def immediate_func(*args, **kwargs):
+ with gtk_lock():
+ return original_func(*args, **kwargs)
+
+ return immediate_func
+
+
+def autostart(func):
+ """
+ >>> @autostart
+ ... def grep_sink(pattern):
+ ... print "Looking for %s" % pattern
+ ... while True:
+ ... line = yield
+ ... if pattern in line:
+ ... print line,
+ >>> g = grep_sink("python")
+ Looking for python
+ >>> g.send("Yeah but no but yeah but no")
+ >>> g.send("A series of tubes")
+ >>> g.send("python generators rock!")
+ python generators rock!
+ >>> g.close()
+ """
+
+ @functools.wraps(func)
+ def start(*args, **kwargs):
+ cr = func(*args, **kwargs)
+ cr.next()
+ return cr
+
+ return start
+
+
+@autostart
+def printer_sink(format = "%s"):
+ """
+ >>> pr = printer_sink("%r")
+ >>> pr.send("Hello")
+ 'Hello'
+ >>> pr.send("5")
+ '5'
+ >>> pr.send(5)
+ 5
+ >>> p = printer_sink()
+ >>> p.send("Hello")
+ Hello
+ >>> p.send("World")
+ World
+ >>> # p.throw(RuntimeError, "Goodbye")
+ >>> # p.send("Meh")
+ >>> # p.close()
+ """
+ while True:
+ item = yield
+ print format % (item, )
+
+
+@autostart
+def null_sink():
+ """
+ Good for uses like with cochain to pick up any slack
+ """
+ while True:
+ item = yield
+
+
+@autostart
+def comap(function, target):
+ """
+ >>> p = printer_sink()
+ >>> cm = comap(lambda x: x+1, p)
+ >>> cm.send((0, ))
+ 1
+ >>> cm.send((1.0, ))
+ 2.0
+ >>> cm.send((-2, ))
+ -1
+ """
+ while True:
+ try:
+ item = yield
+ mappedItem = function(*item)
+ target.send(mappedItem)
+ except Exception, e:
+ _moduleLogger.exception("Forwarding exception!")
+ target.throw(e.__class__, str(e))
+
+
+def _flush_queue(queue):
+ while not queue.empty():
+ yield queue.get()
+
+
+@autostart
+def queue_sink(queue):
+ """
+ >>> q = Queue.Queue()
+ >>> qs = queue_sink(q)
+ >>> qs.send("Hello")
+ >>> qs.send("World")
+ >>> qs.throw(RuntimeError, "Goodbye")
+ >>> qs.send("Meh")
+ >>> qs.close()
+ >>> print [i for i in _flush_queue(q)]
+ [(None, 'Hello'), (None, 'World'), (<type 'exceptions.RuntimeError'>, 'Goodbye'), (None, 'Meh'), (<type 'exceptions.GeneratorExit'>, None)]
+ """
+ while True:
+ try:
+ item = yield
+ queue.put((None, item))
+ except Exception, e:
+ queue.put((e.__class__, str(e)))
+ except GeneratorExit:
+ queue.put((GeneratorExit, None))
+ raise
+
+
+def decode_item(item, target):
+ if item[0] is None:
+ target.send(item[1])
+ return False
+ elif item[0] is GeneratorExit:
+ target.close()
+ return True
+ else:
+ target.throw(item[0], item[1])
+ return False
+
+
+def nonqueue_source(queue, target):
+ isDone = False
+ while not isDone:
+ item = queue.get()
+ isDone = decode_item(item, target)
+ while not queue.empty():
+ queue.get_nowait()
+
+
+def threaded_stage(target, thread_factory = threading.Thread):
+ messages = Queue.Queue()
+
+ run_source = functools.partial(nonqueue_source, messages, target)
+ thread = thread_factory(target=run_source)
+ thread.setDaemon(True)
+ thread.start()
+
+ # Sink running in current thread
+ return queue_sink(messages)
+
+
+def log_exception(logger):
+
+ def log_exception_decorator(func):
+
+ @functools.wraps(func)
+ def wrapper(*args, **kwds):
+ try:
+ return func(*args, **kwds)
+ except Exception:
+ logger.exception(func.__name__)
+
+ return wrapper
+
+ return log_exception_decorator
+
+
+class LoginWindow(object):
+
+ def __init__(self, widgetTree):
+ """
+ @note Thread agnostic
+ """
+ self._dialog = widgetTree.get_widget("loginDialog")
+ self._parentWindow = widgetTree.get_widget("mainWindow")
+ self._serviceCombo = widgetTree.get_widget("serviceCombo")
+ self._usernameEntry = widgetTree.get_widget("usernameentry")
+ self._passwordEntry = widgetTree.get_widget("passwordentry")
+
+ self._serviceList = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING)
+ self._serviceCombo.set_model(self._serviceList)
+ cell = gtk.CellRendererText()
+ self._serviceCombo.pack_start(cell, True)
+ self._serviceCombo.add_attribute(cell, 'text', 1)
+ self._serviceCombo.set_active(0)
+
+ widgetTree.get_widget("loginbutton").connect("clicked", self._on_loginbutton_clicked)
+ widgetTree.get_widget("logins_close_button").connect("clicked", self._on_loginclose_clicked)
+
+ def request_credentials(self,
+ parentWindow = None,
+ defaultCredentials = ("", "")
+ ):
+ """
+ @note UI Thread
+ """
+ if parentWindow is None:
+ parentWindow = self._parentWindow
+
+ self._serviceCombo.hide()
+ self._serviceList.clear()
+
+ self._usernameEntry.set_text(defaultCredentials[0])
+ self._passwordEntry.set_text(defaultCredentials[1])
+
+ try:
+ self._dialog.set_transient_for(parentWindow)
+ self._dialog.set_default_response(gtk.RESPONSE_OK)
+ response = self._dialog.run()
+ if response != gtk.RESPONSE_OK:
+ raise RuntimeError("Login Cancelled")
+
+ username = self._usernameEntry.get_text()
+ password = self._passwordEntry.get_text()
+ self._passwordEntry.set_text("")
+ finally:
+ self._dialog.hide()
+
+ return username, password
+
+ def request_credentials_from(self,
+ services,
+ parentWindow = None,
+ defaultCredentials = ("", "")
+ ):
+ """
+ @note UI Thread
+ """
+ if parentWindow is None:
+ parentWindow = self._parentWindow
+
+ self._serviceList.clear()
+ for serviceIdserviceName in services:
+ self._serviceList.append(serviceIdserviceName)
+ self._serviceCombo.set_active(0)
+ self._serviceCombo.show()
+
+ self._usernameEntry.set_text(defaultCredentials[0])
+ self._passwordEntry.set_text(defaultCredentials[1])
+
+ try:
+ self._dialog.set_transient_for(parentWindow)
+ self._dialog.set_default_response(gtk.RESPONSE_OK)
+ response = self._dialog.run()
+ if response != gtk.RESPONSE_OK:
+ raise RuntimeError("Login Cancelled")
+
+ username = self._usernameEntry.get_text()
+ password = self._passwordEntry.get_text()
+ finally:
+ self._dialog.hide()
+
+ itr = self._serviceCombo.get_active_iter()
+ serviceId = int(self._serviceList.get_value(itr, 0))
+ self._serviceList.clear()
+ return serviceId, username, password
+
+ def _on_loginbutton_clicked(self, *args):
+ self._dialog.response(gtk.RESPONSE_OK)
+
+ def _on_loginclose_clicked(self, *args):
+ self._dialog.response(gtk.RESPONSE_CANCEL)
+
+
+def safecall(f, errorDisplay=None, default=None, exception=Exception):
+ '''
+ Returns modified f. When the modified f is called and throws an
+ exception, the default value is returned
+ '''
+ def _safecall(*args, **argv):
+ try:
+ return f(*args,**argv)
+ except exception, e:
+ if errorDisplay is not None:
+ errorDisplay.push_exception(e)
+ return default
+ return _safecall
+
+
+class ErrorDisplay(object):
+
+ def __init__(self, widgetTree):
+ super(ErrorDisplay, self).__init__()
+ self.__errorBox = widgetTree.get_widget("errorEventBox")
+ self.__errorDescription = widgetTree.get_widget("errorDescription")
+ self.__errorClose = widgetTree.get_widget("errorClose")
+ self.__parentBox = self.__errorBox.get_parent()
+
+ self.__errorBox.connect("button_release_event", self._on_close)
+
+ self.__messages = []
+ self.__parentBox.remove(self.__errorBox)
+
+ def push_message_with_lock(self, message):
+ with gtk_lock():
+ self.push_message(message)
+
+ def push_message(self, message):
+ self.__messages.append(message)
+ if 1 == len(self.__messages):
+ self.__show_message(message)
+
+ def push_exception_with_lock(self):
+ with gtk_lock():
+ self.push_exception()
+
+ def push_exception(self):
+ userMessage = str(sys.exc_info()[1])
+ self.push_message(userMessage)
+ _moduleLogger.exception(userMessage)
+
+ def pop_message(self):
+ del self.__messages[0]
+ if 0 == len(self.__messages):
+ self.__hide_message()
+ else:
+ self.__errorDescription.set_text(self.__messages[0])
+
+ def _on_close(self, *args):
+ self.pop_message()
+
+ def __show_message(self, message):
+ self.__errorDescription.set_text(message)
+ self.__parentBox.pack_start(self.__errorBox, False, False)
+ self.__parentBox.reorder_child(self.__errorBox, 1)
+
+ def __hide_message(self):
+ self.__errorDescription.set_text("")
+ self.__parentBox.remove(self.__errorBox)
+
+
+class DummyErrorDisplay(object):
+
+ def __init__(self):
+ super(DummyErrorDisplay, self).__init__()
+
+ self.__messages = []
+
+ def push_message_with_lock(self, message):
+ self.push_message(message)
+
+ def push_message(self, message):
+ if 0 < len(self.__messages):
+ self.__messages.append(message)
+ else:
+ self.__show_message(message)
+
+ def push_exception(self, exception = None):
+ userMessage = str(sys.exc_value)
+ _moduleLogger.exception(userMessage)
+
+ def pop_message(self):
+ if 0 < len(self.__messages):
+ self.__show_message(self.__messages[0])
+ del self.__messages[0]
+
+ def __show_message(self, message):
+ _moduleLogger.debug(message)
+
+
+class MessageBox(gtk.MessageDialog):
+
+ def __init__(self, message):
+ parent = None
+ gtk.MessageDialog.__init__(
+ self,
+ parent,
+ gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_OK,
+ message,
+ )
+ self.set_default_response(gtk.RESPONSE_OK)
+ self.connect('response', self._handle_clicked)
+
+ def _handle_clicked(self, *args):
+ self.destroy()
+
+
+class MessageBox2(gtk.MessageDialog):
+
+ def __init__(self, message):
+ parent = None
+ gtk.MessageDialog.__init__(
+ self,
+ parent,
+ gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_OK,
+ message,
+ )
+ self.set_default_response(gtk.RESPONSE_OK)
+ self.connect('response', self._handle_clicked)
+
+ def _handle_clicked(self, *args):
+ self.destroy()
+
+
+class PopupCalendar(object):
+
+ def __init__(self, parent, displayDate, title = ""):
+ self._displayDate = displayDate
+
+ self._calendar = gtk.Calendar()
+ self._calendar.select_month(self._displayDate.month, self._displayDate.year)
+ self._calendar.select_day(self._displayDate.day)
+ self._calendar.set_display_options(
+ gtk.CALENDAR_SHOW_HEADING |
+ gtk.CALENDAR_SHOW_DAY_NAMES |
+ gtk.CALENDAR_NO_MONTH_CHANGE |
+ 0
+ )
+ self._calendar.connect("day-selected", self._on_day_selected)
+
+ self._popupWindow = gtk.Window()
+ self._popupWindow.set_title(title)
+ self._popupWindow.add(self._calendar)
+ self._popupWindow.set_transient_for(parent)
+ self._popupWindow.set_modal(True)
+ self._popupWindow.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+ self._popupWindow.set_skip_pager_hint(True)
+ self._popupWindow.set_skip_taskbar_hint(True)
+
+ def run(self):
+ self._popupWindow.show_all()
+
+ def _on_day_selected(self, *args):
+ try:
+ self._calendar.select_month(self._displayDate.month, self._displayDate.year)
+ self._calendar.select_day(self._displayDate.day)
+ except Exception, e:
+ _moduleLogger.exception(e)
+
+
+if __name__ == "__main__":
+ if False:
+ import datetime
+ cal = PopupCalendar(None, datetime.datetime.now())
+ cal._popupWindow.connect("destroy", lambda w: gtk.main_quit())
+ cal.run()
+
+ gtk.main()
--- /dev/null
+#!/usr/bin/env python
+
+"""
+Open Issues
+ @bug not all of a message is shown
+ @bug Buttons are too small
+"""
+
+
+import gobject
+import gtk
+import dbus
+
+
+class _NullHildonModule(object):
+ pass
+
+
+try:
+ import hildon as _hildon
+ hildon = _hildon # Dumb but gets around pyflakiness
+except (ImportError, OSError):
+ hildon = _NullHildonModule
+
+
+IS_HILDON_SUPPORTED = hildon is not _NullHildonModule
+
+
+class _NullHildonProgram(object):
+
+ def add_window(self, window):
+ pass
+
+
+def _hildon_get_app_class():
+ return hildon.Program
+
+
+def _null_get_app_class():
+ return _NullHildonProgram
+
+
+try:
+ hildon.Program
+ get_app_class = _hildon_get_app_class
+except AttributeError:
+ get_app_class = _null_get_app_class
+
+
+def _hildon_set_application_name(name):
+ gtk.set_application_name(name)
+
+
+def _null_set_application_name(name):
+ pass
+
+
+try:
+ gtk.set_application_name
+ set_application_name = _hildon_set_application_name
+except AttributeError:
+ set_application_name = _null_set_application_name
+
+
+def _fremantle_hildonize_window(app, window):
+ oldWindow = window
+ newWindow = hildon.StackableWindow()
+ if oldWindow.get_child() is not None:
+ oldWindow.get_child().reparent(newWindow)
+ app.add_window(newWindow)
+ return newWindow
+
+
+def _hildon_hildonize_window(app, window):
+ oldWindow = window
+ newWindow = hildon.Window()
+ if oldWindow.get_child() is not None:
+ oldWindow.get_child().reparent(newWindow)
+ app.add_window(newWindow)
+ return newWindow
+
+
+def _null_hildonize_window(app, window):
+ return window
+
+
+try:
+ hildon.StackableWindow
+ hildonize_window = _fremantle_hildonize_window
+except AttributeError:
+ try:
+ hildon.Window
+ hildonize_window = _hildon_hildonize_window
+ except AttributeError:
+ hildonize_window = _null_hildonize_window
+
+
+def _fremantle_hildonize_menu(window, gtkMenu):
+ appMenu = hildon.AppMenu()
+ window.set_app_menu(appMenu)
+ gtkMenu.get_parent().remove(gtkMenu)
+ return appMenu
+
+
+def _hildon_hildonize_menu(window, gtkMenu):
+ hildonMenu = gtk.Menu()
+ for child in gtkMenu.get_children():
+ child.reparent(hildonMenu)
+ window.set_menu(hildonMenu)
+ gtkMenu.destroy()
+ return hildonMenu
+
+
+def _null_hildonize_menu(window, gtkMenu):
+ return gtkMenu
+
+
+try:
+ hildon.AppMenu
+ GTK_MENU_USED = False
+ IS_FREMANTLE_SUPPORTED = True
+ hildonize_menu = _fremantle_hildonize_menu
+except AttributeError:
+ GTK_MENU_USED = True
+ IS_FREMANTLE_SUPPORTED = False
+ if IS_HILDON_SUPPORTED:
+ hildonize_menu = _hildon_hildonize_menu
+ else:
+ hildonize_menu = _null_hildonize_menu
+
+
+def _hildon_set_button_auto_selectable(button):
+ button.set_theme_size(hildon.HILDON_SIZE_AUTO_HEIGHT)
+
+
+def _null_set_button_auto_selectable(button):
+ pass
+
+
+try:
+ hildon.HILDON_SIZE_AUTO_HEIGHT
+ gtk.Button.set_theme_size
+ set_button_auto_selectable = _hildon_set_button_auto_selectable
+except AttributeError:
+ set_button_auto_selectable = _null_set_button_auto_selectable
+
+
+def _hildon_set_button_finger_selectable(button):
+ button.set_theme_size(hildon.HILDON_SIZE_FINGER_HEIGHT)
+
+
+def _null_set_button_finger_selectable(button):
+ pass
+
+
+try:
+ hildon.HILDON_SIZE_FINGER_HEIGHT
+ gtk.Button.set_theme_size
+ set_button_finger_selectable = _hildon_set_button_finger_selectable
+except AttributeError:
+ set_button_finger_selectable = _null_set_button_finger_selectable
+
+
+def _hildon_set_button_thumb_selectable(button):
+ button.set_theme_size(hildon.HILDON_SIZE_THUMB_HEIGHT)
+
+
+def _null_set_button_thumb_selectable(button):
+ pass
+
+
+try:
+ hildon.HILDON_SIZE_THUMB_HEIGHT
+ gtk.Button.set_theme_size
+ set_button_thumb_selectable = _hildon_set_button_thumb_selectable
+except AttributeError:
+ set_button_thumb_selectable = _null_set_button_thumb_selectable
+
+
+def _hildon_set_cell_thumb_selectable(renderer):
+ renderer.set_property("scale", 1.5)
+
+
+def _null_set_cell_thumb_selectable(renderer):
+ pass
+
+
+if IS_HILDON_SUPPORTED:
+ set_cell_thumb_selectable = _hildon_set_cell_thumb_selectable
+else:
+ set_cell_thumb_selectable = _null_set_cell_thumb_selectable
+
+
+def _hildon_set_pix_cell_thumb_selectable(renderer):
+ renderer.set_property("stock-size", 48)
+
+
+def _null_set_pix_cell_thumb_selectable(renderer):
+ pass
+
+
+if IS_HILDON_SUPPORTED:
+ set_pix_cell_thumb_selectable = _hildon_set_pix_cell_thumb_selectable
+else:
+ set_pix_cell_thumb_selectable = _null_set_pix_cell_thumb_selectable
+
+
+def _fremantle_show_information_banner(parent, message):
+ hildon.hildon_banner_show_information(parent, "", message)
+
+
+def _hildon_show_information_banner(parent, message):
+ hildon.hildon_banner_show_information(parent, None, message)
+
+
+def _null_show_information_banner(parent, message):
+ pass
+
+
+if IS_FREMANTLE_SUPPORTED:
+ show_information_banner = _fremantle_show_information_banner
+else:
+ try:
+ hildon.hildon_banner_show_information
+ show_information_banner = _hildon_show_information_banner
+ except AttributeError:
+ show_information_banner = _null_show_information_banner
+
+
+def _fremantle_show_busy_banner_start(parent, message):
+ hildon.hildon_gtk_window_set_progress_indicator(parent, True)
+ return parent
+
+
+def _fremantle_show_busy_banner_end(banner):
+ hildon.hildon_gtk_window_set_progress_indicator(banner, False)
+
+
+def _hildon_show_busy_banner_start(parent, message):
+ return hildon.hildon_banner_show_animation(parent, None, message)
+
+
+def _hildon_show_busy_banner_end(banner):
+ banner.destroy()
+
+
+def _null_show_busy_banner_start(parent, message):
+ return None
+
+
+def _null_show_busy_banner_end(banner):
+ assert banner is None
+
+
+try:
+ hildon.hildon_gtk_window_set_progress_indicator
+ show_busy_banner_start = _fremantle_show_busy_banner_start
+ show_busy_banner_end = _fremantle_show_busy_banner_end
+except AttributeError:
+ try:
+ hildon.hildon_banner_show_animation
+ show_busy_banner_start = _hildon_show_busy_banner_start
+ show_busy_banner_end = _hildon_show_busy_banner_end
+ except AttributeError:
+ show_busy_banner_start = _null_show_busy_banner_start
+ show_busy_banner_end = _null_show_busy_banner_end
+
+
+def _hildon_hildonize_text_entry(textEntry):
+ textEntry.set_property('hildon-input-mode', 7)
+
+
+def _null_hildonize_text_entry(textEntry):
+ pass
+
+
+if IS_HILDON_SUPPORTED:
+ hildonize_text_entry = _hildon_hildonize_text_entry
+else:
+ hildonize_text_entry = _null_hildonize_text_entry
+
+
+def _hildon_window_to_portrait(window):
+ # gtk documentation is unclear whether this does a "=" or a "|="
+ flags = hildon.PORTRAIT_MODE_SUPPORT | hildon.PORTRAIT_MODE_REQUEST
+ hildon.hildon_gtk_window_set_portrait_flags(window, flags)
+
+
+def _hildon_window_to_landscape(window):
+ # gtk documentation is unclear whether this does a "=" or a "&= ~"
+ flags = hildon.PORTRAIT_MODE_SUPPORT
+ hildon.hildon_gtk_window_set_portrait_flags(window, flags)
+
+
+def _null_window_to_portrait(window):
+ pass
+
+
+def _null_window_to_landscape(window):
+ pass
+
+
+try:
+ hildon.PORTRAIT_MODE_SUPPORT
+ hildon.PORTRAIT_MODE_REQUEST
+ hildon.hildon_gtk_window_set_portrait_flags
+
+ window_to_portrait = _hildon_window_to_portrait
+ window_to_landscape = _hildon_window_to_landscape
+except AttributeError:
+ window_to_portrait = _null_window_to_portrait
+ window_to_landscape = _null_window_to_landscape
+
+
+def get_device_orientation():
+ bus = dbus.SystemBus()
+ try:
+ rawMceRequest = bus.get_object("com.nokia.mce", "/com/nokia/mce/request")
+ mceRequest = dbus.Interface(rawMceRequest, dbus_interface="com.nokia.mce.request")
+ orientation, standState, faceState, xAxis, yAxis, zAxis = mceRequest.get_device_orientation()
+ except dbus.exception.DBusException:
+ # catching for documentation purposes that when a system doesn't
+ # support this, this is what to expect
+ raise
+
+ if orientation == "":
+ return gtk.ORIENTATION_HORIZONTAL
+ elif orientation == "":
+ return gtk.ORIENTATION_VERTICAL
+ else:
+ raise RuntimeError("Unknown orientation: %s" % orientation)
+
+
+def _hildon_hildonize_password_entry(textEntry):
+ textEntry.set_property('hildon-input-mode', 7 | (1 << 29))
+
+
+def _null_hildonize_password_entry(textEntry):
+ pass
+
+
+if IS_HILDON_SUPPORTED:
+ hildonize_password_entry = _hildon_hildonize_password_entry
+else:
+ hildonize_password_entry = _null_hildonize_password_entry
+
+
+def _hildon_hildonize_combo_entry(comboEntry):
+ comboEntry.set_property('hildon-input-mode', 1 << 4)
+
+
+def _null_hildonize_combo_entry(textEntry):
+ pass
+
+
+if IS_HILDON_SUPPORTED:
+ hildonize_combo_entry = _hildon_hildonize_combo_entry
+else:
+ hildonize_combo_entry = _null_hildonize_combo_entry
+
+
+def _fremantle_hildonize_scrollwindow(scrolledWindow):
+ pannableWindow = hildon.PannableArea()
+
+ child = scrolledWindow.get_child()
+ scrolledWindow.remove(child)
+ pannableWindow.add(child)
+
+ parent = scrolledWindow.get_parent()
+ if parent is not None:
+ parent.remove(scrolledWindow)
+ parent.add(pannableWindow)
+
+ return pannableWindow
+
+
+def _hildon_hildonize_scrollwindow(scrolledWindow):
+ hildon.hildon_helper_set_thumb_scrollbar(scrolledWindow, True)
+ return scrolledWindow
+
+
+def _null_hildonize_scrollwindow(scrolledWindow):
+ return scrolledWindow
+
+
+try:
+ hildon.PannableArea
+ hildonize_scrollwindow = _fremantle_hildonize_scrollwindow
+ hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
+except AttributeError:
+ try:
+ hildon.hildon_helper_set_thumb_scrollbar
+ hildonize_scrollwindow = _hildon_hildonize_scrollwindow
+ hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
+ except AttributeError:
+ hildonize_scrollwindow = _null_hildonize_scrollwindow
+ hildonize_scrollwindow_with_viewport = _null_hildonize_scrollwindow
+
+
+def _hildon_request_number(parent, title, range, default):
+ spinner = hildon.NumberEditor(*range)
+ spinner.set_value(default)
+
+ dialog = gtk.Dialog(
+ title,
+ parent,
+ gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
+ )
+ dialog.set_default_response(gtk.RESPONSE_CANCEL)
+ dialog.get_child().add(spinner)
+
+ try:
+ dialog.show_all()
+ response = dialog.run()
+
+ if response == gtk.RESPONSE_OK:
+ return spinner.get_value()
+ elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+ raise RuntimeError("User cancelled request")
+ else:
+ raise RuntimeError("Unrecognized response %r", response)
+ finally:
+ dialog.hide()
+ dialog.destroy()
+
+
+def _null_request_number(parent, title, range, default):
+ adjustment = gtk.Adjustment(default, range[0], range[1], 1, 5, 0)
+ spinner = gtk.SpinButton(adjustment, 0, 0)
+ spinner.set_wrap(False)
+
+ dialog = gtk.Dialog(
+ title,
+ parent,
+ gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
+ )
+ dialog.set_default_response(gtk.RESPONSE_CANCEL)
+ dialog.get_child().add(spinner)
+
+ try:
+ dialog.show_all()
+ response = dialog.run()
+
+ if response == gtk.RESPONSE_OK:
+ return spinner.get_value_as_int()
+ elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+ raise RuntimeError("User cancelled request")
+ else:
+ raise RuntimeError("Unrecognized response %r", response)
+ finally:
+ dialog.hide()
+ dialog.destroy()
+
+
+try:
+ hildon.NumberEditor # TODO deprecated in fremantle
+ request_number = _hildon_request_number
+except AttributeError:
+ request_number = _null_request_number
+
+
+def _hildon_touch_selector(parent, title, items, defaultIndex):
+ model = gtk.ListStore(gobject.TYPE_STRING)
+ for item in items:
+ model.append((item, ))
+
+ selector = hildon.TouchSelector()
+ selector.append_text_column(model, True)
+ selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
+ selector.set_active(0, defaultIndex)
+
+ dialog = hildon.PickerDialog(parent)
+ dialog.set_selector(selector)
+
+ try:
+ dialog.show_all()
+ response = dialog.run()
+
+ if response == gtk.RESPONSE_OK:
+ return selector.get_active(0)
+ elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+ raise RuntimeError("User cancelled request")
+ else:
+ raise RuntimeError("Unrecognized response %r", response)
+ finally:
+ dialog.hide()
+ dialog.destroy()
+
+
+def _on_null_touch_selector_activated(treeView, path, column, dialog, pathData):
+ dialog.response(gtk.RESPONSE_OK)
+ pathData[0] = path
+
+
+def _null_touch_selector(parent, title, items, defaultIndex = -1):
+ parentSize = parent.get_size()
+
+ model = gtk.ListStore(gobject.TYPE_STRING)
+ for item in items:
+ model.append((item, ))
+
+ cell = gtk.CellRendererText()
+ set_cell_thumb_selectable(cell)
+ column = gtk.TreeViewColumn(title)
+ column.pack_start(cell, expand=True)
+ column.add_attribute(cell, "text", 0)
+
+ treeView = gtk.TreeView()
+ treeView.set_model(model)
+ treeView.append_column(column)
+ selection = treeView.get_selection()
+ selection.set_mode(gtk.SELECTION_SINGLE)
+ if 0 < defaultIndex:
+ selection.select_path((defaultIndex, ))
+
+ scrolledWin = gtk.ScrolledWindow()
+ scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolledWin.add(treeView)
+
+ dialog = gtk.Dialog(
+ title,
+ parent,
+ gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
+ )
+ dialog.set_default_response(gtk.RESPONSE_CANCEL)
+ dialog.get_child().add(scrolledWin)
+ dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
+
+ scrolledWin = hildonize_scrollwindow(scrolledWin)
+ pathData = [None]
+ treeView.connect("row-activated", _on_null_touch_selector_activated, dialog, pathData)
+
+ try:
+ dialog.show_all()
+ response = dialog.run()
+
+ if response == gtk.RESPONSE_OK:
+ if pathData[0] is None:
+ raise RuntimeError("No selection made")
+ return pathData[0][0]
+ elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+ raise RuntimeError("User cancelled request")
+ else:
+ raise RuntimeError("Unrecognized response %r", response)
+ finally:
+ dialog.hide()
+ dialog.destroy()
+
+
+try:
+ hildon.PickerDialog
+ hildon.TouchSelector
+ touch_selector = _hildon_touch_selector
+except AttributeError:
+ touch_selector = _null_touch_selector
+
+
+def _hildon_touch_selector_entry(parent, title, items, defaultItem):
+ # Got a segfault when using append_text_column with TouchSelectorEntry, so using this way
+ try:
+ selector = hildon.TouchSelectorEntry(text=True)
+ except TypeError:
+ selector = hildon.hildon_touch_selector_entry_new_text()
+ defaultIndex = -1
+ for i, item in enumerate(items):
+ selector.append_text(item)
+ if item == defaultItem:
+ defaultIndex = i
+
+ dialog = hildon.PickerDialog(parent)
+ dialog.set_selector(selector)
+
+ if 0 < defaultIndex:
+ selector.set_active(0, defaultIndex)
+ else:
+ selector.get_entry().set_text(defaultItem)
+
+ try:
+ dialog.show_all()
+ response = dialog.run()
+ finally:
+ dialog.hide()
+
+ if response == gtk.RESPONSE_OK:
+ return selector.get_entry().get_text()
+ elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+ raise RuntimeError("User cancelled request")
+ else:
+ raise RuntimeError("Unrecognized response %r", response)
+
+
+def _on_null_touch_selector_entry_entry_changed(entry, result, selection, defaultIndex):
+ custom = entry.get_text().strip()
+ if custom:
+ result[0] = custom
+ selection.unselect_all()
+ else:
+ result[0] = None
+ selection.select_path((defaultIndex, ))
+
+
+def _on_null_touch_selector_entry_entry_activated(customEntry, dialog, result):
+ dialog.response(gtk.RESPONSE_OK)
+ result[0] = customEntry.get_text()
+
+
+def _on_null_touch_selector_entry_tree_activated(treeView, path, column, dialog, result):
+ dialog.response(gtk.RESPONSE_OK)
+ model = treeView.get_model()
+ itr = model.get_iter(path)
+ if itr is not None:
+ result[0] = model.get_value(itr, 0)
+
+
+def _null_touch_selector_entry(parent, title, items, defaultItem):
+ parentSize = parent.get_size()
+
+ model = gtk.ListStore(gobject.TYPE_STRING)
+ defaultIndex = -1
+ for i, item in enumerate(items):
+ model.append((item, ))
+ if item == defaultItem:
+ defaultIndex = i
+
+ cell = gtk.CellRendererText()
+ set_cell_thumb_selectable(cell)
+ column = gtk.TreeViewColumn(title)
+ column.pack_start(cell, expand=True)
+ column.add_attribute(cell, "text", 0)
+
+ treeView = gtk.TreeView()
+ treeView.set_model(model)
+ treeView.append_column(column)
+ selection = treeView.get_selection()
+ selection.set_mode(gtk.SELECTION_SINGLE)
+
+ scrolledWin = gtk.ScrolledWindow()
+ scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolledWin.add(treeView)
+
+ customEntry = gtk.Entry()
+
+ layout = gtk.VBox()
+ layout.pack_start(customEntry, expand=False)
+ layout.pack_start(scrolledWin)
+
+ dialog = gtk.Dialog(
+ title,
+ parent,
+ gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
+ )
+ dialog.set_default_response(gtk.RESPONSE_CANCEL)
+ dialog.get_child().add(layout)
+ dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
+
+ scrolledWin = hildonize_scrollwindow(scrolledWin)
+
+ result = [None]
+ if 0 < defaultIndex:
+ selection.select_path((defaultIndex, ))
+ result[0] = defaultItem
+ else:
+ customEntry.set_text(defaultItem)
+
+ customEntry.connect("activate", _on_null_touch_selector_entry_entry_activated, dialog, result)
+ customEntry.connect("changed", _on_null_touch_selector_entry_entry_changed, result, selection, defaultIndex)
+ treeView.connect("row-activated", _on_null_touch_selector_entry_tree_activated, dialog, result)
+
+ try:
+ dialog.show_all()
+ response = dialog.run()
+
+ if response == gtk.RESPONSE_OK:
+ _, itr = selection.get_selected()
+ if itr is not None:
+ return model.get_value(itr, 0)
+ else:
+ enteredText = customEntry.get_text().strip()
+ if enteredText:
+ return enteredText
+ elif result[0] is not None:
+ return result[0]
+ else:
+ raise RuntimeError("No selection made")
+ elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+ raise RuntimeError("User cancelled request")
+ else:
+ raise RuntimeError("Unrecognized response %r", response)
+ finally:
+ dialog.hide()
+ dialog.destroy()
+
+
+try:
+ hildon.PickerDialog
+ hildon.TouchSelectorEntry
+ touch_selector_entry = _hildon_touch_selector_entry
+except AttributeError:
+ touch_selector_entry = _null_touch_selector_entry
+
+
+if __name__ == "__main__":
+ app = get_app_class()()
+
+ label = gtk.Label("Hello World from a Label!")
+
+ win = gtk.Window()
+ win.add(label)
+ win = hildonize_window(app, win)
+ if False and IS_FREMANTLE_SUPPORTED:
+ appMenu = hildon.AppMenu()
+ for i in xrange(5):
+ b = gtk.Button(str(i))
+ appMenu.append(b)
+ win.set_app_menu(appMenu)
+ win.show_all()
+ appMenu.show_all()
+ gtk.main()
+ elif False:
+ print touch_selector(win, "Test", ["A", "B", "C", "D"], 2)
+ elif False:
+ print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "C")
+ print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "Blah")
+ elif False:
+ import pprint
+ name, value = "", ""
+ goodLocals = [
+ (name, value) for (name, value) in locals().iteritems()
+ if not name.startswith("_")
+ ]
+ pprint.pprint(goodLocals)
+ elif False:
+ import time
+ show_information_banner(win, "Hello World")
+ time.sleep(5)
+ elif False:
+ import time
+ banner = show_busy_banner_start(win, "Hello World")
+ time.sleep(5)
+ show_busy_banner_end(banner)
--- /dev/null
+#!/usr/bin/env python
+
+import os
+import logging
+
+import constants
+import nqaap_gtk
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+try:
+ os.makedirs(constants._data_path_)
+except OSError, e:
+ if e.errno != 17:
+ raise
+
+logging.basicConfig(level=logging.DEBUG, filename=constants._user_logpath_)
+_moduleLogger.info("%s %s-%s" % (constants.__pretty_app_name__, constants.__version__, constants.__build__))
+_moduleLogger.info("OS: %s" % (os.uname()[0], ))
+_moduleLogger.info("Kernel: %s (%s) for %s" % os.uname()[2:])
+_moduleLogger.info("Hostname: %s" % os.uname()[1])
+
+try:
+ nqaap_gtk.run()
+finally:
+ logging.shutdown()
--- /dev/null
+#! /usr/bin/env python
+
+import logging
+
+import dbus
+import dbus.mainloop.glib
+import gobject
+import gtk
+
+import constants
+import hildonize
+from Player import Player
+from Gui import Gui
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+def run():
+ l = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ gobject.threads_init()
+ gtk.gdk.threads_init()
+
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ hildonize.set_application_name("FMRadio")
+ else:
+ hildonize.set_application_name(constants.__pretty_app_name__)
+ gui = Gui()
+ controller = Player(ui = gui)
+ gui.controller = controller
+ gui.load_settings()
+
+ gtk.main()
+
+
+if __name__ == "__main__":
+ logging.basicConfig(level=logging.DEBUG)
+ run()
+++ /dev/null
-from __future__ import with_statement
-
-import os
-
-import logging
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class Audiobook(object):
-
- def __init__(self, path, current_chapter = 0):
- self.title = ""
- self._coverPath = ""
- self._chapterPaths = []
- self.current_chapter = current_chapter
-
- if is_playlist_book(path):
- self._scan_index(path)
- elif is_dir_book(path):
- self._scan_dir(path)
- elif is_single_chapter(path):
- self._scan_chapter(path)
- else:
- _moduleLogger.info("Audiobook not found in path: " + path)
- raise IOError("Audiobook directory not found")
-
- if len(self._chapterPaths) <= self.current_chapter:
- _moduleLogger.warning(
- "Audiobook chapter out of range (%s/%s)" % (
- self.current_chapter, len(self._chapterPaths)
- )
- )
- self.current_chapter = 0
-
- @property
- def chapters(self):
- return self._chapterPaths
-
- def get_current_chapter(self):
- return self._chapterPaths[self.current_chapter]
-
- def set_chapter(self, chapter):
- if chapter in self.chapters:
- self.current_chapter = self.chapters.index(chapter)
- else:
- raise Exception("Unknown chapter set")
-
- def get_previous_chapter(self):
- """
- @returns the file name for the next chapter, without path
- """
- if 0 == self.current_chapter:
- return False
- else:
- self.current_chapter -= 1
- return self._chapterPaths[self.current_chapter]
-
- def get_next_chapter(self):
- """
- @returns the file name for the next chapter, without path
- """
- if len(self._chapterPaths) == self.current_chapter:
- return False
- else:
- self.current_chapter += 1
- return self._chapterPaths[self.current_chapter]
-
- def get_cover_img(self):
- if self._coverPath:
- return self._coverPath
- else:
- return "%s/NoCover.png" % os.path.dirname(__file__)
-
- def _scan_dir(self, root):
- self.title = os.path.split(root)[-1]
- dirContent = (
- os.path.join(root, f)
- for f in os.listdir(root)
- if not f.startswith(".")
- )
-
- files = [
- path
- for path in dirContent
- if os.path.isfile(os.path.join(root, path))
- ]
-
- images = [
- path
- for path in files
- if path.rsplit(".", 1)[-1] in ["png", "gif", "jpg", "jpeg"]
- ]
- if 0 < len(images):
- self._coverPath = images[0]
-
- self._chapterPaths = [
- path
- for path in files
- if is_single_chapter(path)
- ]
- self._chapterPaths.sort()
-
- def _scan_chapter(self, file):
- self._chapterPaths = [file]
-
- def _scan_playlist(self, file):
- root = os.path.dirname(file)
- self.title = os.path.basename(file).rsplit(".")[0]
-
- with open(file, 'r') as f:
- for line in f:
- if line.startswith("#"):
- continue
- path = line
- if not os.path.isabs(path):
- path = os.path.normpath(os.path.join(root, path))
- self._chapterPaths.append(path)
- # Not sorting, assuming the file is in the desired order
-
- def _scan_index(self, file):
- import unicodedata
-
- # Reading file
- looking_for_title = False
- looking_for_cover = False
- looking_for_chapters = False
-
- with open(file, 'r') as f:
- for line in f:
- # title
- ascii = unicodedata.normalize('NFKD', unicode(line, "latin-1")).encode('ascii', 'ignore')
- print line[:-1], "PIC\n" in line, line in "#PIC"
- if "#BOOK" in line:
- looking_for_title = True
- continue
- if looking_for_title:
- self.title = line[:-1]
- looking_for_title = False
- if "#PIC" in line:
- looking_for_cover = True
- continue
- if looking_for_cover:
- self.cover = line[:-1]
- looking_for_cover = False
- if "#TRACKS" in line:
- looking_for_chapters = True
- continue
- if looking_for_chapters:
- if "#CHAPTERS" in line:
- break # no further information needed
- self.chapters.append(line.split(':')[0])
-
-
-def is_dir_book(path):
- return os.path.isdir(path)
-
-
-def is_playlist_book(path):
- return path.rsplit(".", 1)[-1] in ["m3u"]
-
-
-def is_single_chapter(path):
- return path.rsplit(".", 1)[-1] in ["awb", "mp3", "spx", "ogg", "ac3", "wav"]
-
-
-def is_book(path):
- if is_dir_book(path):
- return True
- elif is_playlist_book(path):
- return True
- elif is_single_chapter(path):
- return True
- else:
- return False
+++ /dev/null
-import os\r
-\r
-\r
-def open(url):\r
- os.system('dbus-send --system --type=method_call --dest="com.nokia.osso_browser" /com/nokia/osso_browser/request com.nokia.osso_browser.load_url string:"%s"' % url)\r
+++ /dev/null
-import logging
-
-import gobject
-import dbus
-import telepathy
-
-import gtk_toolbox
-
-
-_moduleLogger = logging.getLogger(__name__)
-DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
-
-
-class NewChannelSignaller(object):
-
- def __init__(self, on_new_channel):
- self._sessionBus = dbus.SessionBus()
- self._on_user_new_channel = on_new_channel
-
- def start(self):
- self._sessionBus.add_signal_receiver(
- self._on_new_channel,
- "NewChannel",
- "org.freedesktop.Telepathy.Connection",
- None,
- None
- )
-
- def stop(self):
- self._sessionBus.remove_signal_receiver(
- self._on_new_channel,
- "NewChannel",
- "org.freedesktop.Telepathy.Connection",
- None,
- None
- )
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_new_channel(
- self, channelObjectPath, channelType, handleType, handle, supressHandler
- ):
- connObjectPath = channel_path_to_conn_path(channelObjectPath)
- serviceName = path_to_service_name(channelObjectPath)
- try:
- self._on_user_new_channel(
- self._sessionBus, serviceName, connObjectPath, channelObjectPath, channelType
- )
- except Exception:
- _moduleLogger.exception("Blocking exception from being passed up")
-
-
-class ChannelClosed(object):
-
- def __init__(self, bus, conn, chan, on_closed):
- self.__on_closed = on_closed
-
- chan[telepathy.interfaces.CHANNEL].connect_to_signal(
- "Closed",
- self._on_closed,
- )
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_closed(self):
- self.__on_closed(self)
-
-
-class CallMonitor(gobject.GObject):
-
- __gsignals__ = {
- 'call_start' : (
- gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_NONE,
- (),
- ),
- 'call_end' : (
- gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_NONE,
- (),
- ),
- }
-
- def __init__(self):
- gobject.GObject.__init__(self)
- self._isActive = False
- self._newChannelMonitor = NewChannelSignaller(self._on_new_channel)
- self._channelClosedMonitors = []
-
- def start(self):
- self._isActive = True
- self._newChannelMonitor.start()
-
- def stop(self):
- self._isActive = False
- self._newChannelMonitor.stop()
-
- def _on_new_channel(self, sessionBus, serviceName, connObjectPath, channelObjectPath, channelType):
- if not self._isActive:
- return
-
- if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA:
- return
-
- cmName = cm_from_path(connObjectPath)
- conn = telepathy.client.Connection(serviceName, connObjectPath)
- try:
- chan = telepathy.client.Channel(serviceName, channelObjectPath)
- except dbus.exceptions.UnknownMethodException:
- _moduleLogger.exception("Client might not have implemented a deprecated method")
- return
-
- missDetection = ChannelClosed(
- sessionBus, conn, chan, self._on_close
- )
- self._outstandingRequests.append(missDetection)
- if len(self._outstandingRequests) == 1:
- self.emit("call_start")
-
- def _on_close(self, channelCloseMonitor):
- self._outstandingRequests.remove(channelCloseMonitor)
- if not self._outstandingRequests:
- self.emit("call_stop")
-
-
-def channel_path_to_conn_path(channelObjectPath):
- """
- >>> channel_path_to_conn_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
- '/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME'
- """
- return channelObjectPath.rsplit("/", 1)[0]
-
-
-def path_to_service_name(path):
- """
- >>> path_to_service_name("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
- 'org.freedesktop.Telepathy.ConnectionManager.theonering.gv.USERNAME'
- """
- return ".".join(path[1:].split("/")[0:7])
-
-
-def cm_from_path(path):
- """
- >>> cm_from_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/gv/USERNAME/Channel1")
- 'theonering'
- """
- return path[1:].split("/")[4]
+++ /dev/null
-from __future__ import with_statement # enable with
-
-import os
-import simplejson
-import logging
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-# @todo Add bookmarks
-
-
-class FileStorage(object):
-
- def __init__(self, path="~/.SornPlayer/"):
- # Setup dir
- _moduleLogger.info("init filestorage")
- self.path = path
- self.books_path = os.path.join(self.path, "books.json")
- self.selected = None
- self._books = {}
-
- def load(self):
- if not os.path.isdir(self.path):
- os.makedirs(self.path)
-
- try:
- with open(self.books_path, "r") as settingsFile:
- settings = simplejson.load(settingsFile)
- except IOError, e:
- _moduleLogger.info("No settings")
- settings = {}
- except ValueError:
- _moduleLogger.info("Settings were corrupt")
- settings = {}
-
- if settings:
- self._books = settings["books"]
- self.selected = settings["selected"]
- else:
- _moduleLogger.info("Falling back to old settings format")
- self._load_old_settings()
-
- def save(self):
- settings = {
- "selected": self.selected,
- "books": self._books,
- }
- with open(self.books_path, "w") as settingsFile:
- simplejson.dump(settings, settingsFile)
-
- def get_selected(self):
- """returns the currently selected book"""
- return self.selected
-
- def select_book(self, bookName):
- """ Sets the book as the currently playing, and adds it to the
- database if it is not already there"""
- book_file = os.path.join(self.books_path, bookName)
- if bookName not in self._books:
- self._books[bookName] = {
- "chapter": 0,
- "position": 0,
- }
-
- self.selected = bookName
-
- def set_time(self, chapter, position):
- """ Sets the current time for the book that is currently selected"""
- bookInfo = self._books[self.selected]
- bookInfo["chapter"] = chapter
- bookInfo["position"] = position
-
- def get_time(self):
- """Returns the current saved time for the current selected book"""
- bookInfo = self._books[self.selected]
- return bookInfo["chapter"], bookInfo["position"]
-
- def _load_old_settings(self):
- conf = os.path.join(self.path, "current")
-
- try:
- with open(conf) as f:
- self.selected = f.readline()
-
- books_path = os.path.join(self.path, "books/")
- for book in os.listdir(books_path):
- book_file = os.path.join(books_path, book)
- with open(book_file, 'r') as f:
- chapter = int(f.readline())
- position = int(f.readline())
- self._books[book] = {
- "chapter": chapter,
- "position": position,
- }
- except IOError, e:
- if e.errno == 2:
- pass
- else:
- raise
- except OSError, e:
- if e.errno == 2:
- pass
- else:
- raise
+++ /dev/null
-from __future__ import with_statement
-
-import os
-import ConfigParser
-import logging
-
-import gobject
-import gtk
-
-import constants
-import hildonize
-import gtk_toolbox
-import Browser
-import CallMonitor
-import settings
-
-if hildonize.IS_FREMANTLE_SUPPORTED:
- # I don't normally do this but I want to error as loudly as possibly when an issue arises
- import hildon
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class Gui(object):
-
- # @todo Jump straight to book selection on first launch?
- # @todo Absolute increments (+/-5 seconds)
- # @todo Show elapsed time / time lef
- # @todo Volume control when screen is off
- # @todo Variable speed
- # http://scaletempo.sourceforge.net/0/task-list.html
- # http://www.gstreamer.net/data/doc/gstreamer/head/gst-plugins-bad-plugins/html/gst-plugins-bad-plugins-scaletempo.html
-
- def __init__(self):
- _moduleLogger.info("Starting GUI")
- self._clipboard = gtk.clipboard_get()
- self._callMonitor = CallMonitor.CallMonitor()
- self.__settingsWindow = None
- self.__settingsManager = None
- self._bookSelection = []
- self._bookSelectionIndex = -1
- self._chapterSelection = []
- self._chapterSelectionIndex = -1
- self._sleepSelection = ["0", "1", "10", "20", "30", "60"]
- self._sleepSelectionIndex = 0
-
- self.__window_in_fullscreen = False #The window isn't in full screen mode initially.
- self.__isPortrait = False
-
- self.controller = None
- self.sleep_timer = None
-
- # set up gui
- self.setup()
- self._callMonitor.connect("call_start", self.__on_call_started)
- self._callMonitor.start()
-
- def setup(self):
- self._app = hildonize.get_app_class()()
- self.win = gtk.Window()
- self.win = hildonize.hildonize_window(self._app, self.win)
- self.win.set_title(constants.__pretty_app_name__)
-
- # Cover image
- self.cover = gtk.Image()
-
- # Controls:
-
- # Label that hold the title of the book,and maybe the chapter
- self.title = gtk.Label()
- self.title.set_justify(gtk.JUSTIFY_CENTER)
- self._set_display_title("Select a book to start listening")
-
- self.chapter = gtk.Label()
- self.chapter.set_justify(gtk.JUSTIFY_CENTER)
-
- # Seekbar
- if hildonize.IS_FREMANTLE_SUPPORTED:
- self.seek = hildon.Seekbar()
- self.seek.set_range(0.0, 100)
- self.seek.set_draw_value(False)
- self.seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
- self.seek.connect('change-value', self.seek_changed) # event
- # self.seek.connect('value-changed',self.seek_changed) # event
- else:
- adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1)
- self.seek = gtk.HScale(adjustment)
- self.seek.set_draw_value(False)
- self.seek.connect('change-value', self.seek_changed) # event
-
- # Pause button
- if hildonize.IS_FREMANTLE_SUPPORTED:
- self.backButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
- image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PREVIOUS, gtk.HILDON_SIZE_FINGER_HEIGHT)
- self.backButton.set_image(image)
-
- self.button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
-
- self.forwardButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
- image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_NEXT, gtk.HILDON_SIZE_FINGER_HEIGHT)
- self.forwardButton.set_image(image)
- else:
- self.backButton = gtk.Button(stock=gtk.STOCK_MEDIA_PREVIOUS)
- self.button = gtk.Button()
- self.forwardButton = gtk.Button(stock=gtk.STOCK_MEDIA_NEXT)
- self.set_button_text("Play", "Start playing the audiobook")
- self.backButton.connect('clicked', self._on_previous_chapter)
- self.button.connect('clicked', self.play_pressed) # event
- self.forwardButton.connect('clicked', self._on_next_chapter)
-
- self._toolbar = gtk.HBox()
- self._toolbar.pack_start(self.backButton, False, False, 0)
- self._toolbar.pack_start(self.button, True, True, 0)
- self._toolbar.pack_start(self.forwardButton, False, False, 0)
-
- # Box to hold the controls:
- self._controlLayout = gtk.VBox()
- self._controlLayout.pack_start(gtk.Label(), True, True, 0)
- self._controlLayout.pack_start(self.title, False, True, 0)
- self._controlLayout.pack_start(self.chapter, False, True, 0)
- self._controlLayout.pack_start(gtk.Label(), True, True, 0)
- self._controlLayout.pack_start(self.seek, False, True, 0)
- self._controlLayout.pack_start(self._toolbar, False, True, 0)
-
- #Box that divides the layout in two: cover on the lefta
- #and controls on the right
- self._viewLayout = gtk.HBox()
- self._viewLayout.pack_start(self.cover, True, True, 0)
- self._viewLayout.add(self._controlLayout)
-
- self._menuBar = gtk.MenuBar()
- self._menuBar.show()
-
- self._mainLayout = gtk.VBox()
- self._mainLayout.pack_start(self._menuBar, False, False, 0)
- self._mainLayout.pack_start(self._viewLayout)
-
- # Add hbox to the window
- self.win.add(self._mainLayout)
-
- #Menu:
- # Create menu
- self._populate_menu()
-
- self.win.connect("delete_event", self.quit) # Add shutdown event
- self.win.connect("key-press-event", self.on_key_press)
- self.win.connect("window-state-event", self._on_window_state_change)
-
- self.win.show_all()
-
- # Run update timer
- self.setup_timers()
-
- def _populate_menu(self):
- self._menuBar = hildonize.hildonize_menu(
- self.win,
- self._menuBar,
- )
- if hildonize.IS_FREMANTLE_SUPPORTED:
- # Create a picker button
- self.book_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
- hildon.BUTTON_ARRANGEMENT_VERTICAL)
- self.book_button.set_title("Audiobook") # Set a title to the button
- self.book_button.connect("clicked", self._on_select_audiobook)
-
- # Create a picker button
- self.chapter_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
- hildon.BUTTON_ARRANGEMENT_VERTICAL)
- self.chapter_button.set_title("Chapter") # Set a title to the button
- self.chapter_button.connect("clicked", self._on_select_chapter)
-
- # Create a picker button
- self.sleeptime_button = hildon.Button(gtk.HILDON_SIZE_AUTO,
- hildon.BUTTON_ARRANGEMENT_VERTICAL)
- self.sleeptime_button.set_title("Sleeptimer") # Set a title to the button
- self.sleeptime_button.connect("clicked", self._on_select_sleep)
-
- settings_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
- settings_button.set_label("Settings")
- settings_button.connect("clicked", self._on_settings)
-
- about_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
- about_button.set_label("About")
- about_button.connect("clicked", self._on_about_activate)
-
- help_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL)
- help_button.set_label("Help")
- help_button.connect("clicked", self.get_help)
-
- self._menuBar.append(self.book_button) # Add the button to menu
- self._menuBar.append(self.chapter_button) # Add the button to menu
- self._menuBar.append(self.sleeptime_button) # Add the button to menu
- self._menuBar.append(settings_button)
- self._menuBar.append(help_button)
- self._menuBar.append(about_button)
- self._menuBar.show_all()
- else:
- self._audiobookMenuItem = gtk.MenuItem("Audiobook: ")
- self._audiobookMenuItem.connect("activate", self._on_select_audiobook)
-
- self._chapterMenuItem = gtk.MenuItem("Chapter: ")
- self._chapterMenuItem.connect("activate", self._on_select_chapter)
-
- self._sleepMenuItem = gtk.MenuItem("Sleeptimer: 0")
- self._sleepMenuItem.connect("activate", self._on_select_sleep)
-
- settingsMenuItem = gtk.MenuItem("Settings")
- settingsMenuItem.connect("activate", self._on_settings)
-
- aboutMenuItem = gtk.MenuItem("About")
- aboutMenuItem.connect("activate", self._on_about_activate)
-
- helpMenuItem = gtk.MenuItem("Help")
- helpMenuItem.connect("activate", self.get_help)
-
- booksMenu = gtk.Menu()
- booksMenu.append(self._audiobookMenuItem)
- booksMenu.append(self._chapterMenuItem)
- booksMenu.append(self._sleepMenuItem)
- booksMenu.append(settingsMenuItem)
- booksMenu.append(helpMenuItem)
- booksMenu.append(aboutMenuItem)
-
- booksMenuItem = gtk.MenuItem("Books")
- booksMenuItem.show()
- booksMenuItem.set_submenu(booksMenu)
- self._menuBar.append(booksMenuItem)
- self._menuBar.show_all()
-
- def setup_timers(self):
- self.seek_timer = timeout_add_seconds(3, self.update_seek)
-
- def save_settings(self):
- config = ConfigParser.SafeConfigParser()
- self._save_settings(config)
- with open(constants._user_settings_, "wb") as configFile:
- config.write(configFile)
- self.controller.save()
-
- def _save_settings(self, config):
- config.add_section(constants.__pretty_app_name__)
- config.set(constants.__pretty_app_name__, "portrait", str(self.__isPortrait))
- config.set(constants.__pretty_app_name__, "fullscreen", str(self.__window_in_fullscreen))
- config.set(constants.__pretty_app_name__, "audiopath", self.controller.get_books_path())
-
- def load_settings(self):
- config = ConfigParser.SafeConfigParser()
- config.read(constants._user_settings_)
- self._load_settings(config)
-
- def _load_settings(self, config):
- isPortrait = False
- window_in_fullscreen = False
- booksPath = constants._default_book_path_
- try:
- isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait")
- window_in_fullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen")
- booksPath = config.get(constants.__pretty_app_name__, "audiopath")
- except ConfigParser.NoSectionError, e:
- _moduleLogger.info(
- "Settings file %s is missing section %s" % (
- constants._user_settings_,
- e.section,
- )
- )
-
- if isPortrait ^ self.__isPortrait:
- if isPortrait:
- orientation = gtk.ORIENTATION_VERTICAL
- else:
- orientation = gtk.ORIENTATION_HORIZONTAL
- self.set_orientation(orientation)
-
- self.__window_in_fullscreen = window_in_fullscreen
- if self.__window_in_fullscreen:
- self.win.fullscreen()
- else:
- self.win.unfullscreen()
-
- self.controller.load(booksPath)
-
- @staticmethod
- def __format_name(path):
- if os.path.isfile(path):
- return os.path.basename(path).rsplit(".", 1)[0]
- else:
- return os.path.basename(path)
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_select_audiobook(self, *args):
- if not self._bookSelection:
- return
- index = hildonize.touch_selector(
- self.win,
- "Audiobook",
- (self.__format_name(bookPath) for bookPath in self._bookSelection),
- self._bookSelectionIndex if 0 <= self._bookSelectionIndex else 0,
- )
- self._bookSelectionIndex = index
- bookName = self._bookSelection[index]
- self.controller.set_book(bookName)
- self.set_button_text("Play", "Start playing the audiobook") # reset button
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_select_chapter(self, *args):
- if not self._chapterSelection:
- return
- index = hildonize.touch_selector(
- self.win,
- "Chapter",
- (self.__format_name(chapterPath) for chapterPath in self._chapterSelection),
- self._chapterSelectionIndex if 0 <= self._chapterSelectionIndex else 0,
- )
- self._chapterSelectionIndex = index
- chapterName = self._chapterSelection[index]
- self.controller.set_chapter(chapterName)
- self.set_button_text("Play", "Start playing the audiobook") # reset button
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_select_sleep(self, *args):
- if self.sleep_timer is not None:
- gobject.source_remove(self.sleep_timer)
-
- try:
- index = hildonize.touch_selector(
- self.win,
- "Sleeptimer",
- self._sleepSelection,
- self._sleepSelectionIndex if 0 <= self._sleepSelectionIndex else 0,
- )
- except RuntimeError:
- _moduleLogger.exception("Handling as if user cancelled")
- hildonize.show_information_banner(self.win, "Sleep timer canceled")
- index = 0
-
- self._sleepSelectionIndex = index
- sleepName = self._sleepSelection[index]
-
- time_out = int(sleepName)
- if 0 < time_out:
- timeout_add_seconds(time_out * 60, self.sleep)
-
- if hildonize.IS_FREMANTLE_SUPPORTED:
- self.sleeptime_button.set_text("Sleeptimer", sleepName)
- else:
- self._sleepMenuItem.get_child().set_text("Sleeptimer: %s" % (sleepName, ))
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def __on_call_started(self, callMonitor):
- self.pause()
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_settings(self, *args):
- if self.__settingsWindow is None:
- vbox = gtk.VBox()
- self.__settingsManager = settings.SettingsDialog(vbox)
-
- self.__settingsWindow = gtk.Window()
- self.__settingsWindow.add(vbox)
- self.__settingsWindow = hildonize.hildonize_window(self._app, self.__settingsWindow)
- self.__settingsManager.window = self.__settingsWindow
-
- self.__settingsWindow.set_title("Settings")
- self.__settingsWindow.set_transient_for(self.win)
- self.__settingsWindow.set_default_size(*self.win.get_size())
- self.__settingsWindow.connect("delete-event", self._on_settings_delete)
- self.__settingsManager.set_portrait_state(self.__isPortrait)
- self.__settingsManager.set_audiobook_path(self.controller.get_books_path())
- self.__settingsWindow.set_modal(True)
- self.__settingsWindow.show_all()
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_settings_delete(self, *args):
- self.__settingsWindow.emit_stop_by_name("delete-event")
- self.__settingsWindow.hide()
- self.__settingsWindow.set_modal(False)
-
- isPortrait = self.__settingsManager.is_portrait()
- if isPortrait ^ self.__isPortrait:
- if isPortrait:
- orientation = gtk.ORIENTATION_VERTICAL
- else:
- orientation = gtk.ORIENTATION_HORIZONTAL
- self.set_orientation(orientation)
- if self.__settingsManager.get_audiobook_path() != self.controller.get_books_path():
- self.controller.reload(self.__settingsManager.get_audiobook_path())
-
- return True
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def update_seek(self):
- #print self.controller.get_percentage()
- if self.controller.is_playing():
- gtk.gdk.threads_enter()
- self.seek.set_value(self.controller.get_percentage() * 100)
- gtk.gdk.threads_leave()
- #self.controller.get_percentage()
- return True # run again
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def sleep(self):
- _moduleLogger.info("sleep time timeout")
- hildonize.show_information_banner(self.win, "Sleep timer")
- self.controller.stop()
- self.set_button_text("Resume", "Resume playing the audiobook")
- return False # do not repeat
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def get_help(self, button):
- Browser.open("file:///opt/Nqa-Audiobook-player/Help/nqaap.html")
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def seek_changed(self, seek, scroll , value):
- # print "sok", scroll
- self.controller.seek_percent(seek.get_value() / 100.0)
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_next_chapter(self, *args):
- self.controller.next_chapter()
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_previous_chapter(self, *args):
- self.controller.previous_chapter()
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def play_pressed(self, button):
- if self.controller.is_playing():
- self.pause()
- else:
- self.play()
-
- @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.win.unfullscreen ()
- else:
- self.win.fullscreen ()
- return True
- elif event.keyval == gtk.keysyms.o and isCtrl:
- self._toggle_rotate()
- return True
- elif (
- event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
- event.get_state() & gtk.gdk.CONTROL_MASK
- ):
- self.quit()
- 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
- elif event.keyval in RETURN_TYPES:
- if self.controller.is_playing():
- self.pause()
- else:
- self.play()
- return True
- elif event.keyval == gtk.keysyms.Left:
- self.controller.previous_chapter()
- return True
- elif event.keyval == gtk.keysyms.Right:
- self.controller.next_chapter()
- return True
-
- @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 quit(self, *args): # what are the arguments?
- _moduleLogger.info("Shutting down")
- try:
- self.save_settings()
- self.controller.stop() # to save the state
- finally:
- gtk.main_quit()
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_about_activate(self, *args):
- dlg = gtk.AboutDialog()
- dlg.set_name(constants.__pretty_app_name__)
- dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
- dlg.set_copyright("Copyright 2010")
- dlg.set_comments("")
- dlg.set_website("http://nqaap.garage.maemo.org/")
- dlg.set_authors(["Pengman <pengmeister@gmail.com>", "Ed Page <eopage@byu.net>"])
- dlg.run()
- dlg.destroy()
-
- # Actions:
-
- def play(self):
- self.set_button_text("Stop", "Stop playing the audiobook")
- self.controller.play()
-
- def pause(self):
- self.set_button_text("Resume", "Resume playing the audiobook")
- self.controller.stop()
-
- def set_orientation(self, orientation):
- if orientation == gtk.ORIENTATION_VERTICAL:
- if self.__isPortrait:
- return
- hildonize.window_to_portrait(self.win)
- self.__isPortrait = True
-
- self._viewLayout.remove(self._controlLayout)
- self._mainLayout.add(self._controlLayout)
- elif orientation == gtk.ORIENTATION_HORIZONTAL:
- if not self.__isPortrait:
- return
- hildonize.window_to_landscape(self.win)
- self.__isPortrait = False
-
- self._mainLayout.remove(self._controlLayout)
- self._viewLayout.add(self._controlLayout)
- else:
- raise NotImplementedError(orientation)
-
- def get_orientation(self):
- return gtk.ORIENTATION_VERTICAL if self.__isPortrait else gtk.ORIENTATION_HORIZONTAL
-
- def _toggle_rotate(self):
- if self.__isPortrait:
- self.set_orientation(gtk.ORIENTATION_HORIZONTAL)
- else:
- self.set_orientation(gtk.ORIENTATION_VERTICAL)
-
- def change_chapter(self, chapterName):
- if chapterName is None:
- _moduleLogger.debug("chapter selection canceled.")
- return True # this should end the function and indicate it has been handled
-
- _moduleLogger.debug("chapter changed (by controller) to: %s" % chapterName)
-
- def set_button_text(self, title, text):
- if hildonize.IS_FREMANTLE_SUPPORTED:
- self.button.set_text(title, text)
- else:
- self.button.set_label("%s" % (title, ))
-
- def set_books(self, books):
- _moduleLogger.debug("new books")
- del self._bookSelection[:]
- self._bookSelection.extend(books)
- if len(books) == 0 and self.controller is not None:
- hildonize.show_information_banner(self.win, "No audiobooks found. \nPlease place your audiobooks in the directory %s" % self.controller.get_books_path())
-
- def set_book(self, bookPath, cover):
- bookName = self.__format_name(bookPath)
-
- self.set_button_text("Play", "Start playing the audiobook") # reset button
- self._set_display_title(bookName)
- if hildonize.IS_FREMANTLE_SUPPORTED:
- self.book_button.set_text("Audiobook", bookName)
- else:
- self._audiobookMenuItem.get_child().set_text("Audiobook: %s" % (bookName, ))
- if cover != "":
- self.cover.set_from_file(cover)
-
- def set_chapter(self, chapterIndex):
- '''
- Called from controller whenever a new chapter is started
-
- chapter parameter is supposed to be the index for the chapter, not the name
- '''
- self.auto_chapter_selected = True
- self._set_display_chapter(str(chapterIndex + 1))
- if hildonize.IS_FREMANTLE_SUPPORTED:
- self.chapter_button.set_text("Chapter", str(chapterIndex))
- else:
- self._chapterMenuItem.get_child().set_text("Chapter: %s" % (chapterIndex+1, ))
-
- def set_chapters(self, chapters):
- _moduleLogger.debug("setting chapters" )
- del self._chapterSelection[:]
- self._chapterSelection.extend(chapters)
-
- def set_sleep_timer(self, mins):
- pass
-
- # Utils
- def set_selected_value(self, button, value):
- i = button.get_selector().get_model(0).index[value] # get index of value from list
- button.set_active(i) # set active index to that index
-
- def _set_display_title(self, title):
- self.title.set_markup("<b><big>%s</big></b>" % title)
-
- def _set_display_chapter(self, chapter):
- self.chapter.set_markup("<b><big>Chapter %s</big></b>" % chapter)
-
-
-def _old_timeout_add_seconds(timeout, callback):
- return gobject.timeout_add(timeout * 1000, callback)
-
-
-def _timeout_add_seconds(timeout, callback):
- return gobject.timeout_add_seconds(timeout, callback)
-
-
-try:
- gobject.timeout_add_seconds
- timeout_add_seconds = _timeout_add_seconds
-except AttributeError:
- timeout_add_seconds = _old_timeout_add_seconds
-
-
-if __name__ == "__main__":
- g = Gui(None)
+++ /dev/null
-import os
-import threading
-import time
-import logging
-
-import constants
-import hildonize
-import Audiobook
-import FileStorage
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class Player(object):
-
- def __init__(self, ui):
- self.storage = FileStorage.FileStorage(path = constants._data_path_)
- if hildonize.IS_HILDON_SUPPORTED and not hildonize.IS_FREMANTLE_SUPPORTED:
- import SimpleOSSOPlayer as _SimplePlayer
- SimplePlayer = _SimplePlayer # silence PyFlakes
- else:
- import SimpleGStreamer as SimplePlayer
- self.player = SimplePlayer.SimplePlayer(self.next_chapter)
- self.ui = ui
- self.audiobook = None
- self._bookDir = None
- self._bookPaths = {}
-
- def get_books_path(self):
- return self._bookDir
-
- def reload(self, booksPath):
- if self.audiobook is not None:
- position = self.player.elapsed()
- self.storage.set_time(self.audiobook.current_chapter, position)
- self.save()
- self.load(booksPath)
-
- def load(self, booksPath):
- _moduleLogger.info("Loading books from %s" % booksPath)
- self.storage.load()
- self._bookDir = booksPath
-
- self._bookPaths = dict(
- (self.__format_name(bookPath), bookPath)
- for bookPath in self._find_books()
- )
- if self.ui is not None:
- bookPaths = self._bookPaths.values()
- bookPaths.sort()
- self.ui.set_books(bookPaths)
-
- lastBookName = self.storage.get_selected()
- if lastBookName is not None:
- _moduleLogger.info("continuing book: %s" % lastBookName)
- try:
- bookPath = self._bookPaths[lastBookName]
- self.set_book(bookPath)
- except KeyError:
- _moduleLogger.exception("Audiobook was not found")
- except IndexError:
- _moduleLogger.exception("Chapter was not found")
- except IOError:
- _moduleLogger.exception("Audiobook could not be loaded")
- except Exception:
- _moduleLogger.exception("Can you say 'confusion'?")
-
- def save(self):
- position = self.player.elapsed()
- if self.audiobook is not None:
- self.storage.set_time(self.audiobook.current_chapter, position)
- self.storage.save()
-
- @staticmethod
- def __format_name(path):
- if os.path.isfile(path):
- return os.path.basename(path).rsplit(".", 1)[0]
- else:
- return os.path.basename(path)
-
- def set_book(self, bookPath):
- oldBookName = self.storage.get_selected()
- try:
- bookName = self.__format_name(bookPath)
- self.storage.select_book(bookName)
- chapter_num, _ = self.storage.get_time()
- self.audiobook = Audiobook.Audiobook(
- bookPath,
- chapter_num
- )
- except:
- self.storage.select_book(oldBookName)
- raise
-
- # self.player.set_file(self.audiobook.get_current_chapter())
- # self.player.seek_time(time)
-
- if self.ui is not None:
- self.ui.set_book(bookPath, self.audiobook.get_cover_img())
- self.ui.set_chapters(self.audiobook.chapters)
-
- chapter_title = self.audiobook.chapters[self.audiobook.current_chapter]
- self.set_chapter(chapter_title, True)
-
- def set_chapter(self, chapter, continuing = False):
- _moduleLogger.info("set chapter:" + chapter + " : Continuing: " + str(continuing))
- self.audiobook.set_chapter(chapter)
- self.player.set_file(self.audiobook.get_current_chapter())
- if not continuing:
- self.storage.set_time(self.audiobook.current_chapter, 0)
-
- if self.ui is not None:
- self.ui.set_chapter(self.audiobook.current_chapter)
-
- def previous_chapter(self, *args):
- _moduleLogger.info("Going back a chapter")
- self.player.stop()
- next_file = self.audiobook.get_previous_chapter()
- if next_file is not False:
- self.set_chapter(next_file)
- self.player.play()
- else: # the book is over
- self.storage.set_time(0, 0)
-
- def next_chapter(self, *args):
- _moduleLogger.info("Advancing a chapter")
- self.player.stop()
- next_file = self.audiobook.get_next_chapter()
- if next_file is not False:
- self.set_chapter(next_file)
- self.player.play()
- else: # the book is over
- self.storage.set_time(0, 0)
-
- def play(self):
- if self.audiobook is not None:
- self.player.play()
- _, target_time = self.storage.get_time()
- if 0 < target_time:
- time.sleep(1)
- self.player.seek_time(target_time)
- #print self.player.elapsed()
- else:
- print "No book selected, find one in ", self._bookDir
-
- def stop(self):
- position = self.player.elapsed()
- self.player.stop()
-
- if self.audiobook is not None:
- self.storage.set_time(self.audiobook.current_chapter, position)
-
- def is_playing(self):
- return self.player.playing
-
- def sleeptimer(self, secs):
- #print "sleeper", secs
- time.sleep(secs)
- #print "now its time to sleep"
- self.stop()
-
- def start_sleeptimer(self, secs):
- #print "startin sleep"
- sleep_thread = threading.Thread(target=self.sleeptimer, args=(secs, ))
- sleep_thread.start()
- #print "started sleep"
-
- def get_percentage(self):
- try:
- return float(self.player.elapsed()) / float(self.player.duration())
- except ZeroDivisionError:
- return 0.0
-
- def seek_percent(self, ratio):
- try:
- target = int(self.player.duration() * ratio) # Calculate where to seek
- self.player.seek_time(target) # seek
-
- position = self.player.elapsed()
- self.storage.set_time(self.audiobook.current_chapter, target) # save position
- return True
- except:
- _moduleLogger.exception("Seek failed")
- return False
-
- def _find_books(self):
- try:
- paths = (
- os.path.join(self._bookDir, f)
- for f in os.listdir(self._bookDir)
- )
- return (
- path
- for path in paths
- if Audiobook.is_book(path)
- )
- except OSError:
- return ()
+++ /dev/null
-import os
-import logging
-
-import gst
-
-import gtk_toolbox
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class SimplePlayer(object):
-
- # @todo Add pitch/speed control
- # http://github.com/jwagner/playitslowly/blob/master/playitslowly/pipeline.py
-
- def __init__(self, on_playing_done = None):
- #Fields
- self.playing = False
- self.__filename = ""
- self.__elapsed = 0
- self.__duration = 0
-
- #Event callbacks
- self.on_playing_done = on_playing_done
-
- #Set up GStreamer
- self.player = gst.element_factory_make("playbin2", "player")
- fakesink = gst.element_factory_make("fakesink", "fakesink")
- self.player.set_property("video-sink", fakesink)
- bus = self.player.get_bus()
- bus.add_signal_watch()
- bus.connect("message", self.on_message)
-
- #Constants
- self.time_format = gst.Format(gst.FORMAT_TIME)
- self.seek_flag = gst.SEEK_FLAG_FLUSH
-
- @property
- def has_file(self):
- return 0 < len(self.__filename)
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def on_message(self, bus, message):
- t = message.type
- if t == gst.MESSAGE_EOS: # End-Of-Stream
- self.player.set_state(gst.STATE_NULL)
- self.playing = False
- if self.on_playing_done is not None: # event callback
- self.on_playing_done(self)
- elif t == gst.MESSAGE_ERROR:
- self.player.set_state(gst.STATE_NULL)
- err, debug = message.parse_error()
- #print "Error: %s" % err, debug
- _moduleLogger.error("Error: %s, (%s)" % (err, debug))
- self.playing = False
-
- def set_file(self, file):
- _moduleLogger.info("set file: %s", file)
- if os.path.isfile(file):
- if self.__filename != file:
- self._invalidate_cache()
- if self.playing:
- self.stop()
-
- file = os.path.abspath(file) # ensure absolute path
- _moduleLogger.debug("set file (absolute path): %s "%file)
- self.player.set_property("uri", "file://" + file)
- self.__filename = file
- else:
- _moduleLogger.error("File: %s not found" % file)
-
- def play(self):
- _moduleLogger.info("Started playing")
- self.player.set_state(gst.STATE_PLAYING)
- self.playing = True
-
- def stop(self):
- self.player.set_state(gst.STATE_NULL)
- self.playing = False
- _moduleLogger.info("Stopped playing")
-
- def elapsed(self):
- try:
- self.__elapsed = self.player.query_position(self.time_format, None)[0]
- except:
- pass
- return self.__elapsed
-
- def duration(self):
- try:
- self.__duration = self.player.query_duration(self.time_format, None)[0]
- except:
- _moduleLogger.exception("Query failed")
- pass
- return self.__duration
-
- def seek_time(self, ns):
- _moduleLogger.debug("Seeking to: %s", ns)
- self.player.seek_simple(self.time_format, self.seek_flag, ns)
-
- def _invalidate_cache(self):
- self.__elapsed = 0
- self.__duration = 0
-
- def __seek_percent(self, percent):
- format = gst.Format(gst.FORMAT_PERCENT)
- self.player.seek_simple(format, self.seek_flag, percent)
+++ /dev/null
-import os
-import logging
-
-import dbus
-
-import gtk_toolbox
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class SimplePlayer(object):
-
- SERVICE_NAME = "com.nokia.osso_media_server"
- OBJECT_PATH = "/com/nokia/osso_media_server"
- AUDIO_INTERFACE_NAME = "com.nokia.osso_media_server.music"
-
- def __init__(self, on_playing_done = None):
- #Fields
- self.has_file = False
- self.playing = False
- self.__elapsed = 0
-
- #Event callbacks
- self.on_playing_done = on_playing_done
-
- session_bus = dbus.SessionBus()
-
- # Get the osso-media-player proxy object
- oms_object = session_bus.get_object(
- self.SERVICE_NAME,
- self.OBJECT_PATH,
- introspect=False,
- follow_name_owner_changes=True,
- )
- # Use the audio interface
- oms_audio_interface = dbus.Interface(
- oms_object,
- self.AUDIO_INTERFACE_NAME,
- )
- self._audioProxy = oms_audio_interface
-
- self._audioProxy.connect_to_signal("state_changed", self._on_state_changed)
- self._audioProxy.connect_to_signal("end_of_stream", self._on_end_of_stream)
-
- error_signals = [
- "no_media_selected",
- "file_not_found",
- "type_not_found",
- "unsupported_type",
- "gstreamer",
- "dsp",
- "device_unavailable",
- "corrupted_file",
- "out_of_memory",
- "audio_codec_not_supported",
- ]
- for error in error_signals:
- self._audioProxy.connect_to_signal(error, self._on_error)
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_error(self, *args):
- self.playing = False
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_end_of_stream(self, *args):
- self.playing = False
- if self.on_playing_done is not None: # event callback
- self.on_playing_done(self)
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_state_changed(self, state):
- _moduleLogger.info("State: %s", state)
-
- def set_file(self, file):
- _moduleLogger.info("set file: %s", file)
- if os.path.isfile(file):
- if self.playing:
- self.stop()
-
- uri = "file://" + file
- self._audioProxy.set_media_location(uri)
- self.has_file = True
- else:
- _moduleLogger.error("File: %s not found" % file)
-
- def play(self):
- _moduleLogger.info("Started playing")
- self._audioProxy.play()
- self.playing = True
-
- def stop(self):
- self._audioProxy.stop()
- self.playing = False
- _moduleLogger.info("Stopped playing")
-
- def elapsed(self):
- pos_info = self._audioProxy.get_position()
- if isinstance(pos_info, tuple):
- pos, _ = pos_info
- return pos
- else:
- return 0
-
- def duration(self):
- pos_info = self._audioProxy.get_position()
- if isinstance(pos_info, tuple):
- _, dur = pos_info
- return dur
- else:
- return 0
-
- def seek_time(self, ns):
- _moduleLogger.debug("Seeking to: %s", ns)
- self._audioProxy.seek( dbus.Int32(1), dbus.Int32(ns) )
+++ /dev/null
-#!/usr/bin/env python
+++ /dev/null
-import os
-
-__pretty_app_name__ = "nQaap"
-__app_name__ = "nqaap"
-__version__ = "0.8.6"
-__build__ = 0
-__app_magic__ = 0xdeadbeef
-_data_path_ = os.path.join(os.path.expanduser("~"), ".%s" % __app_name__)
-_user_settings_ = "%s/settings.ini" % _data_path_
-_user_logpath_ = "%s/%s.log" % (_data_path_, __app_name__)
-_default_book_path_ = os.path.join(os.path.expanduser("~"), "MyDocs/Audiobooks/")
+++ /dev/null
-#!/usr/bin/python
-
-from __future__ import with_statement
-
-import os
-import errno
-import sys
-import time
-import itertools
-import functools
-import contextlib
-import logging
-import threading
-import Queue
-
-import gobject
-import gtk
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-def get_screen_orientation():
- width, height = gtk.gdk.get_default_root_window().get_size()
- if width < height:
- return gtk.ORIENTATION_VERTICAL
- else:
- return gtk.ORIENTATION_HORIZONTAL
-
-
-def orientation_change_connect(handler, *args):
- """
- @param handler(orientation, *args) -> None(?)
- """
- initialScreenOrientation = get_screen_orientation()
- orientationAndArgs = list(itertools.chain((initialScreenOrientation, ), args))
-
- def _on_screen_size_changed(screen):
- newScreenOrientation = get_screen_orientation()
- if newScreenOrientation != orientationAndArgs[0]:
- orientationAndArgs[0] = newScreenOrientation
- handler(*orientationAndArgs)
-
- rootScreen = gtk.gdk.get_default_root_window()
- return gtk.connect(rootScreen, "size-changed", _on_screen_size_changed)
-
-
-@contextlib.contextmanager
-def flock(path, timeout=-1):
- WAIT_FOREVER = -1
- DELAY = 0.1
- timeSpent = 0
-
- acquired = False
-
- while timeSpent <= timeout or timeout == WAIT_FOREVER:
- try:
- fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
- acquired = True
- break
- except OSError, e:
- if e.errno != errno.EEXIST:
- raise
- time.sleep(DELAY)
- timeSpent += DELAY
-
- assert acquired, "Failed to grab file-lock %s within timeout %d" % (path, timeout)
-
- try:
- yield fd
- finally:
- os.unlink(path)
-
-
-@contextlib.contextmanager
-def gtk_lock():
- gtk.gdk.threads_enter()
- try:
- yield
- finally:
- gtk.gdk.threads_leave()
-
-
-def find_parent_window(widget):
- while True:
- parent = widget.get_parent()
- if isinstance(parent, gtk.Window):
- return parent
- widget = parent
-
-
-def make_idler(func):
- """
- Decorator that makes a generator-function into a function that will continue execution on next call
- """
- a = []
-
- @functools.wraps(func)
- def decorated_func(*args, **kwds):
- if not a:
- a.append(func(*args, **kwds))
- try:
- a[0].next()
- return True
- except StopIteration:
- del a[:]
- return False
-
- return decorated_func
-
-
-def asynchronous_gtk_message(original_func):
- """
- @note Idea came from http://www.aclevername.com/articles/python-webgui/
- """
-
- def execute(allArgs):
- args, kwargs = allArgs
- with gtk_lock():
- original_func(*args, **kwargs)
- return False
-
- @functools.wraps(original_func)
- def delayed_func(*args, **kwargs):
- gobject.idle_add(execute, (args, kwargs))
-
- return delayed_func
-
-
-def synchronous_gtk_message(original_func):
- """
- @note Idea came from http://www.aclevername.com/articles/python-webgui/
- """
-
- @functools.wraps(original_func)
- def immediate_func(*args, **kwargs):
- with gtk_lock():
- return original_func(*args, **kwargs)
-
- return immediate_func
-
-
-def autostart(func):
- """
- >>> @autostart
- ... def grep_sink(pattern):
- ... print "Looking for %s" % pattern
- ... while True:
- ... line = yield
- ... if pattern in line:
- ... print line,
- >>> g = grep_sink("python")
- Looking for python
- >>> g.send("Yeah but no but yeah but no")
- >>> g.send("A series of tubes")
- >>> g.send("python generators rock!")
- python generators rock!
- >>> g.close()
- """
-
- @functools.wraps(func)
- def start(*args, **kwargs):
- cr = func(*args, **kwargs)
- cr.next()
- return cr
-
- return start
-
-
-@autostart
-def printer_sink(format = "%s"):
- """
- >>> pr = printer_sink("%r")
- >>> pr.send("Hello")
- 'Hello'
- >>> pr.send("5")
- '5'
- >>> pr.send(5)
- 5
- >>> p = printer_sink()
- >>> p.send("Hello")
- Hello
- >>> p.send("World")
- World
- >>> # p.throw(RuntimeError, "Goodbye")
- >>> # p.send("Meh")
- >>> # p.close()
- """
- while True:
- item = yield
- print format % (item, )
-
-
-@autostart
-def null_sink():
- """
- Good for uses like with cochain to pick up any slack
- """
- while True:
- item = yield
-
-
-@autostart
-def comap(function, target):
- """
- >>> p = printer_sink()
- >>> cm = comap(lambda x: x+1, p)
- >>> cm.send((0, ))
- 1
- >>> cm.send((1.0, ))
- 2.0
- >>> cm.send((-2, ))
- -1
- """
- while True:
- try:
- item = yield
- mappedItem = function(*item)
- target.send(mappedItem)
- except Exception, e:
- _moduleLogger.exception("Forwarding exception!")
- target.throw(e.__class__, str(e))
-
-
-def _flush_queue(queue):
- while not queue.empty():
- yield queue.get()
-
-
-@autostart
-def queue_sink(queue):
- """
- >>> q = Queue.Queue()
- >>> qs = queue_sink(q)
- >>> qs.send("Hello")
- >>> qs.send("World")
- >>> qs.throw(RuntimeError, "Goodbye")
- >>> qs.send("Meh")
- >>> qs.close()
- >>> print [i for i in _flush_queue(q)]
- [(None, 'Hello'), (None, 'World'), (<type 'exceptions.RuntimeError'>, 'Goodbye'), (None, 'Meh'), (<type 'exceptions.GeneratorExit'>, None)]
- """
- while True:
- try:
- item = yield
- queue.put((None, item))
- except Exception, e:
- queue.put((e.__class__, str(e)))
- except GeneratorExit:
- queue.put((GeneratorExit, None))
- raise
-
-
-def decode_item(item, target):
- if item[0] is None:
- target.send(item[1])
- return False
- elif item[0] is GeneratorExit:
- target.close()
- return True
- else:
- target.throw(item[0], item[1])
- return False
-
-
-def nonqueue_source(queue, target):
- isDone = False
- while not isDone:
- item = queue.get()
- isDone = decode_item(item, target)
- while not queue.empty():
- queue.get_nowait()
-
-
-def threaded_stage(target, thread_factory = threading.Thread):
- messages = Queue.Queue()
-
- run_source = functools.partial(nonqueue_source, messages, target)
- thread = thread_factory(target=run_source)
- thread.setDaemon(True)
- thread.start()
-
- # Sink running in current thread
- return queue_sink(messages)
-
-
-def log_exception(logger):
-
- def log_exception_decorator(func):
-
- @functools.wraps(func)
- def wrapper(*args, **kwds):
- try:
- return func(*args, **kwds)
- except Exception:
- logger.exception(func.__name__)
-
- return wrapper
-
- return log_exception_decorator
-
-
-class LoginWindow(object):
-
- def __init__(self, widgetTree):
- """
- @note Thread agnostic
- """
- self._dialog = widgetTree.get_widget("loginDialog")
- self._parentWindow = widgetTree.get_widget("mainWindow")
- self._serviceCombo = widgetTree.get_widget("serviceCombo")
- self._usernameEntry = widgetTree.get_widget("usernameentry")
- self._passwordEntry = widgetTree.get_widget("passwordentry")
-
- self._serviceList = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING)
- self._serviceCombo.set_model(self._serviceList)
- cell = gtk.CellRendererText()
- self._serviceCombo.pack_start(cell, True)
- self._serviceCombo.add_attribute(cell, 'text', 1)
- self._serviceCombo.set_active(0)
-
- widgetTree.get_widget("loginbutton").connect("clicked", self._on_loginbutton_clicked)
- widgetTree.get_widget("logins_close_button").connect("clicked", self._on_loginclose_clicked)
-
- def request_credentials(self,
- parentWindow = None,
- defaultCredentials = ("", "")
- ):
- """
- @note UI Thread
- """
- if parentWindow is None:
- parentWindow = self._parentWindow
-
- self._serviceCombo.hide()
- self._serviceList.clear()
-
- self._usernameEntry.set_text(defaultCredentials[0])
- self._passwordEntry.set_text(defaultCredentials[1])
-
- try:
- self._dialog.set_transient_for(parentWindow)
- self._dialog.set_default_response(gtk.RESPONSE_OK)
- response = self._dialog.run()
- if response != gtk.RESPONSE_OK:
- raise RuntimeError("Login Cancelled")
-
- username = self._usernameEntry.get_text()
- password = self._passwordEntry.get_text()
- self._passwordEntry.set_text("")
- finally:
- self._dialog.hide()
-
- return username, password
-
- def request_credentials_from(self,
- services,
- parentWindow = None,
- defaultCredentials = ("", "")
- ):
- """
- @note UI Thread
- """
- if parentWindow is None:
- parentWindow = self._parentWindow
-
- self._serviceList.clear()
- for serviceIdserviceName in services:
- self._serviceList.append(serviceIdserviceName)
- self._serviceCombo.set_active(0)
- self._serviceCombo.show()
-
- self._usernameEntry.set_text(defaultCredentials[0])
- self._passwordEntry.set_text(defaultCredentials[1])
-
- try:
- self._dialog.set_transient_for(parentWindow)
- self._dialog.set_default_response(gtk.RESPONSE_OK)
- response = self._dialog.run()
- if response != gtk.RESPONSE_OK:
- raise RuntimeError("Login Cancelled")
-
- username = self._usernameEntry.get_text()
- password = self._passwordEntry.get_text()
- finally:
- self._dialog.hide()
-
- itr = self._serviceCombo.get_active_iter()
- serviceId = int(self._serviceList.get_value(itr, 0))
- self._serviceList.clear()
- return serviceId, username, password
-
- def _on_loginbutton_clicked(self, *args):
- self._dialog.response(gtk.RESPONSE_OK)
-
- def _on_loginclose_clicked(self, *args):
- self._dialog.response(gtk.RESPONSE_CANCEL)
-
-
-def safecall(f, errorDisplay=None, default=None, exception=Exception):
- '''
- Returns modified f. When the modified f is called and throws an
- exception, the default value is returned
- '''
- def _safecall(*args, **argv):
- try:
- return f(*args,**argv)
- except exception, e:
- if errorDisplay is not None:
- errorDisplay.push_exception(e)
- return default
- return _safecall
-
-
-class ErrorDisplay(object):
-
- def __init__(self, widgetTree):
- super(ErrorDisplay, self).__init__()
- self.__errorBox = widgetTree.get_widget("errorEventBox")
- self.__errorDescription = widgetTree.get_widget("errorDescription")
- self.__errorClose = widgetTree.get_widget("errorClose")
- self.__parentBox = self.__errorBox.get_parent()
-
- self.__errorBox.connect("button_release_event", self._on_close)
-
- self.__messages = []
- self.__parentBox.remove(self.__errorBox)
-
- def push_message_with_lock(self, message):
- with gtk_lock():
- self.push_message(message)
-
- def push_message(self, message):
- self.__messages.append(message)
- if 1 == len(self.__messages):
- self.__show_message(message)
-
- def push_exception_with_lock(self):
- with gtk_lock():
- self.push_exception()
-
- def push_exception(self):
- userMessage = str(sys.exc_info()[1])
- self.push_message(userMessage)
- _moduleLogger.exception(userMessage)
-
- def pop_message(self):
- del self.__messages[0]
- if 0 == len(self.__messages):
- self.__hide_message()
- else:
- self.__errorDescription.set_text(self.__messages[0])
-
- def _on_close(self, *args):
- self.pop_message()
-
- def __show_message(self, message):
- self.__errorDescription.set_text(message)
- self.__parentBox.pack_start(self.__errorBox, False, False)
- self.__parentBox.reorder_child(self.__errorBox, 1)
-
- def __hide_message(self):
- self.__errorDescription.set_text("")
- self.__parentBox.remove(self.__errorBox)
-
-
-class DummyErrorDisplay(object):
-
- def __init__(self):
- super(DummyErrorDisplay, self).__init__()
-
- self.__messages = []
-
- def push_message_with_lock(self, message):
- self.push_message(message)
-
- def push_message(self, message):
- if 0 < len(self.__messages):
- self.__messages.append(message)
- else:
- self.__show_message(message)
-
- def push_exception(self, exception = None):
- userMessage = str(sys.exc_value)
- _moduleLogger.exception(userMessage)
-
- def pop_message(self):
- if 0 < len(self.__messages):
- self.__show_message(self.__messages[0])
- del self.__messages[0]
-
- def __show_message(self, message):
- _moduleLogger.debug(message)
-
-
-class MessageBox(gtk.MessageDialog):
-
- def __init__(self, message):
- parent = None
- gtk.MessageDialog.__init__(
- self,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_ERROR,
- gtk.BUTTONS_OK,
- message,
- )
- self.set_default_response(gtk.RESPONSE_OK)
- self.connect('response', self._handle_clicked)
-
- def _handle_clicked(self, *args):
- self.destroy()
-
-
-class MessageBox2(gtk.MessageDialog):
-
- def __init__(self, message):
- parent = None
- gtk.MessageDialog.__init__(
- self,
- parent,
- gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_ERROR,
- gtk.BUTTONS_OK,
- message,
- )
- self.set_default_response(gtk.RESPONSE_OK)
- self.connect('response', self._handle_clicked)
-
- def _handle_clicked(self, *args):
- self.destroy()
-
-
-class PopupCalendar(object):
-
- def __init__(self, parent, displayDate, title = ""):
- self._displayDate = displayDate
-
- self._calendar = gtk.Calendar()
- self._calendar.select_month(self._displayDate.month, self._displayDate.year)
- self._calendar.select_day(self._displayDate.day)
- self._calendar.set_display_options(
- gtk.CALENDAR_SHOW_HEADING |
- gtk.CALENDAR_SHOW_DAY_NAMES |
- gtk.CALENDAR_NO_MONTH_CHANGE |
- 0
- )
- self._calendar.connect("day-selected", self._on_day_selected)
-
- self._popupWindow = gtk.Window()
- self._popupWindow.set_title(title)
- self._popupWindow.add(self._calendar)
- self._popupWindow.set_transient_for(parent)
- self._popupWindow.set_modal(True)
- self._popupWindow.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
- self._popupWindow.set_skip_pager_hint(True)
- self._popupWindow.set_skip_taskbar_hint(True)
-
- def run(self):
- self._popupWindow.show_all()
-
- def _on_day_selected(self, *args):
- try:
- self._calendar.select_month(self._displayDate.month, self._displayDate.year)
- self._calendar.select_day(self._displayDate.day)
- except Exception, e:
- _moduleLogger.exception(e)
-
-
-if __name__ == "__main__":
- if False:
- import datetime
- cal = PopupCalendar(None, datetime.datetime.now())
- cal._popupWindow.connect("destroy", lambda w: gtk.main_quit())
- cal.run()
-
- gtk.main()
+++ /dev/null
-#!/usr/bin/env python
-
-"""
-Open Issues
- @bug not all of a message is shown
- @bug Buttons are too small
-"""
-
-
-import gobject
-import gtk
-import dbus
-
-
-class _NullHildonModule(object):
- pass
-
-
-try:
- import hildon as _hildon
- hildon = _hildon # Dumb but gets around pyflakiness
-except (ImportError, OSError):
- hildon = _NullHildonModule
-
-
-IS_HILDON_SUPPORTED = hildon is not _NullHildonModule
-
-
-class _NullHildonProgram(object):
-
- def add_window(self, window):
- pass
-
-
-def _hildon_get_app_class():
- return hildon.Program
-
-
-def _null_get_app_class():
- return _NullHildonProgram
-
-
-try:
- hildon.Program
- get_app_class = _hildon_get_app_class
-except AttributeError:
- get_app_class = _null_get_app_class
-
-
-def _hildon_set_application_name(name):
- gtk.set_application_name(name)
-
-
-def _null_set_application_name(name):
- pass
-
-
-try:
- gtk.set_application_name
- set_application_name = _hildon_set_application_name
-except AttributeError:
- set_application_name = _null_set_application_name
-
-
-def _fremantle_hildonize_window(app, window):
- oldWindow = window
- newWindow = hildon.StackableWindow()
- if oldWindow.get_child() is not None:
- oldWindow.get_child().reparent(newWindow)
- app.add_window(newWindow)
- return newWindow
-
-
-def _hildon_hildonize_window(app, window):
- oldWindow = window
- newWindow = hildon.Window()
- if oldWindow.get_child() is not None:
- oldWindow.get_child().reparent(newWindow)
- app.add_window(newWindow)
- return newWindow
-
-
-def _null_hildonize_window(app, window):
- return window
-
-
-try:
- hildon.StackableWindow
- hildonize_window = _fremantle_hildonize_window
-except AttributeError:
- try:
- hildon.Window
- hildonize_window = _hildon_hildonize_window
- except AttributeError:
- hildonize_window = _null_hildonize_window
-
-
-def _fremantle_hildonize_menu(window, gtkMenu):
- appMenu = hildon.AppMenu()
- window.set_app_menu(appMenu)
- gtkMenu.get_parent().remove(gtkMenu)
- return appMenu
-
-
-def _hildon_hildonize_menu(window, gtkMenu):
- hildonMenu = gtk.Menu()
- for child in gtkMenu.get_children():
- child.reparent(hildonMenu)
- window.set_menu(hildonMenu)
- gtkMenu.destroy()
- return hildonMenu
-
-
-def _null_hildonize_menu(window, gtkMenu):
- return gtkMenu
-
-
-try:
- hildon.AppMenu
- GTK_MENU_USED = False
- IS_FREMANTLE_SUPPORTED = True
- hildonize_menu = _fremantle_hildonize_menu
-except AttributeError:
- GTK_MENU_USED = True
- IS_FREMANTLE_SUPPORTED = False
- if IS_HILDON_SUPPORTED:
- hildonize_menu = _hildon_hildonize_menu
- else:
- hildonize_menu = _null_hildonize_menu
-
-
-def _hildon_set_button_auto_selectable(button):
- button.set_theme_size(hildon.HILDON_SIZE_AUTO_HEIGHT)
-
-
-def _null_set_button_auto_selectable(button):
- pass
-
-
-try:
- hildon.HILDON_SIZE_AUTO_HEIGHT
- gtk.Button.set_theme_size
- set_button_auto_selectable = _hildon_set_button_auto_selectable
-except AttributeError:
- set_button_auto_selectable = _null_set_button_auto_selectable
-
-
-def _hildon_set_button_finger_selectable(button):
- button.set_theme_size(hildon.HILDON_SIZE_FINGER_HEIGHT)
-
-
-def _null_set_button_finger_selectable(button):
- pass
-
-
-try:
- hildon.HILDON_SIZE_FINGER_HEIGHT
- gtk.Button.set_theme_size
- set_button_finger_selectable = _hildon_set_button_finger_selectable
-except AttributeError:
- set_button_finger_selectable = _null_set_button_finger_selectable
-
-
-def _hildon_set_button_thumb_selectable(button):
- button.set_theme_size(hildon.HILDON_SIZE_THUMB_HEIGHT)
-
-
-def _null_set_button_thumb_selectable(button):
- pass
-
-
-try:
- hildon.HILDON_SIZE_THUMB_HEIGHT
- gtk.Button.set_theme_size
- set_button_thumb_selectable = _hildon_set_button_thumb_selectable
-except AttributeError:
- set_button_thumb_selectable = _null_set_button_thumb_selectable
-
-
-def _hildon_set_cell_thumb_selectable(renderer):
- renderer.set_property("scale", 1.5)
-
-
-def _null_set_cell_thumb_selectable(renderer):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- set_cell_thumb_selectable = _hildon_set_cell_thumb_selectable
-else:
- set_cell_thumb_selectable = _null_set_cell_thumb_selectable
-
-
-def _hildon_set_pix_cell_thumb_selectable(renderer):
- renderer.set_property("stock-size", 48)
-
-
-def _null_set_pix_cell_thumb_selectable(renderer):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- set_pix_cell_thumb_selectable = _hildon_set_pix_cell_thumb_selectable
-else:
- set_pix_cell_thumb_selectable = _null_set_pix_cell_thumb_selectable
-
-
-def _fremantle_show_information_banner(parent, message):
- hildon.hildon_banner_show_information(parent, "", message)
-
-
-def _hildon_show_information_banner(parent, message):
- hildon.hildon_banner_show_information(parent, None, message)
-
-
-def _null_show_information_banner(parent, message):
- pass
-
-
-if IS_FREMANTLE_SUPPORTED:
- show_information_banner = _fremantle_show_information_banner
-else:
- try:
- hildon.hildon_banner_show_information
- show_information_banner = _hildon_show_information_banner
- except AttributeError:
- show_information_banner = _null_show_information_banner
-
-
-def _fremantle_show_busy_banner_start(parent, message):
- hildon.hildon_gtk_window_set_progress_indicator(parent, True)
- return parent
-
-
-def _fremantle_show_busy_banner_end(banner):
- hildon.hildon_gtk_window_set_progress_indicator(banner, False)
-
-
-def _hildon_show_busy_banner_start(parent, message):
- return hildon.hildon_banner_show_animation(parent, None, message)
-
-
-def _hildon_show_busy_banner_end(banner):
- banner.destroy()
-
-
-def _null_show_busy_banner_start(parent, message):
- return None
-
-
-def _null_show_busy_banner_end(banner):
- assert banner is None
-
-
-try:
- hildon.hildon_gtk_window_set_progress_indicator
- show_busy_banner_start = _fremantle_show_busy_banner_start
- show_busy_banner_end = _fremantle_show_busy_banner_end
-except AttributeError:
- try:
- hildon.hildon_banner_show_animation
- show_busy_banner_start = _hildon_show_busy_banner_start
- show_busy_banner_end = _hildon_show_busy_banner_end
- except AttributeError:
- show_busy_banner_start = _null_show_busy_banner_start
- show_busy_banner_end = _null_show_busy_banner_end
-
-
-def _hildon_hildonize_text_entry(textEntry):
- textEntry.set_property('hildon-input-mode', 7)
-
-
-def _null_hildonize_text_entry(textEntry):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- hildonize_text_entry = _hildon_hildonize_text_entry
-else:
- hildonize_text_entry = _null_hildonize_text_entry
-
-
-def _hildon_window_to_portrait(window):
- # gtk documentation is unclear whether this does a "=" or a "|="
- flags = hildon.PORTRAIT_MODE_SUPPORT | hildon.PORTRAIT_MODE_REQUEST
- hildon.hildon_gtk_window_set_portrait_flags(window, flags)
-
-
-def _hildon_window_to_landscape(window):
- # gtk documentation is unclear whether this does a "=" or a "&= ~"
- flags = hildon.PORTRAIT_MODE_SUPPORT
- hildon.hildon_gtk_window_set_portrait_flags(window, flags)
-
-
-def _null_window_to_portrait(window):
- pass
-
-
-def _null_window_to_landscape(window):
- pass
-
-
-try:
- hildon.PORTRAIT_MODE_SUPPORT
- hildon.PORTRAIT_MODE_REQUEST
- hildon.hildon_gtk_window_set_portrait_flags
-
- window_to_portrait = _hildon_window_to_portrait
- window_to_landscape = _hildon_window_to_landscape
-except AttributeError:
- window_to_portrait = _null_window_to_portrait
- window_to_landscape = _null_window_to_landscape
-
-
-def get_device_orientation():
- bus = dbus.SystemBus()
- try:
- rawMceRequest = bus.get_object("com.nokia.mce", "/com/nokia/mce/request")
- mceRequest = dbus.Interface(rawMceRequest, dbus_interface="com.nokia.mce.request")
- orientation, standState, faceState, xAxis, yAxis, zAxis = mceRequest.get_device_orientation()
- except dbus.exception.DBusException:
- # catching for documentation purposes that when a system doesn't
- # support this, this is what to expect
- raise
-
- if orientation == "":
- return gtk.ORIENTATION_HORIZONTAL
- elif orientation == "":
- return gtk.ORIENTATION_VERTICAL
- else:
- raise RuntimeError("Unknown orientation: %s" % orientation)
-
-
-def _hildon_hildonize_password_entry(textEntry):
- textEntry.set_property('hildon-input-mode', 7 | (1 << 29))
-
-
-def _null_hildonize_password_entry(textEntry):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- hildonize_password_entry = _hildon_hildonize_password_entry
-else:
- hildonize_password_entry = _null_hildonize_password_entry
-
-
-def _hildon_hildonize_combo_entry(comboEntry):
- comboEntry.set_property('hildon-input-mode', 1 << 4)
-
-
-def _null_hildonize_combo_entry(textEntry):
- pass
-
-
-if IS_HILDON_SUPPORTED:
- hildonize_combo_entry = _hildon_hildonize_combo_entry
-else:
- hildonize_combo_entry = _null_hildonize_combo_entry
-
-
-def _fremantle_hildonize_scrollwindow(scrolledWindow):
- pannableWindow = hildon.PannableArea()
-
- child = scrolledWindow.get_child()
- scrolledWindow.remove(child)
- pannableWindow.add(child)
-
- parent = scrolledWindow.get_parent()
- if parent is not None:
- parent.remove(scrolledWindow)
- parent.add(pannableWindow)
-
- return pannableWindow
-
-
-def _hildon_hildonize_scrollwindow(scrolledWindow):
- hildon.hildon_helper_set_thumb_scrollbar(scrolledWindow, True)
- return scrolledWindow
-
-
-def _null_hildonize_scrollwindow(scrolledWindow):
- return scrolledWindow
-
-
-try:
- hildon.PannableArea
- hildonize_scrollwindow = _fremantle_hildonize_scrollwindow
- hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
-except AttributeError:
- try:
- hildon.hildon_helper_set_thumb_scrollbar
- hildonize_scrollwindow = _hildon_hildonize_scrollwindow
- hildonize_scrollwindow_with_viewport = _hildon_hildonize_scrollwindow
- except AttributeError:
- hildonize_scrollwindow = _null_hildonize_scrollwindow
- hildonize_scrollwindow_with_viewport = _null_hildonize_scrollwindow
-
-
-def _hildon_request_number(parent, title, range, default):
- spinner = hildon.NumberEditor(*range)
- spinner.set_value(default)
-
- dialog = gtk.Dialog(
- title,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
- )
- dialog.set_default_response(gtk.RESPONSE_CANCEL)
- dialog.get_child().add(spinner)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- return spinner.get_value()
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-def _null_request_number(parent, title, range, default):
- adjustment = gtk.Adjustment(default, range[0], range[1], 1, 5, 0)
- spinner = gtk.SpinButton(adjustment, 0, 0)
- spinner.set_wrap(False)
-
- dialog = gtk.Dialog(
- title,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
- )
- dialog.set_default_response(gtk.RESPONSE_CANCEL)
- dialog.get_child().add(spinner)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- return spinner.get_value_as_int()
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-try:
- hildon.NumberEditor # TODO deprecated in fremantle
- request_number = _hildon_request_number
-except AttributeError:
- request_number = _null_request_number
-
-
-def _hildon_touch_selector(parent, title, items, defaultIndex):
- model = gtk.ListStore(gobject.TYPE_STRING)
- for item in items:
- model.append((item, ))
-
- selector = hildon.TouchSelector()
- selector.append_text_column(model, True)
- selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
- selector.set_active(0, defaultIndex)
-
- dialog = hildon.PickerDialog(parent)
- dialog.set_selector(selector)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- return selector.get_active(0)
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-def _on_null_touch_selector_activated(treeView, path, column, dialog, pathData):
- dialog.response(gtk.RESPONSE_OK)
- pathData[0] = path
-
-
-def _null_touch_selector(parent, title, items, defaultIndex = -1):
- parentSize = parent.get_size()
-
- model = gtk.ListStore(gobject.TYPE_STRING)
- for item in items:
- model.append((item, ))
-
- cell = gtk.CellRendererText()
- set_cell_thumb_selectable(cell)
- column = gtk.TreeViewColumn(title)
- column.pack_start(cell, expand=True)
- column.add_attribute(cell, "text", 0)
-
- treeView = gtk.TreeView()
- treeView.set_model(model)
- treeView.append_column(column)
- selection = treeView.get_selection()
- selection.set_mode(gtk.SELECTION_SINGLE)
- if 0 < defaultIndex:
- selection.select_path((defaultIndex, ))
-
- scrolledWin = gtk.ScrolledWindow()
- scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- scrolledWin.add(treeView)
-
- dialog = gtk.Dialog(
- title,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
- )
- dialog.set_default_response(gtk.RESPONSE_CANCEL)
- dialog.get_child().add(scrolledWin)
- dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
-
- scrolledWin = hildonize_scrollwindow(scrolledWin)
- pathData = [None]
- treeView.connect("row-activated", _on_null_touch_selector_activated, dialog, pathData)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- if pathData[0] is None:
- raise RuntimeError("No selection made")
- return pathData[0][0]
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-try:
- hildon.PickerDialog
- hildon.TouchSelector
- touch_selector = _hildon_touch_selector
-except AttributeError:
- touch_selector = _null_touch_selector
-
-
-def _hildon_touch_selector_entry(parent, title, items, defaultItem):
- # Got a segfault when using append_text_column with TouchSelectorEntry, so using this way
- try:
- selector = hildon.TouchSelectorEntry(text=True)
- except TypeError:
- selector = hildon.hildon_touch_selector_entry_new_text()
- defaultIndex = -1
- for i, item in enumerate(items):
- selector.append_text(item)
- if item == defaultItem:
- defaultIndex = i
-
- dialog = hildon.PickerDialog(parent)
- dialog.set_selector(selector)
-
- if 0 < defaultIndex:
- selector.set_active(0, defaultIndex)
- else:
- selector.get_entry().set_text(defaultItem)
-
- try:
- dialog.show_all()
- response = dialog.run()
- finally:
- dialog.hide()
-
- if response == gtk.RESPONSE_OK:
- return selector.get_entry().get_text()
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
-
-
-def _on_null_touch_selector_entry_entry_changed(entry, result, selection, defaultIndex):
- custom = entry.get_text().strip()
- if custom:
- result[0] = custom
- selection.unselect_all()
- else:
- result[0] = None
- selection.select_path((defaultIndex, ))
-
-
-def _on_null_touch_selector_entry_entry_activated(customEntry, dialog, result):
- dialog.response(gtk.RESPONSE_OK)
- result[0] = customEntry.get_text()
-
-
-def _on_null_touch_selector_entry_tree_activated(treeView, path, column, dialog, result):
- dialog.response(gtk.RESPONSE_OK)
- model = treeView.get_model()
- itr = model.get_iter(path)
- if itr is not None:
- result[0] = model.get_value(itr, 0)
-
-
-def _null_touch_selector_entry(parent, title, items, defaultItem):
- parentSize = parent.get_size()
-
- model = gtk.ListStore(gobject.TYPE_STRING)
- defaultIndex = -1
- for i, item in enumerate(items):
- model.append((item, ))
- if item == defaultItem:
- defaultIndex = i
-
- cell = gtk.CellRendererText()
- set_cell_thumb_selectable(cell)
- column = gtk.TreeViewColumn(title)
- column.pack_start(cell, expand=True)
- column.add_attribute(cell, "text", 0)
-
- treeView = gtk.TreeView()
- treeView.set_model(model)
- treeView.append_column(column)
- selection = treeView.get_selection()
- selection.set_mode(gtk.SELECTION_SINGLE)
-
- scrolledWin = gtk.ScrolledWindow()
- scrolledWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- scrolledWin.add(treeView)
-
- customEntry = gtk.Entry()
-
- layout = gtk.VBox()
- layout.pack_start(customEntry, expand=False)
- layout.pack_start(scrolledWin)
-
- dialog = gtk.Dialog(
- title,
- parent,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
- )
- dialog.set_default_response(gtk.RESPONSE_CANCEL)
- dialog.get_child().add(layout)
- dialog.resize(parentSize[0], max(parentSize[1]-100, 100))
-
- scrolledWin = hildonize_scrollwindow(scrolledWin)
-
- result = [None]
- if 0 < defaultIndex:
- selection.select_path((defaultIndex, ))
- result[0] = defaultItem
- else:
- customEntry.set_text(defaultItem)
-
- customEntry.connect("activate", _on_null_touch_selector_entry_entry_activated, dialog, result)
- customEntry.connect("changed", _on_null_touch_selector_entry_entry_changed, result, selection, defaultIndex)
- treeView.connect("row-activated", _on_null_touch_selector_entry_tree_activated, dialog, result)
-
- try:
- dialog.show_all()
- response = dialog.run()
-
- if response == gtk.RESPONSE_OK:
- _, itr = selection.get_selected()
- if itr is not None:
- return model.get_value(itr, 0)
- else:
- enteredText = customEntry.get_text().strip()
- if enteredText:
- return enteredText
- elif result[0] is not None:
- return result[0]
- else:
- raise RuntimeError("No selection made")
- elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
- raise RuntimeError("User cancelled request")
- else:
- raise RuntimeError("Unrecognized response %r", response)
- finally:
- dialog.hide()
- dialog.destroy()
-
-
-try:
- hildon.PickerDialog
- hildon.TouchSelectorEntry
- touch_selector_entry = _hildon_touch_selector_entry
-except AttributeError:
- touch_selector_entry = _null_touch_selector_entry
-
-
-if __name__ == "__main__":
- app = get_app_class()()
-
- label = gtk.Label("Hello World from a Label!")
-
- win = gtk.Window()
- win.add(label)
- win = hildonize_window(app, win)
- if False and IS_FREMANTLE_SUPPORTED:
- appMenu = hildon.AppMenu()
- for i in xrange(5):
- b = gtk.Button(str(i))
- appMenu.append(b)
- win.set_app_menu(appMenu)
- win.show_all()
- appMenu.show_all()
- gtk.main()
- elif False:
- print touch_selector(win, "Test", ["A", "B", "C", "D"], 2)
- elif False:
- print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "C")
- print touch_selector_entry(win, "Test", ["A", "B", "C", "D"], "Blah")
- elif False:
- import pprint
- name, value = "", ""
- goodLocals = [
- (name, value) for (name, value) in locals().iteritems()
- if not name.startswith("_")
- ]
- pprint.pprint(goodLocals)
- elif False:
- import time
- show_information_banner(win, "Hello World")
- time.sleep(5)
- elif False:
- import time
- banner = show_busy_banner_start(win, "Hello World")
- time.sleep(5)
- show_busy_banner_end(banner)
+++ /dev/null
-#!/usr/bin/env python
-
-import os
-import logging
-
-import constants
-import nqaap_gtk
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-try:
- os.makedirs(constants._data_path_)
-except OSError, e:
- if e.errno != 17:
- raise
-
-logging.basicConfig(level=logging.DEBUG, filename=constants._user_logpath_)
-_moduleLogger.info("%s %s-%s" % (constants.__pretty_app_name__, constants.__version__, constants.__build__))
-_moduleLogger.info("OS: %s" % (os.uname()[0], ))
-_moduleLogger.info("Kernel: %s (%s) for %s" % os.uname()[2:])
-_moduleLogger.info("Hostname: %s" % os.uname()[1])
-
-try:
- nqaap_gtk.run()
-finally:
- logging.shutdown()
+++ /dev/null
-#! /usr/bin/env python
-
-import logging
-
-import dbus
-import dbus.mainloop.glib
-import gobject
-import gtk
-
-import constants
-import hildonize
-from Player import Player
-from Gui import Gui
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-def run():
- l = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
- gobject.threads_init()
- gtk.gdk.threads_init()
-
- if hildonize.IS_FREMANTLE_SUPPORTED:
- hildonize.set_application_name("FMRadio")
- else:
- hildonize.set_application_name(constants.__pretty_app_name__)
- gui = Gui()
- controller = Player(ui = gui)
- gui.controller = controller
- gui.load_settings()
-
- gtk.main()
-
-
-if __name__ == "__main__":
- logging.basicConfig(level=logging.DEBUG)
- run()
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-"""
-This file is part of Multilist.
-
-Multilist is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Multilist is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Multilist. If not, see <http://www.gnu.org/licenses/>.
-
-Copyright (C) 2008 Christoph Würstle
-"""
-
-import logging
-
-import gtk
-
-import hildonize
-import gtk_toolbox
-
-try:
- _
-except NameError:
- _ = lambda x: x
-
-
-_moduleLogger = logging.getLogger(__name__)
-
-
-class SettingsDialog(object):
-
- def __init__(self, parent):
- self.window = None
-
- self.__isPortraitCheckbutton = gtk.CheckButton("Portrait Mode")
-
- self.__rotationSection = gtk.VBox()
- self.__rotationSection.pack_start(self.__isPortraitCheckbutton, False, True)
-
- rotationFrame = gtk.Frame("Rotation")
- rotationFrame.add(self.__rotationSection)
-
- self.__audioBooksPath = gtk.Entry()
- self.__audioBooksPathButton = gtk.Button("Choose")
- self.__audioBooksPathButton.connect("clicked", self._on_path_choose)
-
- self.__audiobookPathSection = gtk.HBox()
- self.__audiobookPathSection.pack_start(self.__audioBooksPath, True, True)
- self.__audiobookPathSection.pack_start(self.__audioBooksPathButton, False, True)
-
- self.__audiobookSection = gtk.VBox()
- self.__audiobookSection.pack_start(self.__audiobookPathSection)
-
- audiobookFrame = gtk.Frame("Audiobooks")
- audiobookFrame.add(self.__audiobookSection)
-
- settingsBox = gtk.VBox()
- settingsBox.pack_start(rotationFrame, False, True)
- settingsBox.pack_start(audiobookFrame, False, True)
- settingsView = gtk.Viewport()
- settingsView.add(settingsBox)
- settingsScrollView = gtk.ScrolledWindow()
- settingsScrollView.add(settingsView)
- settingsScrollView.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- parent.pack_start(settingsScrollView, True, True)
-
- settingsScrollView = hildonize.hildonize_scrollwindow(settingsScrollView)
-
- def set_portrait_state(self, isPortrait):
- self.__isPortraitCheckbutton.set_active(isPortrait)
-
- def is_portrait(self):
- return self.__isPortraitCheckbutton.get_active()
-
- def set_audiobook_path(self, path):
- self.__audioBooksPath.set_text(path)
-
- def get_audiobook_path(self):
- return self.__audioBooksPath.get_text()
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_path_choose(self, *args):
- fileChooser = gtk.FileChooserDialog(
- title="Audiobooks",
- action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
- parent=self.window,
- )
- fileChooser.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
- fileChooser.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
- fileChooser.set_filename(self.__audioBooksPath.get_text())
- userResponse = fileChooser.run()
- fileChooser.hide()
- if userResponse == gtk.RESPONSE_OK:
- filename = fileChooser.get_filename()
- self.__audioBooksPath.set_text(filename)
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+This file is part of Multilist.
+
+Multilist is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Multilist is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Multilist. If not, see <http://www.gnu.org/licenses/>.
+
+Copyright (C) 2008 Christoph Würstle
+"""
+
+import logging
+
+import gtk
+
+import hildonize
+import gtk_toolbox
+
+try:
+ _
+except NameError:
+ _ = lambda x: x
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class SettingsDialog(object):
+
+ def __init__(self, parent):
+ self.window = None
+
+ self.__isPortraitCheckbutton = gtk.CheckButton("Portrait Mode")
+
+ self.__rotationSection = gtk.VBox()
+ self.__rotationSection.pack_start(self.__isPortraitCheckbutton, False, True)
+
+ rotationFrame = gtk.Frame("Rotation")
+ rotationFrame.add(self.__rotationSection)
+
+ self.__audioBooksPath = gtk.Entry()
+ self.__audioBooksPathButton = gtk.Button("Choose")
+ self.__audioBooksPathButton.connect("clicked", self._on_path_choose)
+
+ self.__audiobookPathSection = gtk.HBox()
+ self.__audiobookPathSection.pack_start(self.__audioBooksPath, True, True)
+ self.__audiobookPathSection.pack_start(self.__audioBooksPathButton, False, True)
+
+ self.__audiobookSection = gtk.VBox()
+ self.__audiobookSection.pack_start(self.__audiobookPathSection)
+
+ audiobookFrame = gtk.Frame("Audiobooks")
+ audiobookFrame.add(self.__audiobookSection)
+
+ settingsBox = gtk.VBox()
+ settingsBox.pack_start(rotationFrame, False, True)
+ settingsBox.pack_start(audiobookFrame, False, True)
+ settingsView = gtk.Viewport()
+ settingsView.add(settingsBox)
+ settingsScrollView = gtk.ScrolledWindow()
+ settingsScrollView.add(settingsView)
+ settingsScrollView.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ parent.pack_start(settingsScrollView, True, True)
+
+ settingsScrollView = hildonize.hildonize_scrollwindow(settingsScrollView)
+
+ def set_portrait_state(self, isPortrait):
+ self.__isPortraitCheckbutton.set_active(isPortrait)
+
+ def is_portrait(self):
+ return self.__isPortraitCheckbutton.get_active()
+
+ def set_audiobook_path(self, path):
+ self.__audioBooksPath.set_text(path)
+
+ def get_audiobook_path(self):
+ return self.__audioBooksPath.get_text()
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_path_choose(self, *args):
+ fileChooser = gtk.FileChooserDialog(
+ title="Audiobooks",
+ action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ parent=self.window,
+ )
+ fileChooser.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
+ fileChooser.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
+ fileChooser.set_filename(self.__audioBooksPath.get_text())
+ userResponse = fileChooser.run()
+ fileChooser.hide()
+ if userResponse == gtk.RESPONSE_OK:
+ filename = fileChooser.get_filename()
+ self.__audioBooksPath.set_text(filename)
+++ /dev/null
-[Desktop Entry]
-Version=1.0.0
-Encoding=UTF-8
-Name=nQaap
-Exec=/usr/bin/run-standalone.sh python /opt/Nqa-Audiobook-player/nqaap.py
-Icon=nqaap
-X-Icon-path=/usr/share/icons
-X-Window-Icon=nqaap
-Type=Application
-X-Osso-Type=application/x-executable
--- /dev/null
+[Desktop Entry]
+Version=1.0.0
+Encoding=UTF-8
+Name=nQaap
+Exec=/usr/bin/run-standalone.sh python /opt/Nqa-Audiobook-player/nqaap.py
+Icon=nqaap
+X-Icon-path=/usr/share/icons
+X-Window-Icon=nqaap
+Type=Application
+X-Osso-Type=application/x-executable