diff --git a/src/calibre/gui2/tweak_book/editor/smart/__init__.py b/src/calibre/gui2/tweak_book/editor/smart/__init__.py index 135ac12e52..3ef356517a 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/__init__.py +++ b/src/calibre/gui2/tweak_book/editor/smart/__init__.py @@ -28,3 +28,6 @@ class NullSmarts(object): def get_inner_HTML(self, editor): return None + + def handle_key_press(self, ev, editor): + return False diff --git a/src/calibre/gui2/tweak_book/editor/smart/html.py b/src/calibre/gui2/tweak_book/editor/smart/html.py index 4e3b5b62d9..2e445d8ed0 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/html.py +++ b/src/calibre/gui2/tweak_book/editor/smart/html.py @@ -10,12 +10,13 @@ import sys, re from operator import itemgetter from cssutils import parseStyle -from PyQt5.Qt import QTextEdit +from PyQt5.Qt import QTextEdit, Qt -from calibre import prepare_string_for_xml +from calibre import prepare_string_for_xml, xml_entity_to_unicode from calibre.gui2 import error_dialog from calibre.gui2.tweak_book.editor.syntax.html import ATTR_NAME, ATTR_END, ATTR_START, ATTR_VALUE from calibre.utils.icu import utf16_length +from calibre.gui2.tweak_book import tprefs from calibre.gui2.tweak_book.editor.smart import NullSmarts get_offset = itemgetter(0) @@ -273,6 +274,7 @@ class HTMLSmarts(NullSmarts): def __init__(self, *args, **kwargs): NullSmarts.__init__(self, *args, **kwargs) self.last_matched_tag = None + self.entity_pat = re.compile(r'&(#{0,1}[a-zA-Z0-9]{1,8});$') def get_extra_selections(self, editor): ans = [] @@ -529,3 +531,24 @@ class HTMLSmarts(NullSmarts): for tag in reversed(tags): set_style_property(tag, 'text-align', value, editor) + def handle_key_press(self, ev, editor): + text = ev.text() + key = ev.key() + if tprefs['replace_entities_as_typed'] and (key == Qt.Key_Semicolon or ';' in text): + self.replace_possible_entity(editor) + return True + return False + + def replace_possible_entity(self, editor): + c = editor.textCursor() + c.insertText(';') + c.setPosition(c.position() - min(c.positionInBlock(), 10), c.KeepAnchor) + text = editor.selected_text_from_cursor(c) + m = self.entity_pat.search(text) + if m is not None: + ent = m.group() + repl = xml_entity_to_unicode(m) + if repl != ent: + c.setPosition(c.position() + m.start(), c.KeepAnchor) + c.insertText(repl) + editor.setTextCursor(c) diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 7f90600b2f..f2cb315d5f 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -16,7 +16,7 @@ from PyQt5.Qt import ( QTextEdit, QTextFormat, QWidget, QSize, QPainter, Qt, QRect, pyqtSlot, QApplication, QMimeData, QColor, QColorDialog, QTimer, pyqtSignal, QT_VERSION) -from calibre import prepare_string_for_xml, xml_entity_to_unicode +from calibre import prepare_string_for_xml from calibre.constants import isosx from calibre.gui2.tweak_book import tprefs, TOP from calibre.gui2.tweak_book.editor import ( @@ -33,7 +33,6 @@ from calibre.utils.icu import safe_chr, string_length, capitalize, upper, lower, from calibre.utils.titlecase import titlecase PARAGRAPH_SEPARATOR = '\u2029' -entity_pat = re.compile(r'&(#{0,1}[a-zA-Z0-9]{1,8});') def get_highlighter(syntax): ans = {'html':HTMLHighlighter, 'css':CSSHighlighter, 'xml':XMLHighlighter}.get(syntax, SyntaxHighlighter) @@ -762,9 +761,9 @@ class TextEdit(PlainTextEdit): # https://bugreports.qt-project.org/browse/QTBUG-36281 ev.setAccepted(False) return + if self.smarts.handle_key_press(ev, self): + return QPlainTextEdit.keyPressEvent(self, ev) - if (ev.key() == Qt.Key_Semicolon or ';' in unicode(ev.text())) and tprefs['replace_entities_as_typed'] and self.syntax == 'html': - self.replace_possible_entity() def replace_possible_unicode_sequence(self): c = self.textCursor() @@ -789,19 +788,6 @@ class TextEdit(PlainTextEdit): c.insertText(safe_chr(num)) return True - def replace_possible_entity(self): - c = self.textCursor() - c.setPosition(c.position() - min(c.positionInBlock(), 10), c.KeepAnchor) - text = unicode(c.selectedText()).rstrip('\0') - m = entity_pat.search(text) - if m is None: - return - ent = m.group() - repl = xml_entity_to_unicode(m) - if repl != ent: - c.setPosition(c.position() + m.start(), c.KeepAnchor) - c.insertText(repl) - def select_all(self): c = self.textCursor() c.clearSelection()