Add/remove for dictionaries from OpenOffice extensions works

This commit is contained in:
Kovid Goyal 2014-04-07 12:07:33 +05:30
parent b060f55d5f
commit 40204e0d3a
3 changed files with 71 additions and 33 deletions

View File

@ -19,7 +19,8 @@ from calibre.gui2 import choose_files, error_dialog
from calibre.gui2.tweak_book.widgets import Dialog
from calibre.spell.dictionary import (
builtin_dictionaries, custom_dictionaries, best_locale_for_language,
get_dictionary, DictionaryLocale, dprefs)
get_dictionary, DictionaryLocale, dprefs, remove_dictionary)
from calibre.spell.import_from import import_from_oxt
from calibre.utils.localization import calibre_langcode_to_name
from calibre.utils.icu import sort_key
@ -35,7 +36,7 @@ def country_map():
_country_map = cPickle.loads(P('localization/iso3166.pickle', data=True, allow_user_override=False))
return _country_map
class AddDictionary(QDialog):
class AddDictionary(QDialog): # {{{
def __init__(self, parent=None):
QDialog.__init__(self, parent)
@ -79,7 +80,7 @@ class AddDictionary(QDialog):
def choose_file(self):
path = choose_files(self, 'choose-dict-for-import', _('Choose OXT Dictionary'), filters=[
(_('Dictionaries', ['oxt']))], all_files=False, select_only_single_file=True)
(_('Dictionaries'), ['oxt'])], all_files=False, select_only_single_file=True)
if path is not None:
self.path.setText(path[0])
if not self.nickname:
@ -98,7 +99,17 @@ class AddDictionary(QDialog):
if nick in {d.name for d in custom_dictionaries()}:
return error_dialog(self, _('Nickname already used'), _(
'A dictionary with the nick name "%s" already exists.'), show=True)
oxt = unicode(self.path.text())
try:
num = import_from_oxt(oxt, nick)
except:
return error_dialog(self, _('Failed to import dictionaries'), _(
'Failed to import dictionaries from %s. Click "Show Details" for more information') % oxt, show=True)
if num == 0:
return error_dialog(self, _('No dictionaries'), _(
'No dictionaries were found in %s') % oxt, show=True)
QDialog.accept(self)
# }}}
class ManageDictionaries(Dialog):
@ -126,7 +137,8 @@ class ManageDictionaries(Dialog):
self.dl = dl = QVBoxLayout(w)
self.fb = b = QPushButton(self)
b.clicked.connect(self.set_favorite)
self.remove_dictionary = rd = QPushButton(_('&Remove this dictionary'), w)
self.remove_dictionary_button = rd = QPushButton(_('&Remove this dictionary'), w)
rd.clicked.connect(self.remove_dictionary)
dl.addWidget(b), dl.addWidget(rd)
w.setLayout(dl)
s.addWidget(la)
@ -149,8 +161,8 @@ class ManageDictionaries(Dialog):
b.clicked.connect(self.add_dictionary)
l.addWidget(self.bb, l.rowCount(), 0, 1, l.columnCount())
def build_dictionaries(self):
all_dictionaries = builtin_dictionaries() | custom_dictionaries()
def build_dictionaries(self, reread=False):
all_dictionaries = builtin_dictionaries() | custom_dictionaries(reread=reread)
languages = defaultdict(lambda : defaultdict(set))
for d in all_dictionaries:
for locale in d.locales | {d.primary_locale}:
@ -159,6 +171,7 @@ class ManageDictionaries(Dialog):
bf.setBold(True)
itf = QFont(self.dictionaries.font())
itf.setItalic(True)
self.dictionaries.clear()
for lc in sorted(languages, key=lambda x:sort_key(calibre_langcode_to_name(x))):
i = QTreeWidgetItem(self.dictionaries, LANG)
@ -174,12 +187,12 @@ class ManageDictionaries(Dialog):
pd = get_dictionary(DictionaryLocale(lc, countrycode))
for dictionary in sorted(languages[lc][countrycode], key=lambda d:d.name):
k = QTreeWidgetItem(j, DICTIONARY)
pl = calibre_langcode_to_name(d.primary_locale.langcode)
if d.primary_locale.countrycode:
pl += '-' + d.primary_locale.countrycode.upper()
k.setText(0, d.name or (_('<Builtin dictionary for {0}>').format(pl)))
k.setData(0, Qt.UserRole, d)
if pd == d:
pl = calibre_langcode_to_name(dictionary.primary_locale.langcode)
if dictionary.primary_locale.countrycode:
pl += '-' + dictionary.primary_locale.countrycode.upper()
k.setText(0, dictionary.name or (_('<Builtin dictionary for {0}>').format(pl)))
k.setData(0, Qt.UserRole, dictionary)
if pd == dictionary:
k.setData(0, Qt.FontRole, itf)
self.dictionaries.expandAll()
@ -187,8 +200,15 @@ class ManageDictionaries(Dialog):
def add_dictionary(self):
d = AddDictionary(self)
if d.exec_() == d.Accepted:
path = unicode(d.path.text())
print (path)
self.build_dictionaries(reread=True)
def remove_dictionary(self):
item = self.dictionaries.currentItem()
if item is not None and item.type() == DICTIONARY:
dic = item.data(0, Qt.UserRole).toPyObject()
if not dic.builtin:
remove_dictionary(dic)
self.build_dictionaries(reread=True)
def current_item_changed(self):
item = self.dictionaries.currentItem()
@ -242,7 +262,7 @@ class ManageDictionaries(Dialog):
'This is already the preferred dictionary') if preferred else
_('Use this as the preferred dictionary')))
saf.setEnabled(not preferred)
self.remove_dictionary.setEnabled(not item.data(0, Qt.UserRole).toPyObject().builtin)
self.remove_dictionary_button.setEnabled(not item.data(0, Qt.UserRole).toPyObject().builtin)
def set_favorite(self):
item = self.dictionaries.currentItem()
@ -255,7 +275,7 @@ class ManageDictionaries(Dialog):
d = item.data(0, Qt.UserRole).toPyObject()
locale = '%s-%s' % (lc, cc)
pl = dprefs['preferred_dictionaries']
pl[locale] = d.name
pl[locale] = d.id
dprefs['preferred_dictionaries'] = pl
if __name__ == '__main__':

View File

@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
import cPickle, os, glob
import cPickle, os, glob, shutil
from collections import namedtuple
from operator import attrgetter
@ -15,8 +15,8 @@ from calibre.utils.config import JSONConfig
from calibre.utils.localization import get_lang, canonicalize_lang
DictionaryLocale = namedtuple('DictionaryLocale', 'langcode countrycode')
Dictionary = namedtuple('Dictionary', 'primary_locale locales dicpath affpath builtin name')
LoadedDictionary = namedtuple('Dictionary', 'primary_locale locales obj builtin name')
Dictionary = namedtuple('Dictionary', 'primary_locale locales dicpath affpath builtin name id')
LoadedDictionary = namedtuple('Dictionary', 'primary_locale locales obj builtin name id')
hunspell = plugins['hunspell'][0]
if hunspell is None:
raise RuntimeError('Failed to load hunspell: %s' % plugins[1])
@ -60,15 +60,13 @@ def builtin_dictionaries():
base = os.path.dirname(lc)
dics.append(Dictionary(
parse_lang_code(locale), frozenset(map(parse_lang_code, locales)), os.path.join(base, '%s.dic' % locale),
os.path.join(base, '%s.aff' % locale), True, None))
os.path.join(base, '%s.aff' % locale), True, None, None))
_builtins = frozenset(dics)
return _builtins
def custom_dictionaries(reread=False):
global _custom
if reread:
_custom = None
if _custom is None:
if _custom is None or reread:
dics = []
for lc in glob.glob(os.path.join(config_dir, 'dictionaries', '*/locales')):
locales = filter(None, open(lc, 'rb').read().decode('utf-8').splitlines())
@ -76,7 +74,7 @@ def custom_dictionaries(reread=False):
base = os.path.dirname(lc)
dics.append(Dictionary(
parse_lang_code(locale), frozenset(map(parse_lang_code, locales)), os.path.join(base, '%s.dic' % locale),
os.path.join(base, '%s.aff' % locale), False, name))
os.path.join(base, '%s.aff' % locale), False, name, os.path.basename(base)))
_custom = frozenset(dics)
return _custom
@ -88,7 +86,14 @@ def best_locale_for_language(langcode):
return parse_lang_code(best_locale)
def preferred_dictionary(locale):
return {parse_lang_code(k):v for k, v in dprefs['preferred_dictionaries']}.get(locale, None)
return {parse_lang_code(k):v for k, v in dprefs['preferred_dictionaries'].iteritems()}.get(locale, None)
def remove_dictionary(dictionary):
if dictionary.builtin:
raise ValueError('Cannot remove builtin dictionaries')
base = os.path.dirname(dictionary.dicpath)
shutil.rmtree(base)
dprefs['preferred_dictionaries'] = {k:v for k, v in dprefs['preferred_dictionaries'].iteritems() if v != dictionary.id}
def get_dictionary(locale, exact_match=False):
preferred = preferred_dictionary(locale)
@ -97,11 +102,11 @@ def get_dictionary(locale, exact_match=False):
for collection in (custom_dictionaries(), builtin_dictionaries()):
for d in collection:
if d.primary_locale == locale:
exact_matches[d.name] = d
exact_matches[d.id] = d
for d in collection:
for q in d.locales:
if q == locale and d.name not in exact_matches:
exact_matches[d.name] = d
if q == locale and d.id not in exact_matches:
exact_matches[d.id] = d
# If the user has specified a preferred dictionary for this locale, use it,
# otherwise, if a builtin dictionary exists, use that
@ -134,7 +139,7 @@ def get_dictionary(locale, exact_match=False):
def load_dictionary(dictionary):
with open(dictionary.dicpath, 'rb') as dic, open(dictionary.affpath, 'rb') as aff:
obj = hunspell.Dictionary(dic.read(), aff.read())
return LoadedDictionary(dictionary.primary_locale, dictionary.locales, obj, dictionary.builtin, dictionary.name)
return LoadedDictionary(dictionary.primary_locale, dictionary.locales, obj, dictionary.builtin, dictionary.name, dictionary.id)
class Dictionaries(object):

View File

@ -6,10 +6,11 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
import sys, glob, os, shutil
import sys, glob, os, shutil, tempfile
from lxml import etree
from calibre.constants import config_dir
from calibre.utils.zipfile import ZipFile
NS_MAP = {
@ -60,13 +61,25 @@ def import_from_libreoffice_source_tree(source_path):
if want_locales:
raise Exception('Failed to find dictionaries for some wanted locales: %s' % want_locales)
def import_from_oxt(source_path):
def import_from_oxt(source_path, name, dest_dir=None, prefix='dic-'):
dest_dir = dest_dir or os.path.join(config_dir, 'dictionaries')
num = 0
with ZipFile(source_path) as zf:
root = etree.fromstring(zf.open('META-INF/manifest.xml').read())
xcu = XPath('//manifest:file-entry[@manifest:media-type="application/vnd.sun.star.configuration-data"]')(root)[0].get(
'{%s}full-path' % NS_MAP['manifest'])
dictionaries = {(dic.lstrip('/'), aff.lstrip('/')):locales for (dic, aff), locales in parse_xcu(zf.open(xcu).read(), origin='').iteritems()}
return dictionaries
for (dic, aff), locales in parse_xcu(zf.open(xcu).read(), origin='').iteritems():
dic, aff = dic.lstrip('/'), aff.lstrip('/')
d = tempfile.mkdtemp(prefix=prefix, dir=dest_dir)
metadata = [name] + locales
with open(os.path.join(d, 'locales'), 'wb') as f:
f.write(('\n'.join(metadata)).encode('utf-8'))
with open(os.path.join(d, '%s.dic' % locales[0]), 'wb') as f:
shutil.copyfileobj(zf.open(dic), f)
with open(os.path.join(d, '%s.aff' % locales[0]), 'wb') as f:
shutil.copyfileobj(zf.open(aff), f)
num += 1
return num
if __name__ == '__main__':
import_from_libreoffice_source_tree(sys.argv[-1])