#!/usr/bin/env python

import os
import sys
import getopt
import gtk
import xdg.DesktopEntry as dentry
import xdg.Exceptions as exc
import xdg.BaseDirectory as bd
from operator import attrgetter

seticon = False
desktop = False
submenu = True

de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-applications.directory')
applications = de.getName().encode('utf-8')
applications_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-accessories.directory')
accessories = de.getName().encode('utf-8')
accessories_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-development.directory')
development = de.getName().encode('utf-8')
development_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-education.directory')
education = de.getName().encode('utf-8')
education_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-games.directory')
games = de.getName().encode('utf-8')
games_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-graphics.directory')
graphics = de.getName().encode('utf-8')
graphics_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-multimedia.directory')
multimedia = de.getName().encode('utf-8')
multimedia_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-network.directory')
network = de.getName().encode('utf-8')
network_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-office.directory')
office = de.getName().encode('utf-8')
office_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-settings.directory')
settings = de.getName().encode('utf-8')
settings_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-system.directory')
system = de.getName().encode('utf-8')
system_icon = de.getIcon()
de = dentry.DesktopEntry(filename = '/usr/share/desktop-directories/xdgmenumaker-other.directory')
other = de.getName().encode('utf-8')
other_icon = de.getIcon()

def main(argv):
	global desktop
	global seticon
	global submenu
	try:
		opts, args = getopt.getopt(argv, "hinf:", ["help", "icons" , "no-submenu", "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 ("--no-submenu"):
			submenu = False
		elif opt in ("-f", "--format"):
			desktop = arg
	if desktop is False:
		usage()
		sys.exit('ERROR: You must specify the output format with -f')
	elif desktop == "fluxbox":
		fluxboxmenu()
	elif desktop == "windowmaker":
		seticon = False
		windowmakermenu()
	elif desktop == "icewm":
		icewmmenu()
	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. Valid options are fluxbox, icewm and windowmaker'
	print '    -i, --icons      enable support for icons in the menus. Only works with fluxbox, icewm'
	print '    --no-submenu     do not create a submenu. Only works with fluxbox, icewm'
	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'

class MenuEntry:
	def __init__(self, category, name, icon, command):
		self.category = category
		self.name = name
		self.icon = icon
		self.command = command
	
	def __repr__(self):
		return repr((self.category, self.name, self.icon, self.command))

def icon_full_path(icon):
	icon = os.path.basename(icon)
	if icon.endswith('.png'):
		icon = icon.replace('.png', '')
	elif icon.endswith('.svg'):
		icon = icon.replace('.svg', '')
	elif icon.endswith('.xpm'):
		icon = icon.replace('.xpm', '')
	icon_theme = gtk.icon_theme_get_default()
	icon = icon_theme.lookup_icon(icon, 16, gtk.ICON_LOOKUP_NO_SVG)
	if icon:
		icon = icon.get_filename()
	return icon

def get_entry_info(desktopfile):
	global desktop
	global seticon
	show = True
	de = dentry.DesktopEntry(filename = desktopfile)
	name = de.getName().encode('utf-8')

	if seticon == True:
		# strip the directory and extension from the icon name
		icon = de.getIcon()
		icon = icon_full_path(icon)
	else:
		icon = None

	hidden = de.getHidden()
	if hidden == True:
		show = False
	nodisplay = de.getNoDisplay()
	if nodisplay == True:
		show = False

	# removing any %U or %F from the exec line
	command = de.getExec().partition('%')[0]

	terminal = de.getTerminal()
	if terminal is True:
		command = 'xterm -e '+command
	
	# cleaning up categories and keeping only registered freedesktop.org main categories
	categories = de.getCategories()
	if 'AudioVideo' in categories:
		category = multimedia
	elif 'Audio' in categories:
		category = multimedia
	elif 'Video' in categories:
		category = multimedia
	elif 'Development' in categories:
		category = development
	elif 'Education' in categories:
		category = education
	elif 'Game' in categories:
		category = games
	elif 'Graphics' in categories:
		category = graphics
	elif 'Network' in categories:
		category = network
	elif 'Office' in categories:
		category = office
	elif 'System' in categories:
		category = system
	elif 'Settings' in categories:
		category = settings
	elif 'Utility' in categories:
		category = accessories
	else:
		category = other

	onlyshowin = de.getOnlyShowIn()
	notshowin = de.getNotShowIn()
	# none of the freedesktop registered environments are supported by this anyway
	# http://standards.freedesktop.org/menu-spec/latest/apb.html
	if onlyshowin != []:
		show = False
	if desktop in notshowin:
		show = False
	if show == True:
		return [category, name, icon, command]
	else:
		return None

def sortedcategories(applist):
	categories = []
	for e in applist:
		categories.append(e.category)
	categories = sorted(set(categories))
	return categories

def desktopfilelist():
	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:
		dirs.append(i.rstrip('/'))
	dirs = set(dirs)
	filelist = []
	for d in dirs:
		xdgdir = d+'/applications'
		if os.path.isdir(xdgdir):
			for i in os.listdir(xdgdir):
				if i.endswith('.desktop'):
					filelist.append(xdgdir+'/'+i)
	return filelist

def menu():
	applist = []
	for desktopfile in desktopfilelist():
		try:
			e = get_entry_info(desktopfile)
			if e is not None:
				applist.append(MenuEntry(e[0], e[1], e[2], e[3]))
		except exc.ParsingError:
			pass

	sortedapplist = sorted(applist, key=attrgetter('category', 'name'))

	menu = []
	for c in sortedcategories(applist):
		appsincategory = []
		for i in sortedapplist:
			if i.category == c:
				appsincategory.append([i.name, i.icon, i.command])
		menu.append([c, appsincategory])
	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 fluxboxmenu():
	global seticon
	global submenu
	if submenu is True:
		spacing = '  '
		if seticon == True:
			app_icon = icon_full_path(applications_icon)
			print '[submenu] ('+applications+') <'+app_icon+'>'
		else:
			print '[submenu] ('+applications+')'
	else:
		spacing = ''
	for i in menu():
		category = i[0]
		if seticon == True:
			cat_icon = category_icon(category)
			cat_icon = icon_full_path(cat_icon)
			if cat_icon:
				print spacing+'[submenu] ('+category+') <'+cat_icon+'>'
			else:
				print spacing+'[submenu] ('+category+')'
		else:
			print spacing+'[submenu] ('+category+')'
		for j in i[1]:
			name = j[0]
			icon = j[1]
			command = j[2]
			if icon is None:
				print spacing+'  [exec] ('+name+') {'+command+'}'
			else:
				print spacing+'  [exec] ('+name+') {'+command+'} <'+icon+'>'
		print spacing+'[end] # ('+category+')'
	if submenu is True:
		print '[end] # ('+applications+')'

def windowmakermenu():
	print '"'+applications+'" MENU'
	for i in menu():
		category = i[0]
		print ' "'+category+'" MENU'
		for j in i[1]:
			name = j[0]
			command = j[2]
			print '  "'+name+'" EXEC '+command
		print ' "'+category+'" END'
	print '"'+applications+'" END'

def icewmmenu():
	global seticon
	global submenu
	if submenu is True:
		spacing = '  '
		if seticon == True:
			app_icon = icon_full_path(applications_icon)
			print 'menu "'+applications+'" '+app_icon+' {'
		else:
			print 'menu "'+applications+'" _none_ {'
	else:
		spacing = ''
	for i in menu():
		category = i[0]
		cat_icon = category_icon(category)
		cat_icon = icon_full_path(cat_icon)
		if seticon is True and cat_icon is not None:
			print spacing+'menu "'+category+'" '+cat_icon+' {'
		else:
			print spacing+'menu "'+category+'" _none_ {'
		for j in i[1]:
			name = j[0]
			icon = j[1]
			command = j[2]
			if seticon is True and icon is not None:
				print spacing+'  prog "'+name+'" '+icon+' '+command
			else:
				print spacing+'  prog "'+name+'" _none_ '+command
		print spacing+'}'
	if submenu is True:
		print '}'

if __name__ == "__main__":
	main(sys.argv[1:])
