Implement ignoring of words

This commit is contained in:
Kovid Goyal 2014-04-14 12:24:15 +05:30
parent f991fb8ad0
commit 50f4dd503f
3 changed files with 102 additions and 20 deletions

View File

@ -232,6 +232,7 @@ class Boss(QObject):
det_msg=job.traceback, show=True) det_msg=job.traceback, show=True)
if cn: if cn:
self.save_manager.clear_notify_data() self.save_manager.clear_notify_data()
dictionaries.clear_ignored(), dictionaries.clear_caches()
parse_worker.clear() parse_worker.clear()
container = job.result container = job.result
set_current_container(container) set_current_container(container)

View File

@ -304,10 +304,10 @@ class WordsModel(QAbstractTableModel):
def __init__(self, parent=None): def __init__(self, parent=None):
QAbstractTableModel.__init__(self, parent) QAbstractTableModel.__init__(self, parent)
self.counts = (0, 0) self.counts = (0, 0)
self.words = {} self.words = {} # Map of (word, locale) to location data for the word
self.spell_map = {} self.spell_map = {} # Map of (word, locale) to dictionaries.recognized(word, locale)
self.sort_on = (0, False) self.sort_on = (0, False)
self.items = [] self.items = [] # The currently displayed items
self.filter_expression = None self.filter_expression = None
self.show_only_misspelt = True self.show_only_misspelt = True
self.headers = (_('Word'), _('Count'), _('Language'), _('Misspelled?')) self.headers = (_('Word'), _('Count'), _('Language'), _('Misspelled?'))
@ -371,8 +371,7 @@ class WordsModel(QAbstractTableModel):
self.do_sort() self.do_sort()
self.endResetModel() self.endResetModel()
def do_sort(self): def sort_key(self, col):
col, reverse = self.sort_on
if col == 0: if col == 0:
def key(w): def key(w):
return primary_sort_key(w[0]) return primary_sort_key(w[0])
@ -385,7 +384,11 @@ class WordsModel(QAbstractTableModel):
return (calibre_langcode_to_name(locale.langcode), locale.countrycode) return (calibre_langcode_to_name(locale.langcode), locale.countrycode)
else: else:
key = self.spell_map.get key = self.spell_map.get
self.items.sort(key=key, reverse=reverse) return key
def do_sort(self):
col, reverse = self.sort_on
self.items.sort(key=self.sort_key(col), reverse=reverse)
def set_data(self, words, spell_map): def set_data(self, words, spell_map):
self.words, self.spell_map = words, spell_map self.words, self.spell_map = words, spell_map
@ -395,14 +398,38 @@ class WordsModel(QAbstractTableModel):
self.counts = (len([None for w, recognized in spell_map.iteritems() if not recognized]), len(self.words)) self.counts = (len([None for w, recognized in spell_map.iteritems() if not recognized]), len(self.words))
self.endResetModel() self.endResetModel()
def filter_item(self, x):
if self.show_only_misspelt and self.spell_map[x]:
return False
if self.filter_expression is not None and not primary_contains(self.filter_expression, x[0]):
return False
return True
def do_filter(self): def do_filter(self):
def filter_item(x): self.items = filter(self.filter_item, self.words)
if self.show_only_misspelt and self.spell_map[x]:
return False def toggle_ignored(self, row):
if self.filter_expression is not None and not primary_contains(self.filter_expression, x[0]): w = self.word_for_row(row)
return False if w is not None:
return True ignored = dictionaries.is_word_ignored(*w)
self.items = filter(filter_item, self.words) (dictionaries.unignore_word if ignored else dictionaries.ignore_word)(*w)
self.spell_map[w] = dictionaries.recognized(*w)
self.update_word(w)
def update_word(self, w):
should_be_filtered = not self.filter_item(w)
row = self.row_for_word(w)
if should_be_filtered and row != -1:
self.beginRemoveRows(QModelIndex(), row, row)
del self.items[row]
self.endRemoveRows()
elif not should_be_filtered and row == -1:
self.items.append(w)
self.do_sort()
row = self.row_for_word(w)
self.beginInsertRows(QModelIndex(), row, row)
self.endInsertRows()
self.dataChanged.emit(self.index(row, 3), self.index(row, 3))
def word_for_row(self, row): def word_for_row(self, row):
try: try:
@ -468,6 +495,7 @@ class SpellCheck(Dialog):
m.h2 = h = QHBoxLayout() m.h2 = h = QHBoxLayout()
l.addLayout(h) l.addLayout(h)
self.words_view = w = QTableView(m) self.words_view = w = QTableView(m)
w.currentChanged = self.current_word_changed
state = tprefs.get('spell-check-table-state', None) state = tprefs.get('spell-check-table-state', None)
hh = self.words_view.horizontalHeader() hh = self.words_view.horizontalHeader()
w.setSortingEnabled(True), w.setShowGrid(False), w.setAlternatingRowColors(True) w.setSortingEnabled(True), w.setShowGrid(False), w.setAlternatingRowColors(True)
@ -476,19 +504,54 @@ class SpellCheck(Dialog):
h.addWidget(w) h.addWidget(w)
self.words_model = m = WordsModel(self) self.words_model = m = WordsModel(self)
w.setModel(m) w.setModel(m)
m.dataChanged.connect(self.current_word_changed)
m.modelReset.connect(self.current_word_changed)
if state is not None: if state is not None:
hh.restoreState(state) hh.restoreState(state)
# Sort by the restored state, if any # Sort by the restored state, if any
w.sortByColumn(hh.sortIndicatorSection(), hh.sortIndicatorOrder()) w.sortByColumn(hh.sortIndicatorSection(), hh.sortIndicatorOrder())
m.show_only_misspelt = hh.isSectionHidden(3) m.show_only_misspelt = hh.isSectionHidden(3)
self.ignore_button = b = QPushButton(_('&Ignore'))
b.ign_text, b.unign_text = unicode(b.text()), _('Un&ignore')
b.clicked.connect(self.toggle_ignore)
l = QVBoxLayout()
l.addStrut(250)
h.addLayout(l)
l.addWidget(b)
hh.setSectionHidden(3, m.show_only_misspelt) hh.setSectionHidden(3, m.show_only_misspelt)
self.show_only_misspelled = om = QCheckBox(_('Show only misspelled words')) self.show_only_misspelled = om = QCheckBox(_('Show only misspelled words'))
om.setChecked(m.show_only_misspelt) om.setChecked(m.show_only_misspelt)
om.stateChanged.connect(self.update_show_only_misspelt) om.stateChanged.connect(self.update_show_only_misspelt)
self.hb = h = QHBoxLayout() self.hb = h = QHBoxLayout()
self.summary = s = QLabel('') self.summary = s = QLabel('')
l.addLayout(h), h.addWidget(s), h.addWidget(om), h.addStretch(10) self.main.l.addLayout(h), h.addWidget(s), h.addWidget(om), h.addStretch(10)
def current_word_changed(self, *args):
ignored = recognized = False
current = self.words_view.currentIndex()
current_word = ''
if current.isValid():
row = current.row()
w = self.words_model.word_for_row(row)
if w is not None:
ignored = dictionaries.is_word_ignored(*w)
recognized = self.words_model.spell_map[w]
current_word = w[0]
try:
b = self.ignore_button
except AttributeError:
return
prefix = b.unign_text if ignored else b.ign_text
b.setText(prefix + ' ' + current_word)
b.setEnabled(current.isValid() and (ignored or not recognized))
def toggle_ignore(self):
current = self.words_view.currentIndex()
if current.isValid():
self.words_model.toggle_ignored(current.row())
def update_show_only_misspelt(self): def update_show_only_misspelt(self):
m = self.words_model m = self.words_model

View File

@ -162,6 +162,7 @@ class Dictionaries(object):
def __init__(self): def __init__(self):
self.dictionaries = {} self.dictionaries = {}
self.word_cache = {} self.word_cache = {}
self.ignored_words = set()
try: try:
self.default_locale = parse_lang_code(get_lang()) self.default_locale = parse_lang_code(get_lang())
except ValueError: except ValueError:
@ -171,6 +172,9 @@ class Dictionaries(object):
def clear_caches(self): def clear_caches(self):
self.dictionaries.clear(), self.word_cache.clear() self.dictionaries.clear(), self.word_cache.clear()
def clear_ignored(self):
self.ignored_words.clear()
def dictionary_for_locale(self, locale): def dictionary_for_locale(self, locale):
ans = self.dictionaries.get(locale, not_present) ans = self.dictionaries.get(locale, not_present)
if ans is not_present: if ans is not_present:
@ -180,6 +184,17 @@ class Dictionaries(object):
self.dictionaries[locale] = ans self.dictionaries[locale] = ans
return ans return ans
def ignore_word(self, word, locale):
self.ignored_words.add((word, locale))
self.word_cache[(word, locale)] = True
def unignore_word(self, word, locale):
self.ignored_words.discard((word, locale))
self.word_cache.pop((word, locale), None)
def is_word_ignored(self, word, locale):
return (word, locale) in self.ignored_words
def recognized(self, word, locale=None): def recognized(self, word, locale=None):
locale = locale or self.default_locale locale = locale or self.default_locale
if not isinstance(locale, DictionaryLocale): if not isinstance(locale, DictionaryLocale):
@ -188,12 +203,15 @@ class Dictionaries(object):
ans = self.word_cache.get(key, None) ans = self.word_cache.get(key, None)
if ans is None: if ans is None:
ans = False ans = False
d = self.dictionary_for_locale(locale) if key in self.ignored_words:
if d is not None: ans = True
try: else:
ans = d.obj.recognized(word) d = self.dictionary_for_locale(locale)
except ValueError: if d is not None:
pass try:
ans = d.obj.recognized(word)
except ValueError:
pass
self.word_cache[key] = ans self.word_cache[key] = ans
return ans return ans