From 0bb47ed9fe93233850e78235cca63f304e2875db Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 25 Oct 2020 10:40:42 +0530 Subject: [PATCH] Open with: On Linux when reading names from .desktop files, use the first matching language Fixes #1901276 [Name of Gnome Image viewer not visible in the Open cover with dialog](https://bugs.launchpad.net/calibre/+bug/1901276) --- src/calibre/utils/open_with/linux.py | 67 +++++++++++++++++----------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/src/calibre/utils/open_with/linux.py b/src/calibre/utils/open_with/linux.py index 8e2bd27b36..f3fc99edf3 100644 --- a/src/calibre/utils/open_with/linux.py +++ b/src/calibre/utils/open_with/linux.py @@ -13,16 +13,14 @@ from calibre.constants import filesystem_encoding, cache_dir from calibre.utils.icu import numeric_sort_key as sort_key from calibre.utils.localization import canonicalize_lang, get_lang from calibre.utils.serialize import msgpack_dumps, msgpack_loads -from polyglot.builtins import iteritems, itervalues, string_or_bytes, unicode_type +from polyglot.builtins import iteritems, itervalues, string_or_bytes def parse_localized_key(key): name, rest = key.partition('[')[0::2] if not rest: return name, None - rest = rest[:-1] - lang = re.split(r'[_.@]', rest)[0] - return name, canonicalize_lang(lang) + return name, rest[:-1] def unquote_exec(val): @@ -30,6 +28,10 @@ def unquote_exec(val): return shlex.split(val) +def known_localized_items(): + return {'Name': {}, 'GenericName': {}, 'Comment': {}, 'Icon': {}} + + def parse_desktop_file(path): gpat = re.compile(r'^\[(.+?)\]\s*$') kpat = re.compile(r'^([-a-zA-Z0-9\[\]@_.]+)\s*=\s*(.+)$') @@ -41,6 +43,7 @@ def parse_desktop_file(path): group = None ans = {} ans['desktop_file_path'] = path + localized_items = known_localized_items() for line in raw.splitlines(): m = gpat.match(line) if m is not None: @@ -62,15 +65,17 @@ def parse_desktop_file(path): ans[k] = cmdline elif k == 'MimeType': ans[k] = frozenset(x.strip() for x in v.split(';')) - elif k in {'Name', 'GenericName', 'Comment', 'Icon'} or '[' in k: + elif k in localized_items or '[' in k: name, lang = parse_localized_key(k) - if name not in ans: - ans[name] = {} - if isinstance(ans[name], unicode_type): - ans[name] = {None:ans[name]} - ans[name][lang] = v + vals = localized_items.setdefault(name, {}) + vals[lang] = v + if name in ans: + vals[None] = ans.pop(name) else: ans[k] = v + for k, vals in localized_items.items(): + if vals: + ans[k] = dict(vals) if 'Exec' in ans and 'MimeType' in ans and 'Name' in ans: return ans @@ -168,7 +173,32 @@ def find_icons(): def localize_string(data): lang = canonicalize_lang(get_lang()) - return data.get(lang, data.get(None)) or '' + + def key_matches(key): + if key is None: + return True + base = re.split(r'[_.@]', key)[0] + return canonicalize_lang(base) == lang + + matches = tuple(filter(key_matches, data)) + return data[matches[0]] if matches else '' + + +def process_desktop_file(data): + icon = data.get('Icon', {}).get(None) + if icon and not os.path.isabs(icon): + icon = find_icons().get(icon) + if icon: + data['Icon'] = icon + else: + data.pop('Icon') + if not isinstance(data.get('Icon'), string_or_bytes): + data.pop('Icon', None) + for k in ('Name', 'GenericName', 'Comment'): + val = data.get(k) + if val: + data[k] = localize_string(val) + return data def find_programs(extensions): @@ -194,20 +224,7 @@ def find_programs(extensions): traceback.print_exc() continue if data is not None and mime_types.intersection(data['MimeType']): - icon = data.get('Icon', {}).get(None) - if icon and not os.path.isabs(icon): - icon = find_icons().get(icon) - if icon: - data['Icon'] = icon - else: - data.pop('Icon') - if not isinstance(data.get('Icon'), string_or_bytes): - data.pop('Icon', None) - for k in ('Name', 'GenericName', 'Comment'): - val = data.get(k) - if val: - data[k] = localize_string(val) - ans.append(data) + ans.append(process_desktop_file(data)) ans.sort(key=lambda d:sort_key(d.get('Name'))) return ans