mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add/remove for dictionaries from OpenOffice extensions works
This commit is contained in:
parent
b060f55d5f
commit
40204e0d3a
@ -19,7 +19,8 @@ from calibre.gui2 import choose_files, error_dialog
|
|||||||
from calibre.gui2.tweak_book.widgets import Dialog
|
from calibre.gui2.tweak_book.widgets import Dialog
|
||||||
from calibre.spell.dictionary import (
|
from calibre.spell.dictionary import (
|
||||||
builtin_dictionaries, custom_dictionaries, best_locale_for_language,
|
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.localization import calibre_langcode_to_name
|
||||||
from calibre.utils.icu import sort_key
|
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))
|
_country_map = cPickle.loads(P('localization/iso3166.pickle', data=True, allow_user_override=False))
|
||||||
return _country_map
|
return _country_map
|
||||||
|
|
||||||
class AddDictionary(QDialog):
|
class AddDictionary(QDialog): # {{{
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
@ -79,7 +80,7 @@ class AddDictionary(QDialog):
|
|||||||
|
|
||||||
def choose_file(self):
|
def choose_file(self):
|
||||||
path = choose_files(self, 'choose-dict-for-import', _('Choose OXT Dictionary'), filters=[
|
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:
|
if path is not None:
|
||||||
self.path.setText(path[0])
|
self.path.setText(path[0])
|
||||||
if not self.nickname:
|
if not self.nickname:
|
||||||
@ -98,7 +99,17 @@ class AddDictionary(QDialog):
|
|||||||
if nick in {d.name for d in custom_dictionaries()}:
|
if nick in {d.name for d in custom_dictionaries()}:
|
||||||
return error_dialog(self, _('Nickname already used'), _(
|
return error_dialog(self, _('Nickname already used'), _(
|
||||||
'A dictionary with the nick name "%s" already exists.'), show=True)
|
'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)
|
QDialog.accept(self)
|
||||||
|
# }}}
|
||||||
|
|
||||||
class ManageDictionaries(Dialog):
|
class ManageDictionaries(Dialog):
|
||||||
|
|
||||||
@ -126,7 +137,8 @@ class ManageDictionaries(Dialog):
|
|||||||
self.dl = dl = QVBoxLayout(w)
|
self.dl = dl = QVBoxLayout(w)
|
||||||
self.fb = b = QPushButton(self)
|
self.fb = b = QPushButton(self)
|
||||||
b.clicked.connect(self.set_favorite)
|
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)
|
dl.addWidget(b), dl.addWidget(rd)
|
||||||
w.setLayout(dl)
|
w.setLayout(dl)
|
||||||
s.addWidget(la)
|
s.addWidget(la)
|
||||||
@ -149,8 +161,8 @@ class ManageDictionaries(Dialog):
|
|||||||
b.clicked.connect(self.add_dictionary)
|
b.clicked.connect(self.add_dictionary)
|
||||||
l.addWidget(self.bb, l.rowCount(), 0, 1, l.columnCount())
|
l.addWidget(self.bb, l.rowCount(), 0, 1, l.columnCount())
|
||||||
|
|
||||||
def build_dictionaries(self):
|
def build_dictionaries(self, reread=False):
|
||||||
all_dictionaries = builtin_dictionaries() | custom_dictionaries()
|
all_dictionaries = builtin_dictionaries() | custom_dictionaries(reread=reread)
|
||||||
languages = defaultdict(lambda : defaultdict(set))
|
languages = defaultdict(lambda : defaultdict(set))
|
||||||
for d in all_dictionaries:
|
for d in all_dictionaries:
|
||||||
for locale in d.locales | {d.primary_locale}:
|
for locale in d.locales | {d.primary_locale}:
|
||||||
@ -159,6 +171,7 @@ class ManageDictionaries(Dialog):
|
|||||||
bf.setBold(True)
|
bf.setBold(True)
|
||||||
itf = QFont(self.dictionaries.font())
|
itf = QFont(self.dictionaries.font())
|
||||||
itf.setItalic(True)
|
itf.setItalic(True)
|
||||||
|
self.dictionaries.clear()
|
||||||
|
|
||||||
for lc in sorted(languages, key=lambda x:sort_key(calibre_langcode_to_name(x))):
|
for lc in sorted(languages, key=lambda x:sort_key(calibre_langcode_to_name(x))):
|
||||||
i = QTreeWidgetItem(self.dictionaries, LANG)
|
i = QTreeWidgetItem(self.dictionaries, LANG)
|
||||||
@ -174,12 +187,12 @@ class ManageDictionaries(Dialog):
|
|||||||
pd = get_dictionary(DictionaryLocale(lc, countrycode))
|
pd = get_dictionary(DictionaryLocale(lc, countrycode))
|
||||||
for dictionary in sorted(languages[lc][countrycode], key=lambda d:d.name):
|
for dictionary in sorted(languages[lc][countrycode], key=lambda d:d.name):
|
||||||
k = QTreeWidgetItem(j, DICTIONARY)
|
k = QTreeWidgetItem(j, DICTIONARY)
|
||||||
pl = calibre_langcode_to_name(d.primary_locale.langcode)
|
pl = calibre_langcode_to_name(dictionary.primary_locale.langcode)
|
||||||
if d.primary_locale.countrycode:
|
if dictionary.primary_locale.countrycode:
|
||||||
pl += '-' + d.primary_locale.countrycode.upper()
|
pl += '-' + dictionary.primary_locale.countrycode.upper()
|
||||||
k.setText(0, d.name or (_('<Builtin dictionary for {0}>').format(pl)))
|
k.setText(0, dictionary.name or (_('<Builtin dictionary for {0}>').format(pl)))
|
||||||
k.setData(0, Qt.UserRole, d)
|
k.setData(0, Qt.UserRole, dictionary)
|
||||||
if pd == d:
|
if pd == dictionary:
|
||||||
k.setData(0, Qt.FontRole, itf)
|
k.setData(0, Qt.FontRole, itf)
|
||||||
|
|
||||||
self.dictionaries.expandAll()
|
self.dictionaries.expandAll()
|
||||||
@ -187,8 +200,15 @@ class ManageDictionaries(Dialog):
|
|||||||
def add_dictionary(self):
|
def add_dictionary(self):
|
||||||
d = AddDictionary(self)
|
d = AddDictionary(self)
|
||||||
if d.exec_() == d.Accepted:
|
if d.exec_() == d.Accepted:
|
||||||
path = unicode(d.path.text())
|
self.build_dictionaries(reread=True)
|
||||||
print (path)
|
|
||||||
|
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):
|
def current_item_changed(self):
|
||||||
item = self.dictionaries.currentItem()
|
item = self.dictionaries.currentItem()
|
||||||
@ -242,7 +262,7 @@ class ManageDictionaries(Dialog):
|
|||||||
'This is already the preferred dictionary') if preferred else
|
'This is already the preferred dictionary') if preferred else
|
||||||
_('Use this as the preferred dictionary')))
|
_('Use this as the preferred dictionary')))
|
||||||
saf.setEnabled(not preferred)
|
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):
|
def set_favorite(self):
|
||||||
item = self.dictionaries.currentItem()
|
item = self.dictionaries.currentItem()
|
||||||
@ -255,7 +275,7 @@ class ManageDictionaries(Dialog):
|
|||||||
d = item.data(0, Qt.UserRole).toPyObject()
|
d = item.data(0, Qt.UserRole).toPyObject()
|
||||||
locale = '%s-%s' % (lc, cc)
|
locale = '%s-%s' % (lc, cc)
|
||||||
pl = dprefs['preferred_dictionaries']
|
pl = dprefs['preferred_dictionaries']
|
||||||
pl[locale] = d.name
|
pl[locale] = d.id
|
||||||
dprefs['preferred_dictionaries'] = pl
|
dprefs['preferred_dictionaries'] = pl
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import cPickle, os, glob
|
import cPickle, os, glob, shutil
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
@ -15,8 +15,8 @@ from calibre.utils.config import JSONConfig
|
|||||||
from calibre.utils.localization import get_lang, canonicalize_lang
|
from calibre.utils.localization import get_lang, canonicalize_lang
|
||||||
|
|
||||||
DictionaryLocale = namedtuple('DictionaryLocale', 'langcode countrycode')
|
DictionaryLocale = namedtuple('DictionaryLocale', 'langcode countrycode')
|
||||||
Dictionary = namedtuple('Dictionary', 'primary_locale locales dicpath affpath builtin name')
|
Dictionary = namedtuple('Dictionary', 'primary_locale locales dicpath affpath builtin name id')
|
||||||
LoadedDictionary = namedtuple('Dictionary', 'primary_locale locales obj builtin name')
|
LoadedDictionary = namedtuple('Dictionary', 'primary_locale locales obj builtin name id')
|
||||||
hunspell = plugins['hunspell'][0]
|
hunspell = plugins['hunspell'][0]
|
||||||
if hunspell is None:
|
if hunspell is None:
|
||||||
raise RuntimeError('Failed to load hunspell: %s' % plugins[1])
|
raise RuntimeError('Failed to load hunspell: %s' % plugins[1])
|
||||||
@ -60,15 +60,13 @@ def builtin_dictionaries():
|
|||||||
base = os.path.dirname(lc)
|
base = os.path.dirname(lc)
|
||||||
dics.append(Dictionary(
|
dics.append(Dictionary(
|
||||||
parse_lang_code(locale), frozenset(map(parse_lang_code, locales)), os.path.join(base, '%s.dic' % locale),
|
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)
|
_builtins = frozenset(dics)
|
||||||
return _builtins
|
return _builtins
|
||||||
|
|
||||||
def custom_dictionaries(reread=False):
|
def custom_dictionaries(reread=False):
|
||||||
global _custom
|
global _custom
|
||||||
if reread:
|
if _custom is None or reread:
|
||||||
_custom = None
|
|
||||||
if _custom is None:
|
|
||||||
dics = []
|
dics = []
|
||||||
for lc in glob.glob(os.path.join(config_dir, 'dictionaries', '*/locales')):
|
for lc in glob.glob(os.path.join(config_dir, 'dictionaries', '*/locales')):
|
||||||
locales = filter(None, open(lc, 'rb').read().decode('utf-8').splitlines())
|
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)
|
base = os.path.dirname(lc)
|
||||||
dics.append(Dictionary(
|
dics.append(Dictionary(
|
||||||
parse_lang_code(locale), frozenset(map(parse_lang_code, locales)), os.path.join(base, '%s.dic' % locale),
|
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)
|
_custom = frozenset(dics)
|
||||||
return _custom
|
return _custom
|
||||||
|
|
||||||
@ -88,7 +86,14 @@ def best_locale_for_language(langcode):
|
|||||||
return parse_lang_code(best_locale)
|
return parse_lang_code(best_locale)
|
||||||
|
|
||||||
def preferred_dictionary(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):
|
def get_dictionary(locale, exact_match=False):
|
||||||
preferred = preferred_dictionary(locale)
|
preferred = preferred_dictionary(locale)
|
||||||
@ -97,11 +102,11 @@ def get_dictionary(locale, exact_match=False):
|
|||||||
for collection in (custom_dictionaries(), builtin_dictionaries()):
|
for collection in (custom_dictionaries(), builtin_dictionaries()):
|
||||||
for d in collection:
|
for d in collection:
|
||||||
if d.primary_locale == locale:
|
if d.primary_locale == locale:
|
||||||
exact_matches[d.name] = d
|
exact_matches[d.id] = d
|
||||||
for d in collection:
|
for d in collection:
|
||||||
for q in d.locales:
|
for q in d.locales:
|
||||||
if q == locale and d.name not in exact_matches:
|
if q == locale and d.id not in exact_matches:
|
||||||
exact_matches[d.name] = d
|
exact_matches[d.id] = d
|
||||||
|
|
||||||
# If the user has specified a preferred dictionary for this locale, use it,
|
# If the user has specified a preferred dictionary for this locale, use it,
|
||||||
# otherwise, if a builtin dictionary exists, use that
|
# otherwise, if a builtin dictionary exists, use that
|
||||||
@ -134,7 +139,7 @@ def get_dictionary(locale, exact_match=False):
|
|||||||
def load_dictionary(dictionary):
|
def load_dictionary(dictionary):
|
||||||
with open(dictionary.dicpath, 'rb') as dic, open(dictionary.affpath, 'rb') as aff:
|
with open(dictionary.dicpath, 'rb') as dic, open(dictionary.affpath, 'rb') as aff:
|
||||||
obj = hunspell.Dictionary(dic.read(), aff.read())
|
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):
|
class Dictionaries(object):
|
||||||
|
|
||||||
|
@ -6,10 +6,11 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import sys, glob, os, shutil
|
import sys, glob, os, shutil, tempfile
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
|
from calibre.constants import config_dir
|
||||||
from calibre.utils.zipfile import ZipFile
|
from calibre.utils.zipfile import ZipFile
|
||||||
|
|
||||||
NS_MAP = {
|
NS_MAP = {
|
||||||
@ -60,13 +61,25 @@ def import_from_libreoffice_source_tree(source_path):
|
|||||||
if want_locales:
|
if want_locales:
|
||||||
raise Exception('Failed to find dictionaries for some wanted locales: %s' % 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:
|
with ZipFile(source_path) as zf:
|
||||||
root = etree.fromstring(zf.open('META-INF/manifest.xml').read())
|
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(
|
xcu = XPath('//manifest:file-entry[@manifest:media-type="application/vnd.sun.star.configuration-data"]')(root)[0].get(
|
||||||
'{%s}full-path' % NS_MAP['manifest'])
|
'{%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()}
|
for (dic, aff), locales in parse_xcu(zf.open(xcu).read(), origin='').iteritems():
|
||||||
return dictionaries
|
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__':
|
if __name__ == '__main__':
|
||||||
import_from_libreoffice_source_tree(sys.argv[-1])
|
import_from_libreoffice_source_tree(sys.argv[-1])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user