Add framework to allow user interface action plugins to specify what location they can be added to/cannot be removed from

This commit is contained in:
Kovid Goyal 2010-08-22 14:41:58 -06:00
parent dcde8a7e59
commit a97cffc1e9
14 changed files with 76 additions and 26 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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'

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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'

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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)