mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
dcde8a7e59
commit
a97cffc1e9
@ -60,6 +60,17 @@ class InterfaceAction(QObject):
|
|||||||
#: shortcut must be a translated string if not None
|
#: shortcut must be a translated string if not None
|
||||||
action_spec = ('text', 'icon', None, 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):
|
def __init__(self, parent, site_customization):
|
||||||
QObject.__init__(self, parent)
|
QObject.__init__(self, parent)
|
||||||
self.setObjectName(self.name)
|
self.setObjectName(self.name)
|
||||||
|
@ -22,7 +22,9 @@ from calibre.gui2.actions import InterfaceAction
|
|||||||
class AddAction(InterfaceAction):
|
class AddAction(InterfaceAction):
|
||||||
|
|
||||||
name = 'Add Books'
|
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):
|
def genesis(self):
|
||||||
self._add_filesystem_book = self.Dispatcher(self.__add_filesystem_book)
|
self._add_filesystem_book = self.Dispatcher(self.__add_filesystem_book)
|
||||||
|
@ -10,7 +10,9 @@ from calibre.gui2.actions import InterfaceAction
|
|||||||
class AddToLibraryAction(InterfaceAction):
|
class AddToLibraryAction(InterfaceAction):
|
||||||
|
|
||||||
name = 'Add To Library'
|
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):
|
def location_selected(self, loc):
|
||||||
enabled = loc != 'library'
|
enabled = loc != 'library'
|
||||||
|
@ -18,6 +18,7 @@ class GenerateCatalogAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Generate Catalog'
|
name = 'Generate Catalog'
|
||||||
action_spec = (_('Create catalog of books in your calibre library'), None, None, None)
|
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):
|
def generate_catalog(self):
|
||||||
rows = self.gui.library_view.selectionModel().selectedRows()
|
rows = self.gui.library_view.selectionModel().selectedRows()
|
||||||
|
@ -72,6 +72,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
name = 'Choose Library'
|
name = 'Choose Library'
|
||||||
action_spec = (_('%d books'), 'lt.png',
|
action_spec = (_('%d books'), 'lt.png',
|
||||||
_('Choose calibre library to work with'), None)
|
_('Choose calibre library to work with'), None)
|
||||||
|
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
self.count_changed(0)
|
self.count_changed(0)
|
||||||
|
@ -20,6 +20,7 @@ class ConvertAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Convert Books'
|
name = 'Convert Books'
|
||||||
action_spec = (_('Convert books'), 'convert.svg', None, _('C'))
|
action_spec = (_('Convert books'), 'convert.svg', None, _('C'))
|
||||||
|
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
cm = QMenu()
|
cm = QMenu()
|
||||||
|
@ -63,6 +63,7 @@ class CopyToLibraryAction(InterfaceAction):
|
|||||||
action_spec = (_('Copy to library'), 'lt.png',
|
action_spec = (_('Copy to library'), 'lt.png',
|
||||||
_('Copy selected books to the specified library'), None)
|
_('Copy selected books to the specified library'), None)
|
||||||
popup_type = QToolButton.InstantPopup
|
popup_type = QToolButton.InstantPopup
|
||||||
|
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
self.menu = QMenu(self.gui)
|
self.menu = QMenu(self.gui)
|
||||||
|
@ -19,6 +19,7 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
connect_to_itunes = pyqtSignal()
|
connect_to_itunes = pyqtSignal()
|
||||||
config_email = pyqtSignal()
|
config_email = pyqtSignal()
|
||||||
toggle_server = pyqtSignal()
|
toggle_server = pyqtSignal()
|
||||||
|
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QMenu.__init__(self, parent)
|
QMenu.__init__(self, parent)
|
||||||
@ -95,6 +96,8 @@ class SendToDeviceAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Send To Device'
|
name = 'Send To Device'
|
||||||
action_spec = (_('Send to device'), 'sync.svg', None, _('D'))
|
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):
|
def genesis(self):
|
||||||
self.qaction.triggered.connect(self.do_sync)
|
self.qaction.triggered.connect(self.do_sync)
|
||||||
|
@ -10,7 +10,9 @@ from calibre.gui2.actions import InterfaceAction
|
|||||||
class EditCollectionsAction(InterfaceAction):
|
class EditCollectionsAction(InterfaceAction):
|
||||||
|
|
||||||
name = 'Edit Collections'
|
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):
|
def location_selected(self, loc):
|
||||||
enabled = loc != 'library'
|
enabled = loc != 'library'
|
||||||
|
@ -13,6 +13,7 @@ class OpenFolderAction(InterfaceAction):
|
|||||||
name = 'Open Folder'
|
name = 'Open Folder'
|
||||||
action_spec = (_('Open containing folder'), 'document_open.svg', None,
|
action_spec = (_('Open containing folder'), 'document_open.svg', None,
|
||||||
_('O'))
|
_('O'))
|
||||||
|
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
self.qaction.triggered.connect(self.gui.iactions['View'].view_folder)
|
self.qaction.triggered.connect(self.gui.iactions['View'].view_folder)
|
||||||
|
@ -15,6 +15,7 @@ class PreferencesAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Preferences'
|
name = 'Preferences'
|
||||||
action_spec = (_('Preferences'), 'config.svg', None, _('Ctrl+P'))
|
action_spec = (_('Preferences'), 'config.svg', None, _('Ctrl+P'))
|
||||||
|
dont_remove_from = frozenset(['toolbar'])
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
pm = QMenu()
|
pm = QMenu()
|
||||||
|
@ -15,6 +15,7 @@ class ShowBookDetailsAction(InterfaceAction):
|
|||||||
name = 'Show Book Details'
|
name = 'Show Book Details'
|
||||||
action_spec = (_('Show book details'), 'dialog_information.svg', None,
|
action_spec = (_('Show book details'), 'dialog_information.svg', None,
|
||||||
_('I'))
|
_('I'))
|
||||||
|
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
self.qaction.triggered.connect(self.show_book_info)
|
self.qaction.triggered.connect(self.show_book_info)
|
||||||
|
@ -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.dialogs.config.toolbar_ui import Ui_Form
|
||||||
from calibre.gui2.layout import TOOLBAR_NO_DEVICE, TOOLBAR_DEVICE
|
from calibre.gui2.layout import TOOLBAR_NO_DEVICE, TOOLBAR_DEVICE
|
||||||
from calibre.gui2.init import LIBRARY_CONTEXT_MENU, DEVICE_CONTEXT_MENU
|
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 = {
|
DEFAULTS = {
|
||||||
'toolbar': TOOLBAR_NO_DEVICE,
|
'toolbar': TOOLBAR_NO_DEVICE,
|
||||||
@ -27,17 +27,15 @@ UNREMOVABLE = {
|
|||||||
'toolbar-device': ['Send To Device', 'Location Manager'],
|
'toolbar-device': ['Send To Device', 'Location Manager'],
|
||||||
}
|
}
|
||||||
|
|
||||||
UNADDABLE = {
|
|
||||||
'toolbar': ['Location Manager'],
|
|
||||||
'context-menu': ['Location Manager'],
|
|
||||||
'context-menu-device': ['Location Manager'],
|
|
||||||
}
|
|
||||||
|
|
||||||
class FakeAction(object):
|
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.name = name
|
||||||
self.action_spec = (name, icon, tooltip, None)
|
self.action_spec = (name, icon, tooltip, None)
|
||||||
|
self.dont_remove_from = dont_remove_from
|
||||||
|
self.dont_add_to = dont_add_to
|
||||||
|
|
||||||
class BaseModel(QAbstractListModel):
|
class BaseModel(QAbstractListModel):
|
||||||
|
|
||||||
@ -45,7 +43,9 @@ class BaseModel(QAbstractListModel):
|
|||||||
if name == 'Donate':
|
if name == 'Donate':
|
||||||
return FakeAction(name, 'donate.svg')
|
return FakeAction(name, 'donate.svg')
|
||||||
if name == 'Location Manager':
|
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:
|
if name is None:
|
||||||
return FakeAction('--- '+_('Separator')+' ---', None)
|
return FakeAction('--- '+_('Separator')+' ---', None)
|
||||||
return gui.iactions[name]
|
return gui.iactions[name]
|
||||||
@ -90,15 +90,17 @@ class AllModel(BaseModel):
|
|||||||
all = list(gui.iactions.keys()) + ['Donate']
|
all = list(gui.iactions.keys()) + ['Donate']
|
||||||
all = [x for x in all if x not in current] + [None]
|
all = [x for x in all if x not in current] + [None]
|
||||||
all = [self.name_to_action(x, gui) for x in all]
|
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()
|
all.sort()
|
||||||
|
self.gui = gui
|
||||||
|
|
||||||
self._data = all
|
self._data = all
|
||||||
|
|
||||||
def add(self, names):
|
def add(self, names):
|
||||||
actions = []
|
actions = []
|
||||||
for name in actions:
|
for name in names:
|
||||||
if name is None or name.startswith('---'): continue
|
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.extend(actions)
|
||||||
self._data.sort()
|
self._data.sort()
|
||||||
self.reset()
|
self.reset()
|
||||||
@ -126,6 +128,7 @@ class CurrentModel(BaseModel):
|
|||||||
current = gprefs.get('action-layout-'+key, DEFAULTS[key])
|
current = gprefs.get('action-layout-'+key, DEFAULTS[key])
|
||||||
self._data = [self.name_to_action(x, gui) for x in current]
|
self._data = [self.name_to_action(x, gui) for x in current]
|
||||||
self.key = key
|
self.key = key
|
||||||
|
self.gui = gui
|
||||||
|
|
||||||
def move(self, idx, delta):
|
def move(self, idx, delta):
|
||||||
row = idx.row()
|
row = idx.row()
|
||||||
@ -145,11 +148,12 @@ class CurrentModel(BaseModel):
|
|||||||
def add(self, names):
|
def add(self, names):
|
||||||
actions = []
|
actions = []
|
||||||
reject = set([])
|
reject = set([])
|
||||||
for name in actions:
|
for name in names:
|
||||||
if name in UNADDABLE[self.key]:
|
ac = self.name_to_action(name, self.gui)
|
||||||
reject.add(name)
|
if self.key in ac.dont_add_to:
|
||||||
continue
|
reject.add(ac)
|
||||||
actions.append(self.name_to_action(name))
|
else:
|
||||||
|
actions.append(ac)
|
||||||
|
|
||||||
self._data.extend(actions)
|
self._data.extend(actions)
|
||||||
self.reset()
|
self.reset()
|
||||||
@ -157,19 +161,20 @@ class CurrentModel(BaseModel):
|
|||||||
|
|
||||||
def remove(self, indices):
|
def remove(self, indices):
|
||||||
rows = [i.row() for i in indices]
|
rows = [i.row() for i in indices]
|
||||||
r = UNREMOVABLE[self.key]
|
remove, rejected = set([]), set([])
|
||||||
remove, removed = set([]), set([])
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
ac = self._data[row]
|
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)
|
remove.add(row)
|
||||||
removed.add(ac.name)
|
|
||||||
ndata = []
|
ndata = []
|
||||||
for i, ac in enumerate(self._data):
|
for i, ac in enumerate(self._data):
|
||||||
if i not in remove:
|
if i not in remove:
|
||||||
ndata.append(ac)
|
ndata.append(ac)
|
||||||
self._data = ndata
|
self._data = ndata
|
||||||
self.reset()
|
self.reset()
|
||||||
|
return rejected
|
||||||
|
|
||||||
|
|
||||||
class ToolbarLayout(QWidget, Ui_Form):
|
class ToolbarLayout(QWidget, Ui_Form):
|
||||||
@ -212,15 +217,33 @@ class ToolbarLayout(QWidget, Ui_Form):
|
|||||||
names = self.all_actions.model().names(x)
|
names = self.all_actions.model().names(x)
|
||||||
if names:
|
if names:
|
||||||
not_added = self.current_actions.model().add(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)
|
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):
|
def remove_action(self, *args):
|
||||||
x = self.current_actions.selectionModel().selectedIndexes()
|
x = self.current_actions.selectionModel().selectedIndexes()
|
||||||
names = self.current_actions.model().names(x)
|
names = self.current_actions.model().names(x)
|
||||||
if names:
|
if names:
|
||||||
self.current_actions.model().remove(x)
|
not_removed = self.current_actions.model().remove(x)
|
||||||
self.all_actions.model().add(names)
|
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):
|
def move(self, delta, *args):
|
||||||
ci = self.current_actions.currentIndex()
|
ci = self.current_actions.currentIndex()
|
||||||
|
@ -112,6 +112,6 @@ class cmd_commit(_cmd_commit):
|
|||||||
server = xmlrpclib.ServerProxy(url)
|
server = xmlrpclib.ServerProxy(url)
|
||||||
server.ticket.update(int(bug), msg,
|
server.ticket.update(int(bug), msg,
|
||||||
{'status':'closed', 'resolution':'fixed'},
|
{'status':'closed', 'resolution':'fixed'},
|
||||||
notify=True)
|
True)
|
||||||
|
|
||||||
bzrlib.commands.register_command(cmd_commit)
|
bzrlib.commands.register_command(cmd_commit)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user