On 14/09/2013 15:41, Thomas Funk wrote:
> I found another problem with unicode errors in menu names under
> XUbuntu. I will check this in the next time. So, forget the last
> patch. I will send a new one with the unicode error fix included.

I have now fixed this problem.
The attached patch includes:
- if only one xdg menu exists:
    - no output appears with 'fvwm-menu-desktop --get-menus all|desktop'
    - No entry "Regenerate XDG menu(s)" appears with
       'fvwm-menu-desktop --insert-in-menu MenuRoot'

- if an icon not found but the path was given convert errors occur.
  Fixed this with a path check.

- if an converted icon exists the conversion process will be skipped to
  reduce progress time.

- if an DecodeEncodeError occurs in menu/menu entry/file path it will be
  encoded before printing.

- exchanged all Tabs with spaces to prevent indention errors with the
  Python interpreter.

> On sam 14/09/13 19:56 , michel dominique  wrote:
> Until a certain extend. I think many experimented fvwm users will
> install a minimum system, and will not get more than 1 or 2 xdg menus.
>
> Also, many distributions don't have their own menu system like debian,
> so the chances are big they will get only 1 xdg menu with a minimum
> install.
>
> It is even worst, on a fresh gentoo install, with only fvwm,
> fvwm-crystal, xorg-server, stalonetray, mc and rxvt-unicode, I get no
> xdg menu file at all.
>
> On Debian, I experimented a little bit with xfce by modifying its menu
> file in /etc/xdg/menus
>
> For now, this is just a prove of concept. This have to be extended to
> all the other main categories. Eventually, files can be provided into
> /usr/share/desktop-directories for automatic translation. I commented
> it because otherwise all the sub-menus are named Multimedia, so
> files must be provided if we want to support the Directory tag.

I looked into different applications.menu files and they look mostly the same. There're possibilities to implement a skeleton
aplications.menu:
1) this file exists in FVWM and will be copied into /etc/xdg/menus/ e.g.
   as 'fvwm-applications.menu'
2) I implement a function in fvwm-menu-desktop which creates a
   fvwm-applications.menu in /home/<user>/.config/menus. Also
   fvwm-menu-desktop-config.fpl has to be modified to set a creation
   flag and shows a fake menu until it is created.

I prefer 1) but this is only because the overhead to write a menu from
scratch will blow up fvwm-menu-desktop much (the complete xml file
structure must pictured in code).

What is the best? 1), 2) or ... 3) no fvwm-applications.menu
Feedback, please ^^
--- fvwm-menu-desktop.in	2013-09-12 13:09:49.000000000 +0200
+++ fvwm-menu-desktop.in.new	2013-09-15 18:05:09.074888400 +0200
@@ -2,6 +2,14 @@
  
 # Modification History
 
+# Changed on 15/09/13 by Thomas Funk:
+# Some Bugfixes:
+# - DecodeEncodeErrors in menu names
+# - no output appears with 'fvwm-menu-desktop --get-menus all|desktop'
+# - No entry "Regenerate XDG menu(s)" appears with
+#   'fvwm-menu-desktop --insert-in-menu MenuRoot'
+# -  exchange all tabs with spaces to prevent indention errors
+
 # Changed on 15/06/13 by Thomas Funk:
 # support for python-xdg > 0.19.
 # add gettext localization.
@@ -114,7 +122,7 @@
         sys.exit(2)
     global verbose, force, size, theme, icon_dir, top, install_prefix, menu_type, menu_list_length 
     global with_titles, menu_entry_count, get_menus, timestamp, set_menus, printmode, insert_in_menu
-    version = "2.2"
+    version = "2.3"
     verbose = False
     force = False
     desktop=''
@@ -274,9 +282,9 @@
         vprint("\n DE weighting search: DE => [user menus, system menus, overall]")
         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
+            # 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', 'cinnamon', 'mate', 'debian', 'others', 'none']
         else:
             DEs = [desktop]
@@ -298,9 +306,9 @@
                     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.
+                # 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
+                # 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:
@@ -308,7 +316,7 @@
             if filled:
                 desktop_dict[de] = menus
                 filled = False
-			# fill the weight dictionary with the counts
+            # 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]))
     
@@ -326,18 +334,18 @@
                     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
+                    # first compare the total counts
                     if highest < de_total:
                         higher = True
                     elif highest == de_total:
-						# if the totals equal compare the users
+                        # 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
+                            # it the users equal compare the system menus
                             if highest_system < de_system:
                                 higher = True
-							# if the systems equal the last wins
+                            # if the systems equal the last wins
                             elif highest_system == de_system:
                                 higher = True # fixme, should be biunique. -but how? With atime?
             else:
@@ -352,8 +360,8 @@
         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
+                # 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)
@@ -372,11 +380,35 @@
     if verbose:
         sys.stderr.write(text+"\n")
     
-def printtext(text):
-    print text.encode("utf-8")
+def printtext(text, encodetext=True):
+    if encodetext:
+        print text.encode("utf-8")
+    else:
+        print text
 
+# fix unicode decode problem like
+# UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position xyz: ordinal not in range(128) 
+def decodetext(text):
+    old_stdout = sys.stdout
+    fnull = open(os.devnull, 'w')
+    sys.stdout = fnull
+    try:
+        new_text = text.encode("utf-8")
+        printtext('%s' % text)
+    except UnicodeDecodeError:
+        try:
+            unicode_text = text.decode("iso-8859-15")
+            text = "%s" % unicode_text
+            new_text = text.encode("iso-8859-15")
+            print new_text
+        except:
+            new_text = None
+    fnull.close()
+    sys.stdout = old_stdout
+    return new_text
+    
 def geticonfile(icon):
-    iconpath = xdg.IconTheme.getIconPath(icon, size, theme, ["png", "xpm"])
+    iconpath = xdg.IconTheme.getIconPath(icon, None, theme, ["png", "xpm", "svg"])
 
     if not iconpath == None:
         extension = os.path.splitext(iconpath)[1]
@@ -400,8 +432,13 @@
     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))
+    if os.path.exists(iconpath):
+        if not os.path.exists(iconfile):
+            os.system("if test \\( ! -f '%s' \\) -o \\( '%s' -nt '%s' \\) ; then convert '%s' -resize %i '%s' ; fi"% 
+                      (iconfile, iconpath, iconfile, iconpath, size, iconfile))
+    else:
+        sys.stderr.write("%s not found! Use default icon.\n" % iconpath)
+        return None
     return iconfile
 
     
@@ -417,22 +454,19 @@
         iconfile = geticonfile(icon) or getdefaulticonfile(command) or icon
         if not iconfile == '':
             iconfile = '%'+iconfile+'%'
-    # fix unicode decode problem like
-    # UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position xyz: ordinal not in range(128) 
-    try:
-        printtext('+ "%s%s" %s' % (name, iconfile, command))
-    except UnicodeDecodeError:
-        try:
-            unicode_name = name.decode("iso-8859-15")
-            unicode_command = command.decode("iso-8859-15")
-            text = '+ "%s%s" %s' % (unicode_name, iconfile, unicode_command)
-            print text.encode("iso-8859-15")
-        except:
-            sys.stderr.write("A menu entry cannot decode! Skipping it ...\n")
+    name = decodetext(name)
+    command = decodetext(command)
+    iconfile = decodetext(iconfile)
+    if not (name == None or command == None or iconfile == None):
+        printtext('+ "%s%s" %s' % (name, iconfile, command), False)
+    else:
+        sys.stderr.write("A menu entry cannot decode! Skipping ...\n")
+        printtext('# Decode Error! Menu entry skipped.')
 
 def parsemenus(menulist, desktop):
     global menu_entry_count
     if menu_list_length == 1:
+        new_menulist = menulist
         # user defines only one special menu
         parsemenu(xdg.Menu.parse(menulist[0]), top)
     else:
@@ -475,10 +509,9 @@
             printtext('+ "" Nop')
             printmenu("$[gt.Regenerate XDG Menu(s)]", "system-software-update", "Module FvwmPerl -l fvwm-menu-desktop-config.fpl" )
 
-        if not get_menus == '':
-            printtext('%s' % ' '.join(new_menulist))
-        
-    
+    if not get_menus == '':
+        printtext('%s' % ' '.join(new_menulist))
+
 def parsemenu(menu, name="", title=""):
     global menu_entry_count
     m = re.compile('%[A-Z]?', re.I) # Pattern for %A-Z (meant for %U)
@@ -487,43 +520,48 @@
     if not title:
         title = name
     if printmode:
-        if not insert_in_menu or not (insert_in_menu and name == top and menu_list_length == 1):
-            printtext('DestroyMenu "%s"' % name)
-        if with_titles:
-            # for insert-in-menu AddToMenu doesn't have a title for top menu
-            # because this will appear then in the other menu
-            if insert_in_menu and name == top and menu_list_length == 1:
-                printtext('AddToMenu "%s"' % name)
+        name = decodetext(name)
+        title = decodetext(title)
+        if not (name == None or title == None):
+            if not insert_in_menu or not (insert_in_menu and name == top and menu_list_length == 1):
+                printtext('DestroyMenu "%s"' % name, False)
+            if with_titles:
+                # for insert-in-menu AddToMenu doesn't have a title for top menu
+                # because this will appear then in the other menu
+                if insert_in_menu and name == top and menu_list_length == 1:
+                    printtext('AddToMenu "%s"' % name, False)
+                else:
+                    printtext('AddToMenu "%s" "%s" Title' % (name, title), False)
             else:
-                printtext('AddToMenu "%s" "%s" Title' % (name, title))
+                printtext('AddToMenu "%s"' % name, False)
         else:
-            printtext('AddToMenu "%s"' % name)
+            sys.stderr.write("A menu entry cannot decode! Skipping ...\n")
+            printtext('# Decode Error! Menu entry skipped.')
     for entry in menu.getEntries():
-    	if isinstance(entry, xdg.Menu.Menu):
+        if isinstance(entry, xdg.Menu.Menu):
             if printmode:
                 printmenu(entry.getName(), entry.getIcon(), 'Popup "%s"' % entry.getPath())
-    	elif isinstance(entry, xdg.Menu.MenuEntry):
+        elif isinstance(entry, xdg.Menu.MenuEntry):
             if printmode:
                 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:
+        else:
             if printmode:
                 printtext('# not supported: ' + str(entry))
 
-    # should only appear in a single menu. For more it will insert in parsemenus() when the top menu will built
-    if menu_list_length == 1 and not insert_in_menu and name == top:
-        printtext('+ "" Nop')
-        printmenu("$[gt.Regenerate XDG Menu(s)]", "system-software-update", "Module FvwmPerl -l fvwm-menu-desktop-config.fpl" )
-        
     if printmode:
+        # should only appear in a single menu. For more it will insert in parsemenus() when the top menu will built
+        if menu_list_length == 1 and name == top:
+            printtext('+ "" Nop')
+            printmenu("$[gt.Regenerate XDG Menu(s)]", "system-software-update", "Module FvwmPerl -l fvwm-menu-desktop-config.fpl" )
         printtext('')
 
     for entry in menu.getEntries():
-    	if isinstance(entry, xdg.Menu.Menu):
-    	    parsemenu(entry)
+        if isinstance(entry, xdg.Menu.Menu):
+            parsemenu(entry)
 
 usage="""
 A script which parses xdg menu definitions to build

Reply via email to