#!/usr/bin/python -u
# -*- coding: utf-8 -*-
# Copyright (c) 2006 Matt Keenan <matt.keenan@sun.com>
#
# get-menu-location.py
#
# Using gnome-menus (GNOME's freedesktop.org menu spec implementation), 
# get-menu-location.py will determine the menu location for a specified 
# desktop file.
#
# Usage :	 get-menu-location dot_desktop_file_id [output_file]
#
#	dot-desktop-file : The .desktop file to search for, must be supplied
#
#	output_file : Name of file to output result to, this is optional if not
#	              specified, output is sent to stdout.
#
#
# get-menu-location will search menu files located un $XDG_CONFIG_DIRS/menus/*.menu
# If a menu file is included as a sub menu then only the parent is processed.
# First matching menu item is output. The format of the out is in xml as is of the
# following structure:
#
# <menu>
# 	<mainmenu>
#		<item>MAIN MENU TEXT</item>
#	</mainmenu>
#	<submenu>
#		<item>SUB MENU TEXT</item>
#	</submenu>
#	<menuitem>
#		<item>MENU ITEM TEXT</item>
#	</menuitem>
# </menu>
#
# There may be more than one submenu elements depending on how nested the menu item is.

import sys
import os
import re
import shutil
import gmenu


def exit_cleanly(exit_code):
	if os.path.isfile(tmp_dot_desktop_file_id):
		os.remove(tmp_dot_desktop_file_id)
	if os.path.isdir(tmp_xdg_data_dirs_applications):
		os.rmdir(tmp_xdg_data_dirs_applications)
	sys.exit(exit_code)

def usage():
	"""Print Usage and Exit."""
	print >> sys.stderr, "Usage: %s [DESKTOP_FILE] [OUTPUT_FILE]" % (sys.argv[0])
	print >> sys.stderr, "	 DESKTOP_FILE : .desktop file to use."
	print >> sys.stderr, "	 OUTPUT_FILE  : Optional output file."
	exit_cleanly(1)

def build_menu_path (d, i):
	"""Build menu path, e.g. String with '|' separated fields."""
	if len(d) == 0:
		return i
	elif d == "|":
		return d + i
	else:
		return d + "|" + i

# Locate a .desktop file within a menu tree
def find_desktop_file (menu_tree_directory, path, desktop_file_id):
	"""Locate first occurrance of .desktop file within a given menu tree."""
	path = build_menu_path(path, menu_tree_directory.name)
	for item in menu_tree_directory.contents:
		print item.name
		if isinstance (item, gmenu.Directory):
			retval = find_desktop_file (item,
										path,
										desktop_file_id)
			if retval:
				return retval
		elif isinstance (item, gmenu.Entry):
			if item.desktop_file_id == desktop_file_id:
				path = build_menu_path(path, item.name)
				return path
		elif isinstance (item, gmenu.Alias) and \
			 isinstance (item.item, gmenu.Entry):
			if item.item.desktop_file_id == desktop_file_id:
				path = build_menu_path(path, item.name)
				return path
	return None

def output_menu_xml(menu_path):
	"""Output XML for menu item location."""
	# Split path into sections
	output_file.write("<menu>\n")
	items = menu_path.split('|')
	for i in range(len(items)):
		if i == 0:
			# Main Menu
			output_file.write("  <mainmenu>\n")
			output_file.write("    <item>%s</item>\n" % items[i])
			output_file.write("  </mainmenu>\n")
		elif i == (len(items)-1):
			# Menu Item
			output_file.write("  <menuitem>\n")
			output_file.write("    <item>%s</item>\n" % items[i])
			output_file.write("  </menuitem>\n")
		else:
			# Sub Menu
			output_file.write("  <submenu>\n")
			output_file.write("    <item>%s</item>\n" % items[i])
			output_file.write("  </submenu>\n")
	output_file.write("</menu>\n")

def menu_is_submenu(menu_file, menu_path):
	"""Check if menu is submenu, using python version of grep."""
	search=re.compile(menu_file).search
	for file in os.listdir(menu_path):
		if '.menu' in file:
			full_file = os.path.join(menu_path, file)
			for index, line in enumerate(open(full_file)):
				if (search(line)):
					return True
	return False



args = sys.argv[1:]
tmp_xdg_data_dirs='/tmp'
tmp_xdg_data_dirs_applications='/tmp/applications'

if len(args) < 1:
	print >> sys.stderr, "Must specify .desktop argument"
	usage()
elif len(args) == 1:
	dot_desktop_file_id=args[0]
	output_file=sys.stdout
elif len(args) == 2:
	dot_desktop_file_id=args[0]
	try:
		output_file = file(args[1], 'w')
	except:
		print >> sys.stderr, "Error : cannot open file %s for writing." % (args[1])
		exit_cleanly(1)

# Check .desktop file exists
if not os.path.isfile(dot_desktop_file_id):
	print >> sys.stderr, ".desktop file does not exist : %s" % dot_desktop_file_id
	usage()

# Get basename of dot_desktop_file_id
desktop_base_id=os.path.basename(dot_desktop_file_id)

# Need to set XDG_DATA_DIRS to tmp area and copy desktop file there.
os.environ['XDG_DATA_DIRS']=tmp_xdg_data_dirs
if not os.path.isdir(tmp_xdg_data_dirs_applications):
	os.mkdir(tmp_xdg_data_dirs_applications)
tmp_dot_desktop_file_id=os.path.join(tmp_xdg_data_dirs_applications, desktop_base_id)
if os.path.isfile(tmp_dot_desktop_file_id):
	os.remove(tmp_dot_desktop_file_id)
shutil.copy(dot_desktop_file_id, tmp_xdg_data_dirs_applications)

# Process menu files in $XDG_CONFIG_DIRS/menus/*
# First call should be to check the default applications menu
# then process all others, Do not process menus that appear
# as submenus on other items.

# Get default applications.menu menutree
tree = gmenu.lookup_tree('applications.menu', gmenu.FLAGS_INCLUDE_EXCLUDED)
if tree is not None:
	menu_item_path = find_desktop_file (tree.root, "", desktop_base_id)
else:
	print >> sys.stderr, "Failed to get menu tree for applications.menu"
	exit_cleanly(1)

if menu_item_path:
	output_menu_xml(menu_item_path)
	exit_cleanly(0)
else:
	# Looks like we gotta search some more
	
	# Test if XDG_CONFIG_DIRS Is set, if not the use the default of
	# /etc/xdg

	if 'XDG_CONFIG_DIRS' in os.environ:
		xdg_config_dirs=os.environ['XDG_CONFIG_DIRS']
	else:
		xdg_config_dirs='/etc/xdg'
		os.environ['XDG_CONFIG_DIRS']=xdg_config_dirs

	xdg_paths = xdg_config_dirs.split(':')

	# Process all dirs in XDG_CONFIG_DIRS
	for path_idx in range(len(xdg_paths)): 
		menu_path=os.path.join(xdg_paths[path_idx], 'menus')
		if os.path.isdir(menu_path):
			for menu_file in os.listdir(menu_path):
				# Make sure suffix is ".menu", not applications.menu, and not a submenu
				if '.menu' in menu_file and \
					menu_file != 'applications.menu' and \
					not menu_is_submenu(menu_file, menu_path):
	
					tree = gmenu.lookup_tree(menu_file, gmenu.FLAGS_INCLUDE_EXCLUDED)
					if tree is not None:
						menu_item_path = find_desktop_file (tree.root, "", desktop_base_id)
						if menu_item_path:
							output_menu_xml(menu_item_path)
							exit_cleanly(0)
