[sawfish] partial rewrite of fdo-menu



commit b774d5e749d3e49b37b2d794f2e73abd7d9a0f6a
Author: chrisb <zanghar freenet de>
Date:   Sun Sep 6 21:34:38 2009 +0200

    partial rewrite of fdo-menu

 ChangeLog                       |    3 +
 lisp/sawfish/wm/ext/fdo-menu.jl |  403 +++++++++++++++++----------------------
 2 files changed, 175 insertions(+), 231 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index ca8f612..96f9dad 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+2009-09-06  Christopher Bratusek <zanghar freenet de>
+	* lisp/sawfish/wm/ext/fdo-menu.jl: Partial rewrite of fdo-menu [Matthew Love]
+
 2009-09-05  Christopher Bratusek <zanghar freenet de>
 	* sawfish.spec.in: improved [Kim B. Heino]
 
diff --git a/lisp/sawfish/wm/ext/fdo-menu.jl b/lisp/sawfish/wm/ext/fdo-menu.jl
index 35a1d59..eea940f 100644
--- a/lisp/sawfish/wm/ext/fdo-menu.jl
+++ b/lisp/sawfish/wm/ext/fdo-menu.jl
@@ -1,5 +1,5 @@
 ;-*-sawfish-*-
-;; (fdo-menu.jl (v0.6.5) --- sawfish wm menu generation -- librep)
+;; (fdo-menu.jl (v1.0.0) --- sawfish wm menu generation -- librep)
 
 ;; (c) 2009 Matthew Love
 ;; Christopher Bratusek
@@ -11,68 +11,12 @@
 ;; Create a sawfish wm menu from .desktop files
 ;; in your /usr/share/applications folder.
 
-#|
-
-Usage:
-
-Make sure the mk-saw-menu.jl is in your load path
-(i.e. ~/.sawfish/lisp), then in your .sawfish[/]rc file add:
-
-;; Optional Part, config bits, defaults listed below
-;; can be savely skipped, if you're fine with the defaults
-
-;; change xterm -e to your appropriate string (must have
-;; a space at the end)
-
-(setq my-term-string "xterm -e ")
-
-;; what locale for localized strings to use, if available
-;; eg.: set to de to read Dokumentbetrachter instead of Documentviewer
-
-(setq my-lang-string '())
-
-;; if your .desktop files are located somewhere else than
-;; /usr/share/applications, then change the desktop-directory
-
-(setq desktop-directory '("/usr/share/applications"))
-
-;; some entries are hidden from the menu, especially GNOME Apps
-;; like eog, nautilus or evince & Co, if you want to have them
-;; added to your menu, then replace '() by 't in the following
-
-(setq ignore-no-display '())
-
-;; if you don't want your menus to be sorted alphabetically
-;; then replace 't by '() in the following
-
-(setq want-alphabetize 't)
-
-;; Necessary part, can't be skipped
-
-;; load our script
-
-(require 'fdo-menu)
-
-;; generate and set the menu
-
-(update-saw-menu)
-
-|#
-
-;;; TODO:
-
-;; adhere to the desktop entry file specifications.
-;; http://standards.freedesktop.org/desktop-entry-spec/latest/
-;; add support for field codes: <a href="http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html";
-;; add support for Comment=/Comment[lang]=
-;; add support for Icon=
-
 ;;; Code:
 
 (define-structure sawfish.wm.ext.fdo-menu
 
     (export update-saw-menu)
-
+    
     (open rep
 	  rep.io.files
 	  rep.io.streams
@@ -84,28 +28,74 @@ Make sure the mk-saw-menu.jl is in your load path
 
   (unless batch-mode
 
-    ;; Some defaults
-    ;;my-comm ()
-    ;;my-icon ()
-    (defvar my-name nil)
-    (defvar my-disp nil)
-    (defvar my-term nil)
-    (defvar my-exec nil)
-    (defvar my-cat nil)
-    (defvar file-line nil)
+    (defvar this-line nil)
     (defvar *loc-menu* nil)
     (make-variable-special 'apps-menu)
 
+    ;; fdo-desktop-file-parsing
+    
+    (defun desktop-file-p (directory-file)
+      (let ((this-file (open-file directory-file 'read)))
+	(string= (read-line this-file) "[Desktop Entry]\012")))
+
+    (defun desktop-group-p (instring)
+      (string= (substring instring 0 1) "["))
+
+    (defun desktop-skip-line-p (instring)
+      (or (not instring)
+	  (string= (substring instring 0 1) "#")
+	  (string= (substring instring 0 1) "\012")))
+
+    (defun get-key-break (instring key)
+      (if instring
+	  (do ((mcount 0 (1+ mcount))) 
+	      ((or (string= (substring instring mcount (+ mcount 1)) "\n")
+		   (string= (substring instring mcount (+ mcount 1)) key)
+		   (= mcount 398)) mcount))))
+
+    (defun get-desktop-key (instring)
+      (if (> (length instring) 3)
+	  (let ((break-number (get-key-break instring "=")))
+	    (if (< break-number 20)
+		(substring instring 0 break-number)))))
+
+    (defun get-desktop-value (instring)
+      (if (> (length instring) 3)
+	  (let ((break-number (get-key-break instring "=")))
+	    (if (< break-number 20)
+		(substring instring (+ 1 break-number))))))
+
+    (defun get-desktop-group (instring)
+      (substring instring 1 (- (length instring) 2)))
+
+    (defun parse-desktop-file-line (infile)
+      (if (setq this-line (read-line infile))
+	  (if (not (desktop-skip-line-p this-line))
+	      (cons
+	       (if (desktop-group-p this-line)
+		   (get-desktop-group this-line)
+		 (if (not (desktop-group-p this-line))
+		     (cons (get-desktop-key this-line) (get-desktop-value this-line))))
+	       (parse-desktop-file-line infile))
+	    (parse-desktop-file-line infile))))
+
+    (defun parse-desktop-file (infile)
+      (unless (not (desktop-file-p infile))
+	(let ((d-file (open-file infile 'read)))
+	  (parse-desktop-file-line d-file))))
+
+    ;; generic functions
+    
     (defun map-desk-files (in-desk-files in-directory)
       (if in-desk-files
 	  (cons (expand-file-name (car in-desk-files) in-directory)
 		(map-desk-files (cdr in-desk-files) in-directory))))
-
+    
     (defun map-dir-files (directories)
       (if directories
 	  (if (file-directory-p (car directories))
 	      (let ((desk0 (directory-files (car directories))))
-		(cons (map-desk-files desk0 (car directories))
+		(cons (map-desk-files desk0 (car directories)) 
 		      (map-dir-files (cdr directories))))
 	    (map-dir-files (cdr directories)))))
 
@@ -114,91 +104,106 @@ Make sure the mk-saw-menu.jl is in your load path
 	    ((atom input) (list input))
 	    (t (append (flatten (car input))
 		       (flatten (cdr input))))))
+    
+    (defun trim-end (string)
+      (cond
+       ((string= (aref string (- (length string) 3)) 37)
+	(substring string 0 (- (length string) 4)))
+       (string
+	(substring string 0 (- (length string) 1)))))
 
     (defun find-lang-string ()
-      (if (getenv "LANG")
-	  (if (> (length (getenv "LANG")) 2)
-	      (substring (getenv "LANG") 0 5)
-	    (substring (getenv "LANG") 0 2))
-	'()))
-
-    ;; Variables that can be set in .sawfish[/]rc
-    (unless (variable-customized-p 'desktop-directory)
+      (cond
+       ((getenv "LANGUAGE")
+	(let ((mlang (getenv "LANGUAGE")))
+	  (if (> (length mlang) 2)
+	      (substring mlang 0 5)
+	    (substring mlang 0 2))))
+       ((getenv "LC_ALL")
+	(let ((mlang (getenv "LC_ALL")))
+	  (if (> (length mlang) 2)
+	      (substring mlang 0 5)
+	    (substring mlang 0 2))))
+       ((getenv "LC_MESSAGES")
+	(let ((mlang (getenv "LC_MESSAGES")))
+	  (if (> (length mlang) 2)
+	      (substring mlang 0 5)
+	    (substring mlang 0 2))))
+       ((getenv "LANG")
+	(let ((mlang (getenv "LANG")))
+	  (if (> (length mlang) 2)
+	      (substring mlang 0 5)
+	    (substring mlang 0 2))))))
+
+    ;; Variables that can be set in .sawfish[/]rc    
+    
+    (if (not (boundp 'desktop-directory))
 	(defvar desktop-directory '("/usr/share/applications")))
-
-    (unless (variable-customized-p 'my-lang-string)
+    
+    (if (not (boundp 'my-lang-string))
 	(defvar my-lang-string (find-lang-string)))
 
     (if my-lang-string
-	(define name-string "Name[")
-      (defvar name-string "Name="))
+	(defvar name-string "Name["))
 
-    (unless (variable-customized-p 'ignore-no-display)
+    (if (not (boundp 'ignore-no-display))
 	(defvar ignore-no-display '()))
 
-    (unless (variable-customized-p 'want-alphabetize)
+    (if (not (boundp 'want-alphabetize))
 	(defvar want-alphabetize 't))
 
-    (unless (variable-customized-p 'my-term-string)
+    (if (not (boundp 'my-term-string))
 	(defvar my-term-string "xterm -e "))
 
-    (unless (variable-customized-p 'use-fdo-menu)
+    (if (not (boundp 'use-fdo-menu))
 	(defvar use-fdo-menu 't))
 
     ;; The Master Category List
-    (defvar menu-cat-alist
-	  '(("Desktop" .  ("X-Desktop" "X-DesktopApplets" "X-DesktopCountry" \
-			   "DesktopSettings" "GNOME" "KDE" "X-GNOME-PersonalSettings" \
-			   "X-Xfce-Toplevel"))
-	    ("Personal" . ("X-Personal" "X-PersonalUtility" "Calendar" "ContactManagement"))
-	    ("Office" . ("Office" "WordProcessor" "Presentation" "X-Document" \
-			 "TextEditor" "SpreadSheet" "Calculator" "X-Calculate" \
-			 "Chart" "FlowChart" "Finance"))
-	    ("Internet" . ("Telephony" "Network" "Dialup" "VideoConference" \
-			   "RemoteAccess" "News" "HamRadio" "FileTransfer" \
-			   "X-Internet" "P2P" "Email" "WebBrowser" "IRCClient" "Chat" \
-			   "InstantMessaging" "Chat" "WebDevelopment"))
-	    ("Games" . ("Game" "ActionGame" "AdventureGame" "ArcadeGame" "BoardGame" "Emulator"\
-			"BlocksGame" "CardGame" "KidsGame" "LogicGame" "RolePlaying" "Simulation"))
-	    ("Graphics" . ("RasterGraphics" "VectorGraphics" "X-GraphicUtility" \
-			   "2DGraphics" "3dGraphics" "3DGraphics" "Scanning" "OCR" "Photography" \
-			   "Viewer" "Publishing" "Art" "ImageProcessing"))
-	    ("Media" . ("AudioVideo" "Audio", "Video" "Midi" "Mixer" "Sequencer" "Tuner" \
-			"TV" "AudioVideoEditing" "Player" "Recorder" "DiscBurning" "Music"))
-	    ("Science" . ("Science" "Astrology" "ArtificialIntelligence" "Astronomy" \
-			  "Biology" "Chemistry" "ComputerScience" "DataVisualization" \
-			  "Electricity" "Robotics" "Physics" "Math" "Education" "Geography"))
-	    ("Development" . ("GUIDesigner" "IDE" "Profiling" "RevisionControl" \
-			      "ProjectManagement" "Translation" "GTK" "Development" \
-			      "Qt" "Development" "Documentation"))
-	    ("Utility" . ("X-SystemMemory" "Security" "Utility" \
-			  "X-SetupEntry" "X-SetupUtility" "X-SystemMemory" \
-			  "TextTools" "TelephonyTools" "Accessibility" "Clock" \
-			  "ConsoleOnly"))
-	    ("Filesystem" .  ("X-FileSystemFind" "X-FileSystemUtility" "Archiving" \
-			      "FileManager" "X-FileSystemMount" "Compression"))
-	    ("System" . ("X-SystemSchedule" "System" "X-SystemMemory" \
-			 "TerminalEmulator" "Dictionary" "Puppy" "Printing" "Monitor" "Security"))
-	    ("Settings" . ("Settings" "HardwareSettings" "PackageManager"))))
-
-    ;; Some file-verifiers
-    (defun desktop-file-p (directory-file)
-      (let ((this-file (open-file directory-file 'read)))
-	(let ((this-line (read-line this-file)))
-	  (string= this-line "[Desktop Entry]\012"))))
-
-    (defun backup-file-p (input-file-name)
-      (or (string= "~" (substring input-file-name (- (length input-file-name) 1)))
-	  (string= "#" (substring input-file-name 0 1))))
 
-    ;; Helper for (parse-desk-line) - specifically, for categories.
-    ;; line must be excluding the categories= part -> (substring line 11)
-    (defun build-cat-list (line)
-      (if (> (length line) 1)
-	  (let ((this-cat (prin1-to-string (read-from-string line))))
-	    (cons this-cat (build-cat-list (substring line (+ 1 (length this-cat))))))))
-
-    ;; Helper function for (fix-cats)
+    (defvar menu-cat-alist
+      '(("Desktop" .  ("X-Desktop" "X-DesktopApplets" "X-DesktopCountry" \
+		       "DesktopSettings" "GNOME" "KDE" "X-GNOME-PersonalSettings" \
+		       "X-Xfce-Toplevel"))
+	("Personal" . ("X-Personal" "X-PersonalUtility" "Calendar" "ContactManagement"))
+	("Office" . ("Office" "WordProcessor" "Presentation" "X-Document" \
+		     "TextEditor" "SpreadSheet" "Calculator" "X-Calculate" \
+		     "Chart" "FlowChart" "Finance"))
+	("Internet" . ("Telephony" "Network" "Dialup" "VideoConference" \
+		       "RemoteAccess" "News" "HamRadio" "FileTransfer" \
+		       "X-Internet" "P2P" "Email" "WebBrowser" "IRCClient" "Chat" \
+		       "InstantMessaging" "Chat" "WebDevelopment"))
+	("Games" . ("Game" "ActionGame" "AdventureGame" "ArcadeGame" "BoardGame" "Emulator"\
+		    "BlocksGame" "CardGame" "KidsGame" "LogicGame" "RolePlaying" "Simulation"))
+	("Graphics" . ("RasterGraphics" "VectorGraphics" "X-GraphicUtility" \
+		       "2DGraphics" "3dGraphics" "3DGraphics" "Scanning" "OCR" "Photography" \
+		       "Viewer" "Publishing" "Art" "ImageProcessing"))
+	("Media" . ("AudioVideo" "Audio", "Video" "Midi" "Mixer" "Sequencer" "Tuner" \
+		    "TV" "AudioVideoEditing" "Player" "Recorder" "DiscBurning" "Music"))
+	("Science" . ("Science" "Astrology" "ArtificialIntelligence" "Astronomy" \
+		      "Biology" "Chemistry" "ComputerScience" "DataVisualization" \
+		      "Electricity" "Robotics" "Physics" "Math" "Education" "Geography"))
+	("Development" . ("GUIDesigner" "IDE" "Profiling" "RevisionControl" \
+			  "ProjectManagement" "Translation" "GTK" "Development" \
+			  "Qt" "Development" "Documentation"))
+	("Utility" . ("X-SystemMemory" "Security" "Utility" \
+		      "X-SetupEntry" "X-SetupUtility" "X-SystemMemory" \
+		      "TextTools" "TelephonyTools" "Accessibility" "Clock" \
+		      "ConsoleOnly"))
+	("Filesystem" .  ("X-FileSystemFind" "X-FileSystemUtility" "Archiving" \
+			  "FileManager" "X-FileSystemMount" "Compression"))
+	("System" . ("X-SystemSchedule" "System" "X-SystemMemory" \
+		     "TerminalEmulator" "Dictionary" "Puppy" "Printing" "Monitor" "Security"))
+	("Settings" . ("Settings" "HardwareSettings" "PackageManager"))))
+
+    ;; Get the correct Name value based on language settings
+    (defun find-lang-in-desktop-file (fdo-list)
+      (if (assoc (concat name-string my-lang-string "]") fdo-list)
+	  (concat name-string my-lang-string "]")
+	(if (assoc (concat name-string (substring my-lang-string 0 2) "]") fdo-list)
+	    (concat name-string (substring my-lang-string 0 2) "]")
+	  "Name")))
+
+    ;; Functions for categories
     (defun fix-sub-cats (cat-list loc-list)
       (if cat-list
 	  (let ((cat-val (car cat-list)))
@@ -207,11 +212,7 @@ Make sure the mk-saw-menu.jl is in your load path
 		      (fix-sub-cats cat-list (remove (assoc cat-val loc-list) loc-list)))
 	      (fix-sub-cats (cdr cat-list) loc-list)))))
 
-    ;; Second Part of process, after (parse-desktop-file) has run \
-    ;; through and generated the *loc-menu*.
-    ;; Will run through the Category alist and assign menu values accordingly, and \
-    ;; will output the base of the menu, to \
-    ;; be fed into (build-saw-menu)
+    ;; Associate values from the Master Category list with sub-categories from file
     (defun fix-cats (cat-list)
       (if cat-list
 	  (let ((cat-val (car (car cat-list)))
@@ -220,6 +221,14 @@ Make sure the mk-saw-menu.jl is in your load path
 		(cons (cons cat-val c-list) (fix-cats (cdr cat-list)))
 	      (fix-cats (cdr cat-list))))))
 
+    ;; Convert a Categories key value from ; delineated records to a list
+    (defun build-cat-list (line) 
+      (if (> (length line) 1)
+	  (let ((this-cat (prin1-to-string (read-from-string line))))
+	    (cons this-cat 
+		  (if (< (length this-cat) (length line))
+		      (build-cat-list (substring line (+ 1 (length this-cat)))))))))
+
     ;; Helper for (parse-desk-line)
     ;; Determine best category to use... :|
     (defun parse-cat-list (cat-list)
@@ -233,110 +242,42 @@ Make sure the mk-saw-menu.jl is in your load path
 		 (string= this-cat "X-XFCE")
 		 (string= this-cat "Application"))
 		(parse-cat-list (cdr cat-list))
-	      ;; to do specific things for the above entries (uncomment below, \
-	      ;; and comment out the above (parse-cat-list (cdr cat-list)):
-	      ;;(let ((dm-specific this-cat))
-	      ;;  (if (or (string= dm-specific "Application")
-	      ;;      (string= dm-specific "Qt")
-	      ;;      (string= dm-specific "GTK"))
-	      ;;  (parse-cat-list (cdr cat-list))
-	      ;;"Settings"))
 	      this-cat))
 	(car cat-list)))
 
-    ;; Helper function for (create-menu), which will parse the string input there.
-    ;; Will only give an output for specified lines (i.e. category, name, etc.)
-    (defun parse-desk-line (line desk-value)
-      (let ((line-len (length line)))
-	(cond
-
-	 ;; this section is for the multi-lingual name string
-	 ((and (> line-len 7)
-	       my-lang-string
-	       (string= desk-value (substring line 0 5))
-	       (string= (substring line 4 5) "[")
-	       (string= my-lang-string (substring line 5 (+ (length my-lang-string) 5)))
-	       (string= (substring line (+ (length my-lang-string) 5) (+ (length my-lang-string) 6)) "]"))
-	  (substring line (+ 4 (length my-lang-string) 3) (- line-len 1)))
-
-	 ;; this section is for the exec and default name strings
-	 ((and (> line-len 5)
-	       (string= (substring desk-value 0 4) (substring line 0 4))
-	       (string= (substring line 4 5) "="))
-	  (if (string= (aref line (- line-len 3)) 37)
-	      (substring line 5 (- line-len 4))
-	    (substring line 5 (- line-len 1))))
-
-	 ;; this section is for the category string
-	 ((and (> line-len 10) (string= desk-value (substring line 0 11)))
-	  (let ((cat-string (parse-cat-list (build-cat-list (substring line 11)))))
-	    cat-string))
-
-	 ;; this section is for the terminal string
-	 ((and (> line-len 8) (string= desk-value (substring line 0 9)))
-	  (substring line 9 (- line-len 1)))
-
-	 ;; this section is for the nodisplay string
-	 ((and (> line-len 9) (string= desk-value (substring line 0 10)))
-	  (substring line 10 (- line-len 1))))))
-
-    ;; begining of parsing for (parse-desktop-file), feed a .desktop file line into this.
-    ;; (create-menu "Name=Emacs ") ==> "Emacs"
-    ;; (create-menu "Categories=Development;TextEditor;") ==> "Development"
-    (defun create-menu (line)
-      (cond
-       ((parse-desk-line line "Categories=")
-	(setq my-cat (parse-desk-line line "Categories=")))
-       ((parse-desk-line line name-string)
-	(setq my-name (parse-desk-line line name-string)))
-       ;;((parse-desk-line line "Comment=")
-       ;; (setq my-comm (parse-desk-line line "Comment=")))
-       ;;((parse-desk-line line "Icon=")
-       ;; (setq my-icon (parse-desk-line line "Icon=")))
-       ((parse-desk-line line "Exec=")
-	(setq my-exec (concat (parse-desk-line line "Exec=") " &")))
-       ((parse-desk-line line "Terminal=")
-	(setq my-term (parse-desk-line line "Terminal=")))
-       ((parse-desk-line line "NoDisplay=")
-	(setq my-disp (parse-desk-line line "NoDisplay=")))))
-
-    ;; Parse a .desktop file into a list suitable for a menu
-    ;; ex. (parse-desktop-file "emacs.desktop") ==> ("Development" "Emacs Text Editor" (system "emacs &"))
-    (defun parse-desktop-file (desktop-file)
-      (let ((desktop-file-name desktop-file))
-	(if (and (file-exists-p desktop-file-name)
-		 (desktop-file-p desktop-file-name)
-		 (not (file-directory-p desktop-file-name)))
-	    (let ((new-file (open-file desktop-file-name 'read)))
-	      (while (setq file-line (read-line new-file))
-		(create-menu file-line))
-	      (if ignore-no-display
-		  (setq my-disp '()))
-	      (if (not (string= my-disp "true"))
-		  (if (not (string= (string-downcase my-term) "true"))
-		      (cons my-cat (cons my-name (cons (list 'system my-exec) nil)))
-		    ;;(cons my-cat (cons my-icon (cons my-name (cons my-comm (cons (list 'system my-exec) nil)))))
-		    (cons my-cat (cons my-name (cons (list 'system (concat my-term-string my-exec)) nil))))
-		;;(cons my-cat (cons my-icon (cons my-name (cons my-comm (cons (list 'system (concat my-term-string my-exec)) nil))))))
-		(setq my-disp ()))))))
-
     ;; Alphabetize the entries in the category menus
     (defun alphabetize-entries (saw-menu)
       (if saw-menu
-	  (cons (cons (car (car saw-menu))
-		      (sort (cdr (car saw-menu)) string<))
+	  (cons (cons (car (car saw-menu)) 
+		      (sort (cdr (car saw-menu)) string<)) 
 		(alphabetize-entries (cdr saw-menu)))))
 
-    ;; Update the menu and set it to 'apps-menu
+    ;; generate a saw-fish menu entry from a .desktop file
+    (defun generate-menu-entry (desk-file)
+      (if (and (not (file-directory-p desk-file))
+	       (desktop-file-p desk-file))
+	  (let ((fdo-list (parse-desktop-file desk-file)))
+	    (if (not (string= (cdr (assoc "NoDisplay" fdo-list)) "true\n"))
+		(cons (parse-cat-list (build-cat-list (trim-end (cdr (assoc "Categories" fdo-list)))))
+		      (cons (trim-end (cdr (assoc (find-lang-in-desktop-file fdo-list) fdo-list)))
+			    (if (string= (cdr (assoc "Terminal" fdo-list)) "true\012")
+				(cons (list 
+				       'system (concat my-term-string (trim-end (cdr (assoc "Exec" fdo-list))) " &")))
+			      (cons (list 'system (concat (trim-end (cdr (assoc "Exec" fdo-list))) " &"))))))))))
+
+    ;; Update the menu
     (define (update-saw-menu)
+      (interactive)
       (unless (not use-fdo-menu)
 	(setq *loc-menu* nil)
 	(defvar desk-files (flatten (map-dir-files desktop-directory)))
 	(mapc (lambda (x)
-		(setq *loc-menu* (append *loc-menu* (list (parse-desktop-file x))))) desk-files)
+		(setq *loc-menu* (append *loc-menu* (list (generate-menu-entry x))))) desk-files)
 	(if want-alphabetize
 	    (setq apps-menu (alphabetize-entries (fix-cats menu-cat-alist)))
 	  (setq apps-menu (fix-cats menu-cat-alist)))))
 
     (define-command 'update-saw-menu update-saw-menu)
-))
+))    
+    ;; END-FILE
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]