A command line tool that generates XDG menus for several window managers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

890 lines
33 KiB

#!/usr/bin/env python3
# coding: utf-8
# vim:et:sta:sts=4:sw=4:ts=8:tw=79:
import os
import sys
import getopt
import fnmatch
import xdg.DesktopEntry as dentry
import xdg.Exceptions as exc
import xdg.BaseDirectory as bd
from operator import attrgetter
import configparser as cp
# Load the gtk compatibility layer
from gi import pygtkcompat
pygtkcompat.enable()
pygtkcompat.enable_gtk(version='3.0')
import gtk
seticon = False
iconsize = 16
nosvg = False
desktop = False
submenu = True
pekwmdynamic = False
twmtitles = False
max_icon_size = False
# the following line gets changed by the Makefile. If it is set to
# 'not_set' it looks in the currect directory tree for the .directory
# files. If it is actually set to something else, it looks under there
# for them, where they should be if this was installed properly
prefix = 'not_set'
if prefix == 'not_set':
desktop_dir = '../desktop-directories/'
else:
desktop_dir = '{}/share/desktop-directories/'.format(prefix)
if not os.path.isdir(desktop_dir):
sys.exit('ERROR: Could not find {}'.format(desktop_dir))
class App:
'''
A class to keep individual app details in.
'''
def __init__(self, name, icon, command, path):
self.name = name
self.icon = icon
self.command = command
self.path = path
def __repr__(self):
return repr((self.name, self.icon, self.command,
self.path))
class MenuEntry:
'''
A class for each menu entry. Includes the class category and app details
from the App class.
'''
def __init__(self, category, app):
self.category = category
self.app = app
def __repr__(self):
return repr((self.category, self.app.name, self.app.icon,
self.app.command, self.app.path))
class MenuCategory:
'''
A class for each menu category. Keeps the category name and the list of
apps that go in that category.
'''
def __init__(self, category, applist):
self.category = category
self.applist = applist
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-applications.directory')
applications = de.getName().encode('utf-8')
apps_name = applications.decode()
applications_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-accessories.directory')
accessories = de.getName().encode('utf-8')
accessories_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-development.directory')
development = de.getName().encode('utf-8')
development_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-education.directory')
education = de.getName().encode('utf-8')
education_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir + 'xdgmenumaker-games.directory')
games = de.getName().encode('utf-8')
games_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-graphics.directory')
graphics = de.getName().encode('utf-8')
graphics_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-multimedia.directory')
multimedia = de.getName().encode('utf-8')
multimedia_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-network.directory')
network = de.getName().encode('utf-8')
network_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-office.directory')
office = de.getName().encode('utf-8')
office_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-settings.directory')
settings = de.getName().encode('utf-8')
settings_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-system.directory')
system = de.getName().encode('utf-8')
system_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir + 'xdgmenumaker-other.directory')
other = de.getName().encode('utf-8')
other_icon = de.getIcon()
# Find out which terminal emulator to use for apps that need to be
# launched in a terminal.
# First check if the XDGMENUMAKERTERM environment variable is set and use it if
# it is.
# Then see if there is a user specified terminal emulator in the
# xdgmenumaker.cfg file.
terminal_app = os.getenv("XDGMENUMAKERTERM")
if not terminal_app:
try:
config = cp.ConfigParser()
config.read(os.path.expanduser('~/.config/xdgmenumaker.cfg'))
terminal_app = config.get('Terminal', 'terminal')
# if there isn't, on debian and debian-likes, use the alternatives
# system, otherwise default to xterm
except (cp.NoSectionError, cp.NoOptionError) as e:
if (os.path.exists('/etc/alternatives/x-terminal-emulator')
and os.path.exists('/usr/bin/x-terminal-emulator')):
terminal_app = '/usr/bin/x-terminal-emulator'
else:
terminal_app = 'xterm'
def main(argv):
global desktop
global seticon
global iconsize
global nosvg
global submenu
global pekwmdynamic
global twmtitles
global max_icon_size
try:
opts, args = getopt.getopt(argv, "hins:f:", ["help", "icons",
"no-submenu",
"pekwm-dynamic",
"twm-titles",
"max-icon-size",
"no-svg",
"size=",
"format="])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-h", "--help"):
usage()
sys.exit(0)
elif opt in ("-i", "--icons"):
seticon = True
elif opt in ("-s", "--size"):
try:
iconsize = int(arg)
except ValueError:
usage()
sys.exit('ERROR: size must be a number')
elif opt in ("-n", "--no-submenu"):
submenu = False
elif opt in ("--pekwm-dynamic",):
pekwmdynamic = True
elif opt in ("--twm-titles",):
twmtitles = True
elif opt in ("--max-icon-size",):
try:
# Pillow is optional and loaded only if we want to restrict the
# icon sizes (useful for Fvwm). Yeah, I know it's not a good
# idea to load a module in here, but I really don't want to
# load it by default at the top. It would make xdgmenumaker a
# bit slower to run even if it is not needed. This way it only
# slows down when it is actually needed.
global Image
from PIL import Image
max_icon_size = True
except ImportError:
usage()
sys.exit('ERROR: --max-icon-size requires Pillow')
elif opt == "--no-svg":
nosvg = True
elif opt in ("-f", "--format"):
desktop = arg
if not desktop:
usage()
sys.exit('ERROR: You must specify the output format with -f')
elif desktop == "blackbox":
blackbox()
elif desktop == "fluxbox":
fluxbox()
elif desktop == "fvwm":
fvwm()
elif desktop == "windowmaker":
seticon = False
windowmaker()
elif desktop == "icewm":
icewm()
elif desktop == "pekwm":
pekwmmenu()
elif desktop == "jwm":
jwm()
elif desktop == "compizboxmenu":
compizboxmenu()
elif desktop == "twm":
twm()
elif desktop == "amiwm":
seticon = False
amiwm()
else:
usage()
sys.exit(2)
def usage():
print('USAGE:', os.path.basename(sys.argv[0]), '[OPTIONS]')
print()
print('OPTIONS:')
print(' -f, --format the output format to use.')
print(' Valid options are amiwm, blackbox, compizboxmenu,')
print(' fluxbox, fvwm, twm, icewm, jwm, windowmaker and pekwm')
print(' -i, --icons enable support for icons in the')
print(' menus. Does not work with windowmaker or amiwm')
print(' --no-svg Do not use SVG icons even for WMs that support it')
print(' -s, --size preferred icon size in pixels (default: 16)')
print(' -n, --no-submenu do not create a submenu. Does not work with')
print(' windowmaker')
print(' --max-icon-size restrict the icon sizes to the specified size')
print(' --pekwm-dynamic generate dynamic menus for pekwm')
print(' --twm-titles show menu titles in twm menus')
print(' -h, --help show this help message')
print(' You have to specify the output format using the -f switch.')
print()
print('EXAMPLES:')
print(' xdgmenumaker -f windowmaker')
print(' xdgmenumaker -i -f fluxbox')
def icon_strip(icon):
# strip the directory and extension from the icon name
icon = os.path.basename(icon)
main, ext = os.path.splitext(icon)
ext = ext.lower()
if ext == '.png' or ext == '.svg' or ext == '.svgz' or ext == '.xpm':
return main
return icon
def icon_max_size(icon):
# Checks if the icon size is bigger than the requested size and discards
# the icon if it is, only allowing sizes smaller or equal to the requested
# size
try:
img = Image.open(icon)
except:
# if there is any error reading the icon, just discard it
return None
if img.size[0] <= iconsize:
return icon
return None
def icon_full_path(icon):
# If the icon path is absolute and exists, leave it alone.
# This takes care of software that has its own icons stored
# in non-standard directories.
ext = os.path.splitext(icon)[1].lower()
if os.path.exists(icon):
if ext == ".svg" or ext == ".svgz":
# only jwm supports svg icons
if desktop == "jwm" and not nosvg:
return icon
else:
# icon is not svg
if max_icon_size:
return icon_max_size(icon)
else:
return icon
# fall back to looking for the icon elsewhere in the system
icon = icon_strip(icon)
icon_theme = gtk.icon_theme_get_default()
try:
# JWM supports svg icons
if desktop == "jwm" and not nosvg:
icon = icon_theme.lookup_icon(icon, iconsize, gtk.ICON_LOOKUP_FORCE_SVG)
# but none of the other WMs does
else:
icon = icon_theme.lookup_icon(icon, iconsize, gtk.ICON_LOOKUP_NO_SVG)
except AttributeError:
sys.exit('ERROR: You need to run xdgmenumaker inside an X session.')
if icon:
icon = icon.get_filename()
# icon size only matters for non-SVG icons
if icon and max_icon_size and (ext != ".svg" or ext != ".svgz"):
icon = icon_max_size(icon)
return icon
def remove_command_keys(command, desktopfile, icon):
# replace the %i (icon key) if it's there. This is what freedesktop has to
# say about it: "The Icon key of the desktop entry expanded as two
# arguments, first --icon and then the value of the Icon key. Should not
# expand to any arguments if the Icon key is empty or missing."
if icon:
command = command.replace('"%i"', '--icon {}'.format(icon))
command = command.replace("'%i'", '--icon {}'.format(icon))
command = command.replace('%i', '--icon {}'.format(icon))
# some KDE apps have this "-caption %c" in a few variations. %c is "The
# translated name of the application as listed in the appropriate Name key
# in the desktop entry" according to freedesktop. All apps launch without a
# problem without it as far as I can tell, so it's better to remove it than
# have to deal with extra sets of nested quotes which behave differently in
# each WM. This is not 100% failure-proof. There might be other variations
# of this out there, but we can't account for every single one. If someone
# finds one another one, I can always add it later.
command = command.replace('-caption "%c"', '')
command = command.replace("-caption '%c'", '')
command = command.replace('-caption %c', '')
# same as before, although with -qwindowtitle (typical for Qt 5 apps).
command = command.replace('-qwindowtitle "%c"', '')
command = command.replace("-qwindowtitle '%c'", '')
command = command.replace('-qwindowtitle %c', '')
# replace the %k key. This is what freedesktop says about it: "The
# location of the desktop file as either a URI (if for example gotten from
# the vfolder system) or a local filename or empty if no location is
# known."
command = command.replace('"%k"', desktopfile)
command = command.replace("'%k'", desktopfile)
command = command.replace('%k', desktopfile)
# removing any remaining keys from the command. That can potentially remove
# any other trailing options after the keys,
command = command.partition('%')[0]
return command
def clean_up_categories(categories):
# cleaning up categories and keeping only registered freedesktop.org main
# categories
category_menus = {
"AudioVideo": multimedia,
"Audio": multimedia,
"Video": multimedia,
"Development": development,
"Education": education,
"Game": games,
"Graphics": graphics,
"Network": network,
"Office": office,
"System": system,
"Settings": settings,
"Utility": accessories
}
category = other
for candidate in categories:
if candidate in category_menus:
category = category_menus.get(candidate)
break
return category
def get_entry_info(desktopfile, ico_paths=True):
de = dentry.DesktopEntry(filename=desktopfile)
# skip processing the rest of the desktop entry if the item is to not be
# displayed anyway
onlyshowin = de.getOnlyShowIn()
notshowin = de.getNotShowIn()
hidden = de.getHidden()
nodisplay = de.getNoDisplay()
# none of the freedesktop registered environments are supported by
# OnlyShowIn but it might be worth using some extra logic here.
# http://standards.freedesktop.org/menu-spec/latest/apb.html
if (onlyshowin != [] and not (desktop.lower() in (name.lower() for name in onlyshowin))) \
or (desktop.lower() in (name.lower for name in notshowin)) \
or hidden or nodisplay:
return None
name = de.getName().encode('utf-8')
if seticon:
icon = de.getIcon()
if ico_paths:
icon = icon_full_path(icon)
else:
icon = icon_strip(icon)
else:
icon = None
command = de.getExec()
command = remove_command_keys(command, desktopfile, icon)
terminal = de.getTerminal()
if terminal:
command = '{term} -e {cmd}'.format(term=terminal_app, cmd=command)
path = de.getPath()
if not path:
path = None
categories = de.getCategories()
category = clean_up_categories(categories)
app = App(name, icon, command, path)
mentry = MenuEntry(category, app)
return mentry
def sortedcategories(applist):
categories = []
for e in applist:
categories.append(e.category)
categories = sorted(set(categories))
return categories
def desktopfilelist():
# if this env variable is set to 1, then only read .desktop files from the
# tests directory, not systemwide. This gives a standard set of .desktop
# files to compare against for testing.
testing = os.getenv('XDGMENUMAKER_TEST')
if testing == "1":
dirs = ['../tests']
else:
dirs = []
# some directories are mentioned twice in bd.xdg_data_dirs, once
# with and once without a trailing /
for i in bd.xdg_data_dirs:
i = i.rstrip('/')
if i not in dirs:
dirs.append(i)
filelist = []
df_temp = []
for d in dirs:
xdgdir = '{}/applications'.format(d)
if os.path.isdir(xdgdir):
for root, dirnames, filenames in os.walk(xdgdir):
for i in fnmatch.filter(filenames, '*.desktop'):
# for duplicate .desktop files that exist in more
# than one locations, only keep the first occurence.
# That one should have precedence anyway (e.g.
# ~/.local/share/applications has precedence over
# /usr/share/applications
if i not in df_temp:
df_temp.append(i)
filelist.append(os.path.join(root, i))
return filelist
def menu(ico_paths=True):
applist = []
for desktopfile in desktopfilelist():
try:
entry = get_entry_info(desktopfile, ico_paths=ico_paths)
if entry is not None:
applist.append(entry)
except exc.ParsingError:
pass
sortedapplist = sorted(applist, key=attrgetter('category', 'app.name'))
menu = []
for c in sortedcategories(applist):
appsincategory = []
for i in sortedapplist:
if i.category == c:
appsincategory.append(i.app)
menu_category = MenuCategory(c, appsincategory)
menu.append(menu_category)
return menu
def category_icon(category):
if category == accessories:
icon = accessories_icon
elif category == development:
icon = development_icon
elif category == education:
icon = education_icon
elif category == games:
icon = games_icon
elif category == graphics:
icon = graphics_icon
elif category == multimedia:
icon = multimedia_icon
elif category == network:
icon = network_icon
elif category == office:
icon = office_icon
elif category == settings:
icon = settings_icon
elif category == system:
icon = system_icon
elif category == other:
icon = other_icon
else:
icon = None
return icon
def blackbox():
# Blackbox menus are the same as Fluxbox menus. They just don't support
# icons.
global seticon
seticon = False
fluxbox()
def fluxbox():
if submenu:
spacing = ' '
if seticon:
app_icon = icon_full_path(applications_icon)
if app_icon is None:
print('[submenu] ({})'.format(apps_name))
else:
print('[submenu] ({}) <{}>'.format(apps_name, app_icon))
else:
print('[submenu] ({})'.format(apps_name))
else:
spacing = ''
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
if seticon:
cat_icon = category_icon(category)
cat_icon = icon_full_path(cat_icon)
if cat_icon:
print('{s}[submenu] ({c}) <{i}>'.format(s=spacing, c=cat_name,
i=cat_icon))
else:
print('{s}[submenu] ({c})'.format(s=spacing, c=cat_name))
else:
print('{s}[submenu] ({c})'.format(s=spacing, c=cat_name))
for app in menu_category.applist:
# closing parentheses need to be escaped, otherwise they are
# cropped out, along with everything that comes after them
name = app.name.decode().replace(')', '\)')
icon = app.icon
command = app.command
path = app.path
if path is not None:
command = 'cd {p} ; {c}'.format(p=path, c=command)
if icon is None:
print('{s} [exec] ({n}) {{{c}}}'.format(s=spacing, n=name,
c=command))
else:
print('{s} [exec] ({n}) {{{c}}} <{i}>'.format(s=spacing,
n=name,
c=command,
i=icon))
print('{s}[end] # ({c})'.format(s=spacing, c=cat_name))
if submenu:
print('[end] # ({})'.format(apps_name))
def fvwm():
if submenu:
print('DestroyMenu "xdgmenu"')
print('AddToMenu "xdgmenu"')
if seticon:
app_icon = icon_full_path(applications_icon)
if app_icon:
print('+ "{a}%{i}% " Title'.format(a=apps_name, i=app_icon))
else:
print('+ "{a}" Title'.format(a=apps_name))
app_icon = icon_full_path(applications_icon)
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
cat_icon = category_icon(category)
cat_icon = icon_full_path(cat_icon)
if cat_icon:
print('+ "{c}%{i}%" Popup "{c}"'.format(c=cat_name,
i=cat_icon))
else:
print('+ "{c}" Popup "{c}"'.format(c=cat_name))
else:
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
print('+ "{c}" Popup "{c}"'.format(c=cat_name))
print()
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
print('DestroyMenu "{}"'.format(cat_name))
print('AddToMenu "{c}"'.format(c=cat_name))
if seticon:
cat_icon = category_icon(category)
cat_icon = icon_full_path(cat_icon)
if cat_icon:
print('+ "{c}%{i}%" Title'.format(c=cat_name, i=cat_icon))
else:
print('+ "{c}" Title'.format(c=cat_name))
else:
print('+ "{c}" Title'.format(c=cat_name))
for app in menu_category.applist:
name = app.name.decode()
icon = app.icon
command = app.command
path = app.path
if path is not None:
command = 'cd {p} ; {c}'.format(p=path, c=command)
if icon is None:
print('+ "{n}" Exec {c}'.format(n=name, c=command))
else:
print('+ "{n}%{i}%" Exec {c}'.format(n=name,
c=command,
i=icon))
print()
def windowmaker():
print('"{}" MENU'.format(apps_name))
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
print(' "{}" MENU'.format(cat_name))
for app in menu_category.applist:
name = app.name.decode()
command = app.command
print(' "{n}" EXEC {c}'.format(n=name, c=command))
print(' "{}" END'.format(cat_name))
print('"{}" END'.format(apps_name))
def icewm():
if submenu:
spacing = ' '
if seticon:
app_icon = icon_full_path(applications_icon)
if app_icon is None:
app_icon = "_none_"
print('menu "{a}" {i} {{'.format(a=apps_name, i=app_icon))
else:
print('menu "{}" _none_ {{'.format(apps_name))
else:
spacing = ''
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
cat_icon = category_icon(category)
cat_icon = icon_full_path(cat_icon)
if seticon and cat_icon is not None:
print('{s}menu "{c}" {i} {{'.format(s=spacing, c=cat_name,
i=cat_icon))
else:
print('{s}menu "{c}" _none_ {{'.format(s=spacing, c=cat_name))
for app in menu_category.applist:
name = app.name.decode()
icon = app.icon
command = app.command
if seticon and icon is not None:
print('{s} prog "{n}" {i} {c}'.format(s=spacing, n=name,
i=icon, c=command))
else:
print('{s} prog "{n}" _none_ {c}'.format(s=spacing, n=name,
c=command))
print('{}}}'.format(spacing))
if submenu:
print('}')
def pekwmmenu():
if pekwmdynamic:
print("Dynamic {")
dspacing = ' '
else:
dspacing = ''
if submenu:
spacing = ' '
if seticon:
app_icon = icon_full_path(applications_icon)
print('{s}Submenu = "{a}" {{ Icon = "{i}"'.format(s=dspacing,
a=apps_name,
i=app_icon))
else:
print('{s}Submenu = "{a}" {{'.format(s=dspacing, a=apps_name))
else:
spacing = ''
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
cat_icon = category_icon(category)
cat_icon = icon_full_path(cat_icon)
if seticon and cat_icon is not None:
print('{d}{s}Submenu = "{c}" {{ Icon = "{i}"'.format(d=dspacing,
s=spacing,
c=cat_name,
i=cat_icon))
else:
print('{d}{s}Submenu = "{c}" {{'.format(d=dspacing, s=spacing,
c=cat_name))
for app in menu_category.applist:
name = app.name.decode()
icon = app.icon
# for some apps (like netbeans) the command is launched with
# /bin/sh "command"
# and the quotes get mixed up with the quotes pekwm puts
# around Actions, so we're just stripping the quotes
command = app.command.replace('"', '')
path = app.path
if path is not None:
# pekwm doesn't like "cd path ; command", but it works
# with "&&" and "||", so we'll launch the command even if the
# path does not exist
command = 'cd {p} && {c} || {c}'.format(p=path, c=command)
if seticon and icon is not None:
print('{d}{s} Entry = "{n}" {{ Icon = "{i}"; Actions = "Exec {c} &" }}'
.format(d=dspacing, s=spacing, n=name, i=icon,
c=command))
else:
print('{d}{s} Entry = "{n}" {{ Actions = "Exec {c} &" }}'
.format(d=dspacing, s=spacing, n=name, c=command))
print('{d}{s}}}'.format(d=dspacing, s=spacing))
if submenu:
print('{}}}'.format(dspacing))
if pekwmdynamic:
print("}")
def jwm():
print('<?xml version="1.0"?>')
print('<JWM>')
if submenu:
spacing = ' '
if seticon:
app_icon = icon_full_path(applications_icon)
if app_icon is None:
print('<Menu label="{}">'.format(apps_name))
else:
print('<Menu icon="{i}" label="{a}">'.format(i=app_icon,
a=apps_name))
else:
print('<Menu label="{}">'.format(apps_name))
else:
spacing = ''
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
cat_icon = category_icon(category)
cat_icon = icon_full_path(cat_icon)
if seticon and cat_icon is not None:
print('{s}<Menu icon="{i}" label="{c}">'.format(s=spacing,
i=cat_icon,
c=cat_name))
else:
print('{s}<Menu label="{c}">'.format(s=spacing, c=cat_name))
for app in menu_category.applist:
name = app.name.decode()
icon = app.icon
command = app.command
path = app.path
if path is not None:
command = 'cd {p} ; {c}'.format(p=path, c=command)
if seticon and icon is not None:
print('{s} <Program icon="{i}" label="{n}">{c}</Program>'
.format(s=spacing, i=icon, n=name, c=command))
else:
print('{s} <Program label="{n}">{c}</Program>'
.format(s=spacing, n=name, c=command))
print('{}</Menu>'.format(spacing))
if submenu:
print('</Menu>')
print('</JWM>')
def compizboxmenu():
if submenu:
spacing = ' '
if seticon:
app_icon = icon_strip(applications_icon)
print('<menu icon="{i}" name="{a}">'.format(i=app_icon,
a=apps_name))
else:
print('<menu name="{}">'.format(apps_name))
else:
spacing = ''
for menu_category in menu(ico_paths=False):
category = menu_category.category
cat_name = category.decode()
cat_icon = category_icon(category)
if seticon and cat_icon is not None:
print('{s}<menu icon="{i}" name="{c}">'.format(
s=spacing, i=cat_icon, c=cat_name))
else:
print('{s}<menu name="{c}">'.format(s=spacing, c=cat_name))
for app in menu_category.applist:
name = app.name.decode().replace('&', '&amp;')
icon = app.icon
command = app.command.replace("'", "'\\''").replace('&', '&amp;')
path = app.path
if path is not None:
path = path.replace("'", "'\\''")
command = 'sh -c \'cd "{p}" ;{c}\''.format(p=path, c=command)
if seticon and icon is not None:
print(('{s} <item type="launcher"><name>{n}</name>'
'<icon>{i}</icon>'
'<command>{c}</command></item>').format(s=spacing,
n=name, i=icon,
c=command))
else:
print(('{s} <item type="launcher"><name>{n}</name>'
'<command>{c}</command></item>').format(s=spacing,
n=name,
c=command))
print('{}</menu>'.format(spacing))
if submenu:
print('</menu>')
def twm():
if submenu:
print('menu "xdgmenu"')
print("{")
if twmtitles:
print(' "{a}" f.title'.format(a=apps_name))
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
print(' "{c}" f.menu "{c}"'.format(c=cat_name))
print("}")
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
print('menu "{}"'.format(cat_name))
print("{")
if twmtitles:
print(' "{c}" f.title'.format(c=cat_name))
for app in menu_category.applist:
name = app.name.decode()
# for some apps (like netbeans) the command is launched with
# /bin/sh "command"
# and the quotes get mixed up with the quotes twm puts
# around the command, so we're just stripping the quotes
command = app.command.replace('"', '')
path = app.path
if path is not None:
print(' "{n}" f.exec "cd {p} ; {c} &"'.format(n=name, p=path, c=command))
else:
print(' "{n}" f.exec "{c} &"'.format(n=name, c=command))
print("}")
def amiwm():
for menu_category in menu():
category = menu_category.category
cat_name = category.decode()
print('ToolItem "{}" {{'.format(cat_name))
for app in menu_category.applist:
name = app.name.decode()
# for some apps (like netbeans) the command is launched with
# /bin/sh "command"
# and the quotes get mixed up with the quotes amim needs
# around the command, so we're just stripping the quotes
command = app.command.replace('"', '')
path = app.path
if path is not None:
print(' ToolItem "{n}" "cd {p} ; {c}" ""'.format(n=name, p=path, c=command))
else:
print(' ToolItem "{n}" "{c}" ""'.format(n=name, c=command))
print("}")
if __name__ == "__main__":
main(sys.argv[1:])