diff --git a/src/calibre/gui2/tweak_book/editor/__init__.py b/src/calibre/gui2/tweak_book/editor/__init__.py index 0f4f09b9bd..36b910e334 100644 --- a/src/calibre/gui2/tweak_book/editor/__init__.py +++ b/src/calibre/gui2/tweak_book/editor/__init__.py @@ -35,6 +35,7 @@ def editor_from_syntax(syntax, parent=None): SYNTAX_PROPERTY = QTextCharFormat.UserProperty SPELL_PROPERTY = SYNTAX_PROPERTY + 1 +SPELL_LOCALE_PROPERTY = SPELL_PROPERTY + 1 class SyntaxTextCharFormat(QTextCharFormat): @@ -47,4 +48,16 @@ class SyntaxTextCharFormat(QTextCharFormat): id(self), self.foreground().color().name(), self.fontItalic(), self.fontWeight() >= QFont.DemiBold) __str__ = __repr__ +class StoreLocale(object): + __slots__ = ('enabled',) + + def __init__(self): + self.enabled = False + + def __enter__(self): + self.enabled = True + + def __exit__(self, *args): + self.enabled = False +store_locale = StoreLocale() diff --git a/src/calibre/gui2/tweak_book/editor/syntax/base.py b/src/calibre/gui2/tweak_book/editor/syntax/base.py index f8075bcbf0..b3fb9c47b1 100644 --- a/src/calibre/gui2/tweak_book/editor/syntax/base.py +++ b/src/calibre/gui2/tweak_book/editor/syntax/base.py @@ -129,28 +129,32 @@ class SyntaxHighlighter(object): try: block = doc.findBlock(position) while block.isValid() and (block.position() < end_pos or force_next_highlight): - ud, new_ud = self.get_user_data(block) - orig_state = ud.state - pblock = block.previous() - if pblock.isValid(): - start_state = pblock.userData() - if start_state is None: - start_state = self.user_data_factory().state - else: - start_state = start_state.state.copy() - else: - start_state = self.user_data_factory().state - ud.clear(state=start_state) # Ensure no stale user data lingers - formats = [] - for i, num, fmt in run_loop(ud, self.state_map, self.formats, unicode(block.text())): - if fmt is not None: - formats.append((i, num, fmt)) + formats, force_next_highlight = self.parse_single_block(block) self.apply_format_changes(doc, block, formats) - force_next_highlight = new_ud or ud.state != orig_state block = block.next() finally: doc.contentsChange.connect(self.reformat_blocks) + def parse_single_block(self, block): + ud, new_ud = self.get_user_data(block) + orig_state = ud.state + pblock = block.previous() + if pblock.isValid(): + start_state = pblock.userData() + if start_state is None: + start_state = self.user_data_factory().state + else: + start_state = start_state.state.copy() + else: + start_state = self.user_data_factory().state + ud.clear(state=start_state) # Ensure no stale user data lingers + formats = [] + for i, num, fmt in run_loop(ud, self.state_map, self.formats, unicode(block.text())): + if fmt is not None: + formats.append((i, num, fmt)) + force_next_highlight = new_ud or ud.state != orig_state + return formats, force_next_highlight + def reformat_block(self, block): if block.isValid(): self.reformat_blocks(block.position(), 0, 1) diff --git a/src/calibre/gui2/tweak_book/editor/syntax/html.py b/src/calibre/gui2/tweak_book/editor/syntax/html.py index f4f2c5a2a6..e21dd96156 100644 --- a/src/calibre/gui2/tweak_book/editor/syntax/html.py +++ b/src/calibre/gui2/tweak_book/editor/syntax/html.py @@ -16,7 +16,7 @@ from calibre.ebooks.oeb.polish.spell import html_spell_tags, xml_spell_tags from calibre.spell.dictionary import parse_lang_code from calibre.spell.break_iterator import split_into_words_and_positions from calibre.gui2.tweak_book import dictionaries, tprefs -from calibre.gui2.tweak_book.editor import SyntaxTextCharFormat, SPELL_PROPERTY +from calibre.gui2.tweak_book.editor import SyntaxTextCharFormat, SPELL_PROPERTY, SPELL_LOCALE_PROPERTY, store_locale from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter, run_loop from calibre.gui2.tweak_book.editor.syntax.css import ( create_formats as create_css_formats, state_map as css_state_map, CSSState, CSSUserData) @@ -235,9 +235,12 @@ def check_spelling(text, tpos, tlen, fmt, locale, sfmt): if recognized: split_ans.append((length, fmt)) else: - wsfmt = SyntaxTextCharFormat(sfmt) - wsfmt.setProperty(SPELL_PROPERTY, (ctext[start:ppos], locale)) - split_ans.append((length, wsfmt)) + if store_locale.enabled: + s = SyntaxTextCharFormat(sfmt) + s.setProperty(SPELL_LOCALE_PROPERTY, locale) + split_ans.append((length, s)) + else: + split_ans.append((length, sfmt)) if ppos < tlen: split_ans.append((tlen - ppos, fmt)) return split_ans @@ -486,6 +489,7 @@ def create_formats(highlighter, add_css=True): f.setFontWeight(QFont.Bold) if add_css: formats['css_sub_formats'] = create_css_formats(highlighter) + formats['spell'].setProperty(SPELL_PROPERTY, True) return formats diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 68b50982ca..46dabf2bb1 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -18,7 +18,7 @@ from PyQt4.Qt import ( from calibre import prepare_string_for_xml, xml_entity_to_unicode from calibre.gui2.tweak_book import tprefs, TOP -from calibre.gui2.tweak_book.editor import SYNTAX_PROPERTY, SPELL_PROPERTY +from calibre.gui2.tweak_book.editor import SYNTAX_PROPERTY, SPELL_PROPERTY, SPELL_LOCALE_PROPERTY, store_locale from calibre.gui2.tweak_book.editor.themes import get_theme, theme_color, theme_format from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter from calibre.gui2.tweak_book.editor.syntax.html import HTMLHighlighter, XMLHighlighter @@ -427,7 +427,7 @@ class TextEdit(PlainTextEdit): block = c.block() while block.isValid(): for r in block.layout().additionalFormats(): - if r.format.property(SPELL_PROPERTY).toPyObject() is not None: + if r.format.property(SPELL_PROPERTY).toBool(): if not from_cursor or block.position() + r.start + r.length > c.position(): c.setPosition(block.position() + r.start) c.setPosition(c.position() + r.length, c.KeepAnchor) @@ -568,26 +568,42 @@ class TextEdit(PlainTextEdit): return False return QPlainTextEdit.event(self, ev) + def text_for_range(self, block, r): + c = self.textCursor() + c.setPosition(block.position() + r.start) + c.setPosition(c.position() + r.length, c.KeepAnchor) + return unicode(c.selectedText()) + + def spellcheck_locale_for_cursor(self, c): + with store_locale: + formats = self.highlighter.parse_single_block(c.block())[0] + pos = c.positionInBlock() + for i, num, fmt in formats: + if i <= pos < i + num and fmt.property(SPELL_PROPERTY).toBool(): + return fmt.property(SPELL_LOCALE_PROPERTY).toPyObject() + def recheck_word(self, word, locale): c = self.textCursor() c.movePosition(c.Start) block = c.block() while block.isValid(): for r in block.layout().additionalFormats(): - x = r.format.property(SPELL_PROPERTY).toPyObject() - if x is not None and word == x[0]: + if r.format.property(SPELL_PROPERTY).toBool() and self.text_for_range(block, r) == word: self.highlighter.reformat_block(block) break block = block.next() # Tooltips {{{ - def syntax_format_for_cursor(self, cursor): + def syntax_range_for_cursor(self, cursor): if cursor.isNull(): return pos = cursor.positionInBlock() for r in cursor.block().layout().additionalFormats(): if r.start <= pos < r.start + r.length and r.format.property(SYNTAX_PROPERTY).toBool(): - return r.format + return r + + def syntax_format_for_cursor(self, cursor): + return getattr(self.syntax_range_for_cursor(cursor), 'format', None) def show_tooltip(self, ev): c = self.cursorForPosition(ev.pos()) diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 87f8da80d8..f4614f1531 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -383,10 +383,10 @@ class Editor(QMainWindow): m = QMenu(self) a = m.addAction c = self.editor.cursorForPosition(pos) - fmt = self.editor.syntax_format_for_cursor(c) - spell = fmt.property(SPELL_PROPERTY).toPyObject() if fmt is not None else None - if spell is not None: - word, locale = spell + r = self.editor.syntax_range_for_cursor(c) + if r.format.property(SPELL_PROPERTY).toBool(): + word = self.editor.text_for_range(c.block(), r) + locale = self.editor.spellcheck_locale_for_cursor(c) orig_pos = c.position() c.setPosition(orig_pos - utf16_length(word)) found = False