Edit book: Spell check: Add an exclude files button

This commit is contained in:
Kovid Goyal 2021-12-16 21:26:44 +05:30
parent a507bb01ce
commit d350a121d8
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 67 additions and 5 deletions

View File

@ -680,8 +680,8 @@ common in your book and to run a simple search and replace on individual words.
.. note:: .. note::
To exclude an individual file from being spell checked when running the To exclude an individual file from being spell checked when running the
spell check tool, you can add the following comment just under the first spell check tool, you can use the :guilabel:`Exclude files` button or
opening tag in the file:: and the following comment just under the opening tag in the file::
<!-- calibre-no-spell-check --> <!-- calibre-no-spell-check -->

View File

@ -283,12 +283,12 @@ def root_is_excluded_from_spell_check(root):
return False return False
def get_all_words(container, book_locale, get_word_count=False): def get_all_words(container, book_locale, get_word_count=False, excluded_files=()):
words = defaultdict(list) words = defaultdict(list)
words[None] = 0 words[None] = 0
file_names, ncx_toc = get_checkable_file_names(container) file_names, ncx_toc = get_checkable_file_names(container)
for file_name in file_names: for file_name in file_names:
if not container.exists(file_name): if not container.exists(file_name) or file_name in excluded_files:
continue continue
root = container.parsed(file_name) root = container.parsed(file_name)
if root_is_excluded_from_spell_check(root): if root_is_excluded_from_spell_check(root):

View File

@ -360,6 +360,7 @@ class Boss(QObject):
if cn: if cn:
self.save_manager.clear_notify_data() self.save_manager.clear_notify_data()
self.gui.check_book.clear_at_startup() self.gui.check_book.clear_at_startup()
self.gui.spell_check.clear_caches()
dictionaries.clear_ignored(), dictionaries.clear_caches() dictionaries.clear_ignored(), dictionaries.clear_caches()
parse_worker.clear() parse_worker.clear()
container = job.result container = job.result

View File

@ -21,6 +21,7 @@ from qt.core import (
from threading import Thread from threading import Thread
from calibre.constants import __appname__ from calibre.constants import __appname__
from calibre.ebooks.oeb.base import OEB_DOCS, NCX_MIME, OPF_MIME
from calibre.ebooks.oeb.polish.spell import ( from calibre.ebooks.oeb.polish.spell import (
get_all_words, get_checkable_file_names, merge_locations, replace_word, get_all_words, get_checkable_file_names, merge_locations, replace_word,
undo_replace_word undo_replace_word
@ -912,6 +913,41 @@ class WordsView(QTableView):
return self.model().word_for_row(self.currentIndex().row()) return self.model().word_for_row(self.currentIndex().row())
class ManageExcludedFiles(Dialog):
def __init__(self, parent, excluded_files):
self.orig_excluded_files = frozenset(excluded_files)
super().__init__(_('Exclude files from spell check'), 'spell-check-exclude-files2', parent)
def sizeHint(self):
return QSize(500, 600)
def setup_ui(self):
self.la = la = QLabel(_(
'Choose the files to exclude below. In addition to this list any file'
' can be permanently excluded by adding the comment {} just under its opening tag.').format(
'<!-- calibre-no-spell-check -->'))
la.setWordWrap(True)
la.setTextFormat(Qt.TextFormat.PlainText)
self.l = l = QVBoxLayout(self)
l.addWidget(la)
self.files = QListWidget(self)
self.files.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
cc = current_container()
for name, mt in cc.mime_map.items():
if mt in OEB_DOCS or mt in (NCX_MIME, OPF_MIME):
i = QListWidgetItem(self.files)
i.setText(name)
if name in self.orig_excluded_files:
i.setSelected(True)
l.addWidget(self.files)
l.addWidget(self.bb)
@property
def excluded_files(self):
return {item.text() for item in self.files.selectedItems()}
class SpellCheck(Dialog): class SpellCheck(Dialog):
work_finished = pyqtSignal(object, object, object) work_finished = pyqtSignal(object, object, object)
@ -929,6 +965,7 @@ class SpellCheck(Dialog):
self.current_word_changed_timer = t = QTimer() self.current_word_changed_timer = t = QTimer()
t.timeout.connect(self.do_current_word_changed) t.timeout.connect(self.do_current_word_changed)
t.setSingleShot(True), t.setInterval(100) t.setSingleShot(True), t.setInterval(100)
self.excluded_files = set()
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.ConnectionType.QueuedConnection) self.work_finished.connect(self.work_done, type=Qt.ConnectionType.QueuedConnection)
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False) self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
@ -952,6 +989,11 @@ class SpellCheck(Dialog):
b.setToolTip('<p>' + _('Undo the last spell check word replacement, if any')) b.setToolTip('<p>' + _('Undo the last spell check word replacement, if any'))
b.setIcon(QIcon(I('edit-undo.png'))) b.setIcon(QIcon(I('edit-undo.png')))
b.clicked.connect(self.undo_last_change) b.clicked.connect(self.undo_last_change)
b = self.exclude_button = self.bb.addButton('', QDialogButtonBox.ButtonRole.ActionRole)
b.setToolTip('<p>' + _('Exclude some files in the book from spell check'))
b.setIcon(QIcon(I('chapters.png')))
b.clicked.connect(self.change_excluded_files)
self.update_exclude_button()
self.progress = p = QWidget(self) self.progress = p = QWidget(self)
s.addWidget(p) s.addWidget(p)
@ -1062,6 +1104,25 @@ class SpellCheck(Dialog):
return return
return Dialog.keyPressEvent(self, ev) return Dialog.keyPressEvent(self, ev)
def change_excluded_files(self):
d = ManageExcludedFiles(self, self.excluded_files)
if d.exec_() == QDialog.DialogCode.Accepted:
new = d.excluded_files
if new != self.excluded_files:
self.excluded_files = new
self.update_exclude_button()
self.refresh()
def clear_caches(self):
self.excluded_files = set()
self.update_exclude_button()
def update_exclude_button(self):
t = _('E&xclude files')
if self.excluded_files:
t += f' ({len(self.excluded_files)})'
self.exclude_button.setText(t)
def sort_type_changed(self): def sort_type_changed(self):
tprefs['spell_check_case_sensitive_sort'] = bool(self.case_sensitive_sort.isChecked()) tprefs['spell_check_case_sensitive_sort'] = bool(self.case_sensitive_sort.isChecked())
if self.words_model.sort_on[0] == 0: if self.words_model.sort_on[0] == 0:
@ -1257,7 +1318,7 @@ class SpellCheck(Dialog):
def get_words(self, change_request=None): def get_words(self, change_request=None):
try: try:
words = get_all_words(current_container(), dictionaries.default_locale) words = get_all_words(current_container(), dictionaries.default_locale, excluded_files=self.excluded_files)
spell_map = {w:dictionaries.recognized(*w) for w in words} spell_map = {w:dictionaries.recognized(*w) for w in words}
except: except:
import traceback import traceback