Hi,

attached is my first try of a fvwm-menu-desktop which creates a menu
with all desktop related menus (if they have entries).

It' works over file weighting because it's too complicated to check
distribution relevant paths for finding the default desktop.

It works under OpenSuse, Fedora, Mandriva and Debian fine and creates
a menu with a structure like this:
Fedora 14 example
Root:
  Documentation >
  Gnome-applications >
  Gnome-screensavers >
  Gnomecc >
  Preferences >
  Server-settings >
  Settings >
  Start-here >
  System-settings >

I've attached the complete file and not a patch because too much changed
and it's easier for testing ^^

With --verbose debug messages will be printed out to std.err. So, if
something doesn't work correctly a helpful printout exist.

The other functionality with --desktop=<whatever_desktop_name>,
--menu-type=<whatever_menu_name> or/and --install-prefix=<whatever_path>
should work also.

Dan, I don't know if applications-gnome.menu would be created. In my simulation
it is listed but there could be problems with the xdg-automatism. See here:
http://lists.fedoraproject.org/pipermail/devel/2011-August/155342.html
https://bugzilla.redhat.com/show_bug.cgi?id=754845

And I found a very interesting entry at the end here:
http://people.gnome.org/~walters/0001-Ship-applications-gnome.menu.patch
which could be explain the change from gnome-applications.menu to
applications-gnome.menu:
+* Mon Apr 11 2011 Colin Walters <walt...@verbum.org> - 3.0.0-2
+- Ship applications-gnome.desktop; we don't want GNOME 3 to use redhat-menus.


Thomas

#!/usr/bin/python
 
# Modification History

# Changed on 01/26/12 by Dan Espen (dane):
# Make compatible with fvwm-menu-desktop.
# Restored DestroyMenu, needed for reload menus.
# Remove bug, was printing iconpath on converted icons
# Replace obsolete optparse, use getopt instead
# Change from command line arg for applications.menu
# change to using ?$XDG_MENU_PREFIX or theme? fixme
# - use "Exec exec" for all commands, remove option.

# fixme, fix documentation, FvwmForm-Desktop, usage prompt is wrong
# change, mini icons are enabled by default.
# there are rescalable icons.
# themes not working: "hicolor" is a required theme  I see lots of themes but none seem to affect much


# Author: Piotr Zielinski (http://www.cl.cam.ac.uk/~pz215/)
# Licence: GPL 2
# Date: 03.12.2005

# This script takes names of menu files conforming to the XDG Desktop
# Menu Specification, and outputs their FVWM equivalents to the
# standard output.
#
#   http://standards.freedesktop.org/menu-spec/latest/

# This script requires the python-xdg module, which in Debian can be
# installed by typing
#
#   apt-get install python-xdg
#
# On Fedora, python-xdg is installed by default.

import sys
import getopt
import xdg.Menu
import xdg.IconTheme
import xdg.Locale
import os.path
import os
from xdg.DesktopEntry import *
import fnmatch


def main ():

    description = """
Generate Fvwm Menu from xdg files.
Standard output is a series Fvwm commands."""

    obs_args=['check-app',
              'enable-style',
              'enable-tran-style',
              'fvwm-icons',
              'kde_config',
              'mini-icon-path',
              'merge-user-menu',
              'su_gui',
              'utf8',
              'wm-icons']
    dashed_obs_args=[]
    for a in obs_args :
        dashed_obs_args.append('--'+a)

    obs_parms=['check-icons',
               'check-mini-icon',
               'destroy-type',
               'dir',
               'fvwmgtk-alias',
               'icon-app',
               'icon-folder',
               'icon-style',
               'icon-title',
               'icon-toptitle',
               'icons-path',
               'lang',
               'menu-style',
               'name',
               'png-icons-path',
               'submenu-name-prefix',
               'time-limit',
               'tran-icons-path',
               'tran-mini-icons-path',
               'type',
               'uniconv-exec',
               'uniconv',
               'xterm']
    equaled_obs_parms=[]
    for a in obs_parms :
        equaled_obs_parms.append(a+'=')
    dashed_obs_parms=[]
    for a in obs_parms :
        dashed_obs_parms.append('--'+a)

    try:
        opts, args = getopt.getopt(sys.argv[1:], "hs:t:vw",
                                   ["help", "verbose", "enable-mini-icons", "with-titles", "verbose",
                                    "desktop=", "size=", "theme=", "install-prefix=", "menu-type=", "title="]+obs_args+equaled_obs_parms)
    except getopt.GetoptError, err:
        # print help information and exit:
        print str(err) # will print something like "option -a not recognized"
        print usage
        sys.exit(2)
    global verbose, force, size, theme, icon_dir, top, install_prefix, menu_type, with_titles, menu_entry_count
    verbose = False
    force = False
    desktop=''
    size=24
    theme='gnome'
    icon_dir="~/.fvwm/icons"
    top='FvwmMenu'
    install_prefix = ''
    menu_type = ''
    with_titles = False
    menu_entry_count = 0

    for o, a in opts:
        if o == "-v":
            verbose = True
        elif o in ("-h", "--help") :
            print usage
            sys.exit()
        elif o in ("--enable-mini-icons") :
            force=True
        elif o in ("--desktop") :
            desktop=a
        elif o in ("--title") :
            top=a
        elif o in ("--install-prefix") :
            if a and not os.path.isabs(a):
                assert False, "install-prefix must be an absolute path"
            # delete trailing slash if there already
            if a[-1] == '/' : # trailing slash
                a=a[:-1]
            install_prefix = a

        elif o in ("-s","--size") :
            size = int(a)
        elif o in ("--theme") :
            theme = a
        elif o in ("--menu-type") :
            menu_type = a
        elif o in ("-w","--with-titles") :
            with_titles = True
        elif o in ("--verbose") :
            verbose = True
        elif o in (str(dashed_obs_args+dashed_obs_parms)) :
            # Ignore
            sys.stderr.write( "Warning: Arg "+o+" is obsolete and ignored\n" )
        else:
            assert False, "unhandled option"

    xdg_menu_prefix = (os.environ('XDG_MENU_PREFIX') if os.environ.has_key('XDG_MENU_PREFIX') else '')

    # First check if no user presettings made
    if desktop == '':
        # check if $XDG_MENU_PREFIX is set
        if not xdg_menu_prefix == '':
            desktop = xdg_menu_prefix.remove('-', '').lower()
    
    vprint("Parameters for creating menu list:")
    vprint(" XDG_MENU_PREFIX: \'%s\'" %xdg_menu_prefix)
    vprint(" --desktop:       \'%s\'" %desktop)
    vprint(" --menu-type:     \'%s\'" %menu_type)
    
    vprint("\nStart search ...")
    menulist, desktop = getmenulist(desktop, menu_type)
    vprint(" Menu list: %s" %menulist)
        
        
    if len(menulist) == 0:
        sys.stderr.write(install_prefix+"/"+desktop+"-"+menu_type+".menu not available on this system. Exiting...\n")
        sys.exit()
    else:
        parsemenus(menulist, desktop)
             
def getmenulist(desktop, menu_type):
    menudict = {} 
    config_dirs = []
    if not install_prefix == '':
        config_dirs = [install_prefix]
    else:
        config_dirs = xdg_config_dirs

    for dir in config_dirs:
        if install_prefix == '':
            dir = dir + '/menus'
        if os.path.exists(dir):
            filelist = set([])
            dir_list = os.listdir(dir)
            pattern = '*'+desktop+'*'+menu_type+'*.menu'
            for filename in fnmatch.filter(dir_list, pattern):
                filelist.add(filename)
            
            menudict[dir] = filelist
            vprint(" found in %s: %s" %(dir, filelist))
    
    # remove all menus in /etc/xdg/menus if exist in user dir
    for path in menudict.keys():
        if not path == '/etc/xdg/menus':
            menudict['/etc/xdg/menus'] = menudict['/etc/xdg/menus'] - menudict[path]
    
    # sort menus related to desktops and create a weighting
    vprint("\n DE weighting search: DE => [user menus, system menus, overall]")
    desktop_dict = {}
    weight_dict = {}
    if desktop == '':
        DEs = ['gnome', 'kde', 'xfce', 'lxde', 'debian', 'others', 'none']
    else:
        DEs = [desktop]
    for de in DEs:
        menus = set([])
        user_menus = 0
        system_menus = 0
        filled = False
        for path in menudict.keys():
            if de == 'none':
                pattern = '*'
            elif de == 'others':
                pattern = '*-*'
            else:
                pattern = '*'+de+'*'
            menu_names = fnmatch.filter(menudict[path], pattern)
            if not len(menu_names) == 0:
                filled = True
            for name in menu_names:
                menus.add(path+'/'+name)
            menudict[path] = menudict[path]-set(menu_names)
            if not path == '/etc/xdg/menus':
                user_menus = len(menu_names)
            else:
                system_menus = len(menu_names)
        if filled:
            desktop_dict[de] = menus
            filled = False
        weight_dict[de] = [user_menus, system_menus, user_menus+system_menus]
        vprint(" %s => %s" %(de, weight_dict[de]))

    # get the highest rated desktop
    highest = 0
    de_highest = ''
    for de in sorted(weight_dict.keys()):
        de_user = weight_dict[de][0]
        de_system = weight_dict[de][1]
        de_total = weight_dict[de][2]
        higher = False
        if not de_highest == '':
            # don't weight 'none' and 'others cause both not DEs
            if not de == 'none' and not de == 'others':
                highest_user = weight_dict[de_highest][0]
                highest_system = weight_dict[de_highest][1]
                highest_total = weight_dict[de_highest][2]
                if highest < de_total:
                    higher = True
                elif highest == de_total:
                    if highest_user < de_user:
                        higher = True
                    elif highest_user == de_user:
                        if highest_system < de_system:
                            higher = True
                        elif highest_system == de_system and de_highest == 'none':
                            higher = True
        else:
            higher = True
            
        if higher:
            highest = de_total
            de_highest = de
    vprint( "\n Winner: %s" %de_highest)
    
    # Perhaps there're a global menus available which are not in the highest rated list
    if desktop_dict.has_key('none'):
        for menu in desktop_dict['none']:
            name = menu.replace('.menu', '').split('/')
            found = fnmatch.filter(desktop_dict[de_highest], '*'+name[-1]+'*')
            if found == []:
                desktop_dict[de_highest].add(menu)
    
    # Add 'others' menus to list, because these could be tool menus like yast, etc
    if desktop_dict.has_key('others'):
        for menu in desktop_dict['others']:
            desktop_dict[de_highest].add(menu)
            
    if len(desktop_dict) == 0:
        return [], de_highest
    else:
        return list(desktop_dict[de_highest]), de_highest

def vprint(text):
    if verbose:
        sys.stderr.write(text+"\n")
    
def printtext(text):
    print text.encode("utf-8")

def geticonfile(icon):
    iconpath = xdg.IconTheme.getIconPath(icon, size, theme, ["png", "xpm"])

    if not iconpath == None:
        extension = os.path.splitext(iconpath)[1]

    if not iconpath:
        return None

    if not force:
        return iconpath
    
    if iconpath.find("%ix%i" % (size, size)) >= 0: # ugly hack!!!
        return iconpath

    if not os.path.isdir(os.path.expanduser(icon_dir)):
        os.system("mkdir " + os.path.expanduser(icon_dir))

    iconfile = os.path.join(os.path.expanduser(icon_dir),
                            "%ix%i-" % (size, size) + 
                            os.path.basename(iconpath))

    if extension == '.svg':
        iconfile = iconfile.replace('.svg', '.png')

    os.system("if test \\( ! -f '%s' \\) -o \\( '%s' -nt '%s' \\) ; then convert '%s' -resize %i '%s' ; fi"% 
              (iconfile, iconpath, iconfile, iconpath, size, iconfile))
    return iconfile

    
def getdefaulticonfile(command):
    if command.startswith("Popup"):
        return geticonfile("gnome-fs-directory")
    else:
        return geticonfile("gnome-applications")    

def printmenu(name, icon, command):
    iconfile = ''
    if force :
        iconfile = geticonfile(icon) or getdefaulticonfile(command) or icon
        iconfile = '%'+iconfile+'%'
    printtext('+ "%s%s" %s' % (name, iconfile, command))

def parsemenus(menulist, desktop):
    if len(menulist) == 1:
        # user defines only one special menu
        parsemenu(xdg.Menu.parse(menulist[0]), top)
    else:
        # create a top title list
        top_titles = []
        for file in menulist:
            name = file.replace('.menu', '').split('/')
            top_titles.append(name[-1][0].upper()+name[-1][1:])
        
        # create the submenus
        new_toptitles = []
        for title, menu in zip(top_titles, menulist):
            name = 'Fvwm'+title
            vprint("\nCreate submenu \'%s\' from \'%s\' ..." %(name, menu))
            parsemenu(xdg.Menu.parse(menu), name, title)
            # remove a menu if no menu entry was created in its sub menus
            if not menu_entry_count == 0:
                vprint(" Menu is empty - won't use!")
                new_toptitles.append(title)

        # create the root menu
        printtext('DestroyMenu "%s"' % top)
        if with_titles:
            printtext('AddToMenu "%s" "%s" Title' % (top, top))
        else:
            printtext('AddToMenu "%s"' % top)

        for title in sorted(new_toptitles):
            name = 'Fvwm'+title
            printmenu(title, '', 'Popup "%s"' % name)
    
def parsemenu(menu, name="", title=""):
    global menu_entry_count
    m = re.compile('%[A-Z]?', re.I) # Pattern for %A-Z (meant for %U)
    if not name :
        name = menu.getPath()
    if not title:
        title = name
    printtext('DestroyMenu "%s"' % name)
    if with_titles:
        printtext('AddToMenu "%s" "%s" Title' % (name, title))
    else:
        printtext('AddToMenu "%s"' % name)
    for entry in menu.getEntries():
    	if isinstance(entry, xdg.Menu.Menu):
            printmenu(entry.getName(), entry.getIcon(), 'Popup "%s"' % entry.getPath())
    	elif isinstance(entry, xdg.Menu.MenuEntry):
            desktop = DesktopEntry(entry.DesktopEntry.getFileName())
            # eliminate '%U' etc behind execute string
            execProgram = m.sub('', desktop.getExec())
            printmenu(desktop.getName(), desktop.getIcon(), "Exec exec " + " " + execProgram)
            menu_entry_count += 1
    	else:
    	    printtext('# not supported: ' + str(entry))
    printtext('')

    for entry in menu.getEntries():
    	if isinstance(entry, xdg.Menu.Menu):
    	    parsemenu(entry)

    if (re.search('.*System Tools$',name)) : # fixme, find a better way to do this?
        printmenu("Regenerate Applications Menu", "", "FvwmForm " "FvwmForm-Desktop" )

usage="""
A script which parses xdg menu definitions to build
the corresponding fvwm menus.

Usage: $0 [OPTIONS]
Options:
    --help                    show this help and exit
    --version                 show version and exit
    --install-prefix DIR      install prefix of a specific xdg menu. Per default not set.
                              If the system wide should use then /etc/xdg/menus/
    --desktop NAME            desktop name to build a menu for it:
                              gnome, kde4, xfce, lxde, debian, etc.
    --menu-type NAME          applications (default), settings, preferences, etc.
    --theme NAME              gnome (default), oxygen, etc. Don't use hicolor. 
                              It's the default fallback theme if no icon will be found.
    --with-titles             enables menu titles
    --enable-mini-icons       enable mini-icons in menu
    --size                    set size of mini-icons in menu. Default is 24.
    --title NAME              menu title of the top menu used by Popup command.
                              Default is FvwmMenu
    --type NAME               fvwm (default) or gtk for a FvwmGtk menu
    --fvwmgtk-alias NAME      FvwmGtk module name, default is FvwmGtk
    --name NAME               menu name, default depends on --desktop
    --merge-user-menu         merge the system menu with the user menu
    --enable-tran-mini-icons  enable mini-icons in menu and
                              translation of foo.png icon names to foo.xpm
    --mini-icons-path DIR     path of menus icons (relative to your
                              ImagePath), default is 'mini/'
    --png-icons-path DIR      path of .png icons, default is your ImagePath
    --tran-mini-icons-path DIR      path of menus icons for translation
    --check-mini-icons PATH   check if the mini icons are in PATH
    --icon-toptitle micon:law:place:sidepic:color  mini-icon for the top
                                                   title and sidepic for the top menu
    --icon-title micon:law:place:sidepic:color     as above for sub menus
    --icon-folder micon:law:place   mini-icons for folder item
    --icon-app micon:law:place      mini-icon for applications item
    --wm-icons                define menu icon names to use with wm-icons
    --enable-style            build icons and mini-icons style
    --enable-tran-style       as above with translation (for FvwmGtk menus)
    --icon-style micon:icon:law     icons for style
    --icons-path DIR          define the directory of the icons,
                              the default is very good
    --tran-icons-path DIR     similar to the above option.
    --check-icons PATH        check if the icons are in the PATH
    --submenu-name-prefix NAME      in general not useful
    --dir DIR                 use path as desktop menu description
    --destroy-type FLAG       how to destroy menu, valid values:
                              'yes', 'no', 'dynamic', the default depends on --type
    --xterm CMD               complete terminal command to run applications
                              in it, default is 'xterm -e'
    --lang NAME               language, default is \$LANG
    --utf8                    For desktop entries coded in UTF-8 (KDE2)
    --uniconv                 Use (un)iconv for UTF-8 translation
    --uniconv-exec            uniconv or iconv (default)
    --menu-style name         assign specified MenuStyle name to menus
    --[no]check-app           [do not] check that apps are in your path
    --time-limit NUM          limit script running time to NUM seconds
    --verbose                 display debug type info on STDERR
Short options are ok if not ambiguous: -h, -x, -icon-a."""



if __name__ == "__main__":
    main()

# Local Variables:
# mode: python
# compile-command: "python fvwm-menu-desktop.in --enable-mini-icons --menu-type applications"
# End:

Reply via email to