Improvements to the extension system
- Add category metadata in every extension - Show this category in the extension list gui - Add "mandatory_in" and "disable-in" configuration for an extension - Add Ubuntu unity check to enable/disable unity specific extensions - Move "gpodder.win32" and "gpodder.osx" setting to the "gpodder.ui" namespace to be able to use it in the extensions category settings - Only show metadata information in the right-click dialog of an extension
This commit is contained in:
parent
de0cae32aa
commit
d5eae16b9f
2
bin/gpo
2
bin/gpo
|
@ -121,7 +121,7 @@ gpodder.ui.cli = True
|
|||
# Platform detection (i.e. MeeGo 1.2 Harmattan, etc..)
|
||||
gpodder.detect_platform()
|
||||
|
||||
have_ansi = sys.stdout.isatty() and not gpodder.win32
|
||||
have_ansi = sys.stdout.isatty() and not gpodder.ui.win32
|
||||
interactive_console = sys.stdin.isatty() and sys.stdout.isatty()
|
||||
is_single_command = False
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ def main():
|
|||
help=_('Subscribe to the given URL'))
|
||||
|
||||
# On Mac OS X, support the "psn" parameter for compatibility (bug 939)
|
||||
if gpodder.osx:
|
||||
if gpodder.ui.osx:
|
||||
parser.add_option('-p', '--psn', dest='macpsn', metavar='PSN',
|
||||
help=_('Mac OS X application process number'))
|
||||
|
||||
|
@ -129,6 +129,9 @@ def main():
|
|||
gpodder.ui.qml = True
|
||||
else:
|
||||
gpodder.ui.gtk = True
|
||||
|
||||
gpodder.ui.unity = (os.environ.get('DESKTOP_SESSION', 'unknown').lower() in
|
||||
('ubuntu', 'ubuntu-2d'))
|
||||
|
||||
from gpodder import log
|
||||
log.setup(options.verbose)
|
||||
|
@ -156,7 +159,7 @@ def main():
|
|||
except dbus.exceptions.DBusException, dbus_exception:
|
||||
logger.info('Cannot connect to remote object.', exc_info=True)
|
||||
|
||||
if not gpodder.win32 and os.environ.get('DISPLAY', '') == '':
|
||||
if not gpodder.ui.win32 and os.environ.get('DISPLAY', '') == '':
|
||||
logger.error('Cannot start gPodder: $DISPLAY is not set.')
|
||||
sys.exit(1)
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Enqueue in media players')
|
||||
__description__ = _('Add a context menu item for enqueueing episodes in installed media players')
|
||||
__author__ = 'Thomas Perl <thp@gpodder.org>, Bernd Schlapsi <brot@gmx.info>'
|
||||
__category__ = 'interface'
|
||||
__only_for__ = 'gtk'
|
||||
|
||||
AMAROK = (['amarok', '--play', '--append'], 'Enqueue in Amarok')
|
||||
|
@ -40,7 +41,6 @@ class gPodderExtension:
|
|||
vlc = subprocess.Popen(cmd + filenames,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = vlc.communicate()
|
||||
|
||||
def _enqueue_episodes_amarok(self, episodes):
|
||||
self._enqueue_episodes_cmd(episodes, AMAROK[0])
|
||||
|
|
|
@ -23,6 +23,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Convert .flv files from YouTube to .mp4')
|
||||
__description__ = _('Useful for playing downloaded videos on hardware players')
|
||||
__authors__ = 'Thomas Perl <thp@gpodder.org>, Bernd Schlapsi <brot@gmx.info>'
|
||||
__category__ = 'post-download'
|
||||
|
||||
DefaultConfig = {
|
||||
'context_menu': True, # Show the conversion option in the context menu
|
||||
|
|
|
@ -10,7 +10,9 @@ _ = gpodder.gettext
|
|||
|
||||
__title__ = _('Gtk Status Icon')
|
||||
__description__ = _('Show a status icon for Gtk-based Desktops.')
|
||||
__category__ = 'desktop-integration'
|
||||
__only_for__ = 'gtk'
|
||||
__disable_in__ = 'unity'
|
||||
|
||||
import gtk
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Convert M4A audio to MP3 or OGG')
|
||||
__description__ = _('Transcode .m4a files to .mp3 or .ogg using ffmpeg')
|
||||
__authors__ = 'Bernd Schlapsi <brot@gmx.info>, Thomas Perl <thp@gpodder.org>'
|
||||
__category__ = 'post-download'
|
||||
|
||||
|
||||
DefaultConfig = {
|
||||
|
|
|
@ -9,6 +9,7 @@ _ = gpodder.gettext
|
|||
|
||||
__title__ = _('Minimize on start')
|
||||
__description__ = _('Minimizes the gPodder window on startup.')
|
||||
__category__ = 'interface'
|
||||
__only_for__ = 'gtk'
|
||||
|
||||
class gPodderExtension:
|
||||
|
|
|
@ -21,6 +21,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Normalize audio with re-encoding')
|
||||
__description__ = _('Normalize the volume of audio files with normalize-audio')
|
||||
__authors__ = 'Bernd Schlapsi <brot@gmx.info>'
|
||||
__category__ = 'post-download'
|
||||
|
||||
|
||||
DefaultConfig = {
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
__title__ = 'Gtk+ Desktop Notifications'
|
||||
__description__ = 'Display notification bubbles for different events.'
|
||||
__category__ = 'desktop-integration'
|
||||
__only_for__ = 'gtk'
|
||||
__mandatory_in__ = 'gtk'
|
||||
|
||||
import gpodder
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Rename episodes after download')
|
||||
__description__ = _('Rename episodes to "<Episode Title>.<ext>" on download')
|
||||
__authors__ = 'Bernd Schlapsi <brot@gmx.info>, Thomas Perl <thp@gpodder.org>'
|
||||
__category__ = 'post-download'
|
||||
|
||||
|
||||
class gPodderExtension:
|
||||
|
|
|
@ -37,6 +37,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Remove cover art from OGG files')
|
||||
__description__ = _('removes coverart from all downloaded ogg files')
|
||||
__authors__ = 'Bernd Schlapsi <brot@gmx.info>'
|
||||
__category__ = 'post-download'
|
||||
|
||||
|
||||
DefaultConfig = {
|
||||
|
|
|
@ -26,6 +26,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Convert video files to MP4 for Rockbox')
|
||||
__description__ = _('Converts all videos to a Rockbox-compatible format')
|
||||
__authors__ = 'Guy Sheffer <guysoft@gmail.com>, Thomas Perl <thp@gpodder.org>, Bernd Schlapsi <brot@gmx.info>'
|
||||
__category__ = 'post-download'
|
||||
|
||||
|
||||
DefaultConfig = {
|
||||
|
|
|
@ -38,6 +38,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Tag downloaded files using Mutagen')
|
||||
__description__ = _('Add episode and podcast titles to MP3/OGG tags')
|
||||
__authors__ = 'Bernd Schlapsi <brot@gmx.info>'
|
||||
__category__ = 'post-download'
|
||||
|
||||
|
||||
DefaultConfig = {
|
||||
|
|
|
@ -15,8 +15,9 @@ _ = gpodder.gettext
|
|||
|
||||
__title__ = _('Subtitle Downloader for TED Talks')
|
||||
__description__ = _('Downloads .srt subtitles for TED Talks Videos')
|
||||
__only_for__ = 'gtk, cli, qml'
|
||||
__authors__ = 'Danilo Shiga <daniloshiga@gmail.com>'
|
||||
__category__ = 'post-download'
|
||||
__only_for__ = 'gtk, cli, qml'
|
||||
|
||||
|
||||
class gPodderExtension(object):
|
||||
|
|
|
@ -9,7 +9,11 @@ _ = gpodder.gettext
|
|||
|
||||
__title__ = _('Ubuntu App Indicator')
|
||||
__description__ = _('Show a status indicator in the top bar.')
|
||||
__authors__ = 'Thomas Perl <thp@gpodder.org>'
|
||||
__category__ = 'desktop-integration'
|
||||
__only_for__ = 'gtk'
|
||||
__mandatory_in__ = 'unity'
|
||||
|
||||
|
||||
import appindicator
|
||||
import gtk
|
||||
|
|
|
@ -9,7 +9,11 @@ _ = gpodder.gettext
|
|||
|
||||
__title__ = _('Ubuntu Unity Integration')
|
||||
__description__ = _('Show download progress in the Unity Launcher icon.')
|
||||
__only_for__ = 'gtk'
|
||||
__authors__ = 'Thomas Perl <thp@gpodder.org>'
|
||||
__category__ = 'desktop-integration'
|
||||
__only_for__ = 'unity'
|
||||
__mandatory_in__ = 'unity'
|
||||
|
||||
|
||||
# FIXME: Due to the fact that we do not yet use the GI-style bindings, we will
|
||||
# have to run this module in its own interpreter and send commands to it using
|
||||
|
@ -17,7 +21,9 @@ __only_for__ = 'gtk'
|
|||
# this and still expose the same "interface' (LauncherEntry and its methods)
|
||||
# to our callers.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import logging
|
||||
|
||||
if __name__ != '__main__':
|
||||
|
@ -32,6 +38,7 @@ if __name__ != '__main__':
|
|||
|
||||
def on_load(self):
|
||||
logger.info('Starting Ubuntu Unity Integration.')
|
||||
os.environ['PYTHONPATH'] = os.pathsep.join(sys.path)
|
||||
self.process = subprocess.Popen(['python', __file__],
|
||||
stdin=subprocess.PIPE)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ _ = gpodder.gettext
|
|||
__title__ = _('Search for new episodes on startup')
|
||||
__description__ = _('Starts the search for new episodes on startup')
|
||||
__authors__ = 'Bernd Schlapsi <brot@gmx.info>'
|
||||
__category__ = 'interface'
|
||||
__only_for__ = 'gtk'
|
||||
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@
|
|||
<property name="reorderable">True</property>
|
||||
<property name="enable_search">False</property>
|
||||
<property name="search_column">0</property>
|
||||
<signal name="row-activated" handler="on_extensions_row_activated" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_treeview_button_press_event" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -93,9 +93,9 @@ dbus_podcasts = 'org.gpodder.podcasts'
|
|||
dbus_session_bus = None
|
||||
|
||||
# Set "win32" to True if we are on Windows
|
||||
win32 = (platform.system() == 'Windows')
|
||||
ui.win32 = (platform.system() == 'Windows')
|
||||
# Set "osx" to True if we are on Mac OS X
|
||||
osx = (platform.system() == 'Darwin')
|
||||
ui.osx = (platform.system() == 'Darwin')
|
||||
|
||||
# i18n setup (will result in "gettext" to be available)
|
||||
# Use _ = gpodder.gettext in modules to enable string translations
|
||||
|
@ -112,7 +112,7 @@ except AttributeError:
|
|||
gettext = t.gettext
|
||||
ngettext = t.ngettext
|
||||
|
||||
if win32:
|
||||
if ui.win32:
|
||||
try:
|
||||
# Workaround for bug 650
|
||||
from gtk.glade import bindtextdomain
|
||||
|
|
|
@ -88,7 +88,7 @@ defaults = {
|
|||
|
||||
# Software updates from gpodder.org (primary audience: Windows users)
|
||||
'software_update': {
|
||||
'check_on_startup': gpodder.win32, # check for updates on start
|
||||
'check_on_startup': gpodder.ui.win32, # check for updates on start
|
||||
'last_check': 0, # unix timestamp of last update check
|
||||
'interval': 5, # interval (in days) to check for updates
|
||||
},
|
||||
|
|
|
@ -51,6 +51,14 @@ import logging
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CATEGORY_DICT = {
|
||||
'desktop-integration': _('Desktop Integration'),
|
||||
'interface': _('Interface'),
|
||||
'post-download': _('Post download'),
|
||||
}
|
||||
DEFAULT_CATEGORY = _('Other')
|
||||
|
||||
|
||||
def call_extensions(func):
|
||||
"""Decorator to create handler functions in ExtensionManager
|
||||
|
||||
|
@ -91,45 +99,80 @@ class ExtensionMetadata(object):
|
|||
DEFAULTS = {
|
||||
'description': _('No description for this extension.'),
|
||||
}
|
||||
SORTKEYS = {
|
||||
'title': 1,
|
||||
'description': 2,
|
||||
'category': 3,
|
||||
'author': 4,
|
||||
'only_for': 5,
|
||||
'mandatory_in': 6,
|
||||
'disable_in': 7,
|
||||
}
|
||||
|
||||
def __init__(self, container, metadata):
|
||||
if 'title' not in metadata:
|
||||
metadata['title'] = container.name
|
||||
|
||||
category = metadata.get('category', 'other')
|
||||
metadata['category'] = CATEGORY_DICT.get(category, DEFAULT_CATEGORY)
|
||||
|
||||
self.__dict__.update(metadata)
|
||||
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self.DEFAULTS[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name)
|
||||
except KeyError, e:
|
||||
raise AttributeError(name, e)
|
||||
|
||||
def get_sorted(self):
|
||||
kf = lambda x: self.SORTKEYS.get(x[0], 99)
|
||||
return sorted([(k, v) for k, v in self.__dict__.items()], key=kf)
|
||||
|
||||
@property
|
||||
def for_current_ui(self):
|
||||
"""Check if this extension makes sense for the current UI
|
||||
def check_ui(self, target, default):
|
||||
"""Checks metadata information like
|
||||
__only_for__ = 'gtk'
|
||||
__mandatory_in__ = 'gtk'
|
||||
__disable_in__ = 'gtk'
|
||||
|
||||
The __only_for__ metadata field in an extension can be a string with
|
||||
comma-separated values for UIs. This will be checked against boolean
|
||||
variables in the "gpodder.ui" object.
|
||||
The metadata fields in an extension can be a string with
|
||||
comma-separated values for UIs. This will be checked against
|
||||
boolean variables in the "gpodder.ui" object.
|
||||
|
||||
Example metadata field in an extension:
|
||||
|
||||
__only_for__ = 'gtk,qml'
|
||||
__only_for__ = 'unity'
|
||||
|
||||
In this case, this function will return True if any of the following
|
||||
expressions will evaluate to True:
|
||||
In this case, this function will return the value of the default
|
||||
if any of the following expressions will evaluate to True:
|
||||
|
||||
gpodder.ui.gtk
|
||||
gpodder.ui.qml
|
||||
gpodder.ui.unity
|
||||
gpodder.ui.cli
|
||||
gpodder.ui.osx
|
||||
gpodder.ui.win32
|
||||
|
||||
New, unknown UIs are silently ignored and will evaluate to False.
|
||||
"""
|
||||
if not hasattr(self, 'only_for'):
|
||||
return True
|
||||
if not hasattr(self, target):
|
||||
return default
|
||||
|
||||
uis = filter(None, [x.strip() for x in self.only_for.split(',')])
|
||||
uis = filter(None, [x.strip() for x in getattr(self, target).split(',')])
|
||||
return any(getattr(gpodder.ui, ui.lower(), False) for ui in uis)
|
||||
|
||||
@property
|
||||
def available_for_current_ui(self):
|
||||
return self.check_ui('only_for', True)
|
||||
|
||||
@property
|
||||
def mandatory_in_current_ui(self):
|
||||
return self.check_ui('mandatory_in', False)
|
||||
|
||||
@property
|
||||
def disable_in_current_ui(self):
|
||||
return self.check_ui('disable_in', False)
|
||||
|
||||
class MissingDependency(Exception):
|
||||
def __init__(self, message, dependency, cause=None):
|
||||
Exception.__init__(self, message)
|
||||
|
@ -217,7 +260,7 @@ class ExtensionContainer(object):
|
|||
logger.info('Module already loaded.')
|
||||
return
|
||||
|
||||
if not self.metadata.for_current_ui:
|
||||
if not self.metadata.available_for_current_ui:
|
||||
logger.info('Not loading "%s" (only_for = "%s")',
|
||||
self.name, self.metadata.only_for)
|
||||
return
|
||||
|
@ -264,8 +307,12 @@ class ExtensionManager(object):
|
|||
logger.debug('Found extension "%s" in %s', name, filename)
|
||||
config = getattr(core.config.extensions, name)
|
||||
container = ExtensionContainer(self, name, config, filename)
|
||||
if name in enabled_extensions:
|
||||
if (name in enabled_extensions or
|
||||
container.metadata.mandatory_in_current_ui):
|
||||
container.set_enabled(True)
|
||||
if (name in enabled_extensions and
|
||||
container.metadata.disable_in_current_ui):
|
||||
container.set_enabled(False)
|
||||
self.containers.append(container)
|
||||
|
||||
def shutdown(self):
|
||||
|
@ -273,19 +320,23 @@ class ExtensionManager(object):
|
|||
container.set_enabled(False)
|
||||
|
||||
def _config_value_changed(self, name, old_value, new_value):
|
||||
if name == 'extensions.enabled':
|
||||
for container in self.containers:
|
||||
new_enabled = (container.name in new_value)
|
||||
if new_enabled != container.enabled:
|
||||
logger.info('Extension "%s" is now %s', container.name,
|
||||
'enabled' if new_enabled else 'disabled')
|
||||
container.set_enabled(new_enabled)
|
||||
if new_enabled and not container.enabled:
|
||||
logger.warn('Could not enable extension: %s',
|
||||
container.error)
|
||||
self.core.config.extensions.enabled = [x
|
||||
for x in self.core.config.extensions.enabled
|
||||
if x != container.name]
|
||||
if name != 'extensions.enabled':
|
||||
return
|
||||
|
||||
for container in self.containers:
|
||||
new_enabled = (container.name in new_value)
|
||||
if new_enabled == container.enabled:
|
||||
continue
|
||||
|
||||
logger.info('Extension "%s" is now %s', container.name,
|
||||
'enabled' if new_enabled else 'disabled')
|
||||
container.set_enabled(new_enabled)
|
||||
if new_enabled and not container.enabled:
|
||||
logger.warn('Could not enable extension: %s',
|
||||
container.error)
|
||||
self.core.config.extensions.enabled = [x
|
||||
for x in self.core.config.extensions.enabled
|
||||
if x != container.name]
|
||||
|
||||
def _find_extensions(self):
|
||||
extensions = {}
|
||||
|
@ -309,7 +360,10 @@ class ExtensionManager(object):
|
|||
|
||||
def get_extensions(self):
|
||||
"""Get a list of all loaded extensions and their enabled flag"""
|
||||
return self.containers
|
||||
return [c for c in self.containers
|
||||
if c.metadata.available_for_current_ui and
|
||||
not c.metadata.mandatory_in_current_ui and
|
||||
not c.metadata.disable_in_current_ui]
|
||||
|
||||
# Define all known handler functions here, decorate them with the
|
||||
# "call_extension" decorator to forward all calls to extension scripts that have
|
||||
|
|
|
@ -166,7 +166,7 @@ class VideoFormatList(gtk.ListStore):
|
|||
self._config.youtube.preferred_fmt_id = value
|
||||
|
||||
class gPodderPreferences(BuilderWidget):
|
||||
C_TOGGLE, C_LABEL, C_EXTENSION = range(3)
|
||||
C_TOGGLE, C_LABEL, C_EXTENSION, C_SHOW_TOGGLE = range(4)
|
||||
|
||||
def new(self):
|
||||
for cb in (self.combo_audio_player_app, self.combo_video_player_app):
|
||||
|
@ -249,7 +249,6 @@ class gPodderPreferences(BuilderWidget):
|
|||
|
||||
self._config.connect_gtk_togglebutton('device_sync.skip_played_episodes', self.checkbutton_skip_played_episodes)
|
||||
|
||||
|
||||
# Have to do this before calling set_active on checkbutton_enable
|
||||
self._enable_mygpo = self._config.mygpo.enabled
|
||||
|
||||
|
@ -266,29 +265,48 @@ class gPodderPreferences(BuilderWidget):
|
|||
self.set_flattr_preferences()
|
||||
|
||||
# Configure the extensions manager GUI
|
||||
self.set_extension_preferences()
|
||||
|
||||
def set_extension_preferences(self):
|
||||
toggle_cell = gtk.CellRendererToggle()
|
||||
toggle_cell.connect('toggled', self.on_extensions_cell_toggled)
|
||||
toggle_column = gtk.TreeViewColumn('', toggle_cell, active=self.C_TOGGLE)
|
||||
toggle_column.set_clickable(True)
|
||||
self.treeviewExtensions.append_column(toggle_column)
|
||||
toggle_cell.connect('toggled', self.on_extensions_cell_toggled)
|
||||
|
||||
renderer = gtk.CellRendererText()
|
||||
renderer.set_property('ellipsize', pango.ELLIPSIZE_END)
|
||||
column = gtk.TreeViewColumn(_('Name'), renderer, markup=self.C_LABEL)
|
||||
column.set_clickable(False)
|
||||
column.set_resizable(True)
|
||||
column.set_expand(True)
|
||||
self.treeviewExtensions.append_column(column)
|
||||
name_cell = gtk.CellRendererText()
|
||||
name_cell.set_property('ellipsize', pango.ELLIPSIZE_END)
|
||||
|
||||
extension_column = gtk.TreeViewColumn(_('Name'))
|
||||
extension_column.pack_start(toggle_cell, False)
|
||||
extension_column.add_attribute(toggle_cell, 'active', self.C_TOGGLE)
|
||||
extension_column.add_attribute(toggle_cell, 'visible', self.C_SHOW_TOGGLE)
|
||||
extension_column.pack_start(name_cell, True)
|
||||
extension_column.add_attribute(name_cell, 'markup', self.C_LABEL)
|
||||
extension_column.set_clickable(False)
|
||||
extension_column.set_resizable(True)
|
||||
extension_column.set_expand(True)
|
||||
self.treeviewExtensions.append_column(extension_column)
|
||||
|
||||
self.extensions_model = gtk.ListStore(bool, str, object)
|
||||
self.extensions_model = gtk.ListStore(bool, str, object, bool)
|
||||
|
||||
def key_func(pair):
|
||||
category, container = pair
|
||||
return (category, container.metadata.title)
|
||||
|
||||
for container in gpodder.user_extensions.get_extensions():
|
||||
def convert(extensions):
|
||||
for container in extensions:
|
||||
yield (container.metadata.category, container)
|
||||
|
||||
old_category = None
|
||||
for category, container in sorted(convert(gpodder.user_extensions.get_extensions()), key=key_func):
|
||||
if old_category != category:
|
||||
label = '<span weight="bold">%s</span>' % cgi.escape(category)
|
||||
self.extensions_model.append((None, label, None, False))
|
||||
old_category = category
|
||||
|
||||
label = '%s\n<small>%s</small>' % (
|
||||
cgi.escape(container.metadata.title),
|
||||
cgi.escape(container.metadata.description))
|
||||
self.extensions_model.append([container.enabled, label, container])
|
||||
self.extensions_model.append((container.enabled, label, container, True))
|
||||
|
||||
self.extensions_model.set_sort_column_id(self.C_LABEL, gtk.SORT_ASCENDING)
|
||||
self.treeviewExtensions.set_model(self.extensions_model)
|
||||
self.treeviewExtensions.columns_autosize()
|
||||
|
||||
|
@ -347,18 +365,27 @@ class gPodderPreferences(BuilderWidget):
|
|||
self.show_message(container.error.message,
|
||||
_('Extension cannot be activated'), important=True)
|
||||
model.set_value(it, self.C_TOGGLE, False)
|
||||
|
||||
def on_treeview_button_press_event(self, treeview, event):
|
||||
if event.button != 3:
|
||||
return
|
||||
|
||||
def on_extensions_row_activated(self, treeview, path, view_column):
|
||||
x = int(event.x)
|
||||
y = int(event.y)
|
||||
path, _, _, _ = treeview.get_path_at_pos(x, y)
|
||||
model = treeview.get_model()
|
||||
container = model.get_value(model.get_iter(path), self.C_EXTENSION)
|
||||
self.show_extension_info(model, container)
|
||||
|
||||
# This is one ugly hack, but it displays the container's attributes
|
||||
# and the attributes of the metadata object of the container..
|
||||
def show_extension_info(self, model, container):
|
||||
if not container or not model:
|
||||
return
|
||||
|
||||
# This is one ugly hack, but it displays the attributes of
|
||||
# the metadata object of the container..
|
||||
info = '\n'.join('<b>%s:</b> %s' %
|
||||
tuple(map(cgi.escape, map(str, (key, value))))
|
||||
for key, value in sorted(container.__dict__.items() +
|
||||
[('metadata.'+k, v)
|
||||
for k, v in container.metadata.__dict__.items()]))
|
||||
for key, value in container.metadata.get_sorted())
|
||||
|
||||
self.show_message(info, _('Extension module info'), important=True)
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ from gpodder import extensions
|
|||
|
||||
|
||||
macapp = None
|
||||
if gpodder.osx and getattr(gtk.gdk, 'WINDOWING', 'x11') == 'quartz':
|
||||
if gpodder.ui.osx and getattr(gtk.gdk, 'WINDOWING', 'x11') == 'quartz':
|
||||
try:
|
||||
from gtk_osxapplication import *
|
||||
macapp = OSXApplication()
|
||||
|
@ -3450,7 +3450,7 @@ def main(options=None):
|
|||
if options.subscribe:
|
||||
util.idle_add(gp.subscribe_to_url, options.subscribe)
|
||||
|
||||
if gpodder.osx:
|
||||
if gpodder.ui.osx:
|
||||
from gpodder.gtkui import macosx
|
||||
|
||||
# Handle "subscribe to podcast" events from firefox
|
||||
|
|
|
@ -182,7 +182,7 @@ class Exporter(object):
|
|||
if available < 2*len(data)+FREE_DISK_SPACE_AFTER:
|
||||
# On Windows, if we have zero bytes available, assume that we have
|
||||
# not had the win32file module available + assume enough free space
|
||||
if not gpodder.win32 or available > 0:
|
||||
if not gpodder.ui.win32 or available > 0:
|
||||
logger.error('Not enough free disk space to save channel list to %s', self.filename)
|
||||
return False
|
||||
fp = open(self.filename+'.tmp', 'w')
|
||||
|
|
|
@ -196,7 +196,7 @@ class Device(services.ObservableService):
|
|||
|
||||
def close(self):
|
||||
self.notify('status', _('Writing data to disk'))
|
||||
if self._config.device_sync.after_sync.sync_disks and not gpodder.win32:
|
||||
if self._config.device_sync.after_sync.sync_disks and not gpodder.ui.win32:
|
||||
os.system('sync')
|
||||
else:
|
||||
logger.warning('Not syncing disks. Unmount your device before unplugging.')
|
||||
|
|
|
@ -87,7 +87,7 @@ if encoding is None:
|
|||
logger.info('Detected encoding: %s', encoding)
|
||||
elif gpodder.ui.harmattan:
|
||||
encoding = 'utf-8'
|
||||
elif gpodder.win32:
|
||||
elif gpodder.ui.win32:
|
||||
# To quote http://docs.python.org/howto/unicode.html:
|
||||
# ,,on Windows, Python uses the name "mbcs" to refer
|
||||
# to whatever the currently configured encoding is``
|
||||
|
@ -444,7 +444,7 @@ def get_free_disk_space(path):
|
|||
if not os.path.exists(path):
|
||||
return 0
|
||||
|
||||
if gpodder.win32:
|
||||
if gpodder.ui.win32:
|
||||
return get_free_disk_space_win32(path)
|
||||
|
||||
s = os.statvfs(path)
|
||||
|
@ -1020,7 +1020,7 @@ def find_command(command):
|
|||
|
||||
for path in os.environ['PATH'].split(os.pathsep):
|
||||
command_file = os.path.join(path, command)
|
||||
if gpodder.win32 and not os.path.exists(command_file):
|
||||
if gpodder.ui.win32 and not os.path.exists(command_file):
|
||||
for extension in ('.bat', '.exe'):
|
||||
cmd = command_file + extension
|
||||
if os.path.isfile(cmd):
|
||||
|
@ -1230,9 +1230,9 @@ def gui_open(filename):
|
|||
on Win32, os.startfile() is used
|
||||
"""
|
||||
try:
|
||||
if gpodder.win32:
|
||||
if gpodder.ui.win32:
|
||||
os.startfile(filename)
|
||||
elif gpodder.osx:
|
||||
elif gpodder.ui.osx:
|
||||
subprocess.Popen(['open', filename])
|
||||
else:
|
||||
subprocess.Popen(['xdg-open', filename])
|
||||
|
@ -1560,7 +1560,7 @@ def atomic_rename(old_name, new_name):
|
|||
the new contents into a temporary file and then moving the
|
||||
temporary file over the original file to replace it.
|
||||
"""
|
||||
if gpodder.win32:
|
||||
if gpodder.ui.win32:
|
||||
# Win32 does not support atomic rename with os.rename
|
||||
shutil.move(old_name, new_name)
|
||||
else:
|
||||
|
@ -1663,10 +1663,10 @@ def connection_available():
|
|||
if no network interfaces are up (i.e. no connectivity).
|
||||
"""
|
||||
try:
|
||||
if gpodder.win32:
|
||||
if gpodder.ui.win32:
|
||||
# FIXME: Implement for Windows
|
||||
return True
|
||||
elif gpodder.osx:
|
||||
elif gpodder.ui.osx:
|
||||
return len(list(osx_get_active_interfaces())) > 0
|
||||
return True
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue