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.

891 lines
33 KiB

#!/usr/bin/env python3
# coding: utf-8
# vim:et:sta:sts=4:sw=4:ts=8:tw=79:
12 years ago
import os
import sys
import getopt
import fnmatch
12 years ago
import xdg.DesktopEntry as dentry
import xdg.Exceptions as exc
import xdg.BaseDirectory as bd
12 years ago
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
12 years ago
seticon = False
iconsize = 16
nosvg = False
12 years ago
desktop = False
submenu = True
pekwmdynamic = False
twmtitles = False
max_icon_size = False
12 years ago
# 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')
12 years ago
applications = de.getName().encode('utf-8')
apps_name = applications.decode()
applications_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-accessories.directory')
12 years ago
accessories = de.getName().encode('utf-8')
accessories_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-development.directory')
12 years ago
development = de.getName().encode('utf-8')
development_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-education.directory')
12 years ago
education = de.getName().encode('utf-8')
education_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir + 'xdgmenumaker-games.directory')
12 years ago
games = de.getName().encode('utf-8')
games_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-graphics.directory')
12 years ago
graphics = de.getName().encode('utf-8')
graphics_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-multimedia.directory')
12 years ago
multimedia = de.getName().encode('utf-8')
multimedia_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-network.directory')
12 years ago
network = de.getName().encode('utf-8')
network_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-office.directory')
12 years ago
office = de.getName().encode('utf-8')
office_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-settings.directory')
12 years ago
settings = de.getName().encode('utf-8')
settings_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir +
'xdgmenumaker-system.directory')
12 years ago
system = de.getName().encode('utf-8')
system_icon = de.getIcon()
de = dentry.DesktopEntry(filename=desktop_dir + 'xdgmenumaker-other.directory')
12 years ago
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'
12 years ago
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)
12 years ago
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')
12 years ago
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
12 years ago
def sortedcategories(applist):
categories = []
for e in applist:
categories.append(e.category)
categories = sorted(set(categories))
return categories
12 years ago
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
12 years ago
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
12 years ago
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()
12 years ago
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))
12 years ago
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("}")
12 years ago
if __name__ == "__main__":
main(sys.argv[1:])