diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 8b5b6fe3..0abea4cd 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,5 +1,5 @@ scriptdir = $(pkgdatadir)/scripts -script_SCRIPTS = e_gen_menu.pl e_cache_clean e_cache_query session.sh +script_SCRIPTS = e_gen_menu e_cache_clean e_cache_query session.sh EXTRA_DIST = $(script_SCRIPTS) diff --git a/scripts/e_gen_menu b/scripts/e_gen_menu new file mode 100755 index 00000000..185c1f62 --- /dev/null +++ b/scripts/e_gen_menu @@ -0,0 +1,514 @@ +#!/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 + 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) + + +# Process old style GNOME/KDE directories +def ProcessOldStyle(type, dirs): + for dir in dirs: + D(f'Processing directory: {dir}') + if not os.path.isdir(dir): + D(f'- Not found') + confinue + for file in os.listdir(dir): + dir2 = f'{dir}/{file}' + D(f' Subdir: {dir2}') + if not os.path.isdir(dir2): + continue + ProcessDir(dir2, file, 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']: + 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']: + 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', 'Adwaita'] +IconDirs2 = MkDirList(IconDirs2, Themes, sep='/') +IconCats = ['apps', 'filesystems', 'actions', 'devices', 'categories', 'places', 'mimetypes'] +IconSizes = ['48x48', '32x32', '24x24', '16x16'] + +D(f'IconDirs = "{IconDirs}"') +D(f'IconDirs2 = "{IconDirs2}"') + +# Where to look for GNOME1 apps +OldGnomeDirs = MkDirList(Prefixes, ['/share/gnome/apps']) +D(f'OldGnomeDirs = "{OldGnomeDirs}"') + +# Where to look for KDE1/2 apps +OldKdeDirs = MkDirList(Prefixes, ['/share/applnk']) +D(f'OldKdeDirs = "{OldKdeDirs}"') + +# 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:Eterm:Eterm', + 'x:Terminal:Terminal', + 'x:XTerm:xterm', + 'x:rxvt:rxvt', + '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', + 'x:XMMS:xmms' +] + +EeshCloseMessageWindows() +EeshCall('dialog_ok Menus are being generated... Please Wait') + +# Process old style GNOME directories +ProcessOldStyle('GNOME', OldGnomeDirs) + +# Process old style KDE directories +ProcessOldStyle('KDE', OldKdeDirs) + +# 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')