mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement adding and removing of words from user dictionaries
This commit is contained in:
parent
50f4dd503f
commit
03832a6070
@ -74,6 +74,7 @@ TOP = object()
|
||||
dictionaries = Dictionaries()
|
||||
|
||||
def set_book_locale(lang):
|
||||
dictionaries.initialize()
|
||||
try:
|
||||
dictionaries.default_locale = parse_lang_code(lang)
|
||||
if dictionaries.default_locale.langcode == 'und':
|
||||
|
@ -14,7 +14,8 @@ from PyQt4.Qt import (
|
||||
QGridLayout, QApplication, QTreeWidget, QTreeWidgetItem, Qt, QFont, QSize,
|
||||
QStackedLayout, QLabel, QVBoxLayout, QVariant, QWidget, QPushButton, QIcon,
|
||||
QDialogButtonBox, QLineEdit, QDialog, QToolButton, QFormLayout, QHBoxLayout,
|
||||
pyqtSignal, QAbstractTableModel, QModelIndex, QTimer, QTableView, QCheckBox)
|
||||
pyqtSignal, QAbstractTableModel, QModelIndex, QTimer, QTableView, QCheckBox,
|
||||
QComboBox)
|
||||
|
||||
from calibre.constants import __appname__
|
||||
from calibre.gui2 import choose_files, error_dialog
|
||||
@ -416,6 +417,20 @@ class WordsModel(QAbstractTableModel):
|
||||
self.spell_map[w] = dictionaries.recognized(*w)
|
||||
self.update_word(w)
|
||||
|
||||
def add_word(self, row, udname):
|
||||
w = self.word_for_row(row)
|
||||
if w is not None:
|
||||
if dictionaries.add_to_user_dictionary(udname, *w):
|
||||
self.spell_map[w] = dictionaries.recognized(*w)
|
||||
self.update_word(w)
|
||||
|
||||
def remove_word(self, row):
|
||||
w = self.word_for_row(row)
|
||||
if w is not None:
|
||||
if dictionaries.remove_from_user_dictionaries(*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)
|
||||
@ -518,7 +533,20 @@ class SpellCheck(Dialog):
|
||||
l = QVBoxLayout()
|
||||
l.addStrut(250)
|
||||
h.addLayout(l)
|
||||
l.addWidget(b)
|
||||
l.addWidget(b), l.addSpacing(20)
|
||||
self.add_button = b = QPushButton(_('&Add word to dictionary:'))
|
||||
b.add_text, b.remove_text = unicode(b.text()), _('&Remove word from user dictionaries')
|
||||
b.clicked.connect(self.add_remove)
|
||||
self.user_dictionaries = d = QComboBox(self)
|
||||
self.user_dictionaries_missing_label = la = QLabel(_(
|
||||
'You have no active user dictionaries. You must'
|
||||
' choose at least one active user dictionary via'
|
||||
' Preferences->Editor->Manage spelling dictionaries'))
|
||||
la.setWordWrap(True)
|
||||
self.initialize_user_dictionaries()
|
||||
d.setMinimumContentsLength(25)
|
||||
l.addWidget(b), l.addWidget(d), l.addWidget(la)
|
||||
l.addStretch(1)
|
||||
|
||||
hh.setSectionHidden(3, m.show_only_misspelt)
|
||||
self.show_only_misspelled = om = QCheckBox(_('Show only misspelled words'))
|
||||
@ -528,8 +556,23 @@ class SpellCheck(Dialog):
|
||||
self.summary = s = QLabel('')
|
||||
self.main.l.addLayout(h), h.addWidget(s), h.addWidget(om), h.addStretch(10)
|
||||
|
||||
def initialize_user_dictionaries(self):
|
||||
ct = unicode(self.user_dictionaries.currentText())
|
||||
self.user_dictionaries.clear()
|
||||
self.user_dictionaries.addItems([d.name for d in dictionaries.active_user_dictionaries])
|
||||
if ct:
|
||||
idx = self.user_dictionaries.findText(ct)
|
||||
if idx > -1:
|
||||
self.user_dictionaries.setCurrentIndex(idx)
|
||||
self.user_dictionaries.setVisible(self.user_dictionaries.count() > 0)
|
||||
self.user_dictionaries_missing_label.setVisible(not self.user_dictionaries.isVisible())
|
||||
|
||||
def current_word_changed(self, *args):
|
||||
ignored = recognized = False
|
||||
try:
|
||||
b = self.ignore_button
|
||||
except AttributeError:
|
||||
return
|
||||
ignored = recognized = in_user_dictionary = False
|
||||
current = self.words_view.currentIndex()
|
||||
current_word = ''
|
||||
if current.isValid():
|
||||
@ -539,20 +582,31 @@ class SpellCheck(Dialog):
|
||||
ignored = dictionaries.is_word_ignored(*w)
|
||||
recognized = self.words_model.spell_map[w]
|
||||
current_word = w[0]
|
||||
if recognized:
|
||||
in_user_dictionary = dictionaries.word_in_user_dictionary(*w)
|
||||
|
||||
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))
|
||||
if not self.user_dictionaries_missing_label.isVisible():
|
||||
b = self.add_button
|
||||
b.setText(b.remove_text if in_user_dictionary else b.add_text)
|
||||
self.user_dictionaries.setVisible(not in_user_dictionary)
|
||||
|
||||
def toggle_ignore(self):
|
||||
current = self.words_view.currentIndex()
|
||||
if current.isValid():
|
||||
self.words_model.toggle_ignored(current.row())
|
||||
|
||||
def add_remove(self):
|
||||
current = self.words_view.currentIndex()
|
||||
if current.isValid():
|
||||
if self.user_dictionaries.isVisible(): # add
|
||||
udname = unicode(self.user_dictionaries.currentText())
|
||||
self.words_model.add_word(current.row(), udname)
|
||||
else:
|
||||
self.words_model.remove_word(current.row())
|
||||
|
||||
def update_show_only_misspelt(self):
|
||||
m = self.words_model
|
||||
m.show_only_misspelt = self.show_only_misspelled.isChecked()
|
||||
@ -630,6 +684,7 @@ class SpellCheck(Dialog):
|
||||
col, Qt.DescendingOrder if reverse else Qt.AscendingOrder)
|
||||
self.highlight_row(0)
|
||||
self.update_summary()
|
||||
self.initialize_user_dictionaries()
|
||||
|
||||
def update_summary(self):
|
||||
self.summary.setText(_('Misspelled words: {0} Total words: {1}').format(*self.words_model.counts))
|
||||
|
@ -9,6 +9,7 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import cPickle, os, glob, shutil
|
||||
from collections import namedtuple
|
||||
from operator import attrgetter
|
||||
from itertools import chain
|
||||
|
||||
from calibre.constants import plugins, config_dir
|
||||
from calibre.utils.config import JSONConfig
|
||||
@ -23,8 +24,22 @@ if hunspell is None:
|
||||
dprefs = JSONConfig('dictionaries/prefs.json')
|
||||
dprefs.defaults['preferred_dictionaries'] = {}
|
||||
dprefs.defaults['preferred_locales'] = {}
|
||||
dprefs.defaults['user_dictionaries'] = [{'name':_('Default'), 'is_active':True, 'words':[]}]
|
||||
not_present = object()
|
||||
|
||||
class UserDictionary(object):
|
||||
|
||||
__slots__ = ('name', 'is_active', 'words')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.name = kwargs['name']
|
||||
self.is_active = kwargs['is_active']
|
||||
self.words = {(w, langcode) for w, langcode in kwargs['words']}
|
||||
|
||||
def serialize(self):
|
||||
return {'name':self.name, 'is_active': self.is_active, 'words':[
|
||||
(w, l) for w, l in self.words]}
|
||||
|
||||
ccodes, ccodemap, country_names = None, None, None
|
||||
def get_codes():
|
||||
global ccodes, ccodemap, country_names
|
||||
@ -169,6 +184,10 @@ class Dictionaries(object):
|
||||
self.default_locale = parse_lang_code('en-US')
|
||||
self.ui_locale = self.default_locale
|
||||
|
||||
def initialize(self):
|
||||
if not hasattr(self, 'active_user_dictionaries'):
|
||||
self.read_user_dictionaries()
|
||||
|
||||
def clear_caches(self):
|
||||
self.dictionaries.clear(), self.word_cache.clear()
|
||||
|
||||
@ -185,37 +204,98 @@ class Dictionaries(object):
|
||||
return ans
|
||||
|
||||
def ignore_word(self, word, locale):
|
||||
self.ignored_words.add((word, locale))
|
||||
self.ignored_words.add((word, locale.langcode))
|
||||
self.word_cache[(word, locale)] = True
|
||||
|
||||
def unignore_word(self, word, locale):
|
||||
self.ignored_words.discard((word, locale))
|
||||
self.ignored_words.discard((word, locale.langcode))
|
||||
self.word_cache.pop((word, locale), None)
|
||||
|
||||
def is_word_ignored(self, word, locale):
|
||||
return (word, locale) in self.ignored_words
|
||||
return (word, locale.langcode) in self.ignored_words
|
||||
|
||||
@property
|
||||
def all_user_dictionaries(self):
|
||||
return chain(self.active_user_dictionaries, self.inactive_user_dictionaries)
|
||||
|
||||
def user_dictionary(self, name):
|
||||
for ud in self.all_user_dictionaries:
|
||||
if ud.name == name:
|
||||
return ud
|
||||
|
||||
def read_user_dictionaries(self):
|
||||
self.active_user_dictionaries = []
|
||||
self.inactive_user_dictionaries = []
|
||||
for d in dprefs['user_dictionaries']:
|
||||
d = UserDictionary(**d)
|
||||
(self.active_user_dictionaries if d.is_active else self.inactive_user_dictionaries).append(d)
|
||||
|
||||
def save_user_dictionaries(self):
|
||||
dprefs['user_dictionaries'] = [d.serialize() for d in self.all_user_dictionaries]
|
||||
|
||||
def add_to_user_dictionary(self, name, word, locale):
|
||||
ud = self.user_dictionary(name)
|
||||
if ud is None:
|
||||
raise ValueError('Cannot add to the dictionary named: %s as no such dictionary exists' % name)
|
||||
wl = len(ud.words)
|
||||
ud.words.add((word, locale.langcode))
|
||||
if len(ud.words) > wl:
|
||||
self.save_user_dictionaries()
|
||||
self.word_cache.pop((word, locale), None)
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove_from_user_dictionaries(self, word, locale):
|
||||
key = (word, locale.langcode)
|
||||
changed = False
|
||||
for ud in self.active_user_dictionaries:
|
||||
if key in ud.words:
|
||||
changed = True
|
||||
ud.words.discard(key)
|
||||
if changed:
|
||||
self.word_cache.pop((word, locale), None)
|
||||
self.save_user_dictionaries()
|
||||
return changed
|
||||
|
||||
def word_in_user_dictionary(self, word, locale):
|
||||
key = (word, locale.langcode)
|
||||
for ud in self.active_user_dictionaries:
|
||||
if key in ud.words:
|
||||
return ud.name
|
||||
|
||||
def create_user_dictionary(self, name):
|
||||
if name in {d.name for d in self.all_user_dictionaries}:
|
||||
raise ValueError('A dictionary named %s already exists' % name)
|
||||
d = UserDictionary(name=name, is_active=True, words=())
|
||||
self.active_user_dictionaries.append(d)
|
||||
self.save_user_dictionaries()
|
||||
|
||||
def recognized(self, word, locale=None):
|
||||
locale = locale or self.default_locale
|
||||
if not isinstance(locale, DictionaryLocale):
|
||||
locale = parse_lang_code(locale)
|
||||
key = (word, locale)
|
||||
ans = self.word_cache.get(key, None)
|
||||
if ans is None:
|
||||
lkey = (word, locale.langcode)
|
||||
ans = False
|
||||
if key in self.ignored_words:
|
||||
if lkey in self.ignored_words:
|
||||
ans = True
|
||||
else:
|
||||
d = self.dictionary_for_locale(locale)
|
||||
if d is not None:
|
||||
try:
|
||||
ans = d.obj.recognized(word)
|
||||
except ValueError:
|
||||
pass
|
||||
for ud in self.active_user_dictionaries:
|
||||
if lkey in ud.words:
|
||||
ans = True
|
||||
break
|
||||
else:
|
||||
d = self.dictionary_for_locale(locale)
|
||||
if d is not None:
|
||||
try:
|
||||
ans = d.obj.recognized(word)
|
||||
except ValueError:
|
||||
pass
|
||||
self.word_cache[key] = ans
|
||||
return ans
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
dictionaries = Dictionaries()
|
||||
print (dictionaries.recognized('recognized', 'en'))
|
||||
dictionaries.initialize()
|
||||
print (dictionaries.recognized('recognized', parse_lang_code('en')))
|
||||
|
Loading…
x
Reference in New Issue
Block a user