#!/usr/bin/python
#This script generates FreeDesktop application menu for any window manager with a template.
#Original copy Written by Konstantin Korikov <lostclus@ua.fm>, put in the public domain
#adapted to use for fluxbox by anticapitalista <antiX@operamail.com>
#adapted to use for jwm by Dave <david@daveserver.info>
#adapted to auto-detect or specify the session and use the resulting template by Dave <david@daveserver.info>
#adapted to use the gtk theme with --use-gtk by Dave <david@daveserver.info>
#adapted to utilize the xdg separator key by Dave <david@daveserver.info>
#adapted to have no icons by Dave <david@daveserver.info>
#adapted to write out to the applications file directly by Dave <david@daveserver.info>
#adapted to detect when the file has very little to no contents and adds a retry button by Dave <david@daveserver.info>

#Requires pyxdg http://cvs.freedesktop.org/cgi-bin/viewcvs.cgi/pyxdg/

#USAGE EXAMPLE FOR antiX 
#desktop-menu > ~/.WM/applications
#Then link your main WM menu to this application menu.

#To Do:
#Preliminary testing then Rewrite / Clean the code

import sys
import locale
import getopt
import re
import os
import shlex
from subprocess import Popen, PIPE
import xdg.Menu
import xdg.DesktopEntry
import xdg.IconTheme
import xdg.Config


version = "1.0"


def print_usage(exit_code = 1):
  print """Usage: %s [options]
Options:
  Locale:
  --locale=locale               set output language and encoding
                                (This uses your session locale as the default)
  
  Filtering:
  --root-folder folder          folder to generate (for example: /Games)
  --menu-programs               Make a list of menu program links
  
  Specify Menu Type:
  --menu-file                   Specify the menu file to use
  --desktop-code                Specify which WM you are using 
                                (for example: icewm, fluxbox, jwm, etc...)
                                (Requires matching template!)
                                
  Utilities:
  --terminal command            set terminal emulator command 
                                (default: xterm -e %%s)
  --write-out                   Write the output to the applications
                                file from the appropriate template
  --write-out-file              Specify the name of the file rather than 
                                using the template.
  
  Icon Setting:
  --no-gtk                      turn off using gtk to find the menu icons
                                (includes --with-theme-paths function)
  --theme theme                 set icon theme 
                                (Disables gtk theme)
  --with-theme-paths            convert icon base names to icon absolute paths
                                using specified icon theme
  --icon-size                   set default icon size
                                (This is 16 by default)
  --default-folder-icon icon    icon for folders that not provide Icon option
                                (This is blank by default)
  --default-entry-icon icon     icon for entries that not provide Icon option
                                (This is blank by default)
  --no-icons                    Turns off Icons
  
  Help and Information:
  --help                        print this help and exit
  --version                     print version and exit
""" % sys.argv[0]
  sys.exit(exit_code)
  
def print_version():
  print "%s version %s" % (
    os.path.basename(sys.argv[0]), version)
  sys.exit(0)
  
def write_to_file(STRING):
    text = file((USER_HOME+"/"+Var.Conf_Dir+"/"+Var.App_File), "a")
    text.write (STRING+"\n")
    text.close()

def find_icon(entry):
  icon = entry.getIcon()
  
  if icon and not no_gtk:
    old_stdout = sys.stdout; old_stderr = sys.stderr; sys.stdout = open(os.devnull, 'w'); sys.stderr = open(os.devnull, 'w')
    import gtk
    if gtk.icon_theme_get_default():
      icon_theme = gtk.icon_theme_get_default()
      icon_check = icon_theme.lookup_icon(icon, icon_size, 0)
      if icon_check:
        icon_info = icon_theme.lookup_icon(icon, icon_size, 0)
        icon = icon_info.get_filename()
        icon = xdg.IconTheme.getIconPath(icon, icon_size) or icon
      else:
        icon = xdg.IconTheme.getIconPath(icon, icon_size) or icon
    else:
      icon = xdg.IconTheme.getIconPath(icon, icon_size) or icon
    sys.stdout=old_stdout; sys.stderr=old_stderr
      
  if icon and with_theme_paths:
    icon = xdg.IconTheme.getIconPath(icon, icon_size) or icon
    
  icon = re.sub(r' ','\ ', icon)
  return icon

def process_menu(menu):
  for entry in menu.getEntries():
    if isinstance(entry, xdg.Menu.Separator):
      if write_out:
        write_to_file(Var.Separator)
      else:
        print Var.Separator
    elif isinstance(entry, xdg.Menu.Menu):
      name = entry.getName() or entry.DesktopFileID
      if not no_icons:
        icon = find_icon(entry) or default_folder_icon

      if menu_programs:
        print ((Var.Menu_Program % (name, icon, sys.argv[0])) +
          (" --root-folder \"%s\"" % entry.getPath(org=True)) +
          (" --terminal \"%s\"" % terminal)).encode(encoding),
        if not no_icons:
          print ((" --default-folder-icon \"%s\"" % default_folder_icon) +
          (" --default-entry-icon \"%s\"" % default_entry_icon) +
          (" --theme \"%s\"" % xdg.Config.icon_theme) +
          (" --icon-size \"%d\"" % icon_size) +
          (with_theme_paths and " --with-theme-paths" or "")).encode(encoding),
        if locale_str:
          print (" --locale \"%s\"" % locale_str).encode(encoding),
        print
      else:
        if no_icons:
          SUB_ORDER('menu',name,'','')
        else:
          SUB_ORDER('menu',name,icon,'')
        if write_out:
          if Var.Menu_ID == '1':
            write_to_file((Var.Menu_Start % (SUB_ORDER.sub0, SUB_ORDER.sub1, SUB_ORDER.sub2)).encode(encoding))
          else:
            write_to_file((Var.Menu_Start % (SUB_ORDER.sub0, SUB_ORDER.sub1)).encode(encoding))
        else:
          if Var.Menu_ID == '1':
            print (Var.Menu_Start % (SUB_ORDER.sub0, SUB_ORDER.sub1, SUB_ORDER.sub2)).encode(encoding)
          else:
            print (Var.Menu_Start % (SUB_ORDER.sub0, SUB_ORDER.sub1)).encode(encoding)
        process_menu(entry)
        if write_out:
          write_to_file(Var.Menu_End.encode(encoding))
        else:
          print Var.Menu_End.encode(encoding)
    elif isinstance(entry, xdg.Menu.MenuEntry):
      de = entry.DesktopEntry
      desktopFile=str(entry)
      OnlyShowIn = de.getOnlyShowIn()
      if not os.path.isfile("/usr/share/applications/antix/"+desktopFile) and not OnlyShowIn:
        name = de.getName() or entry.DesktopFileID
        if not no_icons:
          icon = find_icon(de) or default_entry_icon
        execute = exec_clean2_re.sub('%', exec_clean1_re.sub('', de.getExec()))
        if de.getTerminal(): execute = terminal % execute      
        if no_icons:
          SUB_ORDER('prog',name,'',execute)
        else:
          SUB_ORDER('prog',name,icon,execute)
        if write_out:
          write_to_file((Var.Program % (SUB_ORDER.sub0, SUB_ORDER.sub1, SUB_ORDER.sub2)).encode(encoding))
        else:
          print (Var.Program % (SUB_ORDER.sub0, SUB_ORDER.sub1, SUB_ORDER.sub2)).encode(encoding)

class Var: 
  def read(self):
    var = Var
    for line in open("/usr/share/desktop-menu/templates/"+DESKTOP+".template", "r").xreadlines():
      if "#" not in line:
        if re.search(r'^.*=', line):
          pieces = line.split(' = ')
          var.VARIABLE=(pieces[0])
          var.VARIABLE = re.sub(r'\n', '', var.VARIABLE)
          OBJECT=(pieces[1])
          OBJECT = re.sub(r'\n', '', OBJECT)
          setattr(var, var.VARIABLE, OBJECT)
          
class SUB_ORDER: 
  def __init__(self,menuORprog,NAME,ICON,EXECUTE):
    var = SUB_ORDER
    count=0
    if menuORprog == "menu":
      SUB = Var.Menu_Sub_Order.split(',')
    elif menuORprog == "prog":
      SUB = Var.Program_Sub_Order.split(',')
      
    for value in SUB:
      str_count=str(count)
      var.VARIABLE="sub"+str_count
      if re.search(r'icon', value):
        setattr(var, var.VARIABLE, ICON)
      elif re.search(r'execute', value):
        setattr(var, var.VARIABLE, EXECUTE)
      elif re.search(r'name', value):
        setattr(var, var.VARIABLE, NAME)
      count=count+1
      
root_folder = ""
menu_file = ""
terminal = "desktop-defaults-run -t %s"
default_folder_icon = "folder"
default_entry_icon = "-"
menu_programs = False
with_theme_paths = False
write_out = False
write_out_global = False
write_out_file = ""
no_gtk = False
no_icons = False
icon_size = 48
DESKTOP_CODE = ""

exec_clean1_re = re.compile(r'%[a-zA-Z]')
exec_clean2_re = re.compile(r'%%')
encoding = None
locale_str = None

try: opts, args = getopt.getopt(sys.argv[1:], "", 
  ("help", "version", "locale=",
   "root-folder=", "terminal=", "default-folder-icon=", 
   "default-entry-icon=", "menu-programs", "theme=", "with-theme-paths",
   "icon-size=", "write-out", "write-out-global", "write-out-file=", "desktop-code=", "no-gtk","no-icons", "menu-file="))
except getopt.GetoptError: print_usage()

locale.setlocale(locale.LC_ALL, "")

for o, v in opts:
  if o == "--locale":
    locale_str = v
    locale.setlocale(locale.LC_ALL, locale_str)
  if o == "--root-folder": root_folder = v
  elif o == "--menu-file": menu_file = v
  elif o == "--terminal": terminal = v
  elif o == "--default-folder-icon": default_folder_icon = v
  elif o == "--default-entry-icon": default_entry_icon = v
  elif o == "--menu-programs" : menu_programs = True
  elif o == "--write-out" : write_out = True
  elif o == "--write-out-global" : write_out = True; write_out_global = True; 
  elif o == "--write-out-file" : write_out_file = v
  elif o == "--with-theme-paths" : with_theme_paths = True
  elif o == "--no-gtk" : no_gtk = True
  elif o == "--no-icons" : no_icons = True
  elif o == "--icon-size": icon_size = int(v)
  elif o == "--theme" : xdg.Config.setIconTheme(v)
  elif o == "--desktop-code" : DESKTOP_CODE = v
  elif o in ("-h", "-?", "--help"): print_usage(0)
  elif o in ("-v", "--version"): print_version()

def build_menu():
    Var().read()
    if write_out_file:
        Var.App_File = write_out_file
  
    if not os.path.isdir(USER_HOME+"/"+Var.Conf_Dir+"/"):
        os.system("mkdir -p %s" % (USER_HOME+"/"+Var.Conf_Dir+"/"))
        os.system("touch %s" % (USER_HOME+"/"+Var.Conf_Dir+"/"+Var.App_File))
    else:
        #open((USER_HOME+"/"+Var.Conf_Dir+"/"+Var.App_File), 'w').close()  
        text = file((USER_HOME+"/"+Var.Conf_Dir+"/"+Var.App_File), "w")
        text.close()
        

    menu = xdg.Menu.parse(menu_file)
    if root_folder: menu = menu.getMenu(root_folder)
  
    if write_out:
      write_to_file(Var.File_Start)
  
    process_menu(menu)
  
    if write_out:
      write_to_file(Var.File_End)
  
    if not (os.path.getsize((USER_HOME+"/"+Var.Conf_Dir+"/"+Var.App_File)) > 50):
      SUB_ORDER('prog',"Retry",'',"desktop-menu --write-out")
      write_to_file((Var.Program % (SUB_ORDER.sub0, SUB_ORDER.sub1, SUB_ORDER.sub2)).encode(encoding))
      
    if Var.Restart_Command and 'DISPLAY' in os.environ:
        os.system("%s" % (Var.Restart_Command))

encoding = locale.getlocale()[1] or 'UTF-8'
if write_out_global:
    USER_HOME="/usr/share/desktop-menu/"
    for i in os.listdir('/usr/share/desktop-menu/templates'):
        if i.endswith(".template"):
            checkwmname = "which " + i.split('.',1)[0]
            myprocess = Popen( shlex.split(checkwmname), stdout=PIPE)
            mystdout = myprocess.communicate()[0]
            ### if the corresponding wm is not currently installed, skip this template
            ###  (to avoid logspam e.g. "Writing Menu: jwm... sh: 1: jwm: not found")
            if mystdout and mystdout[0] != '':
                i = re.sub(r'\..*', '', i)
                print 'Writing Menu: %s' % i
                DESKTOP = i
                build_menu()
else:
    USER_HOME = os.environ['HOME']
    if 'DISPLAY' in os.environ and not write_out_global:
        DISPLAY = os.environ['DISPLAY']
        DISPLAY = re.sub(r':', '', DISPLAY)
        DISPLAY_SPLIT = DISPLAY.split('.')
        DISPLAY = DISPLAY_SPLIT[0]
        if not DESKTOP_CODE:
            with open(USER_HOME+"/.desktop-session/desktop-code."+DISPLAY, "r") as f:
                DESKTOP_CODE = f.readline()
                DESKTOP_CODE = re.sub(r'\n', '', DESKTOP_CODE)
            
    if not DESKTOP_CODE:
      print 'No Desktop Code. Please specify a desktop code with --desktop-code';
      exit
    else:  
      DESKTOP = re.sub(r'.*-', '', DESKTOP_CODE)
      build_menu()
