e16/scripts/e_gen_menu

488 lines
12 KiB
Python
Executable File

#!/usr/bin/python3
#
# generates menus for e16 from .desktop files
#
import os, sys
import getopt
import subprocess
dbg = 0
N = 0
dtis = {}
dt_seen = {}
class DTI: # .desktop item
def __init__(self, file, name, exec, icon, cats, type):
self.file = file
self.name = name
self.exec = exec
self.icon = icon
self.cats = cats
self.type = type
def D(str):
if dbg > 0:
print(str)
def D2(str):
if dbg >= 2:
print(str)
def D3(str):
if dbg >= 3:
print(str)
def getenv(env):
if env in os.environ:
return os.environ[env]
else:
return ''
def FileExt(file):
return os.path.splitext(file)[1]
# Make directory list, check that they exist
def MkDirList(pfxs, sufs, sep=''):
r = []
for pfx in pfxs:
for suf in sufs:
dir = f'{pfx}{sep}{suf}'
if os.path.isdir(dir):
r += [dir]
return r
# Remove duplicates and nulls in dir list
def RemoveDuplicates(arr):
r = []
for itm in arr:
if itm == '' or itm in r:
continue
r.append(itm)
return r
# Make dir if non-existing
def MkDir(dir):
if not os.path.isdir(dir):
os.makedirs(dir, 0o755)
D(f'mkdir {dir}')
# Make simple menus
def MakeMenu(name, tmpl):
file = EdirMenus + '/' + name
D(f'Generating {name} -> {file}')
if os.path.exists(file):
return
f = open(file, 'w')
for line in tmpl:
(t, n, p) = line.split(':')
if t == 't':
f.write(f'"{n}"\n')
elif t == 'm':
f.write(f'"{n}" NULL menu "{p}"\n')
elif t == 'x':
f.write(f'"{n}" NULL exec "{p}"\n')
elif t == 'c':
f.write(f'"{n}" NULL "{p}"\n')
f.close()
# Make the Epplets menu
def MakeEppsMenu(name):
dirs = []
file = EdirMenus + '/' + name
D(f'Generating {name} -> {file}')
f = open(file, 'w')
f.write('"Enlightenment Epplets"\n')
for dir in os.environ['PATH'].split(':'):
# D(f'P = {dir}')
if dir in dirs:
continue
if not os.path.isdir(dir):
continue
dirs.append(dir)
D2(f'Looking for epplets in {dir}')
for file in os.listdir(dir):
if not file.endswith('.epplet'):
continue
epp = file.removesuffix('.epplet')
f.write(f'"{epp}" "{EdirRoot}/epplet_icons/{epp}.icon" exec "{dir}/{file}"\n')
f.close()
# Process a .desktop file
def ProcessFile(file, cats, type):
global N
global dtis
D(f'- File {file}')
if not os.path.isfile(file):
print(f'Not found: {file}')
exit(1)
# Global ref no
N += 1
Name = Exec = Icon = Ndis = ''
Nam1 = Nam2 = Nam3 = ''
Cats = []
if cats:
Cats.append(cats)
Type = type
f = open(file, 'r')
for line in f:
line = line.strip()
# print(line)
if len(line) == 0:
continue
if line.startswith('#'):
continue
if line.startswith('['):
if line != '[Desktop Entry]':
D2(f'Break: {line}')
break
continue
(tok, val) = line.split('=', 1)
if tok.startswith('Name'):
if tok == 'Name':
Name = val
elif loc1 and tok == f'Name[{loc1}]':
Nam1 = val
elif loc2 and tok == f'Name[{loc2}]':
Nam2 = val
elif loc3 and tok == f'Name[{loc3}]':
Nam3 = val
if Nam1 or Nam2 or Nam3:
if Nam1:
Name = Nam1
elif Nam2:
Name = Nam2
else:
Name = Nam3
elif tok == 'Exec':
Exec = val
elif tok == 'Icon':
Icon = val
elif tok == 'OnlyShowIn':
Type = val.split(';')[0]
elif tok == 'Categories':
if cats:
continue
for cat in val.split(';'):
if cat == 'KDE':
Type = 'KDE'
continue
if cat in CatsRemove:
continue
if cat.startswith('X-'):
continue
Cats.append(cat)
elif tok == 'Type':
if val == 'Application':
continue
Name = ''
break
elif tok == 'NoDisplay':
Ndis = val
f.close()
if Ndis == 'true' or Name == '' or Exec == '' or len(Cats) == 0:
D3('Skipped: %-24s %-4s %-24s %-20s %-20s %s' %
(file, Name, Ndis, Exec, Icon, Cats))
return
# Basename
File = os.path.basename(file)
D3('%-24s: %-24s %-20s %-20s %s\n' % (File, Name, Exec, Icon, Cats))
if not Type:
if File.startswith('gnome'):
Type = 'GNOME'
elif File.startswith('kde'):
Type = 'KDE'
else:
Type = 'Other'
ndti = f'{Name}-{N}' # Make key unique
ndti = ndti.lower() # To lower case (for sorting)
# $Exec =~ s/\s*%(f|F|i|k|m|n|N|u|U|v)//g; # Strip unwanted args
# $Exec =~ s/\s*-\w+\s*"%c"//g; # Strip option with caption
## $Exec =~ s/"%c"/'$Name'/g; # Alternatively - Substitute caption
Exec = Exec.split('%', 1)[0].strip() # Strip all args
dti = DTI(File, Name, Exec, Icon, Cats, Type)
dtis[ndti] = dti
# Process all .desktop files in a directory
def ProcessDir(dir, cats, type):
global dt_seen
for name in os.listdir(dir):
if not name.endswith('.desktop'):
continue
if name in dt_seen:
D(f'Skip duplicate {name} (in {dir})')
continue
dt_seen[name] = 1
file = f'{dir}/{name}'
ProcessFile(file, cats, type)
# Find that $#@! thing
def FindIcon(icon):
if not icon:
return icon
if icon.startswith('/'):
if os.path.isfile(icon):
return icon
return ''
for dir in IconDirs:
file = f'{dir}/{icon}'
D2(f'Check icon: {icon} : {file}')
if os.path.isfile(file):
return file
if not FileExt(icon) in ['png', 'xpm', 'svg']:
for ext in ['png', 'xpm']:
fil2 = f'{file}.{ext}'
D2(f'Check icon: {icon} : {fil2}')
if os.path.isfile(fil2):
return fil2
for dir in IconDirs2:
for sz in IconSizes:
sdir = f'{dir}/{sz}'
if not os.path.isdir(sdir):
continue
D2(f'Check icon: {icon} in {sdir}')
if icon.startswith('stock'):
glob = f'{sdir}/stock/*/{icon}.png'
D2(f'Check icon: {icon} in {glob}')
# FIXME
# $ii = glob("$i");
# return $ii if (-f $ii);
else:
for icat in IconCats:
idir = f'{sdir}/{icat}'
if not os.path.isdir(idir):
continue
file = f'{idir}/{icon}'
if FileExt(icon) in ['png', 'xpm', 'svg']:
D2(f'Check icon: {icon}: {file}')
if os.path.isfile(file):
return file
else:
for ext in ['png', 'xpm', 'svg']:
fil2 = f'{file}.{ext}'
D2(f'Check icon: {icon}: {fil2}')
if os.path.isfile(fil2):
return fil2
D(f'Icon not found: {icon}')
return icon
# Make the menu for a given app type
def MakeAppsMenu(type):
mdir = f'menus_{type}'
dir = f'{EdirMenus}/{mdir}'
D(f'Generating menu: {type} in {dir}')
MkDir(dir)
# Sort the apps into categories
menus = {}
for ndti in sorted(dtis):
dti = dtis[ndti]
# if dti.type != type: continue
cat = dti.cats[0] # First category
if cat not in menus:
menus[cat] = []
menus[cat].append(ndti)
# Make top- and sub-menus
ftop = open(f'{dir}/index.menu', 'w')
ftop.write(f'"{type} Menu"\n')
for cat in sorted(menus):
fsub = open(f'{dir}/{cat}.menu', 'w')
D(f'- Submenu: {cat}')
ftop.write(f'"{cat}" "" menu "{mdir}/{cat}.menu"\n')
fsub.write(f'"{cat}"\n')
for ndti in sorted(menus[cat]):
dti = dtis[ndti]
D2(f' - Item: {ndti}: {dti.file}')
icon = FindIcon(dti.icon)
fsub.write('"%s" "%s" exec "%s"\n' % (dti.name, icon, dti.exec))
fsub.close()
ftop.close()
def EeshCall(cmd):
# os.system(f'eesh -e "{cmd}" >/dev/null')
subprocess.run(["eesh", "-e", cmd], stdout=subprocess.DEVNULL)
# Close all windows named "Message" (we assume they are E dialogs)
def EeshCloseMessageWindows():
EeshCall('wop Message* close')
##############################################################################
# Here we go
##############################################################################
opts, args = getopt.getopt(sys.argv[1:], 'd', ['debug'])
for opt, val in opts:
if opt in ['-d', '--debug']:
dbg += 1
# Likely prefixes
Prefixes = ['/usr/local', '/usr', '/opt']
Prefixes += ['/opt/kde', '/opt/kde3', getenv('KDEDIR')]
Prefixes += ['/opt/gnome'] # SUSE
Prefixes += [getenv('HOME') + '/.local']
D(f'Prefixes = "{Prefixes}"')
Prefixes = RemoveDuplicates(Prefixes)
D(f'Prefixes = "{Prefixes}"')
SufDirs = ['/share/applications', '/share/applications/kde', '/share/applications/kde4']
# Where to look for GNOME/KDE stuff
AppDirs = MkDirList(Prefixes, SufDirs)
D(f'AppDirs = "{AppDirs}"')
IconDirs = MkDirList(Prefixes, ['/share/pixmaps', '/share/icons'])
IconDirs2 = MkDirList(Prefixes, ['/share/icons'])
Themes = ['default.kde', 'gnome', 'hicolor', 'mate', 'Adwaita']
#Themes += ['HighContrast']
IconDirs2 = MkDirList(IconDirs2, Themes, sep='/')
IconCats = ['apps', 'filesystems', 'actions', 'devices', 'categories', 'places', 'mimetypes']
IconCats += ['legacy']
#IconCats += ['stock']
IconSizes = ['48x48', '32x32', '24x24', '128x128', '16x16']
IconSizes += ['scalable']
D(f'IconDirs = "{IconDirs}"')
D(f'IconDirs2 = "{IconDirs2}"')
# Pick up env vars
EdirUser = getenv('ECONFDIR')
EdirRoot = getenv('EROOT')
EdirBin = getenv('EBIN')
if EdirUser == '':
EdirUser = getenv('HOME') + '/.e16'
if EdirRoot == '':
EdirRoot = '/usr/share/e16'
if EdirBin == '':
EdirBin = '/usr/bin'
D(f'EdirUser = "{EdirUser}"')
D(f'EdirRoot = "{EdirRoot}"')
D(f'EdirBin = "{EdirBin}"')
EdirMenus = EdirUser + '/menus'
D(f'EdirMenus = "{EdirMenus}"')
# Localization bits. There may be better ways to do this.
Lang = getenv('LANG')
loc1 = Lang
loc2 = Lang.split('.')[0].split('@')[0]
loc3 = Lang.split('_')[0]
if loc1 == loc2:
loc1 = ''
D(f'Locale = "{Lang}:{loc1}:{loc2}:{loc3}"')
# Put EBIN first in path
os.environ['PATH'] = f"{EdirBin}:{os.environ['PATH']}"
CatsRemove = [
'ConsoleOnly',
'Qt',
'QT',
'GTK',
'GNOME',
'KDE',
'UtilityApplication',
'Applications',
'Application',
#'X-.*',
]
MainMenu = [
't:User menus:',
'm:User application list:user_apps.menu',
'm:Applications:menus_apps/index.menu',
'm:Epplets:epplets.menu',
'c:Restart:exit restart',
'c:Log out:exit logout'
]
UserAppsMenu = [
't:User Application List:',
'x:XTerm:xterm',
'x:urxvt:urxvt',
'x:Firefox:firefox',
'x:Thunderbird:thunderbird',
'x:Seamonkey:seamonkey',
'x:Shotwell:shotwell',
'x:Pidgin:pidgin',
'x:Gmplayer:gmplayer',
'x:Xine:xine',
'x:The GIMP:gimp',
'x:Geeqie:geeqie',
'x:XV:xv',
'x:XMag:xmag',
'x:Grip:grip',
'x:Audacious:audacious',
]
EeshCloseMessageWindows()
EeshCall('dialog_ok Menus are being generated... Please Wait')
# Process new style (GNOME2, KDE2/3) directories
for dir in AppDirs:
D(f'Processing directory: {dir}')
if not os.path.isdir(dir):
D(f'- Not found')
continue
ProcessDir(dir, None, None)
# Make menu dir and scaled icon dir
MkDir(EdirMenus)
# Make the menus
MakeMenu('file.menu', MainMenu)
MakeMenu('user_apps.menu', UserAppsMenu)
MakeEppsMenu('epplets.menu')
MakeAppsMenu('apps')
EeshCloseMessageWindows()
EeshCall('menus reload')
EeshCall('dialog_ok Menu generation complete')