mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
52f80efdb9
@ -1097,6 +1097,12 @@ class ActionBooklistContextMenu(InterfaceActionBase):
|
||||
description = _('Open the context menu for the column')
|
||||
|
||||
|
||||
class ActionAllActions(InterfaceActionBase):
|
||||
name = 'All GUI actions'
|
||||
author = 'Charles Haley'
|
||||
actual_plugin = 'calibre.gui2.actions.all_actions:AllGUIActions'
|
||||
description = _('Open a menu showing all installed GUI actions')
|
||||
|
||||
class ActionVirtualLibrary(InterfaceActionBase):
|
||||
name = 'Virtual Library'
|
||||
actual_plugin = 'calibre.gui2.actions.virtual_library:VirtualLibraryAction'
|
||||
@ -1128,7 +1134,7 @@ class ActionPluginUpdater(InterfaceActionBase):
|
||||
actual_plugin = 'calibre.gui2.actions.plugin_updates:PluginUpdaterAction'
|
||||
|
||||
|
||||
plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
|
||||
plugins += [ActionAdd, ActionAllActions, ActionFetchAnnotations, ActionGenerateCatalog,
|
||||
ActionConvert, ActionDelete, ActionEditMetadata, ActionView,
|
||||
ActionFetchNews, ActionSaveToDisk, ActionQuickview, ActionPolish,
|
||||
ActionShowBookDetails,ActionRestart, ActionOpenFolder, ActionConnectShare,
|
||||
|
193
src/calibre/gui2/actions/all_actions.py
Normal file
193
src/calibre/gui2/actions/all_actions.py
Normal file
@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python
|
||||
# License: GPLv3 Copyright: 2022, Charles Haley
|
||||
|
||||
from functools import partial
|
||||
from math import ceil
|
||||
|
||||
from qt.core import QIcon, QMenu, QToolButton, Qt
|
||||
|
||||
from calibre.gui2.actions import InterfaceAction, show_menu_under_widget
|
||||
from calibre.gui2.preferences.toolbar import AllModel, CurrentModel
|
||||
from calibre.utils.icu import sort_key
|
||||
|
||||
|
||||
class AllGUIActions(InterfaceAction):
|
||||
|
||||
name = 'All GUI actions'
|
||||
action_spec = (_('All GUI actions'), 'wizard.png',
|
||||
_("Show a menu of all available GUI and plugin actions.\nThis menu "
|
||||
"is not available when looking at books on a device"), None)
|
||||
|
||||
action_type = 'current'
|
||||
popup_type = QToolButton.ToolButtonPopupMode.InstantPopup
|
||||
action_add_menu = True
|
||||
dont_add_to = frozenset()
|
||||
|
||||
def genesis(self):
|
||||
self.layout_icon = QIcon.ic('wizard.png')
|
||||
self.menu = m = self.qaction.menu()
|
||||
m.aboutToShow.connect(self.about_to_show_menu)
|
||||
|
||||
# Create a "hidden" menu that can have a shortcut.
|
||||
self.hidden_menu = QMenu()
|
||||
self.shortcut_action = self.create_menu_action(
|
||||
menu=self.hidden_menu,
|
||||
unique_name='Main window layout',
|
||||
shortcut=None,
|
||||
text=_("Save and restore layout item sizes, and add/remove/toggle "
|
||||
"layout items such as the search bar, tag browser, etc. "),
|
||||
icon='layout.png',
|
||||
triggered=self.show_menu)
|
||||
|
||||
# We want to show the menu when a shortcut is used. Apparently the only way
|
||||
# to do that is to scan the toolbar(s) for the action button then exec the
|
||||
# associated menu. The search is done here to take adding and removing the
|
||||
# action from toolbars into account.
|
||||
#
|
||||
# If a shortcut is triggered and there isn't a toolbar button visible then
|
||||
# show the menu in the upper left corner of the library view pane. Yes, this
|
||||
# is a bit weird but it works as well as a popping up a dialog.
|
||||
def show_menu(self):
|
||||
show_menu_under_widget(self.gui, self.menu, self.qaction, self.name)
|
||||
|
||||
def gui_layout_complete(self):
|
||||
self.qaction.menu().aboutToShow.connect(self.about_to_show_menu)
|
||||
|
||||
def initialization_complete(self):
|
||||
self.populate_menu()
|
||||
|
||||
def about_to_show_menu(self):
|
||||
self.populate_menu()
|
||||
|
||||
def location_selected(self, loc):
|
||||
self.qaction.setEnabled(loc == 'library')
|
||||
|
||||
def populate_menu(self):
|
||||
# Need to do this on every invocation because shortcuts can change
|
||||
m = self.qaction.menu()
|
||||
m.clear()
|
||||
|
||||
name_data = {} # A dict of display names to actions data
|
||||
|
||||
# Use model data from Preferences / Toolbars, with location 'toolbar' or
|
||||
# 'toolbar-device' depending on whether a device is connected.
|
||||
location = 'toolbar' + ('-device' if self.gui.location_manager.has_device else '')
|
||||
for model in (AllModel(location, self.gui), CurrentModel(location, self.gui)):
|
||||
for i in range(0, model.rowCount(None)):
|
||||
dex = model.index(i)
|
||||
name = model.names((dex,))[0] # this is the action name
|
||||
if name is not None and not name.startswith('---'):
|
||||
name_data[model.data(dex, Qt.ItemDataRole.DisplayRole)] = {
|
||||
'action': model.name_to_action(name, self.gui),
|
||||
'action_name':name,
|
||||
'icon': model.data(dex, Qt.ItemDataRole.DecorationRole)}
|
||||
# The loop leaves the variable 'model' set to a valid value
|
||||
|
||||
# Get display names of builtin and user plugins. We tell the difference
|
||||
# using the class full module name. Plugins start with 'calibre_plugins'
|
||||
builtin_actions = list()
|
||||
user_plugins = list()
|
||||
for display_name, act_data in name_data.items():
|
||||
act = model.name_to_action(act_data['action_name'], self.gui)
|
||||
if act is not None:
|
||||
module = f'{act.__module__}.{act.__class__ .__name__}'
|
||||
if module.startswith('calibre_plugins'):
|
||||
user_plugins.append(display_name)
|
||||
else:
|
||||
builtin_actions.append(display_name)
|
||||
|
||||
builtin_actions = sorted(builtin_actions, key=sort_key)
|
||||
user_plugins = sorted(user_plugins, key=sort_key)
|
||||
|
||||
# Build the map of action shortcuts so we can display the shortcuts for
|
||||
# the actions. For shortcuts sometimes the action name and sometimes the
|
||||
# display name is used. Test both. Unfortunately, there are case
|
||||
# differences to deal with so we use lower case keys.
|
||||
lower_names = ({n['action_name'].lower() for n in name_data.values()} |
|
||||
{n.lower() for n in name_data.keys()})
|
||||
kbd = self.gui.keyboard
|
||||
shortcut_map = {}
|
||||
for n,v in kbd.keys_map.items():
|
||||
act_name = kbd.shortcuts[n]['name'].lower()
|
||||
if act_name in lower_names:
|
||||
shortcuts = list((sc.toString() for sc in v))
|
||||
shortcut_map[act_name] = f'\t{", ".join(shortcuts)}'
|
||||
|
||||
# This function constructs a menu action, dealing with the action being
|
||||
# either a popup menu or a dialog. It adds the shortcuts to the menu
|
||||
# line. Happily, a tab causes the shortcuts to be right-aligned. The tab
|
||||
# is added when the shortcut map is built.
|
||||
def add_action(menu, display_name):
|
||||
shortcuts = shortcut_map.get(display_name.lower(), '')
|
||||
act = name_data[display_name]['action']
|
||||
if not hasattr(act, 'popup_type'): # FakeAction
|
||||
return
|
||||
menu_text = f'{display_name}{shortcuts}'
|
||||
icon = name_data[display_name]['icon']
|
||||
if act.popup_type == QToolButton.ToolButtonPopupMode.MenuButtonPopup:
|
||||
if act.action_add_menu:
|
||||
# The action offers both a 'click' and a menu. Use the menu.
|
||||
menu.addAction(icon, menu_text, partial(self._do_menu, display_name, act))
|
||||
else:
|
||||
# The action is a dialog.
|
||||
menu.addAction(act.qaction.icon(), menu_text, partial(self._do_action, act))
|
||||
else:
|
||||
# The action is a menu.
|
||||
menu.addAction(icon, menu_text, partial(self._do_menu, display_name, act))
|
||||
|
||||
# Finally the real work, building the action menu. Partition long lists
|
||||
# of actions into sublists of some arbitrary length.
|
||||
def partition(names):
|
||||
count_in_partition = 10 # arbitrary
|
||||
if len(names) >= count_in_partition:
|
||||
partition_count = len(names) // (count_in_partition - 1)
|
||||
step = int(ceil(len(names) / partition_count))
|
||||
for first in range(0, len(names), step):
|
||||
last = min(first + step - 1, len(names) - 1)
|
||||
dnf = names[first]
|
||||
dnl = names[last]
|
||||
if dnf != dnl:
|
||||
sm = m.addMenu(QIcon.ic('wizard.png'), f'{dnf} - {dnl}')
|
||||
else:
|
||||
sm = m.addMenu(QIcon.ic('wizard.png'), f'{dnf}')
|
||||
for name in names[first:last+1]:
|
||||
add_action(sm, name)
|
||||
else:
|
||||
for name in names:
|
||||
act = self.gui.iactions[name]
|
||||
add_action(m, name)
|
||||
|
||||
# Add a named section for builtin actions if user plugins are installed.
|
||||
if user_plugins:
|
||||
m.addSection(_('Built-in calibre actions') + ' ')
|
||||
partition(builtin_actions)
|
||||
m.addSection(_('Plugins') + ' ')
|
||||
partition(user_plugins)
|
||||
else:
|
||||
partition(builtin_actions)
|
||||
# Add access to the toolbars and keyboard shortcuts preferences dialogs
|
||||
m.addSection(_('Preferences') + ' ')
|
||||
m.addAction(_('Toolbars'), self._do_pref_toolbar)
|
||||
m.addAction(_('Keyboard shortcuts'), self._do_pref_shortcuts)
|
||||
|
||||
def _do_pref_toolbar(self):
|
||||
self.gui.iactions['Preferences'].do_config(initial_plugin=('Interface', 'Toolbar'), close_after_initial=True)
|
||||
|
||||
def _do_pref_shortcuts(self):
|
||||
self.gui.iactions['Preferences'].do_config(initial_plugin=('Advanced', 'Keyboard'), close_after_initial=True)
|
||||
|
||||
def _do_action(self, action):
|
||||
action.qaction.trigger()
|
||||
|
||||
def _do_menu(self, name, action):
|
||||
# This method clones and shows the action's menu. If the action is in a
|
||||
# context menu, the action menu is displayed there without using this
|
||||
# method. If not it is displayed underneath a toolbar or menu button. If
|
||||
# neither is true then the menu is displayed in the upper left corner.
|
||||
menu = QMenu()
|
||||
menu.addSection(name)
|
||||
action.qaction.menu().aboutToShow.emit()
|
||||
for m in action.qaction.menu().actions():
|
||||
menu.addAction(m)
|
||||
show_menu_under_widget(self.gui, menu, self.qaction, self.name)
|
||||
|
@ -40,13 +40,15 @@ class SortByAction(InterfaceAction):
|
||||
name = 'Sort By'
|
||||
action_spec = (_('Sort by'), 'sort.png', _('Sort the list of books'), None)
|
||||
action_type = 'current'
|
||||
popup_type = QToolButton.ToolButtonPopupMode.MenuButtonPopup
|
||||
popup_type = QToolButton.ToolButtonPopupMode.InstantPopup
|
||||
action_add_menu = True
|
||||
dont_add_to = frozenset(('context-menu-cover-browser', ))
|
||||
|
||||
def genesis(self):
|
||||
self.sorted_icon = QIcon.ic('ok.png')
|
||||
self.qaction.triggered.connect(self.show_menu)
|
||||
self.menu = m = self.qaction.menu()
|
||||
m.aboutToShow.connect(self.about_to_show_menu)
|
||||
# self.qaction.triggered.connect(self.show_menu)
|
||||
|
||||
# Create a "hidden" menu that can have a shortcut. This also lets us
|
||||
# manually show the menu instead of letting Qt do it to work around a
|
||||
@ -69,10 +71,10 @@ class SortByAction(InterfaceAction):
|
||||
c('reverse_sort_action', _('Reverse current sort'), _('Reverse the current sort order'), self.reverse_sort, 'shift+f5')
|
||||
c('reapply_sort_action', _('Re-apply current sort'), _('Re-apply the current sort'), self.reapply_sort, 'f5')
|
||||
|
||||
def show_menu(self):
|
||||
# We manually show the menu instead of letting Qt do it to work around a
|
||||
# problem where the menu can show on the wrong screen.
|
||||
def about_to_show_menu(self):
|
||||
self.update_menu()
|
||||
|
||||
def show_menu(self):
|
||||
show_menu_under_widget(self.gui, self.qaction.menu(), self.qaction, self.name)
|
||||
|
||||
def reverse_sort(self):
|
||||
@ -91,12 +93,6 @@ class SortByAction(InterfaceAction):
|
||||
|
||||
def initialization_complete(self):
|
||||
self.update_menu()
|
||||
# Remove the down arrow from the buttons as they serve no purpose. They
|
||||
# show exactly what clicking the button shows
|
||||
for w in toolbar_widgets_for_action(self.gui, self.qaction):
|
||||
# Not sure why both styles are necessary, but they are
|
||||
w.setStyleSheet('QToolButton::menu-button {image: none; }'
|
||||
'QToolButton::menu-arrow {image: none; }')
|
||||
|
||||
def update_menu(self, menu=None):
|
||||
menu = menu or self.qaction.menu()
|
||||
|
Loading…
x
Reference in New Issue
Block a user