mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Editor preferences
This commit is contained in:
parent
43770ce0f2
commit
ea49fe8462
@ -13,7 +13,7 @@ from PyQt4.Qt import (QFontInfo, QFontMetrics, Qt, QFont, QFontDatabase, QPen,
|
|||||||
QStyledItemDelegate, QSize, QStyle, QStringListModel, pyqtSignal,
|
QStyledItemDelegate, QSize, QStyle, QStringListModel, pyqtSignal,
|
||||||
QDialog, QVBoxLayout, QApplication, QFontComboBox, QPushButton,
|
QDialog, QVBoxLayout, QApplication, QFontComboBox, QPushButton,
|
||||||
QToolButton, QGridLayout, QListView, QWidget, QDialogButtonBox, QIcon,
|
QToolButton, QGridLayout, QListView, QWidget, QDialogButtonBox, QIcon,
|
||||||
QHBoxLayout, QLabel, QModelIndex, QLineEdit)
|
QHBoxLayout, QLabel, QModelIndex, QLineEdit, QSizePolicy)
|
||||||
|
|
||||||
from calibre.constants import config_dir
|
from calibre.constants import config_dir
|
||||||
from calibre.gui2 import choose_files, error_dialog, info_dialog
|
from calibre.gui2 import choose_files, error_dialog, info_dialog
|
||||||
@ -230,9 +230,11 @@ class FontFamilyDialog(QDialog):
|
|||||||
|
|
||||||
def find(self, backwards=False):
|
def find(self, backwards=False):
|
||||||
i = self.view.currentIndex().row()
|
i = self.view.currentIndex().row()
|
||||||
if i < 0: i = 0
|
if i < 0:
|
||||||
|
i = 0
|
||||||
q = icu_lower(unicode(self.search.text())).strip()
|
q = icu_lower(unicode(self.search.text())).strip()
|
||||||
if not q: return
|
if not q:
|
||||||
|
return
|
||||||
r = (xrange(i-1, -1, -1) if backwards else xrange(i+1,
|
r = (xrange(i-1, -1, -1) if backwards else xrange(i+1,
|
||||||
len(self.families)))
|
len(self.families)))
|
||||||
for j in r:
|
for j in r:
|
||||||
@ -263,7 +265,8 @@ class FontFamilyDialog(QDialog):
|
|||||||
files = choose_files(self, 'add fonts to calibre',
|
files = choose_files(self, 'add fonts to calibre',
|
||||||
_('Select font files'), filters=[(_('TrueType/OpenType Fonts'),
|
_('Select font files'), filters=[(_('TrueType/OpenType Fonts'),
|
||||||
['ttf', 'otf'])], all_files=False)
|
['ttf', 'otf'])], all_files=False)
|
||||||
if not files: return
|
if not files:
|
||||||
|
return
|
||||||
families = set()
|
families = set()
|
||||||
for f in files:
|
for f in files:
|
||||||
try:
|
try:
|
||||||
@ -298,7 +301,8 @@ class FontFamilyDialog(QDialog):
|
|||||||
@property
|
@property
|
||||||
def font_family(self):
|
def font_family(self):
|
||||||
idx = self.view.currentIndex().row()
|
idx = self.view.currentIndex().row()
|
||||||
if idx == 0: return None
|
if idx == 0:
|
||||||
|
return None
|
||||||
return self.families[idx]
|
return self.families[idx]
|
||||||
|
|
||||||
def current_changed(self):
|
def current_changed(self):
|
||||||
@ -313,9 +317,11 @@ class FontFamilyChooser(QWidget):
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
self.l = l = QHBoxLayout()
|
self.l = l = QHBoxLayout()
|
||||||
|
l.setContentsMargins(0, 0, 0, 0)
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
self.button = QPushButton(self)
|
self.button = QPushButton(self)
|
||||||
self.button.setIcon(QIcon(I('font.png')))
|
self.button.setIcon(QIcon(I('font.png')))
|
||||||
|
self.button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||||
l.addWidget(self.button)
|
l.addWidget(self.button)
|
||||||
self.default_text = _('Choose &font family')
|
self.default_text = _('Choose &font family')
|
||||||
self.font_family = None
|
self.font_family = None
|
||||||
|
@ -66,7 +66,9 @@ class Boss(QObject):
|
|||||||
|
|
||||||
def preferences(self):
|
def preferences(self):
|
||||||
p = Preferences(self.gui)
|
p = Preferences(self.gui)
|
||||||
p.exec_()
|
if p.exec_() == p.Accepted:
|
||||||
|
for ed in editors.itervalues():
|
||||||
|
ed.apply_settings()
|
||||||
|
|
||||||
def mkdtemp(self, prefix=''):
|
def mkdtemp(self, prefix=''):
|
||||||
self.container_count += 1
|
self.container_count += 1
|
||||||
|
@ -55,12 +55,12 @@ class TextEdit(QPlainTextEdit):
|
|||||||
self.current_cursor_line = None
|
self.current_cursor_line = None
|
||||||
self.current_search_mark = None
|
self.current_search_mark = None
|
||||||
self.highlighter = SyntaxHighlighter(self)
|
self.highlighter = SyntaxHighlighter(self)
|
||||||
|
self.line_number_area = LineNumbers(self)
|
||||||
self.apply_settings()
|
self.apply_settings()
|
||||||
self.setMouseTracking(True)
|
self.setMouseTracking(True)
|
||||||
self.cursorPositionChanged.connect(self.highlight_cursor_line)
|
self.cursorPositionChanged.connect(self.highlight_cursor_line)
|
||||||
self.blockCountChanged[int].connect(self.update_line_number_area_width)
|
self.blockCountChanged[int].connect(self.update_line_number_area_width)
|
||||||
self.updateRequest.connect(self.update_line_number_area)
|
self.updateRequest.connect(self.update_line_number_area)
|
||||||
self.line_number_area = LineNumbers(self)
|
|
||||||
self.syntax = None
|
self.syntax = None
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
@ -118,6 +118,7 @@ class TextEdit(QPlainTextEdit):
|
|||||||
self.number_width = max(map(lambda x:w.width(str(x)), xrange(10)))
|
self.number_width = max(map(lambda x:w.width(str(x)), xrange(10)))
|
||||||
self.size_hint = QSize(100 * w.averageCharWidth(), 50 * w.height())
|
self.size_hint = QSize(100 * w.averageCharWidth(), 50 * w.height())
|
||||||
self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg')
|
self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg')
|
||||||
|
self.highlight_cursor_line()
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def load_text(self, text, syntax='html', process_template=False):
|
def load_text(self, text, syntax='html', process_template=False):
|
||||||
|
@ -77,6 +77,9 @@ class Editor(QMainWindow):
|
|||||||
if current != raw:
|
if current != raw:
|
||||||
self.editor.replace_text(raw)
|
self.editor.replace_text(raw)
|
||||||
|
|
||||||
|
def apply_settings(self, prefs=None):
|
||||||
|
self.editor.apply_settings(prefs=None)
|
||||||
|
|
||||||
def set_focus(self):
|
def set_focus(self):
|
||||||
self.editor.setFocus(Qt.OtherFocusReason)
|
self.editor.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
|
@ -6,12 +6,135 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
from operator import attrgetter, methodcaller
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
from PyQt4.Qt import (
|
from PyQt4.Qt import (
|
||||||
QDialog, QGridLayout, QStackedWidget, QDialogButtonBox, QListWidget,
|
QDialog, QGridLayout, QStackedWidget, QDialogButtonBox, QListWidget,
|
||||||
QListWidgetItem, QIcon)
|
QListWidgetItem, QIcon, QWidget, QSize, QFormLayout, Qt, QSpinBox,
|
||||||
|
QCheckBox, pyqtSignal, QDoubleSpinBox, QComboBox)
|
||||||
|
|
||||||
from calibre.gui2.keyboard import ShortcutConfig
|
from calibre.gui2.keyboard import ShortcutConfig
|
||||||
from calibre.gui2.tweak_book import tprefs
|
from calibre.gui2.tweak_book import tprefs
|
||||||
|
from calibre.gui2.tweak_book.editor.themes import default_theme, THEMES
|
||||||
|
from calibre.gui2.font_family_chooser import FontFamilyChooser
|
||||||
|
|
||||||
|
class BasicSettings(QWidget): # {{{
|
||||||
|
|
||||||
|
changed_signal = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.settings = {}
|
||||||
|
self._prevent_changed = False
|
||||||
|
self.Setting = namedtuple('Setting', 'name prefs widget getter setter initial_value')
|
||||||
|
|
||||||
|
def __call__(self, name, widget=None, getter=None, setter=None, prefs=None):
|
||||||
|
prefs = prefs or tprefs
|
||||||
|
defval = prefs.defaults[name]
|
||||||
|
inval = prefs[name]
|
||||||
|
if widget is None:
|
||||||
|
if isinstance(defval, bool):
|
||||||
|
widget = QCheckBox(self)
|
||||||
|
getter = getter or methodcaller('isChecked')
|
||||||
|
setter = setter or (lambda x, v: x.setChecked(v))
|
||||||
|
widget.toggled.connect(self.emit_changed)
|
||||||
|
elif isinstance(defval, (int, float)):
|
||||||
|
widget = (QSpinBox if isinstance(defval, int) else QDoubleSpinBox)(self)
|
||||||
|
getter = getter or methodcaller('value')
|
||||||
|
setter = setter or (lambda x, v:x.setValue(v))
|
||||||
|
widget.valueChanged.connect(self.emit_changed)
|
||||||
|
else:
|
||||||
|
raise TypeError('Unknown setting type for setting: %s' % name)
|
||||||
|
else:
|
||||||
|
if getter is None or setter is None:
|
||||||
|
raise ValueError("getter or setter not provided for: %s" % name)
|
||||||
|
self._prevent_changed = True
|
||||||
|
setter(widget, inval)
|
||||||
|
self._prevent_changed = False
|
||||||
|
|
||||||
|
self.settings[name] = self.Setting(name, prefs, widget, getter, setter, inval)
|
||||||
|
return widget
|
||||||
|
|
||||||
|
def choices_widget(self, name, choices, fallback_val, none_val, prefs=None):
|
||||||
|
prefs = prefs or tprefs
|
||||||
|
widget = QComboBox(self)
|
||||||
|
widget.currentIndexChanged[int].connect(self.emit_changed)
|
||||||
|
for key, human in choices.iteritems():
|
||||||
|
widget.addItem(human or key, key)
|
||||||
|
|
||||||
|
def getter(w):
|
||||||
|
ans = unicode(w.itemData(w.currentIndex()).toString())
|
||||||
|
return {none_val:None}.get(ans, ans)
|
||||||
|
|
||||||
|
def setter(w, val):
|
||||||
|
val = {None:none_val}.get(val, val)
|
||||||
|
idx = w.findData(val, flags=Qt.MatchFixedString|Qt.MatchCaseSensitive)
|
||||||
|
if idx == -1:
|
||||||
|
idx = w.findData(fallback_val, flags=Qt.MatchFixedString|Qt.MatchCaseSensitive)
|
||||||
|
w.setCurrentIndex(idx)
|
||||||
|
|
||||||
|
return self(name, widget=widget, getter=getter, setter=setter, prefs=prefs)
|
||||||
|
|
||||||
|
def emit_changed(self, *args):
|
||||||
|
if not self._prevent_changed:
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
with tprefs:
|
||||||
|
for name in self.settings:
|
||||||
|
cv = self.current_value(name)
|
||||||
|
if self.initial_value(name) != cv:
|
||||||
|
prefs = self.settings[name].prefs
|
||||||
|
if cv == self.default_value(name):
|
||||||
|
del prefs[name]
|
||||||
|
else:
|
||||||
|
prefs[name] = cv
|
||||||
|
|
||||||
|
def restore_defaults(self):
|
||||||
|
for setting in self.settings.itervalues():
|
||||||
|
setting.setter(setting.widget, self.default_value(setting.name))
|
||||||
|
|
||||||
|
def initial_value(self, name):
|
||||||
|
return self.settings[name].initial_value
|
||||||
|
|
||||||
|
def current_value(self, name):
|
||||||
|
s = self.settings[name]
|
||||||
|
return s.getter(s.widget)
|
||||||
|
|
||||||
|
def default_value(self, name):
|
||||||
|
s = self.settings[name]
|
||||||
|
return s.prefs.defaults[name]
|
||||||
|
|
||||||
|
def setting_changed(self, name):
|
||||||
|
return self.current_value(name) != self.initial_value(name)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class EditorSettings(BasicSettings):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
BasicSettings.__init__(self, parent)
|
||||||
|
self.l = l = QFormLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
|
||||||
|
fc = FontFamilyChooser(self)
|
||||||
|
self('editor_font_family', widget=fc, getter=attrgetter('font_family'), setter=lambda x, val: setattr(x, 'font_family', val))
|
||||||
|
fc.family_changed.connect(self.emit_changed)
|
||||||
|
l.addRow(_('Editor font &family:'), fc)
|
||||||
|
|
||||||
|
fs = self('editor_font_size')
|
||||||
|
fs.setMinimum(8), fs.setSuffix(' pt'), fs.setMaximum(50)
|
||||||
|
l.addRow(_('Editor font &size:'), fs)
|
||||||
|
|
||||||
|
auto_theme = _('Automatic (%s)') % default_theme()
|
||||||
|
choices = {k:k for k in THEMES}
|
||||||
|
choices['auto'] = auto_theme
|
||||||
|
theme = self.choices_widget('editor_theme', choices, 'auto', 'auto')
|
||||||
|
l.addRow(_('&Color scheme:'), theme)
|
||||||
|
|
||||||
|
lw = self('editor_line_wrap')
|
||||||
|
lw.setText(_('&Wrap long lines in the editor'))
|
||||||
|
l.addRow(lw)
|
||||||
|
|
||||||
class Preferences(QDialog):
|
class Preferences(QDialog):
|
||||||
|
|
||||||
@ -32,7 +155,7 @@ class Preferences(QDialog):
|
|||||||
cl.setFlow(cl.TopToBottom)
|
cl.setFlow(cl.TopToBottom)
|
||||||
cl.setMovement(cl.Static)
|
cl.setMovement(cl.Static)
|
||||||
cl.setWrapping(False)
|
cl.setWrapping(False)
|
||||||
cl.setSpacing(10)
|
cl.setSpacing(15)
|
||||||
cl.setWordWrap(True)
|
cl.setWordWrap(True)
|
||||||
l.addWidget(cl, 0, 0, 1, 1)
|
l.addWidget(cl, 0, 0, 1, 1)
|
||||||
|
|
||||||
@ -40,7 +163,11 @@ class Preferences(QDialog):
|
|||||||
bb.accepted.connect(self.accept)
|
bb.accepted.connect(self.accept)
|
||||||
bb.rejected.connect(self.reject)
|
bb.rejected.connect(self.reject)
|
||||||
self.rdb = b = bb.addButton(_('Restore all defaults'), bb.ResetRole)
|
self.rdb = b = bb.addButton(_('Restore all defaults'), bb.ResetRole)
|
||||||
|
b.setToolTip(_('Restore defaults for all preferences'))
|
||||||
b.clicked.connect(self.restore_all_defaults)
|
b.clicked.connect(self.restore_all_defaults)
|
||||||
|
self.rcdb = b = bb.addButton(_('Restore current defaults'), bb.ResetRole)
|
||||||
|
b.setToolTip(_('Restore defaults for currently displayed preferences'))
|
||||||
|
b.clicked.connect(self.restore_current_defaults)
|
||||||
l.addWidget(bb, 1, 0, 1, 2)
|
l.addWidget(bb, 1, 0, 1, 2)
|
||||||
|
|
||||||
self.resize(800, 600)
|
self.resize(800, 600)
|
||||||
@ -50,22 +177,34 @@ class Preferences(QDialog):
|
|||||||
|
|
||||||
self.keyboard_panel = ShortcutConfig(self)
|
self.keyboard_panel = ShortcutConfig(self)
|
||||||
self.keyboard_panel.initialize(gui.keyboard)
|
self.keyboard_panel.initialize(gui.keyboard)
|
||||||
|
self.editor_panel = EditorSettings(self)
|
||||||
|
|
||||||
for name, icon, panel in [(_('Keyboard Shortcuts'), 'keyboard-prefs.png', 'keyboard')]:
|
for name, icon, panel in [
|
||||||
|
(_('Editor settings'), 'modified.png', 'editor'),
|
||||||
|
(_('Keyboard Shortcuts'), 'keyboard-prefs.png', 'keyboard')
|
||||||
|
]:
|
||||||
i = QListWidgetItem(QIcon(I(icon)), name, cl)
|
i = QListWidgetItem(QIcon(I(icon)), name, cl)
|
||||||
cl.addItem(i)
|
cl.addItem(i)
|
||||||
self.stacks.addWidget(getattr(self, panel + '_panel'))
|
self.stacks.addWidget(getattr(self, panel + '_panel'))
|
||||||
|
|
||||||
cl.setCurrentRow(0)
|
cl.setCurrentRow(0)
|
||||||
cl.item(0).setSelected(True)
|
cl.item(0).setSelected(True)
|
||||||
cl.setMaximumWidth(cl.sizeHintForColumn(0) + 30)
|
w, h = cl.sizeHintForColumn(0), 0
|
||||||
l.setColumnStretch(1, 10)
|
for i in xrange(cl.count()):
|
||||||
|
h = max(h, cl.sizeHintForRow(i))
|
||||||
|
cl.item(i).setSizeHint(QSize(w, h))
|
||||||
|
|
||||||
|
cl.setMaximumWidth(cl.sizeHintForColumn(0) + 35)
|
||||||
|
cl.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
def restore_all_defaults(self):
|
def restore_all_defaults(self):
|
||||||
for i in xrange(self.stacks.count()):
|
for i in xrange(self.stacks.count()):
|
||||||
w = self.stacks.widget(i)
|
w = self.stacks.widget(i)
|
||||||
w.restore_defaults()
|
w.restore_defaults()
|
||||||
|
|
||||||
|
def restore_current_defaults(self):
|
||||||
|
self.stacks.currentWidget().restore_defaults()
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
tprefs.set('preferences_geom', bytearray(self.saveGeometry()))
|
tprefs.set('preferences_geom', bytearray(self.saveGeometry()))
|
||||||
for i in xrange(self.stacks.count()):
|
for i in xrange(self.stacks.count()):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user