mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Refactor open with code
This commit is contained in:
parent
973e0e9d6f
commit
c920ab7298
@ -6,8 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import os, re, shlex, cPickle
|
import os
|
||||||
from collections import defaultdict
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
@ -16,14 +15,12 @@ from PyQt5.Qt import (
|
|||||||
QListWidget, QSize, pyqtSignal, QListWidgetItem, QIcon, QByteArray,
|
QListWidget, QSize, pyqtSignal, QListWidgetItem, QIcon, QByteArray,
|
||||||
QBuffer, QPixmap)
|
QBuffer, QPixmap)
|
||||||
|
|
||||||
from calibre import force_unicode, walk, guess_type, prints, as_unicode
|
from calibre import as_unicode
|
||||||
from calibre.constants import iswindows, isosx, filesystem_encoding, cache_dir
|
from calibre.constants import iswindows, isosx
|
||||||
from calibre.gui2 import error_dialog, choose_files
|
from calibre.gui2 import error_dialog, choose_files
|
||||||
from calibre.gui2.widgets2 import Dialog
|
from calibre.gui2.widgets2 import Dialog
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||||
from calibre.utils.config import JSONConfig
|
from calibre.utils.config import JSONConfig
|
||||||
from calibre.utils.icu import numeric_sort_key as sort_key
|
|
||||||
from calibre.utils.localization import canonicalize_lang, get_lang
|
|
||||||
|
|
||||||
DESC_ROLE = Qt.UserRole
|
DESC_ROLE = Qt.UserRole
|
||||||
ENTRY_ROLE = DESC_ROLE + 1
|
ENTRY_ROLE = DESC_ROLE + 1
|
||||||
@ -59,190 +56,19 @@ if iswindows:
|
|||||||
elif isosx:
|
elif isosx:
|
||||||
oprefs = JSONConfig('osx_open_with')
|
oprefs = JSONConfig('osx_open_with')
|
||||||
else:
|
else:
|
||||||
|
# XDG {{{
|
||||||
oprefs = JSONConfig('xdg_open_with')
|
oprefs = JSONConfig('xdg_open_with')
|
||||||
# XDG find_programs {{{
|
from calibre.utils.open_with.linux import entry_to_cmdline, find_programs, entry_sort_key
|
||||||
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)
|
|
||||||
|
|
||||||
def unquote_exec(val):
|
def entry_to_icon_text(entry):
|
||||||
val = val.replace(r'\\', '\\')
|
data = entry.get('icon_data')
|
||||||
return shlex.split(val)
|
if data is None:
|
||||||
|
icon = QIcon(I('blank.png'))
|
||||||
def parse_desktop_file(path):
|
else:
|
||||||
gpat = re.compile(r'^\[(.+?)\]\s*$')
|
pmap = QPixmap()
|
||||||
kpat = re.compile(r'^([-a-zA-Z0-9\[\]@_.]+)\s*=\s*(.+)$')
|
pmap.loadFromData(bytes(data))
|
||||||
try:
|
icon = QIcon(pmap)
|
||||||
with open(path, 'rb') as f:
|
return icon, entry['Name']
|
||||||
raw = f.read().decode('utf-8')
|
|
||||||
except (EnvironmentError, UnicodeDecodeError):
|
|
||||||
return
|
|
||||||
group = None
|
|
||||||
ans = {}
|
|
||||||
ans['desktop_file_path'] = path
|
|
||||||
for line in raw.splitlines():
|
|
||||||
m = gpat.match(line)
|
|
||||||
if m is not None:
|
|
||||||
if group == 'Desktop Entry':
|
|
||||||
break
|
|
||||||
group = m.group(1)
|
|
||||||
continue
|
|
||||||
if group == 'Desktop Entry':
|
|
||||||
m = kpat.match(line)
|
|
||||||
if m is not None:
|
|
||||||
k, v = m.group(1), m.group(2)
|
|
||||||
if k == 'Hidden' and v == 'true':
|
|
||||||
return
|
|
||||||
if k == 'Type' and v != 'Application':
|
|
||||||
return
|
|
||||||
if k == 'Exec':
|
|
||||||
cmdline = unquote_exec(v)
|
|
||||||
if cmdline and (not os.path.isabs(cmdline[0]) or os.access(cmdline[0], os.X_OK)):
|
|
||||||
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:
|
|
||||||
name, lang = parse_localized_key(k)
|
|
||||||
if name not in ans:
|
|
||||||
ans[name] = {}
|
|
||||||
if isinstance(ans[name], type('')):
|
|
||||||
ans[name] = {None:ans[name]}
|
|
||||||
ans[name][lang] = v
|
|
||||||
else:
|
|
||||||
ans[k] = v
|
|
||||||
if 'Exec' in ans and 'MimeType' in ans and 'Name' in ans:
|
|
||||||
return ans
|
|
||||||
|
|
||||||
icon_data = None
|
|
||||||
|
|
||||||
def find_icons():
|
|
||||||
global icon_data
|
|
||||||
if icon_data is not None:
|
|
||||||
return icon_data
|
|
||||||
base_dirs = [os.path.expanduser('~/.icons')] + [
|
|
||||||
os.path.join(b, 'icons') for b in os.environ.get(
|
|
||||||
'XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(os.pathsep)] + [
|
|
||||||
'/usr/share/pixmaps']
|
|
||||||
ans = defaultdict(list)
|
|
||||||
sz_pat = re.compile(r'/((?:\d+x\d+)|scalable)/')
|
|
||||||
cache_file = os.path.join(cache_dir(), 'icon-theme-cache.pickle')
|
|
||||||
exts = {'.svg', '.png', '.xpm'}
|
|
||||||
|
|
||||||
def read_icon_theme_dir(dirpath):
|
|
||||||
ans = defaultdict(list)
|
|
||||||
for path in walk(dirpath):
|
|
||||||
bn = os.path.basename(path)
|
|
||||||
name, ext = os.path.splitext(bn)
|
|
||||||
if ext in exts:
|
|
||||||
sz = sz_pat.findall(path)
|
|
||||||
if sz:
|
|
||||||
sz = sz[-1]
|
|
||||||
if sz == 'scalable':
|
|
||||||
sz = 100000
|
|
||||||
else:
|
|
||||||
sz = int(sz.partition('x')[0])
|
|
||||||
idx = len(ans[name])
|
|
||||||
ans[name].append((-sz, idx, sz, path))
|
|
||||||
for icons in ans.itervalues():
|
|
||||||
icons.sort()
|
|
||||||
return {k:(-v[0][2], v[0][3]) for k, v in ans.iteritems()}
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(cache_file, 'rb') as f:
|
|
||||||
cache = cPickle.load(f)
|
|
||||||
mtimes, cache = cache['mtimes'], cache['data']
|
|
||||||
except Exception:
|
|
||||||
mtimes, cache = defaultdict(int), defaultdict(dict)
|
|
||||||
|
|
||||||
seen_dirs = set()
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
for loc in base_dirs:
|
|
||||||
try:
|
|
||||||
subdirs = os.listdir(loc)
|
|
||||||
except EnvironmentError:
|
|
||||||
continue
|
|
||||||
for dname in subdirs:
|
|
||||||
d = os.path.join(loc, dname)
|
|
||||||
if os.path.isdir(d):
|
|
||||||
try:
|
|
||||||
mtime = os.stat(d).st_mtime
|
|
||||||
except EnvironmentError:
|
|
||||||
continue
|
|
||||||
seen_dirs.add(d)
|
|
||||||
if mtime != mtimes[d]:
|
|
||||||
changed = True
|
|
||||||
try:
|
|
||||||
cache[d] = read_icon_theme_dir(d)
|
|
||||||
except Exception:
|
|
||||||
prints('Failed to read icon theme dir: %r with error:' % d)
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
mtimes[d] = mtime
|
|
||||||
for name, data in cache[d].iteritems():
|
|
||||||
ans[name].append(data)
|
|
||||||
for removed in set(mtimes) - seen_dirs:
|
|
||||||
mtimes.pop(removed), cache.pop(removed)
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if changed:
|
|
||||||
try:
|
|
||||||
with open(cache_file, 'wb') as f:
|
|
||||||
cPickle.dump({'data':cache, 'mtimes':mtimes}, f, -1)
|
|
||||||
except Exception:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
for icons in ans.itervalues():
|
|
||||||
icons.sort()
|
|
||||||
icon_data = {k:v[0][1] for k, v in ans.iteritems()}
|
|
||||||
return icon_data
|
|
||||||
|
|
||||||
def localize_string(data):
|
|
||||||
lang = canonicalize_lang(get_lang())
|
|
||||||
return data.get(lang, data.get(None)) or ''
|
|
||||||
|
|
||||||
def find_programs(extensions):
|
|
||||||
extensions = {ext.lower() for ext in extensions}
|
|
||||||
data_dirs = [os.environ.get('XDG_DATA_HOME') or os.path.expanduser('~/.local/share')]
|
|
||||||
data_dirs += (os.environ.get('XDG_DATA_DIRS') or '/usr/local/share/:/usr/share/').split(os.pathsep)
|
|
||||||
data_dirs = [force_unicode(x, filesystem_encoding).rstrip(os.sep) for x in data_dirs]
|
|
||||||
data_dirs = [x for x in data_dirs if x and os.path.isdir(x)]
|
|
||||||
desktop_files = {}
|
|
||||||
mime_types = {guess_type('file.' + ext)[0] for ext in extensions}
|
|
||||||
ans = []
|
|
||||||
for base in data_dirs:
|
|
||||||
for f in walk(os.path.join(base, 'applications')):
|
|
||||||
if f.endswith('.desktop'):
|
|
||||||
bn = os.path.basename(f)
|
|
||||||
if f not in desktop_files:
|
|
||||||
desktop_files[bn] = f
|
|
||||||
for bn, path in desktop_files.iteritems():
|
|
||||||
try:
|
|
||||||
data = parse_desktop_file(path)
|
|
||||||
except Exception:
|
|
||||||
import traceback
|
|
||||||
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')
|
|
||||||
for k in ('Name', 'GenericName', 'Comment'):
|
|
||||||
val = data.get(k)
|
|
||||||
if val:
|
|
||||||
data[k] = localize_string(val)
|
|
||||||
ans.append(data)
|
|
||||||
ans.sort(key=lambda d:sort_key(d.get('Name')))
|
|
||||||
return ans
|
|
||||||
|
|
||||||
def entry_to_item(entry, parent):
|
def entry_to_item(entry, parent):
|
||||||
icon_path = entry.get('Icon') or I('blank.png')
|
icon_path = entry.get('Icon') or I('blank.png')
|
||||||
@ -273,42 +99,6 @@ else:
|
|||||||
entry['icon_data'] = pixmap_to_data(pmap)
|
entry['icon_data'] = pixmap_to_data(pmap)
|
||||||
entry['MimeType'] = tuple(entry['MimeType'])
|
entry['MimeType'] = tuple(entry['MimeType'])
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
def entry_sort_key(entry):
|
|
||||||
return sort_key(entry['Name'])
|
|
||||||
|
|
||||||
def entry_to_icon_text(entry):
|
|
||||||
data = entry.get('icon_data')
|
|
||||||
if data is None:
|
|
||||||
icon = QIcon(I('blank.png'))
|
|
||||||
else:
|
|
||||||
pmap = QPixmap()
|
|
||||||
pmap.loadFromData(bytes(data))
|
|
||||||
icon = QIcon(pmap)
|
|
||||||
return icon, entry['Name']
|
|
||||||
|
|
||||||
def entry_to_cmdline(entry, path):
|
|
||||||
path = os.path.abspath(path)
|
|
||||||
rmap = {
|
|
||||||
'f':path, 'F':path, 'u':'file://'+path, 'U':'file://'+path, '%':'%',
|
|
||||||
'c':entry.get('Name', ''), 'k':entry.get('desktop_file_path', ''),
|
|
||||||
}
|
|
||||||
def replace(match):
|
|
||||||
char = match.group()[-1]
|
|
||||||
repl = rmap.get(char)
|
|
||||||
return match.group() if repl is None else repl
|
|
||||||
sub = re.compile(r'%[fFuUdDnNickvm%]').sub
|
|
||||||
cmd = entry['Exec']
|
|
||||||
try:
|
|
||||||
idx = cmd.index('%i')
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
icon = entry.get('Icon')
|
|
||||||
repl = ['--icon', icon] if icon else []
|
|
||||||
cmd[idx:idx+1] = repl
|
|
||||||
return cmd[:1] + [sub(replace, x) for x in cmd[1:]]
|
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class ChooseProgram(Dialog): # {{{
|
class ChooseProgram(Dialog): # {{{
|
||||||
|
1
src/calibre/utils/open_with/__init__.py
Normal file
1
src/calibre/utils/open_with/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
223
src/calibre/utils/open_with/linux.py
Normal file
223
src/calibre/utils/open_with/linux.py
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
import re, shlex, os, cPickle
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from calibre import walk, guess_type, prints, force_unicode
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def unquote_exec(val):
|
||||||
|
val = val.replace(r'\\', '\\')
|
||||||
|
return shlex.split(val)
|
||||||
|
|
||||||
|
def parse_desktop_file(path):
|
||||||
|
gpat = re.compile(r'^\[(.+?)\]\s*$')
|
||||||
|
kpat = re.compile(r'^([-a-zA-Z0-9\[\]@_.]+)\s*=\s*(.+)$')
|
||||||
|
try:
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
raw = f.read().decode('utf-8')
|
||||||
|
except (EnvironmentError, UnicodeDecodeError):
|
||||||
|
return
|
||||||
|
group = None
|
||||||
|
ans = {}
|
||||||
|
ans['desktop_file_path'] = path
|
||||||
|
for line in raw.splitlines():
|
||||||
|
m = gpat.match(line)
|
||||||
|
if m is not None:
|
||||||
|
if group == 'Desktop Entry':
|
||||||
|
break
|
||||||
|
group = m.group(1)
|
||||||
|
continue
|
||||||
|
if group == 'Desktop Entry':
|
||||||
|
m = kpat.match(line)
|
||||||
|
if m is not None:
|
||||||
|
k, v = m.group(1), m.group(2)
|
||||||
|
if k == 'Hidden' and v == 'true':
|
||||||
|
return
|
||||||
|
if k == 'Type' and v != 'Application':
|
||||||
|
return
|
||||||
|
if k == 'Exec':
|
||||||
|
cmdline = unquote_exec(v)
|
||||||
|
if cmdline and (not os.path.isabs(cmdline[0]) or os.access(cmdline[0], os.X_OK)):
|
||||||
|
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:
|
||||||
|
name, lang = parse_localized_key(k)
|
||||||
|
if name not in ans:
|
||||||
|
ans[name] = {}
|
||||||
|
if isinstance(ans[name], type('')):
|
||||||
|
ans[name] = {None:ans[name]}
|
||||||
|
ans[name][lang] = v
|
||||||
|
else:
|
||||||
|
ans[k] = v
|
||||||
|
if 'Exec' in ans and 'MimeType' in ans and 'Name' in ans:
|
||||||
|
return ans
|
||||||
|
|
||||||
|
icon_data = None
|
||||||
|
|
||||||
|
def find_icons():
|
||||||
|
global icon_data
|
||||||
|
if icon_data is not None:
|
||||||
|
return icon_data
|
||||||
|
base_dirs = [os.path.expanduser('~/.icons')] + [
|
||||||
|
os.path.join(b, 'icons') for b in os.environ.get(
|
||||||
|
'XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(os.pathsep)] + [
|
||||||
|
'/usr/share/pixmaps']
|
||||||
|
ans = defaultdict(list)
|
||||||
|
sz_pat = re.compile(r'/((?:\d+x\d+)|scalable)/')
|
||||||
|
cache_file = os.path.join(cache_dir(), 'icon-theme-cache.pickle')
|
||||||
|
exts = {'.svg', '.png', '.xpm'}
|
||||||
|
|
||||||
|
def read_icon_theme_dir(dirpath):
|
||||||
|
ans = defaultdict(list)
|
||||||
|
for path in walk(dirpath):
|
||||||
|
bn = os.path.basename(path)
|
||||||
|
name, ext = os.path.splitext(bn)
|
||||||
|
if ext in exts:
|
||||||
|
sz = sz_pat.findall(path)
|
||||||
|
if sz:
|
||||||
|
sz = sz[-1]
|
||||||
|
if sz == 'scalable':
|
||||||
|
sz = 100000
|
||||||
|
else:
|
||||||
|
sz = int(sz.partition('x')[0])
|
||||||
|
idx = len(ans[name])
|
||||||
|
ans[name].append((-sz, idx, sz, path))
|
||||||
|
for icons in ans.itervalues():
|
||||||
|
icons.sort()
|
||||||
|
return {k:(-v[0][2], v[0][3]) for k, v in ans.iteritems()}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(cache_file, 'rb') as f:
|
||||||
|
cache = cPickle.load(f)
|
||||||
|
mtimes, cache = cache['mtimes'], cache['data']
|
||||||
|
except Exception:
|
||||||
|
mtimes, cache = defaultdict(int), defaultdict(dict)
|
||||||
|
|
||||||
|
seen_dirs = set()
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
for loc in base_dirs:
|
||||||
|
try:
|
||||||
|
subdirs = os.listdir(loc)
|
||||||
|
except EnvironmentError:
|
||||||
|
continue
|
||||||
|
for dname in subdirs:
|
||||||
|
d = os.path.join(loc, dname)
|
||||||
|
if os.path.isdir(d):
|
||||||
|
try:
|
||||||
|
mtime = os.stat(d).st_mtime
|
||||||
|
except EnvironmentError:
|
||||||
|
continue
|
||||||
|
seen_dirs.add(d)
|
||||||
|
if mtime != mtimes[d]:
|
||||||
|
changed = True
|
||||||
|
try:
|
||||||
|
cache[d] = read_icon_theme_dir(d)
|
||||||
|
except Exception:
|
||||||
|
prints('Failed to read icon theme dir: %r with error:' % d)
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
mtimes[d] = mtime
|
||||||
|
for name, data in cache[d].iteritems():
|
||||||
|
ans[name].append(data)
|
||||||
|
for removed in set(mtimes) - seen_dirs:
|
||||||
|
mtimes.pop(removed), cache.pop(removed)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
try:
|
||||||
|
with open(cache_file, 'wb') as f:
|
||||||
|
cPickle.dump({'data':cache, 'mtimes':mtimes}, f, -1)
|
||||||
|
except Exception:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
for icons in ans.itervalues():
|
||||||
|
icons.sort()
|
||||||
|
icon_data = {k:v[0][1] for k, v in ans.iteritems()}
|
||||||
|
return icon_data
|
||||||
|
|
||||||
|
def localize_string(data):
|
||||||
|
lang = canonicalize_lang(get_lang())
|
||||||
|
return data.get(lang, data.get(None)) or ''
|
||||||
|
|
||||||
|
def find_programs(extensions):
|
||||||
|
extensions = {ext.lower() for ext in extensions}
|
||||||
|
data_dirs = [os.environ.get('XDG_DATA_HOME') or os.path.expanduser('~/.local/share')]
|
||||||
|
data_dirs += (os.environ.get('XDG_DATA_DIRS') or '/usr/local/share/:/usr/share/').split(os.pathsep)
|
||||||
|
data_dirs = [force_unicode(x, filesystem_encoding).rstrip(os.sep) for x in data_dirs]
|
||||||
|
data_dirs = [x for x in data_dirs if x and os.path.isdir(x)]
|
||||||
|
desktop_files = {}
|
||||||
|
mime_types = {guess_type('file.' + ext)[0] for ext in extensions}
|
||||||
|
ans = []
|
||||||
|
for base in data_dirs:
|
||||||
|
for f in walk(os.path.join(base, 'applications')):
|
||||||
|
if f.endswith('.desktop'):
|
||||||
|
bn = os.path.basename(f)
|
||||||
|
if f not in desktop_files:
|
||||||
|
desktop_files[bn] = f
|
||||||
|
for bn, path in desktop_files.iteritems():
|
||||||
|
try:
|
||||||
|
data = parse_desktop_file(path)
|
||||||
|
except Exception:
|
||||||
|
import traceback
|
||||||
|
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')
|
||||||
|
for k in ('Name', 'GenericName', 'Comment'):
|
||||||
|
val = data.get(k)
|
||||||
|
if val:
|
||||||
|
data[k] = localize_string(val)
|
||||||
|
ans.append(data)
|
||||||
|
ans.sort(key=lambda d:sort_key(d.get('Name')))
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def entry_sort_key(entry):
|
||||||
|
return sort_key(entry['Name'])
|
||||||
|
|
||||||
|
def entry_to_cmdline(entry, path):
|
||||||
|
path = os.path.abspath(path)
|
||||||
|
rmap = {
|
||||||
|
'f':path, 'F':path, 'u':'file://'+path, 'U':'file://'+path, '%':'%',
|
||||||
|
'c':entry.get('Name', ''), 'k':entry.get('desktop_file_path', ''),
|
||||||
|
}
|
||||||
|
def replace(match):
|
||||||
|
char = match.group()[-1]
|
||||||
|
repl = rmap.get(char)
|
||||||
|
return match.group() if repl is None else repl
|
||||||
|
sub = re.compile(r'%[fFuUdDnNickvm%]').sub
|
||||||
|
cmd = entry['Exec']
|
||||||
|
try:
|
||||||
|
idx = cmd.index('%i')
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
icon = entry.get('Icon')
|
||||||
|
repl = ['--icon', icon] if icon else []
|
||||||
|
cmd[idx:idx+1] = repl
|
||||||
|
return cmd[:1] + [sub(replace, x) for x in cmd[1:]]
|
Loading…
x
Reference in New Issue
Block a user