From a97cffc1e9f0c9b04a14b653eef827f5b2026b9a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 22 Aug 2010 14:41:58 -0600 Subject: [PATCH] Add framework to allow user interface action plugins to specify what location they can be added to/cannot be removed from --- src/calibre/gui2/actions/__init__.py | 11 +++ src/calibre/gui2/actions/add.py | 4 +- src/calibre/gui2/actions/add_to_library.py | 4 +- src/calibre/gui2/actions/catalog.py | 1 + src/calibre/gui2/actions/choose_library.py | 1 + src/calibre/gui2/actions/convert.py | 1 + src/calibre/gui2/actions/copy_to_library.py | 1 + src/calibre/gui2/actions/device.py | 3 + src/calibre/gui2/actions/edit_collections.py | 4 +- src/calibre/gui2/actions/open.py | 1 + src/calibre/gui2/actions/preferences.py | 1 + src/calibre/gui2/actions/show_book_details.py | 1 + src/calibre/gui2/dialogs/config/toolbar.py | 67 +++++++++++++------ src/calibre/trac/bzr_commit_plugin.py | 2 +- 14 files changed, 76 insertions(+), 26 deletions(-) diff --git a/src/calibre/gui2/actions/__init__.py b/src/calibre/gui2/actions/__init__.py index 9c20f2adef..57ad900fba 100644 --- a/src/calibre/gui2/actions/__init__.py +++ b/src/calibre/gui2/actions/__init__.py @@ -60,6 +60,17 @@ class InterfaceAction(QObject): #: shortcut must be a translated string if not None action_spec = ('text', 'icon', None, None) + #: Set of locations to which this action must not be added. + #: See :attr:`all_locations` for a list of possible locations + dont_add_to = frozenset([]) + + #: Set of locations from which this action must not be removed. + #: See :attr:`all_locations` for a list of possible locations + dont_remove_from = frozenset([]) + + all_locations = frozenset(['toolbar', 'toolbar-device', 'context-menu', + 'context-menu-device']) + def __init__(self, parent, site_customization): QObject.__init__(self, parent) self.setObjectName(self.name) diff --git a/src/calibre/gui2/actions/add.py b/src/calibre/gui2/actions/add.py index deb03021c5..95fdb9a6c6 100644 --- a/src/calibre/gui2/actions/add.py +++ b/src/calibre/gui2/actions/add.py @@ -22,7 +22,9 @@ from calibre.gui2.actions import InterfaceAction class AddAction(InterfaceAction): name = 'Add Books' - action_spec = (_('Add books'), 'add_book.svg', None, _('A')) + action_spec = (_('Add books'), 'add_book.svg', + _('Add books to the calibre library/device from files on your computer') + , _('A')) def genesis(self): self._add_filesystem_book = self.Dispatcher(self.__add_filesystem_book) diff --git a/src/calibre/gui2/actions/add_to_library.py b/src/calibre/gui2/actions/add_to_library.py index b2d1091dbb..adc9f83333 100644 --- a/src/calibre/gui2/actions/add_to_library.py +++ b/src/calibre/gui2/actions/add_to_library.py @@ -10,7 +10,9 @@ from calibre.gui2.actions import InterfaceAction class AddToLibraryAction(InterfaceAction): name = 'Add To Library' - action_spec = (_('Add books to library'), 'add_book.svg', None, None) + action_spec = (_('Add books to library'), 'add_book.svg', + _('Add books to your calibre library from the connected device'), None) + dont_add_to = frozenset(['toolbar', 'context-menu']) def location_selected(self, loc): enabled = loc != 'library' diff --git a/src/calibre/gui2/actions/catalog.py b/src/calibre/gui2/actions/catalog.py index 6807d7717e..6feaec978d 100644 --- a/src/calibre/gui2/actions/catalog.py +++ b/src/calibre/gui2/actions/catalog.py @@ -18,6 +18,7 @@ class GenerateCatalogAction(InterfaceAction): name = 'Generate Catalog' action_spec = (_('Create catalog of books in your calibre library'), None, None, None) + dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) def generate_catalog(self): rows = self.gui.library_view.selectionModel().selectedRows() diff --git a/src/calibre/gui2/actions/choose_library.py b/src/calibre/gui2/actions/choose_library.py index 29e508aff2..20cf7b432d 100644 --- a/src/calibre/gui2/actions/choose_library.py +++ b/src/calibre/gui2/actions/choose_library.py @@ -72,6 +72,7 @@ class ChooseLibraryAction(InterfaceAction): name = 'Choose Library' action_spec = (_('%d books'), 'lt.png', _('Choose calibre library to work with'), None) + dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) def genesis(self): self.count_changed(0) diff --git a/src/calibre/gui2/actions/convert.py b/src/calibre/gui2/actions/convert.py index ef8a8688c5..20f5bc457a 100644 --- a/src/calibre/gui2/actions/convert.py +++ b/src/calibre/gui2/actions/convert.py @@ -20,6 +20,7 @@ class ConvertAction(InterfaceAction): name = 'Convert Books' action_spec = (_('Convert books'), 'convert.svg', None, _('C')) + dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) def genesis(self): cm = QMenu() diff --git a/src/calibre/gui2/actions/copy_to_library.py b/src/calibre/gui2/actions/copy_to_library.py index 9ab6504e37..0dcee7507e 100644 --- a/src/calibre/gui2/actions/copy_to_library.py +++ b/src/calibre/gui2/actions/copy_to_library.py @@ -63,6 +63,7 @@ class CopyToLibraryAction(InterfaceAction): action_spec = (_('Copy to library'), 'lt.png', _('Copy selected books to the specified library'), None) popup_type = QToolButton.InstantPopup + dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) def genesis(self): self.menu = QMenu(self.gui) diff --git a/src/calibre/gui2/actions/device.py b/src/calibre/gui2/actions/device.py index f8b2e751f1..e71b2d1f45 100644 --- a/src/calibre/gui2/actions/device.py +++ b/src/calibre/gui2/actions/device.py @@ -19,6 +19,7 @@ class ShareConnMenu(QMenu): # {{{ connect_to_itunes = pyqtSignal() config_email = pyqtSignal() toggle_server = pyqtSignal() + dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) def __init__(self, parent=None): QMenu.__init__(self, parent) @@ -95,6 +96,8 @@ class SendToDeviceAction(InterfaceAction): name = 'Send To Device' action_spec = (_('Send to device'), 'sync.svg', None, _('D')) + dont_remove_from = frozenset(['toolbar-device']) + dont_add_to = frozenset(['toolbar', 'context-menu']) def genesis(self): self.qaction.triggered.connect(self.do_sync) diff --git a/src/calibre/gui2/actions/edit_collections.py b/src/calibre/gui2/actions/edit_collections.py index 8f13b012e2..3b15aa63a1 100644 --- a/src/calibre/gui2/actions/edit_collections.py +++ b/src/calibre/gui2/actions/edit_collections.py @@ -10,7 +10,9 @@ from calibre.gui2.actions import InterfaceAction class EditCollectionsAction(InterfaceAction): name = 'Edit Collections' - action_spec = (_('Manage collections'), None, None, None) + action_spec = (_('Manage collections'), None, + _('Manage the collections on this device'), None) + dont_add_to = frozenset(['toolbar', 'context-menu']) def location_selected(self, loc): enabled = loc != 'library' diff --git a/src/calibre/gui2/actions/open.py b/src/calibre/gui2/actions/open.py index 40fc6372b7..2c7da7b669 100644 --- a/src/calibre/gui2/actions/open.py +++ b/src/calibre/gui2/actions/open.py @@ -13,6 +13,7 @@ class OpenFolderAction(InterfaceAction): name = 'Open Folder' action_spec = (_('Open containing folder'), 'document_open.svg', None, _('O')) + dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) def genesis(self): self.qaction.triggered.connect(self.gui.iactions['View'].view_folder) diff --git a/src/calibre/gui2/actions/preferences.py b/src/calibre/gui2/actions/preferences.py index d047e51dac..d667e46b03 100644 --- a/src/calibre/gui2/actions/preferences.py +++ b/src/calibre/gui2/actions/preferences.py @@ -15,6 +15,7 @@ class PreferencesAction(InterfaceAction): name = 'Preferences' action_spec = (_('Preferences'), 'config.svg', None, _('Ctrl+P')) + dont_remove_from = frozenset(['toolbar']) def genesis(self): pm = QMenu() diff --git a/src/calibre/gui2/actions/show_book_details.py b/src/calibre/gui2/actions/show_book_details.py index 06c63714a7..40ea29bd3b 100644 --- a/src/calibre/gui2/actions/show_book_details.py +++ b/src/calibre/gui2/actions/show_book_details.py @@ -15,6 +15,7 @@ class ShowBookDetailsAction(InterfaceAction): name = 'Show Book Details' action_spec = (_('Show book details'), 'dialog_information.svg', None, _('I')) + dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) def genesis(self): self.qaction.triggered.connect(self.show_book_info) diff --git a/src/calibre/gui2/dialogs/config/toolbar.py b/src/calibre/gui2/dialogs/config/toolbar.py index 7e4a04e125..1d11127192 100644 --- a/src/calibre/gui2/dialogs/config/toolbar.py +++ b/src/calibre/gui2/dialogs/config/toolbar.py @@ -13,7 +13,7 @@ from PyQt4.Qt import QWidget, QAbstractListModel, Qt, QIcon, \ from calibre.gui2.dialogs.config.toolbar_ui import Ui_Form from calibre.gui2.layout import TOOLBAR_NO_DEVICE, TOOLBAR_DEVICE from calibre.gui2.init import LIBRARY_CONTEXT_MENU, DEVICE_CONTEXT_MENU -from calibre.gui2 import gprefs, NONE +from calibre.gui2 import gprefs, NONE, warning_dialog DEFAULTS = { 'toolbar': TOOLBAR_NO_DEVICE, @@ -27,17 +27,15 @@ UNREMOVABLE = { 'toolbar-device': ['Send To Device', 'Location Manager'], } -UNADDABLE = { - 'toolbar': ['Location Manager'], - 'context-menu': ['Location Manager'], - 'context-menu-device': ['Location Manager'], -} class FakeAction(object): - def __init__(self, name, icon, tooltip=None): + def __init__(self, name, icon, tooltip=None, + dont_add_to=frozenset([]), dont_remove_from=frozenset([])): self.name = name self.action_spec = (name, icon, tooltip, None) + self.dont_remove_from = dont_remove_from + self.dont_add_to = dont_add_to class BaseModel(QAbstractListModel): @@ -45,7 +43,9 @@ class BaseModel(QAbstractListModel): if name == 'Donate': return FakeAction(name, 'donate.svg') if name == 'Location Manager': - return FakeAction(name, None) + return FakeAction(name, None, + _('Switch between library and device views'), + dont_remove_from=set(['toolbar-device'])) if name is None: return FakeAction('--- '+_('Separator')+' ---', None) return gui.iactions[name] @@ -90,15 +90,17 @@ class AllModel(BaseModel): all = list(gui.iactions.keys()) + ['Donate'] all = [x for x in all if x not in current] + [None] all = [self.name_to_action(x, gui) for x in all] + all = [x for x in all if key not in x.dont_add_to] all.sort() + self.gui = gui self._data = all def add(self, names): actions = [] - for name in actions: + for name in names: if name is None or name.startswith('---'): continue - actions.append(self.name_to_action(name)) + actions.append(self.name_to_action(name, self.gui)) self._data.extend(actions) self._data.sort() self.reset() @@ -126,6 +128,7 @@ class CurrentModel(BaseModel): current = gprefs.get('action-layout-'+key, DEFAULTS[key]) self._data = [self.name_to_action(x, gui) for x in current] self.key = key + self.gui = gui def move(self, idx, delta): row = idx.row() @@ -145,11 +148,12 @@ class CurrentModel(BaseModel): def add(self, names): actions = [] reject = set([]) - for name in actions: - if name in UNADDABLE[self.key]: - reject.add(name) - continue - actions.append(self.name_to_action(name)) + for name in names: + ac = self.name_to_action(name, self.gui) + if self.key in ac.dont_add_to: + reject.add(ac) + else: + actions.append(ac) self._data.extend(actions) self.reset() @@ -157,19 +161,20 @@ class CurrentModel(BaseModel): def remove(self, indices): rows = [i.row() for i in indices] - r = UNREMOVABLE[self.key] - remove, removed = set([]), set([]) + remove, rejected = set([]), set([]) for row in rows: ac = self._data[row] - if ac.name in r: continue + if self.key in ac.dont_remove_from: + rejected.add(ac) + continue remove.add(row) - removed.add(ac.name) ndata = [] for i, ac in enumerate(self._data): if i not in remove: ndata.append(ac) self._data = ndata self.reset() + return rejected class ToolbarLayout(QWidget, Ui_Form): @@ -212,15 +217,33 @@ class ToolbarLayout(QWidget, Ui_Form): names = self.all_actions.model().names(x) if names: not_added = self.current_actions.model().add(names) - added = set(names) - not_added + ns = set([x.name for x in not_added]) + added = set(names) - ns self.all_actions.model().remove(x, added) + if not_added: + warning_dialog(self, _('Cannot add'), + _('Cannot add the actions %s to this location') % + ','.join([a.action_spec[0] for a in not_added]), + show=True) + if added: + ca = self.current_actions + idx = ca.model().index(ca.model().rowCount(None)-1) + ca.scrollTo(idx) def remove_action(self, *args): x = self.current_actions.selectionModel().selectedIndexes() names = self.current_actions.model().names(x) if names: - self.current_actions.model().remove(x) - self.all_actions.model().add(names) + not_removed = self.current_actions.model().remove(x) + ns = set([x.name for x in not_removed]) + removed = set(names) - ns + self.all_actions.model().add(removed) + if not_removed: + warning_dialog(self, _('Cannot remove'), + _('Cannot remove the actions %s from this location') % + ','.join([a.action_spec[0] for a in not_removed]), + show=True) + def move(self, delta, *args): ci = self.current_actions.currentIndex() diff --git a/src/calibre/trac/bzr_commit_plugin.py b/src/calibre/trac/bzr_commit_plugin.py index 047d8a6cce..f2a40e6266 100644 --- a/src/calibre/trac/bzr_commit_plugin.py +++ b/src/calibre/trac/bzr_commit_plugin.py @@ -112,6 +112,6 @@ class cmd_commit(_cmd_commit): server = xmlrpclib.ServerProxy(url) server.ticket.update(int(bug), msg, {'status':'closed', 'resolution':'fixed'}, - notify=True) + True) bzrlib.commands.register_command(cmd_commit)