From 08a536ff48f826959ac7078c9b989a1d920348c4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 9 Jan 2015 14:10:19 +0530 Subject: [PATCH] Refactor for better code organization --- .../gui2/tweak_book/editor/snippets.py | 18 ++++- src/calibre/gui2/tweak_book/editor/text.py | 70 +------------------ src/calibre/gui2/tweak_book/search.py | 20 +----- src/calibre/gui2/tweak_book/widgets.py | 67 +++++++++++++++++- 4 files changed, 86 insertions(+), 89 deletions(-) diff --git a/src/calibre/gui2/tweak_book/editor/snippets.py b/src/calibre/gui2/tweak_book/editor/snippets.py index bde4b5bea8..67338e1045 100644 --- a/src/calibre/gui2/tweak_book/editor/snippets.py +++ b/src/calibre/gui2/tweak_book/editor/snippets.py @@ -19,7 +19,7 @@ from PyQt5.Qt import ( from calibre.gui2 import error_dialog from calibre.gui2.tweak_book.editor import all_text_syntaxes from calibre.gui2.tweak_book.editor.smarts.utils import get_text_before_cursor -from calibre.gui2.tweak_book.widgets import Dialog +from calibre.gui2.tweak_book.widgets import Dialog, PlainTextEdit from calibre.utils.config import JSONConfig from calibre.utils.icu import string_length as strlen @@ -413,6 +413,19 @@ class SnippetManager(QObject): # Config {{{ +class SnippetTextEdit(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 EditSnippet(QWidget): def __init__(self, parent=None): @@ -451,11 +464,10 @@ class EditSnippet(QWidget): add_row(_('&File types:'), t) t.setToolTip(_('Which file types this snippet should be active in')) - from calibre.gui2.tweak_book.search import TextEdit self.frame = f = QFrame(self) f.setFrameShape(f.HLine) add_row(f) - self.test = d = TextEdit('', self) + self.test = d = SnippetTextEdit('', self) d.snippet_manager.snip_func = self.snip_func d.setToolTip(_('You can test your snippet here')) d.setMaximumHeight(t.maximumHeight() + 15) diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index be0d9f63e0..6ccd00c359 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -13,8 +13,8 @@ from future_builtins import map import regex from PyQt5.Qt import ( QPlainTextEdit, QFontDatabase, QToolTip, QPalette, QFont, QKeySequence, - QTextEdit, QTextFormat, QWidget, QSize, QPainter, Qt, QRect, pyqtSlot, - QApplication, QMimeData, QColor, QColorDialog, QTimer, pyqtSignal, QT_VERSION) + QTextEdit, QTextFormat, QWidget, QSize, QPainter, Qt, QRect, QColor, + QColorDialog, QTimer, pyqtSignal, QT_VERSION) from calibre import prepare_string_for_xml from calibre.constants import isosx @@ -26,14 +26,11 @@ from calibre.gui2.tweak_book.editor.themes import get_theme, theme_color, theme_ from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter from calibre.gui2.tweak_book.editor.smarts import NullSmarts from calibre.gui2.tweak_book.editor.snippets import SnippetManager +from calibre.gui2.tweak_book.widgets import PlainTextEdit, PARAGRAPH_SEPARATOR from calibre.spell.break_iterator import index_of from calibre.utils.icu import safe_chr, string_length, capitalize, upper, lower, swapcase from calibre.utils.titlecase import titlecase -PARAGRAPH_SEPARATOR = '\u2029' - -def selected_text_from_cursor(cursor): - return unicodedata.normalize('NFC', unicode(cursor.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0')) def get_highlighter(syntax): if syntax: @@ -76,67 +73,6 @@ class LineNumbers(QWidget): # {{{ self.parent().paint_line_numbers(ev) # }}} -class PlainTextEdit(QPlainTextEdit): - - ''' A class that overrides some methods from QPlainTextEdit to fix handling - of the nbsp unicode character. ''' - - 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 - # space, so we re-implement it using QTextCursor, which does not do - # that - c = self.textCursor() - c.clearSelection() - c.movePosition(c.Start) - c.movePosition(c.End, c.KeepAnchor) - ans = c.selectedText().replace(PARAGRAPH_SEPARATOR, '\n') - # QTextCursor pads the return value of selectedText with null bytes if - # non BMP characters such as 0x1f431 are present. - return ans.rstrip('\0') - - @pyqtSlot() - def copy(self): - # Workaround Qt replacing nbsp with normal spaces on copy - c = self.textCursor() - if not c.hasSelection(): - return - md = QMimeData() - md.setText(self.selected_text) - QApplication.clipboard().setMimeData(md) - - @pyqtSlot() - def cut(self): - # Workaround Qt replacing nbsp with normal spaces on copy - self.copy() - self.textCursor().removeSelectedText() - - def selected_text_from_cursor(self, cursor): - return selected_text_from_cursor(cursor) - - @property - def selected_text(self): - return self.selected_text_from_cursor(self.textCursor()) - - def selection_changed(self): - # Workaround Qt replacing nbsp with normal spaces on copy - clipboard = QApplication.clipboard() - if clipboard.supportsSelection() and self.textCursor().hasSelection(): - md = QMimeData() - md.setText(self.selected_text) - clipboard.setMimeData(md, clipboard.Selection) - - def event(self, ev): - if ev.type() == ev.ShortcutOverride and ev in (QKeySequence.Copy, QKeySequence.Cut): - ev.accept() - (self.copy if ev == QKeySequence.Copy else self.cut)() - return True - return QPlainTextEdit.event(self, ev) - class TextEdit(PlainTextEdit): link_clicked = pyqtSignal(object) diff --git a/src/calibre/gui2/tweak_book/search.py b/src/calibre/gui2/tweak_book/search.py index fe8b4f15f1..0c559171cc 100644 --- a/src/calibre/gui2/tweak_book/search.py +++ b/src/calibre/gui2/tweak_book/search.py @@ -26,8 +26,7 @@ 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.gui2.tweak_book.editor.snippets import find_matching_snip, parse_template, string_length, SnippetTextEdit from calibre.utils.icu import primary_contains @@ -53,19 +52,6 @@ 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): @@ -594,12 +580,12 @@ class EditSearch(QFrame): # {{{ h.addWidget(la), h.addWidget(n) l.addLayout(h) - self.find = f = TextEdit('', self) + self.find = f = SnippetTextEdit('', self) self.la2 = la = QLabel(_('&Find:')) la.setBuddy(f) l.addWidget(la), l.addWidget(f) - self.replace = r = TextEdit('', self) + self.replace = r = SnippetTextEdit('', self) self.la3 = la = QLabel(_('&Replace:')) la.setBuddy(r) l.addWidget(la), l.addWidget(r) diff --git a/src/calibre/gui2/tweak_book/widgets.py b/src/calibre/gui2/tweak_book/widgets.py index dce7f3a6ab..0c0bd04dee 100644 --- a/src/calibre/gui2/tweak_book/widgets.py +++ b/src/calibre/gui2/tweak_book/widgets.py @@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal ' -import os, textwrap +import os, textwrap, unicodedata from itertools import izip from collections import OrderedDict @@ -16,7 +16,7 @@ from PyQt5.Qt import ( QPainter, QStaticText, pyqtSignal, QTextOption, QAbstractListModel, QModelIndex, QStyledItemDelegate, QStyle, QCheckBox, QListView, QTextDocument, QSize, QComboBox, QFrame, QCursor, QGroupBox, QSplitter, - QPixmap, QRect) + QPixmap, QRect, QPlainTextEdit, pyqtSlot, QMimeData, QKeySequence) from calibre import prepare_string_for_xml, human_readable from calibre.ebooks.oeb.polish.utils import lead_text, guess_type @@ -28,6 +28,7 @@ from calibre.utils.matcher import get_char, Matcher from calibre.gui2.complete2 import EditWithComplete ROOT = QModelIndex() +PARAGRAPH_SEPARATOR = '\u2029' class BusyCursor(object): @@ -1111,6 +1112,68 @@ class AddCover(Dialog): # }}} +class PlainTextEdit(QPlainTextEdit): # {{{ + + ''' A class that overrides some methods from QPlainTextEdit to fix handling + of the nbsp unicode character. ''' + + 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 + # space, so we re-implement it using QTextCursor, which does not do + # that + c = self.textCursor() + c.clearSelection() + c.movePosition(c.Start) + c.movePosition(c.End, c.KeepAnchor) + ans = c.selectedText().replace(PARAGRAPH_SEPARATOR, '\n') + # QTextCursor pads the return value of selectedText with null bytes if + # non BMP characters such as 0x1f431 are present. + return ans.rstrip('\0') + + @pyqtSlot() + def copy(self): + # Workaround Qt replacing nbsp with normal spaces on copy + c = self.textCursor() + if not c.hasSelection(): + return + md = QMimeData() + md.setText(self.selected_text) + QApplication.clipboard().setMimeData(md) + + @pyqtSlot() + def cut(self): + # Workaround Qt replacing nbsp with normal spaces on copy + self.copy() + self.textCursor().removeSelectedText() + + def selected_text_from_cursor(self, cursor): + return unicodedata.normalize('NFC', unicode(cursor.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n').rstrip('\0')) + + @property + def selected_text(self): + return self.selected_text_from_cursor(self.textCursor()) + + def selection_changed(self): + # Workaround Qt replacing nbsp with normal spaces on copy + clipboard = QApplication.clipboard() + if clipboard.supportsSelection() and self.textCursor().hasSelection(): + md = QMimeData() + md.setText(self.selected_text) + clipboard.setMimeData(md, clipboard.Selection) + + def event(self, ev): + if ev.type() == ev.ShortcutOverride and ev in (QKeySequence.Copy, QKeySequence.Cut): + ev.accept() + (self.copy if ev == QKeySequence.Copy else self.cut)() + return True + return QPlainTextEdit.event(self, ev) +# }}} + if __name__ == '__main__': app = QApplication([]) AddCover.test()