diff --git a/src/calibre/gui2/tweak_book/editor/snippets.py b/src/calibre/gui2/tweak_book/editor/snippets.py index d451f8d061..ff939d4395 100644 --- a/src/calibre/gui2/tweak_book/editor/snippets.py +++ b/src/calibre/gui2/tweak_book/editor/snippets.py @@ -326,9 +326,9 @@ def expand_template(editor, trigger, template): c.endEditBlock() return tl -def find_matching_snip(text, syntax): +def find_matching_snip(text, syntax=None): for key, snip in snippets().iteritems(): - if text.endswith(key.trigger) and syntax in key.syntaxes: + if text.endswith(key.trigger) and (syntax in key.syntaxes or syntax is None): return snip, key.trigger return None, None @@ -393,7 +393,6 @@ class SnippetManager(QObject): return False # }}} - # Config {{{ class EditSnippet(QWidget): diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 69e773c507..be0d9f63e0 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -84,6 +84,7 @@ class PlainTextEdit(QPlainTextEdit): def __init__(self, parent=None): QPlainTextEdit.__init__(self, parent) self.selectionChanged.connect(self.selection_changed) + self.syntax = None def toPlainText(self): # QPlainTextEdit's toPlainText implementation replaces nbsp with normal diff --git a/src/calibre/gui2/tweak_book/search.py b/src/calibre/gui2/tweak_book/search.py index f366b7a45a..fe8b4f15f1 100644 --- a/src/calibre/gui2/tweak_book/search.py +++ b/src/calibre/gui2/tweak_book/search.py @@ -14,7 +14,7 @@ from PyQt5.Qt import ( QWidget, QToolBar, Qt, QHBoxLayout, QSize, QIcon, QGridLayout, QLabel, QTimer, QPushButton, pyqtSignal, QComboBox, QCheckBox, QSizePolicy, QVBoxLayout, QFont, QLineEdit, QToolButton, QListView, QFrame, QApplication, QStyledItemDelegate, - QAbstractListModel, QPlainTextEdit, QModelIndex, QMenu, QItemSelection, QStackedLayout) + QAbstractListModel, QModelIndex, QMenu, QItemSelection, QStackedLayout) import regex @@ -26,6 +26,8 @@ from calibre.gui2.tweak_book import tprefs, editors, current_container from calibre.gui2.tweak_book.function_replace import ( FunctionBox, functions as replace_functions, FunctionEditor, remove_function, Function) from calibre.gui2.tweak_book.widgets import BusyCursor +from calibre.gui2.tweak_book.editor.text import PlainTextEdit +from calibre.gui2.tweak_book.editor.snippets import find_matching_snip, parse_template, string_length, SnippetManager from calibre.utils.icu import primary_contains @@ -51,12 +53,42 @@ class AnimatablePushButton(QPushButton): self.setDown(False) self.update() +class TextEdit(PlainTextEdit): + + def __init__(self, text, parent=None): + PlainTextEdit.__init__(self, parent) + if text: + self.setPlainText(text) + self.snippet_manager = SnippetManager(self) + + def keyPressEvent(self, ev): + if self.snippet_manager.handle_key_press(ev): + return + PlainTextEdit.keyPressEvent(self, ev) + class PushButton(AnimatablePushButton): def __init__(self, text, action, parent): AnimatablePushButton.__init__(self, text, parent) self.clicked.connect(lambda : parent.search_triggered.emit(action)) +def expand_template(line_edit): + pos = line_edit.cursorPosition() + text = line_edit.text()[:pos] + if text: + snip, trigger = find_matching_snip(text) + if snip is None: + error_dialog(line_edit, _('No snippet found'), _( + 'No matching snippet was found'), show=True) + return False + text, tab_stops = parse_template(snip['template']) + ft = line_edit.text() + l = string_length(trigger) + line_edit.setText(ft[:pos - l] + text + ft[pos:]) + line_edit.setCursorPosition(pos - l + string_length(text)) + return True + return False + class HistoryBox(HistoryComboBox): max_history_items = 100 @@ -67,6 +99,17 @@ class HistoryBox(HistoryComboBox): HistoryComboBox.__init__(self, parent) self.disable_popup = tprefs['disable_completion_popup_for_search'] self.clear_msg = clear_msg + self.ignore_snip_expansion = False + + def event(self, ev): + if ev.type() in (ev.ShortcutOverride, ev.KeyPress) and ev.key() == Qt.Key_Tab and ev.modifiers() & Qt.CTRL: + if not self.ignore_snip_expansion: + self.ignore_snip_expansion = True + expand_template(self.lineEdit()) + QTimer.singleShot(100, lambda : setattr(self, 'ignore_snip_expansion', False)) + ev.accept() + return True + return HistoryComboBox.event(self, ev) def contextMenuEvent(self, event): menu = self.lineEdit().createStandardContextMenu() @@ -551,12 +594,12 @@ class EditSearch(QFrame): # {{{ h.addWidget(la), h.addWidget(n) l.addLayout(h) - self.find = f = QPlainTextEdit('', self) + self.find = f = TextEdit('', self) self.la2 = la = QLabel(_('&Find:')) la.setBuddy(f) l.addWidget(la), l.addWidget(f) - self.replace = r = QPlainTextEdit('', self) + self.replace = r = TextEdit('', self) self.la3 = la = QLabel(_('&Replace:')) la.setBuddy(r) l.addWidget(la), l.addWidget(r)