diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 91052bbc2b..083bc03723 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -393,9 +393,16 @@ class PreferencesPlugin(Plugin): # {{{ #: The category name displayed to the user for this plugin gui_category = None + #: The name displayed to the user for this plugin gui_name = None + #: The icon for this plugin, should be an absolute path + icon = None + + #: The description used for tooltips and the like + description = None + def create_widget(self, parent=None): ''' Create and return the actual Qt widget used for setting this group of diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index d1e5db7f6d..d95a7d1c8a 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -678,138 +678,177 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog, class LookAndFeel(PreferencesPlugin): name = 'Look & Feel' + icon = I('lookfeel.png') gui_name = _('Look and Feel') category = 'Interface' gui_category = _('Interface') category_order = 1 name_order = 1 config_widget = 'calibre.gui2.preferences.look_feel' + description = _('Adjust the look and feel of the calibre interface' + ' to suit your tastes') class Behavior(PreferencesPlugin): name = 'Behavior' + icon = I('config.png') gui_name = _('Behavior') category = 'Interface' gui_category = _('Interface') category_order = 1 name_order = 2 config_widget = 'calibre.gui2.preferences.behavior' + description = _('Change the way calibre behaves') class Columns(PreferencesPlugin): name = 'Custom Columns' + icon = I('column.png') gui_name = _('Add your own columns') category = 'Interface' gui_category = _('Interface') category_order = 1 name_order = 3 config_widget = 'calibre.gui2.preferences.columns' + description = _('Add/remove your own columns to the calibre book list') class Toolbar(PreferencesPlugin): name = 'Toolbar' + icon = I('wizard.png') gui_name = _('Customize the toolbar') category = 'Interface' gui_category = _('Interface') category_order = 1 name_order = 4 config_widget = 'calibre.gui2.preferences.toolbar' + description = _('Customize the toolbars and context menus, changing which' + ' actions are available in each') class InputOptions(PreferencesPlugin): name = 'Input Options' + icon = I('arrow-down.png') gui_name = _('Input Options') category = 'Conversion' gui_category = _('Conversion') category_order = 2 name_order = 1 config_widget = 'calibre.gui2.preferences.conversion:InputOptions' + description = _('Set conversion options specific to each input format') class CommonOptions(PreferencesPlugin): name = 'Common Options' + icon = I('convert.png') gui_name = _('Common Options') category = 'Conversion' gui_category = _('Conversion') category_order = 2 name_order = 2 config_widget = 'calibre.gui2.preferences.conversion:CommonOptions' + description = _('Set conversion options common to all formats') class OutputOptions(PreferencesPlugin): name = 'Output Options' + icon = I('arrow-up.png') gui_name = _('Output Options') category = 'Conversion' gui_category = _('Conversion') category_order = 2 name_order = 3 config_widget = 'calibre.gui2.preferences.conversion:OutputOptions' + description = _('Set conversion options specific to each output format') class Adding(PreferencesPlugin): name = 'Adding' + icon = I('add_book.png') gui_name = _('Adding books') category = 'Import/Export' gui_category = _('Import/Export') category_order = 3 name_order = 1 config_widget = 'calibre.gui2.preferences.adding' + description = _('Control how calibre reads metadata from files when ' + 'adding books') class Saving(PreferencesPlugin): name = 'Saving' + icon = I('save.png') gui_name = _('Saving books to disk') category = 'Import/Export' gui_category = _('Import/Export') category_order = 3 name_order = 2 config_widget = 'calibre.gui2.preferences.saving' + description = _('Control how calibre exports files from its database ' + 'to disk when using Save to disk') class Sending(PreferencesPlugin): name = 'Sending' + icon = I('sync.png') gui_name = _('Sending books to devices') category = 'Import/Export' gui_category = _('Import/Export') category_order = 3 name_order = 3 config_widget = 'calibre.gui2.preferences.sending' + description = _('Control how calibre transfers files to your ' + 'ebook reader') class Email(PreferencesPlugin): name = 'Email' - gui_name = _('Sending books by email') + icon = I('mail.png') + gui_name = _('Sharing books by email') category = 'Sharing' gui_category = _('Sharing') category_order = 4 name_order = 1 config_widget = 'calibre.gui2.preferences.emailp' + description = _('Setup sharing of books via email. Can be used ' + 'for automatic sending of downloaded news to your devices') class Server(PreferencesPlugin): name = 'Server' + icon = I('network-server.png') gui_name = _('Sharing over the net') category = 'Sharing' gui_category = _('Sharing') category_order = 4 name_order = 2 config_widget = 'calibre.gui2.preferences.server' + description = _('Setup the calibre Content Server which will ' + 'give you access to your calibre library from anywhere, ' + 'on any device, over the internet') class Plugins(PreferencesPlugin): name = 'Plugins' + icon = I('plugins.png') gui_name = _('Plugins') category = 'Advanced' gui_category = _('Advanced') category_order = 5 name_order = 1 config_widget = 'calibre.gui2.preferences.plugins' + description = _('Add/remove/customize various bits of calibre ' + 'functionality') class Tweaks(PreferencesPlugin): name = 'Tweaks' + icon = I('drawer.png') gui_name = _('Tweaks') category = 'Advanced' gui_category = _('Advanced') category_order = 5 name_order = 2 config_widget = 'calibre.gui2.preferences.tweaks' + description = _('Fine tune how calibre behaves in various contexts') class Misc(PreferencesPlugin): name = 'Misc' + icon = I('exec.png') gui_name = _('Miscellaneous') category = 'Advanced' gui_category = _('Advanced') category_order = 5 name_order = 3 config_widget = 'calibre.gui2.preferences.misc' + description = _('Miscellaneous advanced configuration') plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions, CommonOptions, OutputOptions, Adding, Saving, Sending, Email, Server, diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py new file mode 100644 index 0000000000..f75a748006 --- /dev/null +++ b/src/calibre/gui2/preferences/main.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +from functools import partial + +from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \ + QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, \ + QToolBar, QSize, pyqtSignal + +from calibre.constants import __appname__, __version__ +from calibre.gui2 import gprefs +from calibre.gui2.preferences import init_gui +from calibre.customize.ui import preferences_plugins +from calibre.utils.ordered_dict import OrderedDict + +class StatusBar(QStatusBar): # {{{ + + def __init__(self, parent=None): + QStatusBar.__init__(self, parent) + self.default_message = __appname__ + ' ' + _('version') + ' ' + \ + __version__ + ' ' + _('created by Kovid Goyal') + self.device_string = '' + self._font = QFont() + self._font.setBold(True) + self.setFont(self._font) + + self.messageChanged.connect(self.message_changed, + type=Qt.QueuedConnection) + self.message_changed('') + + def message_changed(self, msg): + if not msg or msg.isEmpty() or msg.isNull() or \ + not unicode(msg).strip(): + self.showMessage(self.default_message) + +# }}} + +class Category(QWidget): + + plugin_activated = pyqtSignal(object) + + def __init__(self, name, plugins, parent=None): + QWidget.__init__(self, parent) + self._layout = QVBoxLayout() + self.setLayout(self._layout) + self.label = QLabel(name) + self.sep = QFrame(self) + self.bf = QFont() + self.bf.setBold(True) + self.label.setFont(self.bf) + self.sep.setFrameShape(QFrame.HLine) + self._layout.addWidget(self.label) + self._layout.addWidget(self.sep) + + self.plugins = plugins + + self.bar = QToolBar(self) + self.bar.setIconSize(QSize(48, 48)) + self.bar.setMovable(False) + self.bar.setFloatable(False) + self.bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + self._layout.addWidget(self.bar) + self.actions = [] + for p in plugins: + target = partial(self.triggered, p) + ac = self.bar.addAction(QIcon(p.icon), p.gui_name, target) + ac.setToolTip(p.description) + ac.setWhatsThis(p.description) + ac.setStatusTip(p.description) + self.actions.append(ac) + + def triggered(self, plugin, *args): + self.plugin_activated.emit(plugin) + + +class Browser(QScrollArea): + + def __init__(self, parent=None): + QScrollArea.__init__(self, parent) + self.setWidgetResizable(True) + + category_map = {} + for plugin in preferences_plugins(): + if plugin.category not in category_map: + category_map[plugin.category] = plugin.category_order + if category_map[plugin.category] < plugin.category_order: + category_map[plugin.category] = plugin.category_order + + categories = list(category_map.keys()) + categories.sort(cmp=lambda x, y: cmp(category_map[x], category_map[y])) + + self.category_map = OrderedDict() + for c in categories: + self.category_map[c] = [] + + for plugin in preferences_plugins(): + self.category_map[plugin.category].append(plugin) + + for plugins in self.category_map.values(): + plugins.sort(cmp=lambda x, y: cmp(x.name_order, y.name_order)) + + self.widgets = [] + self._layout = QVBoxLayout() + self.container = QWidget(self) + self.container.setLayout(self._layout) + self.setWidget(self.container) + for name, plugins in self.category_map.items(): + w = Category(name, plugins, self) + self.widgets.append(w) + self._layout.addWidget(w) + + + +class Preferences(QMainWindow): + + def __init__(self, gui): + QMainWindow.__init__(self, gui) + + self.resize(780, 665) + geom = gprefs.get('preferences_window_geometry', None) + if geom is not None: + self.restoreGeometry(geom) + + self.setWindowModality(Qt.WindowModal) + self.setWindowTitle(__appname__ + ' - ' + _('Preferences')) + self.setWindowIcon(QIcon(I('config.png'))) + + self.status_bar = StatusBar(self) + self.setStatusBar(self.status_bar) + + self.stack = QStackedWidget(self) + self.setCentralWidget(self.stack) + self.browser = Browser(self) + self.stack.addWidget(self.browser) + self.scroll_area = QScrollArea(self) + self.stack.addWidget(self.scroll_area) + self.scroll_area.setWidgetResizable(True) + + self.stack.setCurrentIndex(0) + + def closeEvent(self, *args): + gprefs.set('preferences_window_geometry', + bytearray(self.saveGeometry())) + return QMainWindow.closeEvent(self, *args) + +if __name__ == '__main__': + from PyQt4.Qt import QApplication + app = QApplication([]) + app + gui = init_gui() + + p = Preferences(gui) + p.show() + app.exec_() + gui.shutdown()