1#!@PYTHON@ 2 3# Modification History 4 5# Changed on 18/03/19 by Jaimos Skriletz: 6# - Updated script for and require Python 3. 7# - Drop support for Python 2. 8# - Added support for xdg.Menu.Separator. 9# - Added option --term-cmd to state the terminal emulator command 10# to use with Terminal=True .desktop entries. Default: xterm -e 11 12# Changed on 16/12/31 by Jaimos Skriletz: 13# - Added check for FVWM_USERDIR env variable. 14# - Added check for python-xdg module to print less errors if not found. 15# - Added option -e/--menu-error to output phython-xdg not found as 16# a menu for the default-config. 17 18# Changed on 16/10/27 by Jaimos Skrietz: 19# - Renamed default menu to XDGMenu and changed the name of the 20# FvwmForm to FvwmForm-XDGMenu-Config 21# - Modified the FvwmForm and added the abilty to load defaults from 22# the Form's data file. 23# - Changed default to generate menu titles. Disable with --without-titles 24# - The top level menu now has two additional items: 25# 'Regenerate' - Regenerates menu. 26# 'Configure' - Opens up FvwmForm-XDGMenu-Config. 27# - Added --regen-cmd "CMD" for a fvwm CMD to use on the Regenerate item. 28# Default: PipeRead `fvwm-menu-desktop` 29# - Added --include-items [config|regenerate|both|none] option 30# to control if the additional items are included in the menu. 31# - Added --dynamic option to be used with dynamic menus. 32# - Added --all-menus option to generate all menus and not try to determine 33# which one is best 34# - Changed default behavior to include menu titles. 35# - Added new option --without-titles 36 37# Changed on 25/02/14 by Thomas Funk: 38# - Converting of icons always to png 39 40# Changed on 06/10/13 by Thomas Funk: 41# Some Bugfixes: 42# - DecodeEncodeErrors in menu names 43# - no output appears with 'fvwm-menu-desktop --get-menus all|desktop' 44# - No entry "Regenerate XDG menu(s)" appears with 45# 'fvwm-menu-desktop --insert-in-menu MenuRoot' 46# - exchange all tabs with spaces to prevent indention errors 47# - add two new options: --app-icon --dir-icon 48# to handle default icons for not available app/dir icons 49# - fix bug in convert icon routine that background of svg icons are 50# transparent 51 52# Changed on 15/06/13 by Thomas Funk: 53# support for python-xdg > 0.19. 54# add gettext localization. 55 56# Changed on 10/01/12 by Thomas Funk: 57# Unicode support. 58 59# Changed on 01/26/12 by Dan Espen (dane): 60# Make compatible with fvwm-menu-desktop. 61# Restored DestroyMenu, needed for reload menus. 62# Remove bug, was printing iconpath on converted icons 63# Replace obsolete optparse, use getopt instead 64# Change from command line arg for applications.menu 65# change to using ?$XDG_MENU_PREFIX or theme? fixme 66# - use "Exec exec" for all commands, remove option. 67 68# fixme, fix documentation, FvwmForm-Desktop, usage prompt is wrong 69# change, mini icons are enabled by default. 70# there are rescalable icons. 71 72# Author: Piotr Zielinski (http://www.cl.cam.ac.uk/~pz215/) 73# Licence: GPL 2 74# Date: 03.12.2005 75 76# This script takes names of menu files conforming to the XDG Desktop 77# Menu Specification, and outputs their FVWM equivalents to the 78# standard output. 79# 80# http://standards.freedesktop.org/menu-spec/latest/ 81 82# This script requires the python-xdg module, which in Debian can be 83# installed by typing 84# 85# apt-get install python3-xdg 86# 87# On Fedora, python-xdg is installed by default. 88 89import sys 90import getopt 91import os.path 92import os 93import fnmatch 94import time 95 96# Test for python-xdg 97try: 98 import xdg.Menu 99except ImportError: 100 xdg_import_error = True 101else: 102 xdg_import_error = False 103 import xdg.IconTheme 104 import xdg.Locale 105 from xdg.DesktopEntry import * 106 from xdg.BaseDirectory import * 107 108 109# Main Function 110def main (): 111 112 description = """ 113Generate Fvwm Menu from xdg files. 114Standard output is a series Fvwm commands.""" 115 116 obs_args=['check-app', 117 'enable-style', 118 'enable-tran-style', 119 'fvwm-icons', 120 'kde_config', 121 'mini-icon-path', 122 'merge-user-menu', 123 'su_gui', 124 'utf8', 125 'wm-icons'] 126 dashed_obs_args=[] 127 for a in obs_args : 128 dashed_obs_args.append('--'+a) 129 130 obs_parms=['check-icons', 131 'check-mini-icon', 132 'destroy-type', 133 'dir', 134 'icon-app', 135 'icon-folder', 136 'icon-style', 137 'icon-title', 138 'icon-toptitle', 139 'icons-path', 140 'lang', 141 'menu-style', 142 'name', 143 'png-icons-path', 144 'submenu-name-prefix', 145 'time-limit', 146 'tran-icons-path', 147 'tran-mini-icons-path', 148 'type', 149 'uniconv-exec', 150 'uniconv', 151 'xterm'] 152 equaled_obs_parms=[] 153 for a in obs_parms : 154 equaled_obs_parms.append(a+'=') 155 dashed_obs_parms=[] 156 for a in obs_parms : 157 dashed_obs_parms.append('--'+a) 158 159 try: 160 opts, args = getopt.getopt(sys.argv[1:], "hs:t:vwe", 161 ["help", "verbose", "enable-mini-icons", "with-titles", "without-titles", "version", 162 "desktop=", "size=", "theme=", "install-prefix=", "menu-type=", "regen-cmd=", "term-cmd=", 163 "title=", "get-menus=", "set-menus=", "insert-in-menu=", "mini-icon-dir=", "menu-error", 164 "app-icon=", "dir-icon=", "include-items=", "dynamic", "all-menus"]+obs_args+equaled_obs_parms) 165 except getopt.GetoptError as err: 166 # print help information and exit: 167 print(str(err)) # will print something like "option -a not recognized" 168 print(usage) 169 sys.exit(2) 170 global verbose, force, size, current_theme, icon_dir, top, install_prefix, menu_type, menu_list_length, term_cmd 171 global with_titles, menu_entry_count, get_menus, timestamp, set_menus, printmode, insert_in_menu, previous_theme 172 global default_app_icon, default_dir_icon, include_items, config_menus, regen_cmd, dynamic_menu, build_all_menus 173 version = "2.4" 174 verbose = False 175 force = False 176 desktop='' 177 size=24 178 current_theme='gnome' 179 previous_theme='gnome' 180 icon_dir="~/.fvwm/icons" 181 top='XDGMenu' 182 insert_in_menu = False 183 install_prefix = '' 184 menu_type = '' 185 with_titles = True 186 menu_entry_count = 0 187 menu_list_length = 0 188 get_menus = '' 189 printmode = True 190 set_menus = [] 191 build_all_menus = False 192 config_menus = [] 193 default_app_icon = "gnome-applications" 194 default_dir_icon = "gnome-fs-directory" 195 include_items = 'both' 196 regen_cmd = 'PipeRead `fvwm-menu-desktop`' 197 dynamic_menu = False 198 menu_error = False 199 term_cmd = "xterm -e" 200 201 # Loads config options from $FVWM_USERDIR/.FvwmForm-XDGMenu-Config 202 if "FVWM_USERDIR" in os.environ: 203 config_file = "%s/.FvwmForm-XDGMenu-Config" % os.environ['FVWM_USERDIR'] 204 else: 205 config_file = "%s/.fvwm/.FvwmForm-XDGMenu-Config" % os.environ['HOME'] 206 if os.path.isfile(config_file): 207 fvwmform_config = open(config_file, "r", errors="ignore") 208 209 for l in fvwmform_config: 210 o = l.split() 211 if len(o)>2 and o[0] != '#': 212 if o[1][:3] == 'MEN' and o[2] == 'on': 213 config_menus.append(o[1][3:]) 214 if o[1] == 'IconsOn' and o[2] == 'on': 215 force = True 216 elif o[1] == 'Size': 217 size = int(o[2]) 218 elif o[1] == 'TitlesOn' and o[2] == 'on': 219 with_titles = True 220 elif o[1] == 'Theme': 221 current_theme = o[2] 222 elif o[1] == 'Title': 223 top = o[2] 224 elif o[1] == 'InsertInto': 225 top = o[2] 226 insert_in_menu = True 227 elif o[1] == 'Installprefix': 228 install_prefix = o[2] 229 elif o[1] == 'IconDir': 230 icon_dir = o[2] 231 elif o[1] == 'DirIcon': 232 default_dir_icon = o[2] 233 elif o[1] == 'AppIcon': 234 default_app_icon = o[2] 235 elif o[1] == 'IncludeConfig' and o[2] == 'on': 236 include_items = "config" 237 elif o[1] == 'IncludeRegen' and o[2] == 'on': 238 include_items = "regenerate" 239 elif o[1] == 'IncludeBoth' and o[2] == 'on': 240 include_items = "both" 241 elif o[1] == 'IncludeNone' and o[2] == 'on': 242 include_items = "none" 243 elif o[1] == 'TermCmd': 244 term_cmd = " ".join(o[2:]) 245 fvwmform_config.close() 246 247 for o, a in opts: 248 if o in ("-v", "--verbose"): 249 verbose = True 250 if os.path.isfile(config_file): 251 vprint("Defaults loaded from %s\n" % config_file) 252 else: 253 vprint("Config file not found: %s\nUsing built-in defaults.\n" % config_file) 254 elif o in ("-h", "--help") : 255 print(usage) 256 sys.exit() 257 elif o in ("--version") : 258 print("fvwm-menu-desktop version " + version) 259 sys.exit() 260 elif o in ("-e", "--menu-error") : 261 menu_error = True 262 elif o in ("--enable-mini-icons") : 263 force=True 264 elif o in ("--insert-in-menu") : 265 top=a 266 insert_in_menu = True 267 elif o in ("--desktop") : 268 desktop=a 269 elif o in ("-t", "--title") : 270 top=a 271 elif o in ("--get-menus") : 272 if a == 'all' or a == 'desktop' : 273 get_menus=a 274 printmode = False 275 else : 276 sys.stderr.write( "--get-menus argument must be 'all' or 'desktop' found "+a ) 277 print(usage) 278 sys.exit(1) 279 elif o in ("-s","--size") : 280 size = int(a) 281 elif o in ("--mini-icon-dir") : 282 icon_dir = a 283 elif o in ("--set-menus") : 284 if a[-1] == ' ': 285 a = a[:-1] 286 set_menus=a.split(' ') 287 elif o in ("--install-prefix") : 288 if a and not os.path.isabs(a): 289 assert False, "install-prefix must be an absolute path" 290 # add trailing slash if not there already 291 if not a[-1] == '/' : # trailing slash 292 a=a + '/' 293 install_prefix = a 294 elif o in ("--theme") : 295 current_theme = a 296 elif o in ("--menu-type") : 297 menu_type = a 298 elif o in ("-w", "--with-titles") : 299 with_titles = True 300 elif o in ("--without-titles") : 301 with_titles = False 302 elif o in ("--app-icon") : 303 default_app_icon = a 304 elif o in ("--dir-icon") : 305 default_dir_icon = a 306 elif o in ("--include-items") : 307 if a in ("both", "none", "config", "regenerate") : 308 include_items = a 309 else: 310 sys.stderr.write( "--include-items argument must be 'config', 'regenerate', 'both' or 'none' found "+a ) 311 print(usage) 312 sys.exit(1) 313 elif o in ("--regen-cmd") : 314 regen_cmd = a 315 elif o in ("--term-cmd") : 316 term_cmd = a 317 elif o in ("--dynamic") : 318 dynamic_menu = True 319 elif o in ("--all-menus") : 320 build_all_menus = True 321 elif o in (str(dashed_obs_args+dashed_obs_parms)) : 322 # Ignore 323 sys.stderr.write( "Warning: Arg "+o+" is obsolete and ignored\n" ) 324 else: 325 assert False, "unhandled option" 326 327 # Exit if python-xdg not found 328 if xdg_import_error: 329 if menu_error: 330 printtext('DestroyMenu "%s"' % top) 331 printtext('AddToMenu "%s" "%s" Title' % (top, top)) 332 printtext('+ "Error: python-xdg not found" Nop') 333 printtext('+ "" Nop') 334 printtext('+ "Regenerate" PipeRead `fvwm-menu-desktop -e`') 335 else: 336 sys.stderr.write('Python module python-xdg not found.') 337 sys.exit(1) 338 339 340 timestamp = time.time() 341 342 if len(set_menus) == 0: 343 xdg_menu_prefix = ((os.environ['XDG_MENU_PREFIX'] if 'XDG_MENU_PREFIX' in os.environ else '')) 344 345 # First check if no user presettings made 346 if desktop == '': 347 # check if $XDG_MENU_PREFIX is set 348 if not xdg_menu_prefix == '': 349 desktop = xdg_menu_prefix.replace('-', '').lower() 350 351 vprint("Parameters for creating menu list:") 352 vprint(" XDG_MENU_PREFIX: \'%s\'" %xdg_menu_prefix) 353 vprint(" --install-prefix: \'%s\'" %install_prefix) 354 vprint(" --desktop: \'%s\'" %desktop) 355 vprint(" --menu-type: \'%s\'" %menu_type) 356 357 vprint("\nStart search ...") 358 menulist, desktop_temp = getmenulist(desktop, menu_type) 359 if not desktop_temp == '': 360 desktop = desktop_temp 361 362 else: 363 menulist = set_menus 364 365 vprint(" Menu list: %s\n" %menulist) 366 menu_list_length = len(menulist) 367 368 if menu_list_length == 0: 369 if not desktop == '': 370 desktop = desktop + '-' 371 if menu_error: 372 printtext('DestroyMenu "%s"' % top) 373 printtext('AddToMenu "%s" "%s" Title' % (top, top)) 374 printtext('+ "Error: No menus found" Nop') 375 printtext('+ "" Nop') 376 printtext('+ "Regenerate" PipeRead `fvwm-menu-desktop -e`') 377 else: 378 sys.stderr.write(install_prefix+desktop+menu_type+".menu not available on this system. Exiting...\n") 379 sys.exit(1) 380 else: 381 # set previous_theme if <icon_dir>/.theme exist 382 if os.path.exists(os.path.join(os.path.expanduser(icon_dir), ".theme")): 383 previous_theme = next(open(os.path.join(os.path.expanduser(icon_dir), ".theme"), 'r')).replace('\n', '') 384 vprint(" Previous used theme: %s" %previous_theme) 385 vprint(" Current used theme: %s\n" %current_theme) 386 387 sys.stderr.flush() 388 parsemenus(menulist, desktop) 389 390 # write current_theme to <icon_dir>/.theme if --enable-mini-icons and printmode is set 391 if printmode and force: 392 fh = open(os.path.join(os.path.expanduser(icon_dir), ".theme"), "w") 393 fh.write(current_theme) 394 fh.close() 395 396 sys.stdout.flush() 397 vprint("\nProcess took " + str(time.time()-timestamp) + " seconds") 398 399def getmenulist(desktop, menu_type): 400 menudict = {} 401 config_dirs = [] 402 if not install_prefix == '': 403 config_dirs = [install_prefix] 404 else: 405 config_dirs = xdg_config_dirs # xdg_config_dirs is a built-in list from python-xdg 406 407 found_menus = 0 408 for dir in config_dirs: 409 if install_prefix == '': 410 dir = os.path.join(dir, 'menus') 411 # skipping all paths which not available 412 if os.path.exists(dir): 413 filelist = set([]) 414 dir_list = os.listdir(dir) 415 #pattern = '*'+desktop+'*'+menu_type+'*.menu' 416 # Always find all menus 417 pattern = '*.menu' 418 for filename in fnmatch.filter(dir_list, pattern): 419 filelist.add(filename) 420 421 # the menudict dictionary has a unsorted list (set) for the values. 422 # set is easier to use then a list for removing items 423 menudict[dir] = filelist 424 found_menus += len(filelist) 425 vprint(" found in %s: %s" %(dir, list(filelist))) 426 427 desktop_dict = {} 428 if not found_menus == 0: 429 all_menus = [] 430 # remove all menus in /usr/local/etc/xdg/menus if exist in user dir 431 for path in list(menudict.keys()): 432 if not path == '/usr/local/etc/xdg/menus': 433 if path == os.path.join(os.getenv("HOME"), '.config/menus'): 434 menudict['/usr/local/etc/xdg/menus'] = menudict['/usr/local/etc/xdg/menus'] - menudict[path] 435 #else: 436 # menudict[path] = menudict[path] - menudict['/usr/local/etc/xdg/menus'] 437 for menu in list(menudict[path]): 438 all_menus.append(path + '/' + menu) 439 440 if not menudict['/usr/local/etc/xdg/menus'] == 0: 441 for menu in list(menudict['/usr/local/etc/xdg/menus']): 442 all_menus.append('/usr/local/etc/xdg/menus/' + menu) 443 444 if get_menus == 'all' or (build_all_menus and desktop == '' and menu_type == ''): 445 return all_menus, '' 446 447 # get menus selected in config file 448 if len(config_menus) > 0: 449 config_menulist = [] 450 for i in config_menus: 451 for j in all_menus: 452 if fnmatch.fnmatch( j, "*%s.menu" % i ): 453 config_menulist.append(j) 454 vprint("\n Selected menus from config file: %s " % list(config_menulist)) 455 # Use config file if --dekstop not set 456 if len(config_menulist) == 0: 457 vprint(" No menus in config found. Using all menus.") 458 elif desktop == '' and menu_type == '': 459 vprint(" Using menus from config file.") 460 return config_menulist, '' 461 else: 462 vprint(" Ignoring menus in config file, due to --desktop or --menu-type.") 463 464 # filter --desktop and --menu-type options 465 if desktop != '' or menu_type != '': 466 vprint("\n Filtering menus according to --desktop %s and --menu-type %s" % (desktop, menu_type) ) 467 pattern = '*'+desktop+'*'+menu_type+'*' 468 for path in list(menudict.keys()): 469 for menu in list(menudict[path]): 470 if not fnmatch.fnmatch( menu, pattern ): 471 menudict[path].remove(menu) 472 if menudict[path] == set([]): 473 del menudict[path] 474 if menudict == {}: 475 sys.stderr.write("No menus found matching --desktop %s and --menu-type %s. Exiting...\n" % (desktop, menu_type) ) 476 sys.exit(1) 477 478 vprint("\n Finding best menu in Menu List: %s" % menudict ) 479 if build_all_menus: 480 all_menus = [] 481 for key in menudict: 482 for i in menudict[key]: 483 all_menus.append(key+'/'+i) 484 return all_menus, '' 485 486 # sort menus related to desktops and create a weighting 487 vprint("\n DE weighting search: DE => [user menus, system menus, overall]") 488 weight_dict = {} 489 if desktop == '': 490 # first the desktops, then debian (shouldn't appear in others) then others holding 491 # all other non DE menus e.g. tools and at the end the nones without prefixes 492 # If there're other prefixes from other WMs - should be added BEFORE debian 493 DEs = ['gnome', 'kde', 'xfce', 'lxde', 'cinnamon', 'mate', 'debian', 'others', 'none'] 494 else: 495 DEs = [desktop] 496 for de in DEs: 497 menus = set([]) 498 user_menus = 0 499 system_menus = 0 500 filled = False 501 for path in list(menudict.keys()): 502 if de == 'none': 503 pattern = '*' 504 elif de == 'others': 505 pattern = '*-*' 506 else: 507 pattern = '*'+de+'*' 508 # fnmatch.filter returns a list of files the pattern match 509 menu_names = fnmatch.filter(menudict[path], pattern) 510 if not len(menu_names) == 0: 511 filled = True 512 for name in menu_names: 513 menus.add(path+'/'+name) 514 # delete each found DE menu from the actual path. So, the menus will be reduced loop by loop. 515 menudict[path] = menudict[path]-set(menu_names) 516 # count the menus found in the users and systems menu path for later weighting 517 if not path == '/usr/local/etc/xdg/menus': 518 user_menus = len(menu_names) 519 else: 520 system_menus = len(menu_names) 521 if filled: 522 desktop_dict[de] = menus 523 filled = False 524 # fill the weight dictionary with the counts 525 weight_dict[de] = [user_menus, system_menus, user_menus+system_menus] 526 vprint(" %s => %s" %(de, weight_dict[de])) 527 528 # get the highest rated desktop 529 highest = 0 530 de_highest = '' 531 for de in sorted(weight_dict.keys()): 532 de_user = weight_dict[de][0] 533 de_system = weight_dict[de][1] 534 de_total = weight_dict[de][2] 535 higher = False 536 if not de_highest == '': 537 # don't weight 'none' and 'others cause both not DEs 538 if not de == 'none' and not de == 'others': 539 highest_user = weight_dict[de_highest][0] 540 highest_system = weight_dict[de_highest][1] 541 highest_total = weight_dict[de_highest][2] 542 # first compare the total counts 543 if highest < de_total: 544 higher = True 545 elif highest == de_total: 546 # if the totals equal compare the users 547 if highest_user < de_user: 548 higher = True 549 elif highest_user == de_user: 550 # it the users equal compare the system menus 551 if highest_system < de_system: 552 higher = True 553 # if the systems equal the last wins 554 elif highest_system == de_system: 555 higher = True # fixme, should be biunique. -but how? With atime? 556 else: 557 higher = True 558 559 if higher: 560 highest = de_total 561 de_highest = de 562 563 if highest == 0 : # no dev environments? 564 de_highest = 'others' # use 'others' 565 vprint( "\n Winner: %s" %de_highest) 566 567 # Perhaps there're a global menus available which are not in the highest rated list 568 if 'none' in desktop_dict: 569 for menu in desktop_dict['none']: 570 name = menu.replace('.menu', '').split('/') 571 # the fnmatch.filter will be used to find NO match because then 572 # the menu is not in the list 573 found = fnmatch.filter(desktop_dict[de_highest], '*'+name[-1]+'*') 574 if found == []: 575 desktop_dict[de_highest].add(menu) 576 577 # Add 'others' menus to list, because these could be tool menus like yast, etc 578 if 'others' in desktop_dict: 579 for menu in desktop_dict['others']: 580 desktop_dict[de_highest].add(menu) 581 582 if len(desktop_dict) == 0: 583 return [], '' 584 else: 585 return list(desktop_dict[de_highest]), de_highest 586 587def vprint(text): 588 if verbose: 589 sys.stderr.write(text+"\n") 590 591# Encoding error handling of menu entries. 592def printtext(text): 593 try: print(text) 594 except UnicodeEncodeError: 595 if verbose: print("# UnicodeEncodeError - Attempting to encode") 596 try: 597 sys.stdout.flush() 598 sys.stdout.buffer.write(text.encode()) 599 print() 600 except: print(text.encode("ascii", errors="replace").decode("ascii")) 601 except: 602 if verbose: print("# Unknown error - Skipping entry") 603 604def geticonfile(icon): 605 iconpath = xdg.IconTheme.getIconPath(icon, size, current_theme, ["png", "xpm", "svg"]) 606 607 if not iconpath == None: 608 extension = os.path.splitext(iconpath)[1] 609 610 if not iconpath: 611 return None 612 613 if not force: 614 return iconpath 615 616 if iconpath.find("%ix%i" % (size, size)) >= 0: # ugly hack!!! 617 return iconpath 618 619 if not os.path.isdir(os.path.expanduser(icon_dir)): 620 os.makedirs(os.path.expanduser(icon_dir)) 621 622 iconfile = os.path.join(os.path.expanduser(icon_dir), 623 "%ix%i-" % (size, size) + 624 os.path.basename(iconpath)) 625 626 if os.path.exists(iconpath): 627 iconfile = iconfile.replace(extension, '.png') 628 if extension == '.svg': 629 os.system("if test \\( \\( ! -f '%s' \\) -o \\( '%s' -nt '%s' \\) \\) -o \\( '%s' != '%s' \\); then convert -background none '%s' -resize %i '%s' ; fi"% 630 (iconfile, iconpath, iconfile, current_theme, previous_theme, iconpath, size, iconfile)) 631 else: 632 os.system("if test \\( \\( ! -f '%s' \\) -o \\( '%s' -nt '%s' \\) \\) -o \\( '%s' != '%s' \\); then convert '%s' -resize %i '%s' ; fi"% 633 (iconfile, iconpath, iconfile, current_theme, previous_theme, iconpath, size, iconfile)) 634 else: 635 sys.stderr.write("%s not found! Using default icon.\n" % iconpath) 636 return None 637 return iconfile 638 639 640def getdefaulticonfile(command): 641 if command.startswith("Popup"): 642 return geticonfile(default_dir_icon) 643 else: 644 return geticonfile(default_app_icon) 645 646def printmenu(name, icon, command): 647 iconfile = '' 648 if force : 649 iconfile = geticonfile(icon) or getdefaulticonfile(command) or icon 650 if not (iconfile == '' or iconfile == None): 651 iconfile = '%'+iconfile+'%' 652 else: 653 sys.stderr.write("%s icon or default icon not found!\n") 654 printtext('+ "%s%s" %s' % (name, iconfile, command)) 655 656def parsemenus(menulist, desktop): 657 global menu_entry_count 658 if menu_list_length == 1: 659 new_menulist = menulist 660 # user defines only one special menu 661 parsemenu(xdg.Menu.parse(menulist[0]), top) 662 else: 663 # create a top title list 664 top_titles = [] 665 for file in menulist: 666 # extract and split the filename and set first char of each word to capital 667 name_parts = file.replace('.menu', '').split('/')[-1].split('-') 668 name_parts = [name[0].replace(name[0], name[0].upper())+name[1:] for name in name_parts] 669 top_titles.append(' '.join(name_parts)) 670 671 # create the submenus 672 new_toptitles = [] 673 new_menulist = [] 674 for title, menu in zip(top_titles, menulist): 675 name = 'Fvwm'+title 676 vprint("Create submenu \'%s\' from \'%s\'" %(name, menu)) 677 parsemenu(xdg.Menu.parse(menu), name, title) 678 # remove a menu if no menu entry was created in its sub menus 679 if not menu_entry_count == 0: 680 new_toptitles.append(title) 681 new_menulist.append(menu) 682 menu_entry_count = 0 683 else: 684 vprint(" Menu is empty - won't be used!") 685 686 # create the root menu 687 if printmode: 688 if not insert_in_menu: 689 if dynamic_menu: 690 printtext('DestroyMenu recreate "%s"' % top) 691 else: 692 printtext('DestroyMenu "%s"' % top) 693 if with_titles and not insert_in_menu: 694 printtext('AddToMenu "%s" "%s" Title' % (top, top)) 695 else: 696 printtext('AddToMenu "%s"' % top) 697 698 for title in sorted(new_toptitles): 699 name = 'Fvwm'+title 700 printmenu(title, '', 'Popup "%s"' % name) 701 702 if include_items != 'none': 703 printtext('+ "" Nop') 704 if include_items in ("both", "regenerate"): 705 printmenu("$[gt.Regenerate]", "system-software-update", regen_cmd ) 706 if include_items in ("both", "config"): 707 printmenu("$[gt.Configure]", "system-software-update", "Module FvwmPerl -l fvwm-menu-desktop-config.fpl" ) 708 709 if not get_menus == '': 710 printtext('%s' % ' '.join(new_menulist)) 711 712def parsemenu(menu, name="", title=""): 713 global menu_entry_count 714 m = re.compile('%[A-Z]?', re.I) # Pattern for %A-Z (meant for %U) 715 if not name : 716 name = menu.getPath() 717 if not title: 718 title = name 719 if printmode: 720 if not insert_in_menu or not (insert_in_menu and name == top and menu_list_length == 1): 721 if name == top and dynamic_menu: 722 printtext('DestroyMenu recreate "%s"' % name) 723 else: 724 printtext('DestroyMenu "%s"' % name) 725 if with_titles: 726 # for insert-in-menu AddToMenu doesn't have a title for top menu 727 # because this will appear then in the other menu 728 if insert_in_menu and name == top and menu_list_length == 1: 729 printtext('AddToMenu "%s"' % name) 730 else: 731 printtext('AddToMenu "%s" "%s" Title' % (name, title)) 732 else: 733 printtext('AddToMenu "%s"' % name) 734 for entry in menu.getEntries(): 735 if isinstance(entry, xdg.Menu.Menu): 736 if printmode: 737 printmenu(entry.getName(), entry.getIcon(), 'Popup "%s"' % entry.getPath()) 738 elif isinstance(entry, xdg.Menu.MenuEntry): 739 if printmode: 740 desktop = DesktopEntry(entry.DesktopEntry.getFileName()) 741 # eliminate '%U' etc behind execute string 742 execProgram = m.sub('', desktop.getExec()) 743 if desktop.getTerminal(): 744 execProgram = "%s %s" % (term_cmd, execProgram) 745 printmenu(desktop.getName(), desktop.getIcon(), "Exec exec " + execProgram) 746 menu_entry_count += 1 747 elif isinstance(entry, xdg.Menu.Separator): 748 if printmode: 749 printtext( '+ "" Nop' ) 750 else: 751 if printmode: 752 printtext('# not supported: ' + str(entry)) 753 754 if printmode: 755 # should only appear in a single menu. For more it will insert in parsemenus() when the top menu will built 756 if menu_list_length == 1 and name == top and include_items != 'none': 757 printtext('+ "" Nop') 758 if include_items in ("both", "regenerate"): 759 printmenu("$[gt.Regenerate]", "system-software-update", regen_cmd ) 760 if include_items in ("both", "config"): 761 printmenu("$[gt.Configure]", "system-software-update", "Module FvwmPerl -l fvwm-menu-desktop-config.fpl" ) 762 printtext('') 763 764 for entry in menu.getEntries(): 765 if isinstance(entry, xdg.Menu.Menu): 766 parsemenu(entry) 767 768usage=""" 769A script which parses xdg menu definitions to build 770the corresponding fvwm menus. 771 772Usage: $0 [OPTIONS] 773Options: 774 -h, --help show this help and exit. 775 --version show version and exit. 776 --install-prefix DIR install prefix of the desktop menu files. 777 Per default not set. For system wide menus 778 use /usr/local/etc/xdg/menus/. 779 --desktop NAME use menus that include NAME in the file name: 780 gnome, kde, xfce, lxde, debian, etc. 781 --menu-type NAME use menus that include NAME in the file name: 782 applications, settings, preferences, etc. When 783 used with --desktop only menus whose file name 784 mathces '*desktop*menutype*' are used. 785 --theme NAME icon theme: gnome (default), oxygen, etc. Don't 786 use hicolor. It's the default fallback theme if 787 no icon is found. 788 -w, --with-titles generate menus with titles. Default. 789 --without-titles generate menus without titles. 790 --enable-mini-icons enable mini-icons in menu. 791 -s, --size NUM set size of mini-icons in menu. Default is 24. 792 --mini-icon-dir DIR set directory for mini-icons. 793 Default is ~/.fvwm/icons. 794 --app-icon NAME set default application icon if no others found. 795 Default is 'gnome-applications'. 796 --dir-icon NAME set default directory icon if no others found. 797 Default is 'gnome-fs-directory'. 798 -t, --title NAME menu title of the top menu used by Popup command. 799 Default is XDGMenu. 800 --insert-in-menu NAME generates a menu to place it in the root level 801 of the menu NAME. 802 --get-menus all|desktop prints a space separated list of full menu paths. 803 'all' is all menus on the system except empty 804 ones. 'desktop' list the menus that would have 805 been generated. No menu generation is done. 806 --set-menus menu_paths expects a space separated list of full menu paths 807 to generate user specified menus. 808 --all-menus generate all menus found. 809 --include-items NAME include additional menu items NAME in top level 810 menu. NAME can be 'config', 'regenerate', 'both' 811 or 'none'. Default both. 812 --regen-cmd ACTION The fvwm ACTION for the 'Regenerate' menu item. 813 Default: 'PipeRead `fvwm-menu-desktop`' 814 --term-cmd CMD Terminal emulator CMD used on terminal entries. 815 Default: xterm -e 816 --dynamic used with dynamic menus. 817 -e, --menu-error out python-xdg not found error in menu. 818 -v, --verbose run and display debug info on STDERR.""" 819 820if __name__ == "__main__": 821 main() 822 823# Local Variables: 824# mode: python 825# compile-command: "python3 fvwm-menu-desktop.in --version" 826# End: 827