Edit Book: Spell check dialog: Add a button to undo the last spelling change

This commit is contained in:
Kovid Goyal 2015-10-19 12:43:50 +05:30
parent 155c35f765
commit 02b8f775cb
2 changed files with 32 additions and 6 deletions

View File

@ -221,7 +221,7 @@ def replace(text, original_word, new_word, lang):
text = text[:idx] + new_word + text[idx+len(original_word):] text = text[:idx] + new_word + text[idx+len(original_word):]
return text, bool(indices) return text, bool(indices)
def replace_word(container, new_word, locations, locale): def replace_word(container, new_word, locations, locale, undo_cache=None):
changed = set() changed = set()
for loc in locations: for loc in locations:
node = loc.location_node node = loc.location_node
@ -231,16 +231,26 @@ def replace_word(container, new_word, locations, locale):
else: else:
text = getattr(node, attr) text = getattr(node, attr)
replacement = loc.elided_prefix + new_word replacement = loc.elided_prefix + new_word
text, replaced = replace(text, loc.original_word, replacement, locale.langcode) rtext, replaced = replace(text, loc.original_word, replacement, locale.langcode)
if replaced: if replaced:
if undo_cache is not None:
undo_cache[(loc.file_name, node, is_attr, attr)] = text
if is_attr: if is_attr:
node.set(attr, text) node.set(attr, rtext)
else: else:
setattr(node, attr, text) setattr(node, attr, rtext)
container.replace(loc.file_name, node.getroottree().getroot()) container.replace(loc.file_name, node.getroottree().getroot())
changed.add(loc.file_name) changed.add(loc.file_name)
return changed return changed
def undo_replace_word(container, undo_cache):
changed = set()
for (file_name, node, is_attr, attr), text in undo_cache.iteritems():
node.set(attr, text) if is_attr else setattr(node, attr, text)
container.replace(file_name, node.getroottree().getroot())
changed.add(file_name)
return changed
if __name__ == '__main__': if __name__ == '__main__':
import pprint import pprint
from calibre.gui2.tweak_book import set_book_locale, dictionaries from calibre.gui2.tweak_book import set_book_locale, dictionaries

View File

@ -20,7 +20,7 @@ from PyQt5.Qt import (
QT_VERSION_STR) QT_VERSION_STR)
from calibre.constants import __appname__, plugins from calibre.constants import __appname__, plugins
from calibre.ebooks.oeb.polish.spell import replace_word, get_all_words, merge_locations, get_checkable_file_names from calibre.ebooks.oeb.polish.spell import replace_word, get_all_words, merge_locations, get_checkable_file_names, undo_replace_word
from calibre.gui2 import choose_files, error_dialog from calibre.gui2 import choose_files, error_dialog
from calibre.gui2.complete2 import LineEdit from calibre.gui2.complete2 import LineEdit
from calibre.gui2.languages import LanguagesEdit from calibre.gui2.languages import LanguagesEdit
@ -883,6 +883,7 @@ class SpellCheck(Dialog):
Dialog.__init__(self, _('Check spelling'), 'spell-check', parent) Dialog.__init__(self, _('Check spelling'), 'spell-check', parent)
self.work_finished.connect(self.work_done, type=Qt.QueuedConnection) self.work_finished.connect(self.work_done, type=Qt.QueuedConnection)
self.setAttribute(Qt.WA_DeleteOnClose, False) self.setAttribute(Qt.WA_DeleteOnClose, False)
self.undo_cache = {}
def setup_ui(self): def setup_ui(self):
self.state_name = 'spell-check-table-state-' + QT_VERSION_STR.partition('.')[0] self.state_name = 'spell-check-table-state-' + QT_VERSION_STR.partition('.')[0]
@ -899,6 +900,10 @@ class SpellCheck(Dialog):
b.setToolTip('<p>' + _('Re-scan the book for words, useful if you have edited the book since opening this dialog')) b.setToolTip('<p>' + _('Re-scan the book for words, useful if you have edited the book since opening this dialog'))
b.setIcon(QIcon(I('view-refresh.png'))) b.setIcon(QIcon(I('view-refresh.png')))
b.clicked.connect(partial(self.refresh, change_request=None)) b.clicked.connect(partial(self.refresh, change_request=None))
b = self.bb.addButton(_('&Undo last change'), self.bb.ActionRole)
b.setToolTip('<p>' + _('Undo the last spell check word replacement, if any'))
b.setIcon(QIcon(I('edit-undo.png')))
b.clicked.connect(self.undo_last_change)
self.progress = p = QWidget(self) self.progress = p = QWidget(self)
s.addWidget(p) s.addWidget(p)
@ -1110,7 +1115,8 @@ class SpellCheck(Dialog):
self.change_requested.emit(w, new_word) self.change_requested.emit(w, new_word)
def do_change_word(self, w, new_word): def do_change_word(self, w, new_word):
changed_files = replace_word(current_container(), new_word, self.words_model.words[w], w[1]) self.undo_cache.clear()
changed_files = replace_word(current_container(), new_word, self.words_model.words[w], w[1], undo_cache=self.undo_cache)
if changed_files: if changed_files:
self.word_replaced.emit(changed_files) self.word_replaced.emit(changed_files)
w = self.words_model.replace_word(w, new_word) w = self.words_model.replace_word(w, new_word)
@ -1118,6 +1124,16 @@ class SpellCheck(Dialog):
if row > -1: if row > -1:
self.words_view.highlight_row(row) self.words_view.highlight_row(row)
def undo_last_change(self):
if not self.undo_cache:
return error_dialog(self, _('No changed word'), _(
'There is no spelling replacement to undo'), show=True)
changed_files = undo_replace_word(current_container(), self.undo_cache)
self.undo_cache.clear()
if changed_files:
self.word_replaced.emit(changed_files)
self.refresh()
def toggle_ignore(self): def toggle_ignore(self):
current = self.words_view.currentIndex() current = self.words_view.currentIndex()
if current.isValid(): if current.isValid():