Dan Espen <des...@verizon.net> wrote:
> So maybe you can clean up this version a bit more
> and submit as a patch?
Attached. It's diffed against the latest CVS version of fvwm-menu-desktop.in

>This popped out:
>
>This can't be right:
>
>    if len(menulist) == 0:
> sys.stderr.write(install_prefix+"/"+desktop+"-"+menu_type+".menu not available on this system. Exiting...\n")
>
>For example:
>
>python fvwm-menu-desktop.in.py --menu-type notfound
>/debian-notfound.menu not available on this system. Exiting...
Fixed.

>Not sure what this is:
>
>python fvwm-menu-desktop.in.py --menu-type system-settings
>Traceback (most recent call last):
>  File "fvwm-menu-desktop.in.py", line 470, in <module>
>    main()
>  File "fvwm-menu-desktop.in.py", line 170, in main
>    menulist, desktop = getmenulist(desktop, menu_type)
>  File "fvwm-menu-desktop.in.py", line 285, in getmenulist
>    desktop_dict[de_highest].add(menu)
>KeyError: 'debian'
This happens because the the 'others' and 'none' were ignored in the
weighting. I did that before I sorted the keys to prevent that in the
first loop 'none' or 'others' are the start menus. And so in your case
debian was the winner but hasn't any menu and therefore no dict entry
exists. Only 'others'. So no key 'debian' -> KeyError: 'debian'
Fixed.

>1. We have less chance of losing the #!@PYTHON@ line.
In the CVS version #!/usr/bin/python is set instead of #!@PYTHON@
Should changed back?

>Meanwhile, the man page is about half done.
Updated the fvwm-menu-desktop help also.

>If we go this route,
>it would be nice if you included some of the comments
>you made.   At least for me, not all of this was obvious.
Done.

Also I fixed some issues I found while testing:
- The not working regex with trailing slash in install-prefix
  interpretation.
- Eliminate '%' behind menu entries if no icon will be found.
- Wrong conditional expression which tests the availability of
  $XDG_MENU_PREFIX.

Thomas




--- fvwm-menu-desktop.orig.py	2012-07-03 22:56:01.316247721 +0200
+++ Old/fvwm-menu-desktop.in.p4.py	2012-07-09 23:55:48.005438739 +0200
@@ -42,6 +42,7 @@
 import os.path
 import os
 from xdg.DesktopEntry import *
+import fnmatch
 
 
 def main ():
@@ -81,7 +82,6 @@
                'png-icons-path',
                'submenu-name-prefix',
                'time-limit',
-               'title',
                'tran-icons-path',
                'tran-mini-icons-path',
                'type',
@@ -97,14 +97,14 @@
 
     try:
         opts, args = getopt.getopt(sys.argv[1:], "hs:t:vw",
-                                   ["help", "verbose", "enable-mini-icons", "with-titles",
-                                    "desktop=", "size=", "theme=", "install-prefix=", "menu-type="]+obs_args+equaled_obs_parms)
+                                   ["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
+    global verbose, force, size, theme, icon_dir, top, install_prefix, menu_type, with_titles, menu_entry_count
     verbose = False
     force = False
     desktop=''
@@ -112,9 +112,10 @@
     theme='gnome'
     icon_dir="~/.fvwm/icons"
     top='FvwmMenu'
-    install_prefix = '/etc/xdg/menus/'
-    menu_type = 'applications'
+    install_prefix = ''
+    menu_type = ''
     with_titles = False
+    menu_entry_count = 0
 
     for o, a in opts:
         if o == "-v":
@@ -125,14 +126,15 @@
         elif o in ("--enable-mini-icons") :
             force=True
         elif o in ("--desktop") :
-            #?desktop=a + '-'
             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"
             # add trailing slash if not there already
-            if (not re.search('.*[.]/$',a)) : # no trailing slash
-                a=a+'/'
+            if not a[-1] == '/' : # trailing slash
+                a=a + '/'
             install_prefix = a
 
         elif o in ("-s","--size") :
@@ -143,33 +145,177 @@
             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 '')
 
-    check=install_prefix+desktop+menu_type+'.menu'
-    menu = checkmenu(check)
+    # 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.replace('-', '').lower()
+    
+    vprint("Parameters for creating menu list:")
+    vprint(" XDG_MENU_PREFIX:  \'%s\'" %xdg_menu_prefix)
+    vprint(" --install-prefix: \'%s\'" %install_prefix)
+    vprint(" --desktop:        \'%s\'" %desktop)
+    vprint(" --menu-type:      \'%s\'" %menu_type)
+    
+    vprint("\nStart search ...")
+    menulist, desktop_temp = getmenulist(desktop, menu_type)
+    if not desktop_temp == '':
+		desktop = desktop_temp
 
-    if menu == None and menu_type == 'applications':
-        # it could be a Debian system
-        check = install_prefix + 'debian-menu.menu'
-        menu = checkmenu(check)
+    vprint(" Menu list: %s" %menulist)
         
-    if menu == None:
-        # fixme, no point in trying to print "None".
-        sys.stderr.write(check+" not available on this system. Exiting...\n")
-        sys.exit()
+        
+    if len(menulist) == 0:
+        if not desktop == '':
+            desktop = desktop + '-'
+        sys.stderr.write(install_prefix+desktop+menu_type+".menu not available on this system. Exiting...\n")
+        sys.exit(1)
     else:
-        parsemenu(xdg.Menu.parse(menu), top)
+        parsemenus(menulist, desktop)
              
-def checkmenu(menu):
-    if not os.path.exists(menu):
-        menu = None
-    return menu
+def getmenulist(desktop, menu_type):
+    menudict = {} 
+    config_dirs = []
+    if not install_prefix == '':
+        config_dirs = [install_prefix]
+    else:
+        config_dirs = xdg_config_dirs # xdg_config_dirs is a built-in list from python-xdg
 
+    for dir in config_dirs:
+        if install_prefix == '':
+            dir = os.path.join(dir, 'menus')
+        # skipping all paths which not available
+        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)
+            
+            # the menudict dictionary has a unsorted list (set) for the values.
+            # set is easier to use then a list for removing items 
+            menudict[dir] = filelist
+            vprint(" found in %s: %s" %(dir, filelist))
+    
+    # remove all double menus in /etc/xdg/menus if they in user dir, too
+    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 == '':
+        # first the desktops, then debian (shouldn't appear in others) then others holding
+        # all other non DE menus e.g. tools and at the end the nones without prefixes
+        # If there're other prefixes from other WMs - should be added BEFORE debian
+        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+'*'
+            # fnmatch.filter returns a list of files the pattern match
+            menu_names = fnmatch.filter(menudict[path], pattern)
+            if not len(menu_names) == 0:
+                filled = True
+            for name in menu_names:
+                menus.add(path+'/'+name)
+            # delete each found DE menu from the actual path. So, the menus will be reduced loop by loop.
+            menudict[path] = menudict[path]-set(menu_names)
+            # count the menus found in the users and systems menu path for later weighting
+            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
+        # fill the weight dictionary with the counts
+        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 == '':
+            highest_user = weight_dict[de_highest][0]
+            highest_system = weight_dict[de_highest][1]
+            highest_total = weight_dict[de_highest][2]
+            # first compare the total counts
+            if highest < de_total:
+                higher = True
+            elif highest == de_total:
+                # if the totals equal compare the users
+                if highest_user < de_user:
+                    higher = True
+                elif highest_user == de_user:
+                    # it the users equal compare the system menus
+                    if highest_system < de_system:
+                        higher = True
+                    # if the systems equal the last wins
+                    elif highest_system == de_system:
+                        higher = True # fixme, should be biunique. -but how? With atime?
+        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('/')
+            # the fnmatch.filter will be used to find NO match because then
+            # the menu is not in the list
+            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']:
+            name = menu.replace('.menu', '').split('/')
+            found = fnmatch.filter(desktop_dict[de_highest], '*'+name[-1]+'*')
+            if found == []:
+                desktop_dict[de_highest].add(menu)
+            
+    if len(desktop_dict) == 0:
+        return [], ''
+    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")
 
@@ -213,63 +359,104 @@
     iconfile = ''
     if force :
         iconfile = geticonfile(icon) or getdefaulticonfile(command) or icon
-        iconfile = '%'+iconfile+'%'
+        if not iconfile == '':
+            iconfile = '%'+iconfile+'%'
     printtext('+ "%s%s" %s' % (name, iconfile, command))
 
-def parsemenu(menu, name=""):
+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 = []
+        new_menulist = []
+        for title, menu in zip(top_titles, menulist):
+            name = 'Fvwm'+title
+            vprint("Create 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:
+                new_toptitles.append(title)
+                new_menulist.append(menu)
+            else:
+                vprint(" Menu is empty - won't be used!")
+        
+        # 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, name))
+        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())
+    	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 " + desktop.getExec() )
-	else:
-	    printtext('# not supported: ' + str(entry))
+            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 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:
-	-h, --help                show this help and exit
-	--version                 show version and exit
-	--install-prefix DIR      install prefix of the desktop menu files.
-                                  Default is /etc/xdg/menus
-	--desktop NAME            desktop to build the menu for:
-		gnome-sys, gnome-user, gnome-redhat, gnome-mandriva,
-		kde-sys, kde-user.
-	--title NAME              menu title, default depends on --desktop
-	--enable-mini-icons       enable mini-icons in menus
-        -s, --size                Icon size in pixels.  Default is 24.
-        --theme                   theme name for icon selection.
-	-v, --verbose             run and display debug info on STDERR
-	-w, --with-titles         Generate menus with titles."""
+    -h, --help                show this help and exit
+    --version                 show version and exit
+    --install-prefix DIR      install prefix of the desktop menu files. Per default not set.
+                              For the system wide menus use /etc/xdg/menus/
+    --desktop NAME            desktop name to build menus for:
+                              gnome, kde, 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.
+    -w, --with-titles         generate menus with titles.
+    --enable-mini-icons       enable mini-icons in menu
+    -s, --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
+    -v, --verbose             run and display debug info on STDERR"""
 
 if __name__ == "__main__":
     main()
 
 # Local Variables:
 # mode: python
-# compile-command: "python fvwm-menu-desktop.in --enable-mini-icons --menu-type applications --with-titles"
+# compile-command: "python fvwm-menu-desktop.in --enable-mini-icons --with-titles"
 # End:

Reply via email to