mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
Edit Book: Allow customization of toolbars
This commit is contained in:
parent
177901a239
commit
5036b68d8a
@ -84,7 +84,7 @@ actions = NonReplaceDict()
|
||||
editors = NonReplaceDict()
|
||||
toolbar_actions = NonReplaceDict()
|
||||
editor_toolbar_actions = {
|
||||
'html':NonReplaceDict(), 'xml':NonReplaceDict(), 'css':NonReplaceDict()}
|
||||
'format':NonReplaceDict(), 'html':NonReplaceDict(), 'xml':NonReplaceDict(), 'css':NonReplaceDict()}
|
||||
|
||||
TOP = object()
|
||||
dictionaries = Dictionaries()
|
||||
|
@ -129,6 +129,11 @@ class Boss(QObject):
|
||||
if p.dictionaries_changed:
|
||||
dictionaries.clear_caches()
|
||||
dictionaries.initialize(force=True) # Reread user dictionaries
|
||||
if p.toolbars_changed:
|
||||
self.gui.populate_toolbars()
|
||||
for ed in editors.itervalues():
|
||||
if hasattr(ed, 'populate_toolbars'):
|
||||
ed.populate_toolbars()
|
||||
if ret == p.Accepted:
|
||||
setup_cssutils_serialization()
|
||||
self.gui.apply_settings()
|
||||
|
@ -39,7 +39,7 @@ def create_icon(text, palette=None, sz=32, divider=2):
|
||||
def register_text_editor_actions(_reg, palette):
|
||||
def reg(*args, **kw):
|
||||
ac = _reg(*args)
|
||||
for s in kw.get('syntaxes', ('html',)):
|
||||
for s in kw.get('syntaxes', ('format',)):
|
||||
editor_toolbar_actions[s][args[3]] = ac
|
||||
return ac
|
||||
|
||||
@ -67,7 +67,7 @@ def register_text_editor_actions(_reg, palette):
|
||||
ac = reg('view-image', _('&Insert image'), ('insert_resource', 'image'), 'insert-image', (), _('Insert an image into the text'), syntaxes=('html', 'css'))
|
||||
ac.setToolTip(_('<h3>Insert image</h3>Insert an image into the text'))
|
||||
|
||||
ac = reg('insert-link', _('Insert &hyperlink'), ('insert_hyperlink',), 'insert-hyperlink', (), _('Insert hyperlink'), syntaxes=('html', 'css'))
|
||||
ac = reg('insert-link', _('Insert &hyperlink'), ('insert_hyperlink',), 'insert-hyperlink', (), _('Insert hyperlink'), syntaxes=('html',))
|
||||
ac.setToolTip(_('<h3>Insert hyperlink</h3>Insert a hyperlink into the text'))
|
||||
|
||||
for i, name in enumerate(('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p')):
|
||||
|
@ -10,15 +10,18 @@ from operator import attrgetter, methodcaller
|
||||
from collections import namedtuple
|
||||
from future_builtins import map
|
||||
from itertools import product
|
||||
from functools import partial
|
||||
from copy import copy, deepcopy
|
||||
|
||||
from PyQt4.Qt import (
|
||||
QDialog, QGridLayout, QStackedWidget, QDialogButtonBox, QListWidget,
|
||||
QListWidgetItem, QIcon, QWidget, QSize, QFormLayout, Qt, QSpinBox,
|
||||
QCheckBox, pyqtSignal, QDoubleSpinBox, QComboBox, QLabel, QFont,
|
||||
QFontComboBox, QPushButton, QSizePolicy, QHBoxLayout)
|
||||
QFontComboBox, QPushButton, QSizePolicy, QHBoxLayout, QGroupBox,
|
||||
QToolButton, QVBoxLayout, QSpacerItem)
|
||||
|
||||
from calibre.gui2.keyboard import ShortcutConfig
|
||||
from calibre.gui2.tweak_book import tprefs
|
||||
from calibre.gui2.tweak_book import tprefs, toolbar_actions, editor_toolbar_actions
|
||||
from calibre.gui2.tweak_book.editor.themes import default_theme, all_theme_names, ThemeEditor
|
||||
from calibre.gui2.tweak_book.spell import ManageDictionaries
|
||||
from calibre.gui2.font_family_chooser import FontFamilyChooser
|
||||
@ -64,7 +67,7 @@ class BasicSettings(QWidget): # {{{
|
||||
prefs = prefs or tprefs
|
||||
widget = QComboBox(self)
|
||||
widget.currentIndexChanged[int].connect(self.emit_changed)
|
||||
for key, human in sorted(choices.iteritems(), key=lambda (key, human): human or key):
|
||||
for key, human in sorted(choices.iteritems(), key=lambda key_human: key_human[1] or key_human[0]):
|
||||
widget.addItem(human or key, key)
|
||||
|
||||
def getter(w):
|
||||
@ -226,7 +229,7 @@ class EditorSettings(BasicSettings):
|
||||
s = self.settings['editor_theme']
|
||||
current_val = s.getter(s.widget)
|
||||
s.widget.clear()
|
||||
for key, human in sorted(choices.iteritems(), key=lambda (key, human): human or key):
|
||||
for key, human in sorted(choices.iteritems(), key=lambda key_human1: key_human1[1] or key_human1[0]):
|
||||
s.widget.addItem(human or key, key)
|
||||
s.setter(s.widget, current_val)
|
||||
if d.theme_name:
|
||||
@ -312,6 +315,193 @@ class PreviewSettings(BasicSettings):
|
||||
w.setMinimum(4), w.setMaximum(100), w.setSuffix(' px')
|
||||
l.addRow(_('Mi&nimum font size:'), w)
|
||||
|
||||
# ToolbarSettings {{{
|
||||
|
||||
class ToolbarList(QListWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QListWidget.__init__(self, parent)
|
||||
self.setSelectionMode(self.ExtendedSelection)
|
||||
|
||||
class ToolbarSettings(QWidget):
|
||||
|
||||
changed_signal = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.l = gl = QGridLayout(self)
|
||||
self.setLayout(gl)
|
||||
self.changed = False
|
||||
|
||||
self.bars = b = QComboBox(self)
|
||||
b.addItem(_('Choose which toolbar you want to customize'))
|
||||
ft = _('Tools for %s editors')
|
||||
for name, text in (
|
||||
('global_book_toolbar', _('Book wide actions'),),
|
||||
('global_tools_toolbar', _('Book wide tools'),),
|
||||
('editor_html_toolbar', ft % 'HTML',),
|
||||
('editor_css_toolbar', ft % 'CSS',),
|
||||
('editor_xml_toolbar', ft % 'XML',),
|
||||
('editor_format_toolbar', _('Text formatting actions'),),
|
||||
):
|
||||
b.addItem(text, name)
|
||||
self.la = la = QLabel(_('&Toolbar to customize:'))
|
||||
la.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
la.setBuddy(b)
|
||||
gl.addWidget(la), gl.addWidget(b, 0, 1)
|
||||
self.sl = l = QGridLayout()
|
||||
gl.addLayout(l, 1, 0, 1, -1)
|
||||
|
||||
self.gb1 = gb1 = QGroupBox(_('A&vailable actions'), self)
|
||||
self.gb2 = gb2 = QGroupBox(_('&Current actions'), self)
|
||||
gb1.setFlat(True), gb2.setFlat(True)
|
||||
gb1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
gb2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
l.addWidget(gb1, 0, 0, -1, 1), l.addWidget(gb2, 0, 2, -1, 1)
|
||||
self.available, self.current = ToolbarList(self), ToolbarList(self)
|
||||
self.ub = b = QToolButton(self)
|
||||
b.clicked.connect(partial(self.move, up=True))
|
||||
b.setToolTip(_('Move selected action up')), b.setIcon(QIcon(I('arrow-up.png')))
|
||||
self.db = b = QToolButton(self)
|
||||
b.clicked.connect(partial(self.move, up=False))
|
||||
b.setToolTip(_('Move selected action down')), b.setIcon(QIcon(I('arrow-down.png')))
|
||||
self.gl1 = gl1 = QVBoxLayout()
|
||||
gl1.addWidget(self.available), gb1.setLayout(gl1)
|
||||
self.gl2 = gl2 = QGridLayout()
|
||||
gl2.addWidget(self.current, 0, 0, -1, 1)
|
||||
gl2.addWidget(self.ub, 0, 1), gl2.addWidget(self.db, 2, 1)
|
||||
gb2.setLayout(gl2)
|
||||
self.lb = b = QToolButton(self)
|
||||
b.setToolTip(_('Add selected actions to toolbar')), b.setIcon(QIcon(I('forward.png')))
|
||||
l.addWidget(b, 1, 1), b.clicked.connect(self.add_action)
|
||||
self.rb = b = QToolButton(self)
|
||||
b.setToolTip(_('Remove selected actions from toolbar')), b.setIcon(QIcon(I('back.png')))
|
||||
l.addWidget(b, 3, 1), b.clicked.connect(self.remove_action)
|
||||
self.si = QSpacerItem(20, 10, hPolicy=QSizePolicy.Preferred, vPolicy=QSizePolicy.Expanding)
|
||||
l.setRowStretch(0, 10), l.setRowStretch(2, 10), l.setRowStretch(4, 10)
|
||||
l.addItem(self.si, 4, 1)
|
||||
|
||||
self.read_settings()
|
||||
self.toggle_visibility(False)
|
||||
self.bars.currentIndexChanged.connect(self.bar_changed)
|
||||
|
||||
def read_settings(self, prefs=None):
|
||||
prefs = prefs or tprefs
|
||||
val = self.original_settings = {}
|
||||
for i in xrange(1, self.bars.count()):
|
||||
name = unicode(self.bars.itemData(i).toString())
|
||||
val[name] = copy(prefs[name])
|
||||
self.current_settings = deepcopy(val)
|
||||
|
||||
@property
|
||||
def current_name(self):
|
||||
return unicode(self.bars.itemData(self.bars.currentIndex()).toString())
|
||||
|
||||
def build_lists(self):
|
||||
self.available.clear(), self.current.clear()
|
||||
name = self.current_name
|
||||
if not name:
|
||||
return
|
||||
items = self.current_settings[name]
|
||||
applied = set(items)
|
||||
all_items = toolbar_actions if name.startswith('global_') else editor_toolbar_actions[name.split('_')[1]]
|
||||
blank = QIcon(I('blank.png'))
|
||||
|
||||
def to_item(key, ac, parent):
|
||||
ic = ac.icon()
|
||||
if not ic or ic.isNull():
|
||||
ic = blank
|
||||
ans = QListWidgetItem(ic, unicode(ac.text()).replace('&', ''), parent)
|
||||
ans.setData(Qt.UserRole, key)
|
||||
ans.setToolTip(ac.toolTip())
|
||||
return ans
|
||||
|
||||
for key, ac in sorted(all_items.iteritems(), key=lambda k_ac: unicode(k_ac[1].text())):
|
||||
if key not in applied:
|
||||
to_item(key, ac, self.available)
|
||||
if name == 'global_book_toolbar' and 'donate' not in applied:
|
||||
QListWidgetItem(QIcon(I('donate.png')), _('Donate'), self.available).setData(Qt.UserRole, 'donate')
|
||||
|
||||
QListWidgetItem(blank, '--- %s ---' % _('Separator'), self.available)
|
||||
for key in items:
|
||||
if key is None:
|
||||
QListWidgetItem(blank, '--- %s ---' % _('Separator'), self.current)
|
||||
else:
|
||||
if key == 'donate':
|
||||
QListWidgetItem(QIcon(I('donate.png')), _('Donate'), self.current).setData(Qt.UserRole, 'donate')
|
||||
else:
|
||||
try:
|
||||
ac = all_items[key]
|
||||
except KeyError:
|
||||
pass
|
||||
to_item(key, ac, self.current)
|
||||
|
||||
def bar_changed(self):
|
||||
name = self.current_name
|
||||
self.toggle_visibility(bool(name))
|
||||
self.build_lists()
|
||||
|
||||
def toggle_visibility(self, visible):
|
||||
for x in ('gb1', 'gb2', 'lb', 'rb'):
|
||||
getattr(self, x).setVisible(visible)
|
||||
|
||||
def move(self, up=True):
|
||||
r = self.current.currentRow()
|
||||
v = self.current
|
||||
if r < 0 or (r < 1 and up) or (r > v.count() - 2 and not up):
|
||||
return
|
||||
try:
|
||||
s = self.current_settings[self.current_name]
|
||||
except KeyError:
|
||||
return
|
||||
item = v.takeItem(r)
|
||||
nr = r + (-1 if up else 1)
|
||||
v.insertItem(nr, item)
|
||||
v.setCurrentItem(item)
|
||||
s[r], s[nr] = s[nr], s[r]
|
||||
self.changed_signal.emit()
|
||||
|
||||
def add_action(self):
|
||||
try:
|
||||
s = self.current_settings[self.current_name]
|
||||
except KeyError:
|
||||
return
|
||||
names = [unicode(i.data(Qt.UserRole).toString()) for i in self.available.selectedItems()]
|
||||
if not names:
|
||||
return
|
||||
for n in names:
|
||||
s.append(n or None)
|
||||
self.build_lists()
|
||||
self.changed_signal.emit()
|
||||
|
||||
def remove_action(self):
|
||||
try:
|
||||
s = self.current_settings[self.current_name]
|
||||
except KeyError:
|
||||
return
|
||||
rows = sorted({self.current.row(i) for i in self.current.selectedItems()}, reverse=True)
|
||||
if not rows:
|
||||
return
|
||||
for r in rows:
|
||||
s.pop(r)
|
||||
self.build_lists()
|
||||
self.changed_signal.emit()
|
||||
|
||||
def restore_defaults(self):
|
||||
o = self.original_settings
|
||||
self.read_settings(tprefs.defaults)
|
||||
self.original_settings = o
|
||||
self.build_lists()
|
||||
self.changed_signal.emit()
|
||||
|
||||
def commit(self):
|
||||
if self.original_settings != self.current_settings:
|
||||
self.changed = True
|
||||
with tprefs:
|
||||
tprefs.update(self.current_settings)
|
||||
|
||||
# }}}
|
||||
|
||||
class Preferences(QDialog):
|
||||
|
||||
def __init__(self, gui, initial_panel=None):
|
||||
@ -357,12 +547,14 @@ class Preferences(QDialog):
|
||||
self.integration_panel = IntegrationSettings(self)
|
||||
self.main_window_panel = MainWindowSettings(self)
|
||||
self.preview_panel = PreviewSettings(self)
|
||||
self.toolbars_panel = ToolbarSettings(self)
|
||||
|
||||
for name, icon, panel in [
|
||||
(_('Main window'), 'page.png', 'main_window'),
|
||||
(_('Editor settings'), 'modified.png', 'editor'),
|
||||
(_('Preview settings'), 'viewer.png', 'preview'),
|
||||
(_('Keyboard shortcuts'), 'keyboard-prefs.png', 'keyboard'),
|
||||
(_('Toolbars'), 'wizard.png', 'toolbars'),
|
||||
(_('Integration with calibre'), 'lt.png', 'integration'),
|
||||
]:
|
||||
i = QListWidgetItem(QIcon(I(icon)), name, cl)
|
||||
@ -383,6 +575,10 @@ class Preferences(QDialog):
|
||||
def dictionaries_changed(self):
|
||||
return self.editor_panel.dictionaries_changed
|
||||
|
||||
@property
|
||||
def toolbars_changed(self):
|
||||
return self.toolbars_panel.changed
|
||||
|
||||
def restore_all_defaults(self):
|
||||
for i in xrange(self.stacks.count()):
|
||||
w = self.stacks.widget(i)
|
||||
|
@ -310,7 +310,7 @@ class Main(MainWindow):
|
||||
self.action_save = treg('save.png', _('&Save'), self.boss.save_book, 'save-book', 'Ctrl+S', _('Save book'))
|
||||
self.action_save.setEnabled(False)
|
||||
self.action_save_copy = treg('save.png', _('Save a ©'), self.boss.save_copy, 'save-copy', 'Ctrl+Alt+S', _('Save a copy of the book'))
|
||||
self.action_quit = treg('quit.png', _('&Quit'), self.boss.quit, 'quit', 'Ctrl+Q', _('Quit'))
|
||||
self.action_quit = treg('window-close.png', _('&Quit'), self.boss.quit, 'quit', 'Ctrl+Q', _('Quit'))
|
||||
self.action_preferences = treg('config.png', _('&Preferences'), self.boss.preferences, 'preferences', 'Ctrl+P', _('Preferences'))
|
||||
self.action_new_book = treg('book.png', _('Create &new, empty book'), self.boss.new_book, 'new-book', (), _('Create a new, empty book'))
|
||||
self.action_import_book = treg('book.png', _('&Import an HTML or DOCX file as a new book'),
|
||||
@ -564,19 +564,18 @@ class Main(MainWindow):
|
||||
if ac is None:
|
||||
bar.addSeparator()
|
||||
elif ac == 'donate':
|
||||
if not hasattr(self, 'donate_button'):
|
||||
self.donate_button = b = ThrobbingButton(self)
|
||||
b.clicked.connect(open_donate)
|
||||
b.setAutoRaise(True)
|
||||
self.donate_widget = w = create_donate_widget(b)
|
||||
if hasattr(w, 'filler'):
|
||||
w.filler.setVisible(False)
|
||||
b.set_normal_icon_size(self.global_bar.iconSize().width(), self.global_bar.iconSize().height())
|
||||
b.setIcon(QIcon(I('donate.png')))
|
||||
b.setToolTip(_('Donate to support calibre development'))
|
||||
if animate:
|
||||
QTimer.singleShot(10, b.start_animation)
|
||||
bar.addWidget(w)
|
||||
self.donate_button = b = ThrobbingButton(self)
|
||||
b.clicked.connect(open_donate)
|
||||
b.setAutoRaise(True)
|
||||
self.donate_widget = w = create_donate_widget(b)
|
||||
if hasattr(w, 'filler'):
|
||||
w.filler.setVisible(False)
|
||||
b.set_normal_icon_size(self.global_bar.iconSize().width(), self.global_bar.iconSize().height())
|
||||
b.setIcon(QIcon(I('donate.png')))
|
||||
b.setToolTip(_('Donate to support calibre development'))
|
||||
if animate:
|
||||
QTimer.singleShot(10, b.start_animation)
|
||||
bar.addWidget(w)
|
||||
else:
|
||||
try:
|
||||
bar.addAction(actions[ac])
|
||||
|
Loading…
x
Reference in New Issue
Block a user