changeset 2545:9caf26dd924f

[xemacs-hg @ 2005-02-03 05:03:36 by ben] behavior ws #2: menu-related changes menubar.c: New fun to compare menu itext as if the two were normalized. menubar.c: Rename; there are no external callers of this function. Remove unneeded BUFFER argument. Don't downcase. (This will be done in compare-menu-text.) Document that return value may be same string. easymenu.el, map-ynp.el: Use normalize-menu-text not normalize-menu-item-name. menubar-items.el, menubar.el: Move to menubar.el and rewrite for cleanliness. menubar-items.el: Use menu-split-long-menu-and-sort. menubar-items.el, menubar.el: Move to menubar.el. menubar.el: New funs. menubar.el: Split up find-menu-item w/find-menu-item-1, since PARENT is not an external item. Rewrite to use compare-menu-text. menubar.el: Don't normalize items as find-menu-item does not need it. menubar-items.el: Delete old Behavior menu defn, replaced by behavior-menu-filter. Planning to [[Delete many menus from Tools menu -- they have been integrated as part of the behavior system.]] Currently the new Tools menu (very short, just a call to the behavior-menu-filter) is commented out, and the old Toold menu defn remains. Once the new packages are in place (c. 1 or 2 weeks), I'll make the switchover. Use menu-split-long-menu-and-sort.
author ben
date Thu, 03 Feb 2005 05:03:45 +0000
parents b4a8cd0dd8df
children 5d1743698fb3
files lisp/ChangeLog lisp/easymenu.el lisp/map-ynp.el lisp/menubar-items.el lisp/menubar.el src/ChangeLog src/menubar.c
diffstat 7 files changed, 372 insertions(+), 277 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Thu Feb 03 04:29:33 2005 +0000
+++ b/lisp/ChangeLog	Thu Feb 03 05:03:45 2005 +0000
@@ -1,3 +1,57 @@
+2005-02-02  Ben Wing  <ben@xemacs.org>
+
+	* easymenu.el (easy-menu-add):
+	* easymenu.el (easy-menu-remove):
+	* map-ynp.el (map-y-or-n-p):
+	Use normalize-menu-text not normalize-menu-item-name.
+
+	* menubar-items.el (submenu-generate-accelerator-spec): Removed.
+	* menubar.el (submenu-generate-accelerator-spec): New.
+	Move to menubar.el and rewrite for cleanliness.
+	
+	* menubar-items.el (coding-system-menu-filter):
+	Use menu-split-long-menu-and-sort.
+	
+	* menubar-items.el (menu-item-strip-accelerator-spec): Removed.
+	* menubar-items.el (menu-item-generate-accelerator-spec): Removed.
+	* menubar-items.el (menu-max-items): Removed.
+	* menubar-items.el (menu-submenu-max-items): Removed.
+	* menubar-items.el (menu-submenu-name-format): Removed.
+	* menubar-items.el (menu-split-long-menu): Removed.
+	* menubar-items.el (menu-sort-menu): Removed.
+	* menubar.el (menu-item-strip-accelerator-spec): New.
+	* menubar.el (menu-item-generate-accelerator-spec): New.
+	* menubar.el (menu-max-items): New.
+	* menubar.el (menu-submenu-max-items): New.
+	* menubar.el (menu-submenu-name-format): New.
+	* menubar.el (menu-split-long-menu): New.
+	* menubar.el (menu-sort-menu): New.
+	Move to menubar.el.
+	
+	* menubar.el (menu-item-text): New.
+	* menubar.el (menu-split-long-menu-and-sort): New.
+	New funs.
+	
+	* menubar.el (find-menu-item):
+	* menubar.el (find-menu-item-1): New.
+	Split up find-menu-item w/find-menu-item-1, since PARENT is not
+	an external item.
+	Rewrite to use compare-menu-text.
+	
+	* menubar.el (add-menu-item-1):
+	Don't normalize items as find-menu-item does not need it.
+
+	* menubar-items.el (default-menubar):
+	Delete old Behavior menu defn, replaced by behavior-menu-filter.
+	Planning to [[Delete many menus from Tools menu -- they have been
+	integrated as part of the behavior system.]] Currently the new
+	Tools menu (very short, just a call to the behavior-menu-filter)
+	is commented out, and the old Toold menu defn remains.  Once the
+	new packages are in place (c. 1 or 2 weeks), I'll make the
+	switchover.
+	
+	Use menu-split-long-menu-and-sort.
+
 2005-02-02  Ben Wing  <ben@xemacs.org>
 
 	* cus-dep.el (Custom-make-dependencies-1):
@@ -183,55 +237,6 @@
 	* behavior.el (behavior-menu-filter): New.
 	Major update.  Add documentation of how it works.
 	
-	* easymenu.el (easy-menu-add):
-	* easymenu.el (easy-menu-remove):
-	* map-ynp.el (map-y-or-n-p):
-	Use normalize-menu-text not normalize-menu-item-name.
-
-	* menubar-items.el (submenu-generate-accelerator-spec): Removed.
-	* menubar.el (submenu-generate-accelerator-spec): New.
-	Move to menubar.el and rewrite for cleanliness.
-	
-	* menubar-items.el (coding-system-menu-filter):
-	Use menu-split-long-menu-and-sort.
-	
-	* menubar-items.el (menu-item-strip-accelerator-spec): Removed.
-	* menubar-items.el (menu-item-generate-accelerator-spec): Removed.
-	* menubar-items.el (menu-max-items): Removed.
-	* menubar-items.el (menu-submenu-max-items): Removed.
-	* menubar-items.el (menu-submenu-name-format): Removed.
-	* menubar-items.el (menu-split-long-menu): Removed.
-	* menubar-items.el (menu-sort-menu): Removed.
-	* menubar.el (menu-item-strip-accelerator-spec): New.
-	* menubar.el (menu-item-generate-accelerator-spec): New.
-	* menubar.el (menu-max-items): New.
-	* menubar.el (menu-submenu-max-items): New.
-	* menubar.el (menu-submenu-name-format): New.
-	* menubar.el (menu-split-long-menu): New.
-	* menubar.el (menu-sort-menu): New.
-	Move to menubar.el.
-	
-	* menubar.el (menu-item-text): New.
-	* menubar.el (menu-split-long-menu-and-sort): New.
-	New funs.
-	
-	* menubar-items.el (default-menubar):
-	Delete many menus from Tools menu -- they have been integrated
-	as part of the behavior system.
-
-	Delete old Behavior menu defn.  Use behavior-menu-filter.
-
-	Use menu-split-long-menu-and-sort.
-	
-	* menubar.el (find-menu-item):
-	* menubar.el (find-menu-item-1): New.
-	Split up find-menu-item w/find-menu-item-1, since PARENT is not
-	an external item.
-	Rewrite to use compare-menu-text.
-	
-	* menubar.el (add-menu-item-1):
-	Don't normalize items as find-menu-item does not need it.
-	
 	* mwheel.el:
 	* mwheel.el ('mwheel): New.
 	Add define-behavior for mwheel.
--- a/lisp/easymenu.el	Thu Feb 03 04:29:33 2005 +0000
+++ b/lisp/easymenu.el	Thu Feb 03 05:03:45 2005 +0000
@@ -181,7 +181,7 @@
 				    (reverse easy-menu-all-popups))
 			    (let ((same-as-menu
 				   (car easy-menu-all-popups)))
-			      (cons (normalize-menu-item-name
+			      (cons (normalize-menu-text
 				     (car same-as-menu))
 				    (cdr same-as-menu)))))
 
@@ -208,7 +208,7 @@
 				    (reverse easy-menu-all-popups))
 			    (let ((same-as-menu
 				   (car easy-menu-all-popups)))
-			      (cons (normalize-menu-item-name
+			      (cons (normalize-menu-text
 				     (car same-as-menu))
 				    (cdr same-as-menu)))))
 
--- a/lisp/map-ynp.el	Thu Feb 03 04:29:33 2005 +0000
+++ b/lisp/map-ynp.el	Thu Feb 03 05:03:45 2005 +0000
@@ -231,8 +231,9 @@
 					    (lambda (elt)
 					      (format "%c to %s"
 						      (nth 0 elt)
-						      (normalize-menu-item-name
-						       (nth 2 elt)))))
+						      (downcase
+						       (normalize-menu-text
+							(nth 2 elt))))))
 					   action-alist
 					   ";\n")
 				(if action-alist ";\n")
--- a/lisp/menubar-items.el	Thu Feb 03 04:29:33 2005 +0000
+++ b/lisp/menubar-items.el	Thu Feb 03 05:03:45 2005 +0000
@@ -67,148 +67,6 @@
 	      list
 	    (butlast list (- (length list) count)))))
 
-(defun submenu-generate-accelerator-spec (list &optional omit-chars-list)
-  "Add auto-generated accelerator specifications to a submenu.
-This can be used to add accelerators to the return value of a menu filter
-function.  It correctly ignores unselectable items.  It will destructively
-modify the list passed to it.  If an item already has an auto-generated
-accelerator spec, this will be removed before the new one is added, making
-this function idempotent.
-
-If OMIT-CHARS-LIST is given, it should be a list of lowercase characters,
-which will not be used as accelerators."
-  (let ((n 0))
-    (dolist (item list list)
-      (cond
-       ((vectorp item)
-	(setq n (1+ n))
-	(aset item 0
-	      (concat
-	       (menu-item-generate-accelerator-spec n omit-chars-list)
-	       (menu-item-strip-accelerator-spec (aref item 0)))))
-       ((consp item)
-	(setq n (1+ n))
-	(setcar item
-		(concat
-		 (menu-item-generate-accelerator-spec n omit-chars-list)
-		 (menu-item-strip-accelerator-spec (car item)))))))))
-
-(defun menu-item-strip-accelerator-spec (item)
-  "Strip an auto-generated accelerator spec off of ITEM.
-ITEM should be a string.  This removes specs added by
-`menu-item-generate-accelerator-spec' and `submenu-generate-accelerator-spec'."
-  (if (string-match "%_. " item)
-      (substring item 4)
-    item))
-
-(defun menu-item-generate-accelerator-spec (n &optional omit-chars-list)
-  "Return an accelerator specification for use with auto-generated menus.
-This should be concat'd onto the beginning of each menu line.  The spec
-allows the Nth line to be selected by the number N.  '0' is used for the
-10th line, and 'a' through 'z' are used for the following 26 lines.
-
-If OMIT-CHARS-LIST is given, it should be a list of lowercase characters,
-which will not be used as accelerators."
-  (cond ((< n 10) (concat "%_" (int-to-string n) " "))
-	((= n 10) "%_0 ")
-	((<= n 36)
-	 (setq n (- n 10))
-	 (let ((m 0))
-	   (while (> n 0)
-	     (setq m (1+ m))
-	     (while (memq (int-to-char (+ m (- (char-to-int ?a) 1)))
-			  omit-chars-list)
-	       (setq m (1+ m)))
-	     (setq n (1- n)))
-	   (if (<= m 26)
-	       (concat
-		"%_"
-		(char-to-string (int-to-char (+ m (- (char-to-int ?a) 1))))
-		" ")
-	     "")))
-	(t "")))
-
-(defcustom menu-max-items 25
-  "*Maximum number of items in generated menus.
-If number of entries in such a menu is larger than this value, split menu
-into submenus of nearly equal length (see `menu-submenu-max-items').  If
-nil, never split menu into submenus."
-  :group 'menu
-  :type '(choice (const :tag "no submenus" nil)
-		 (integer)))
-
-(defcustom menu-submenu-max-items 20
-  "*Maximum number of items in submenus when splitting menus.
-We split large menus into submenus of this many items, and then balance
-them out as much as possible (otherwise the last submenu may have very few
-items)."
-  :group 'menu
-  :type 'integer)
-
-(defcustom menu-submenu-name-format "%-12.12s ... %.12s"
-  "*Format specification of the submenu name when splitting menus.
-Used by `menu-split-long-menu' if the number of entries in a menu is
-larger than `menu-menu-max-items'.
-This string should contain one %s for the name of the first entry and
-one %s for the name of the last entry in the submenu.
-If the value is a function, it should return the submenu name.  The
-function is be called with two arguments, the names of the first and
-the last entry in the menu."
-  :group 'menu
-  :type '(choice (string :tag "Format string")
-		 (function)))
-
-(defun menu-split-long-menu (menu)
-  "Split MENU according to `menu-max-items' and add accelerator specs.
-
-You should normally use the idiom
-
-\(menu-split-long-menu (menu-sort-menu menu))
-
-See also `menu-sort-menu'."
-  (let ((len (length menu)))
-    (if (or (null menu-max-items)
-	    (<= len menu-max-items))
-	(submenu-generate-accelerator-spec menu)
-      (let* ((outer (/ (+ len (1- menu-submenu-max-items))
-		       menu-submenu-max-items))
-	     (inner (/ (+ len (1- outer)) outer))
-	     (result nil))
-	(while menu
-	  (let ((sub nil)
-		(from (car menu)))
-	    (dotimes (foo (min inner len))
-	      (setq sub  (cons (car menu) sub)
-		    menu (cdr menu)))
-	    (setq len (- len inner))
-	    (let ((to (car sub)))
-	      (setq sub (nreverse sub))
-	      (setq result
-		    (cons (cons (if (stringp menu-submenu-name-format)
-				    (format menu-submenu-name-format
-					    (menu-item-strip-accelerator-spec
-					     (aref from 0))
-					    (menu-item-strip-accelerator-spec
-					     (aref to 0)))
-				  (funcall menu-submenu-name-format
-					   (menu-item-strip-accelerator-spec
-					    (aref from 0))
-					   (menu-item-strip-accelerator-spec
-					    (aref to 0))))
-				(submenu-generate-accelerator-spec sub))
-			  result)))))
-	(submenu-generate-accelerator-spec (nreverse result))))))
-
-(defun menu-sort-menu (menu)
-  "Sort MENU alphabetically.
-
-You should normally use the idiom
-
-\(menu-split-long-menu (menu-sort-menu menu))
-
-See also `menu-split-long-menu'."
-  (sort menu
-	#'(lambda (a b) (string-lessp (aref a 0) (aref b 0)))))
 
 (defun coding-system-menu-filter (fun active &optional dots)
   "Filter for menu entries with a submenu listing all coding systems.
@@ -225,21 +83,20 @@
     (lambda (entry) ...)
     (lambda (entry) ...))
 "
-  (menu-split-long-menu
-   (menu-sort-menu
-    (mapcar
-     #'(lambda (_csmf_entry)
-	 `[ ,(concat (coding-system-description _csmf_entry)
-		     (if dots "..." ""))
-	    (funcall ,fun ',_csmf_entry)
-	    :active (funcall ,active ',_csmf_entry)
-	    ])
-     (delete-if
-      #'(lambda (name)
-	  (or (coding-system-alias-p name)
-	      (not (eq name (coding-system-name
-			     (coding-system-base name))))))
-      (coding-system-list))))))
+  (menu-split-long-menu-and-sort
+   (mapcar
+    #'(lambda (_csmf_entry)
+	`[ ,(concat (coding-system-description _csmf_entry)
+		    (if dots "..." ""))
+	   (funcall ,fun ',_csmf_entry)
+	   :active (funcall ,active ',_csmf_entry)
+	   ])
+    (delete-if
+     #'(lambda (name)
+	 (or (coding-system-alias-p name)
+	     (not (eq name (coding-system-name
+			    (coding-system-base name))))))
+     (coding-system-list)))))
 
 (defconst default-menubar
 ; (purecopy-menubar ;purespace is dead
@@ -422,7 +279,6 @@
        )
       )
 
-
      ("C%_mds"
       ["Repeat Last Comple%_x Command..." repeat-complex-command]
       ["E%_valuate Lisp Expression..." eval-expression]
@@ -554,17 +410,27 @@
 	["Tab to Tab %_Stop" tab-to-tab-stop]
 	["Edit Ta%_b Stops" edit-tab-stops]
 	)
-	"---"
-       ("Spell-Chec%_k"
-	["%_Buffer" ispell-buffer
-	 :active (fboundp 'ispell-buffer)]
-	"---"
-	["%_Word" ispell-word]
-	["%_Complete Word" ispell-complete-word]
-	["%_Region" ispell-region]
+       "---"
+       ("%_Tags"
+	["%_Find Tag..." find-tag]
+	["Find %_Other Window..." find-tag-other-window]
+	["%_Next Tag..." (find-tag nil)]
+	["N%_ext Other Window..." (find-tag-other-window nil)]
+	["Next %_File" next-file]
+	"-----"
+	["Tags %_Search..." tags-search]
+	["Tags %_Replace..." tags-query-replace]
+	["%_Continue Search/Replace" tags-loop-continue]
+	"-----"
+	["%_Pop stack" pop-tag-mark]
+	["%_Apropos..." tags-apropos]
+	"-----"
+	["%_Set Tags Table File..." visit-tags-table]
 	)
        )
-     
+
+     ;; #### Delete this entire menu as soon as the new package source is
+     ;; committed.
      ("%_Tools"
       ("%_Packages"
        ("%_Set Download Site"
@@ -816,24 +682,10 @@
       "----"
       )
 
+;      ("%_Tools"
+;       :filter behavior-menu-filter)
+
      ("%_Options"
-      ("%_Behaviors"
-       :filter 
-       (lambda (menu)
-	 (menu-split-long-menu
-	  (menu-sort-menu
-	   (loop for behavior being the hash-keys in behavior-hash-table
-	     using (hash-value plist)
-	     collect (vector (format "%s (%s)" behavior
-				     (getf plist :short-doc))
-			     `(if (memq ',behavior enabled-behavior-list)
-				  (disable-behavior ',behavior)
-				(enable-behavior ',behavior))
-			     :style 'toggle
-			     :selected `(memq ',behavior
-					      enabled-behavior-list))
-	     )))))
-
       ("%_Advanced (Customize)"
        ("%_Emacs" :filter (lambda (&rest junk)
 			    (cdr (custom-menu-create 'emacs))))
@@ -1142,17 +994,16 @@
 	     ("Set %_Language Environment"
 	      :filter
 	      (lambda (menu)
-		(menu-split-long-menu
-		 (menu-sort-menu
-		  (mapcar #'(lambda (entry)
-			      `[ ,(car entry)
-				 (set-language-environment ',(car entry))
-				 :style radio
-				 :selected
-				 ,(equal (car entry)
-					 current-language-environment)])
-			  language-info-alist)
-		  ))))
+		(menu-split-long-menu-and-sort
+		 (mapcar #'(lambda (entry)
+			     `[ ,(car entry)
+				(set-language-environment ',(car entry))
+				:style radio
+				:selected
+				,(equal (car entry)
+					current-language-environment)])
+			 language-info-alist)
+		 )))
 	     ["%_Toggle Input Method" toggle-input-method]
 	     ["Select %_Input Method" set-input-method]
 	     )))
@@ -1681,18 +1532,17 @@
 	     ("Describe %_Language Support"
 	      :filter
 	      (lambda (menu)
-		(menu-split-long-menu
-		 (menu-sort-menu
-		  (mapcar #'(lambda (entry)
-			      `[ ,(car entry)
-				 (describe-language-environment
-				  ',(car entry))
+		(menu-split-long-menu-and-sort
+		 (mapcar #'(lambda (entry)
+			     `[ ,(car entry)
+				(describe-language-environment
+				 ',(car entry))
 				:style radio
 				:selected
 				,(equal (car entry)
 					current-language-environment)])
-			  language-info-alist)
-		  ))))
+			 language-info-alist)
+		 )))
 	     ["Describe %_Input Method" describe-input-method]
 	     ["Describe Current %_Coding Systems"
 	      describe-current-coding-system]
--- a/lisp/menubar.el	Thu Feb 03 04:29:33 2005 +0000
+++ b/lisp/menubar.el	Thu Feb 03 05:03:45 2005 +0000
@@ -2,7 +2,7 @@
 
 ;; Copyright (C) 1991-4, 1997-1998 Free Software Foundation, Inc.
 ;; Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
-;; Copyright (C) 1995, 1996 Ben Wing.
+;; Copyright (C) 1995, 1996, 2003 Ben Wing.
 
 ;; Maintainer: XEmacs Development Team
 ;; Keywords: internal, extensions, dumped
@@ -163,17 +163,27 @@
       (setq menu (cdr menu)))))
 
 
-;;; menu manipulation functions
+;;; basic menu manipulation functions
 
-(defun find-menu-item (menubar item-path-list &optional parent)
-  "Search MENUBAR for item given by ITEM-PATH-LIST starting from PARENT.
+(defun menu-item-text (item &optional normalize)
+  "Return the text that is displayed for a menu item.
+If ITEM is a string (unselectable text), it is returned; otherwise,
+the first element of the cons or vector is returned.
+If NORMALIZE is non-nil, pass the text through `normalize-menu-text'
+before being returned, to remove accelerator specs and convert %% to %."
+  (let ((val (if (stringp item) item (elt item 0))))
+    (if normalize (normalize-menu-text val) val)))
+
+(defun find-menu-item (menubar item-path-list)
+  "Search MENUBAR for item given by ITEM-PATH-LIST.
 Returns (ITEM . PARENT), where PARENT is the immediate parent of
  the item found.
 If the item does not exist, the car of the returned value is nil.
 If some menu in the ITEM-PATH-LIST does not exist, an error is signalled."
+  (find-menu-item-1 menubar item-path-list))
+
+(defun find-menu-item-1 (menubar item-path-list &optional parent)
   (check-argument-type 'listp item-path-list)
-  (unless parent
-    (setq item-path-list (mapcar 'normalize-menu-item-name item-path-list)))
   (if (not (consp menubar))
       nil
     (let ((rest menubar)
@@ -184,14 +194,9 @@
 	(setq rest (cddr rest)))
       (while rest
 	(if (and (car rest)
-		 (equal (car item-path-list)
-			(normalize-menu-item-name
-			 (cond ((vectorp (car rest))
-				(aref (car rest) 0))
-			       ((stringp (car rest))
-				(car rest))
-			       (t
-				(caar rest))))))
+		 (stringp (car item-path-list))
+		 (= 0 (compare-menu-text (car item-path-list)
+					 (menu-item-text (car rest)))))
 	    (setq result (car rest)
 		  rest nil)
 	  (setq rest (cdr rest))))
@@ -208,7 +213,6 @@
 (defun add-menu-item-1 (leaf-p menu-path new-item before in-menu)
   ;; This code looks like it could be cleaned up some more
   ;; Do we really need 6 calls to find-menu-item?
-  (when before (setq before (normalize-menu-item-name before)))
   (let* ((item-name
 	  (cond ((vectorp new-item) (aref new-item 0))
 		((consp   new-item) (car  new-item))
@@ -464,6 +468,151 @@
   (enable-menu-item-1 path t nil))
 
 
+;;; functions for manipulating whole menus -- adding accelerators, sorting,
+;;; splitting long menus, etc.
+
+(defun submenu-generate-accelerator-spec (list &optional omit-chars-list)
+  "Add auto-generated accelerator specifications to a submenu.
+This can be used to add accelerators to the return value of a menu filter
+function.  It correctly ignores unselectable items.  It will destructively
+modify the list passed to it.  If an item already has an auto-generated
+accelerator spec, this will be removed before the new one is added, making
+this function idempotent.
+
+If OMIT-CHARS-LIST is given, it should be a list of lowercase characters,
+which will not be used as accelerators."
+  (let ((n 0))
+    (dolist (item list list)
+      (cond
+       ((or (vectorp item) (consp item))
+	(incf n)
+	(setf (elt item 0)
+	      (concat
+	       (menu-item-generate-accelerator-spec n omit-chars-list)
+	       (menu-item-strip-accelerator-spec (elt item 0)))))))))
+
+(defun menu-item-strip-accelerator-spec (item)
+  "Strip an auto-generated accelerator spec off of ITEM.
+ITEM should be a string.  This removes specs added by
+`menu-item-generate-accelerator-spec' and `submenu-generate-accelerator-spec'."
+  (if (string-match "%_. " item)
+      (substring item 4)
+    item))
+
+(defun menu-item-generate-accelerator-spec (n &optional omit-chars-list)
+  "Return an accelerator specification for use with auto-generated menus.
+This should be concat'd onto the beginning of each menu line.  The spec
+allows the Nth line to be selected by the number N.  '0' is used for the
+10th line, and 'a' through 'z' are used for the following 26 lines.
+
+If OMIT-CHARS-LIST is given, it should be a list of lowercase characters,
+which will not be used as accelerators."
+  (cond ((< n 10) (concat "%_" (int-to-string n) " "))
+	((= n 10) "%_0 ")
+	((<= n 36)
+	 (setq n (- n 10))
+	 (let ((m 0))
+	   (while (> n 0)
+	     (setq m (1+ m))
+	     (while (memq (int-to-char (+ m (- (char-to-int ?a) 1)))
+			  omit-chars-list)
+	       (setq m (1+ m)))
+	     (setq n (1- n)))
+	   (if (<= m 26)
+	       (concat
+		"%_"
+		(char-to-string (int-to-char (+ m (- (char-to-int ?a) 1))))
+		" ")
+	     "")))
+	(t "")))
+
+(defcustom menu-max-items 25
+  "*Maximum number of items in generated menus.
+If number of entries in such a menu is larger than this value, split menu
+into submenus of nearly equal length (see `menu-submenu-max-items').  If
+nil, never split menu into submenus."
+  :group 'menu
+  :type '(choice (const :tag "no submenus" nil)
+		 (integer)))
+
+(defcustom menu-submenu-max-items 20
+  "*Maximum number of items in submenus when splitting menus.
+We split large menus into submenus of this many items, and then balance
+them out as much as possible (otherwise the last submenu may have very few
+items)."
+  :group 'menu
+  :type 'integer)
+
+(defcustom menu-submenu-name-format "%-12.12s ... %.12s"
+  "*Format specification of the submenu name when splitting menus.
+Used by `menu-split-long-menu' if the number of entries in a menu is
+larger than `menu-menu-max-items'.
+This string should contain one %s for the name of the first entry and
+one %s for the name of the last entry in the submenu.
+If the value is a function, it should return the submenu name.  The
+function is be called with two arguments, the names of the first and
+the last entry in the menu."
+  :group 'menu
+  :type '(choice (string :tag "Format string")
+		 (function)))
+
+(defun menu-split-long-menu-and-sort (menu)
+  "Sort MENU, split according to `menu-max-items' and add accelerator specs.
+This is useful for menus generated by filter functions, to make them look
+nice.  This is equivalent to
+
+\(menu-split-long-menu (menu-sort-menu menu))
+
+and you can call those functions individually if necessary.
+You can also call `submenu-generate-accelerator-spec' yourself to add
+accelerator specs -- this works even if the specs have already been added."
+  (menu-split-long-menu (menu-sort-menu menu)))
+
+(defun menu-split-long-menu (menu)
+  "Split MENU according to `menu-max-items' and add accelerator specs.
+If MENU already has accelerator specs, they will be removed and new ones
+generated.  You should normally use `menu-split-long-menu-and-sort' instead.
+The menu should already be sorted to get meaningful results when it is
+split, since the outer menus are of the format `FROM ... TO'."
+  (let ((len (length menu)))
+    (if (or (null menu-max-items)
+	    (<= len menu-max-items))
+	(submenu-generate-accelerator-spec menu)
+      (let* ((outer (/ (+ len (1- menu-submenu-max-items))
+		       menu-submenu-max-items))
+	     (inner (/ (+ len (1- outer)) outer))
+	     (result nil))
+	(while menu
+	  (let ((sub nil)
+		(from (car menu)))
+	    (dotimes (foo (min inner len))
+	      (setq sub  (cons (car menu) sub)
+		    menu (cdr menu)))
+	    (setq len (- len inner))
+	    (let* ((to (car sub))
+		   (ftext (menu-item-strip-accelerator-spec
+			   (menu-item-text from)))
+		   (ttext (menu-item-strip-accelerator-spec
+			   (menu-item-text to))))
+	      (setq sub (nreverse sub))
+	      (setq result
+		    (cons (cons (if (stringp menu-submenu-name-format)
+				    (format menu-submenu-name-format
+					    ftext ttext)
+				  (funcall menu-submenu-name-format
+					   ftext ttext))
+				(submenu-generate-accelerator-spec sub))
+			  result)))))
+	(submenu-generate-accelerator-spec (nreverse result))))))
+
+(defun menu-sort-menu (menu)
+  "Sort MENU alphabetically.
+You should normally use `menu-split-long-menu-and-sort' instead."
+  (sort menu
+	#'(lambda (a b) (< (compare-menu-text
+			    (menu-item-text a) (menu-item-text b))
+			   0))))
+
 
 ;;;;;;; popup menus
 
--- a/src/ChangeLog	Thu Feb 03 04:29:33 2005 +0000
+++ b/src/ChangeLog	Thu Feb 03 05:03:45 2005 +0000
@@ -1,3 +1,56 @@
+2005-02-02  Ben Wing  <ben@xemacs.org>
+
+	* menubar.c:
+	* menubar.c (Fcompare_menu_text):
+	New fun to compare menu itext as if the two were normalized.
+	
+	* menubar.c (Fnormalize_menu_text):
+	* menubar.c (syms_of_menubar):
+	Rename; there are no external callers of this function.
+	Remove unneeded BUFFER argument.  Don't downcase.
+	(This will be done in compare-menu-text.)
+	Document that return value may be same string.
+	
+2005-02-02  Ben Wing  <ben@xemacs.org>
+
+	* lread.c:
+	* lread.c (check_if_suppressed):
+	* lread.c (Fload_internal):
+	* lread.c (locate_file_in_directory_mapper):
+	* lread.c (readevalloop):
+	* lread.c (syms_of_lread):
+	* lread.c (vars_of_lread):
+	* menubar.c:
+	* menubar.c (Fcompare_menu_text):
+	* menubar.c (Fnormalize_menu_text):
+	* menubar.c (syms_of_menubar):
+	* menubar.c (vars_of_menubar):
+
+2004-11-09  Ben Wing  <ben@xemacs.org>
+
+	* lisp.h:
+	
+	* lread.c:
+	* lread.c (check_if_suppressed):
+	* lread.c (Fload_internal):
+	* lread.c (locate_file_in_directory_mapper):
+	* lread.c (readevalloop):
+	* lread.c (syms_of_lread):
+	* lread.c (vars_of_lread):
+	Remove undeeded Vload_file_name_internal_the_purecopy,
+	Qload_file_name -- use internal_bind_lisp_object instead of
+	specbind.
+
+	Add load-suppress-alist.
+	
+2003-02-15  Ben Wing  <ben@xemacs.org>
+
+	* syswindows.h: Define W32API_2_2 for w32api.h v2.2 or higher.
+	Use it when defining a missing structure that is present in this
+	version of w32api.h.
+	* event-msw.c (mswindows_wnd_proc): Conditionalize bug fix on
+	!W32API_2_2.
+
 2005-01-31  Ben Wing  <ben@xemacs.org>
 
 	* emacs.c:
--- a/src/menubar.c	Thu Feb 03 04:29:33 2005 +0000
+++ b/src/menubar.c	Thu Feb 03 05:03:45 2005 +0000
@@ -1,7 +1,7 @@
 /* Implements an elisp-programmable menubar.
    Copyright (C) 1993, 1994 Free Software Foundation, Inc.
    Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
-   Copyright (C) 2001, 2002 Ben Wing.
+   Copyright (C) 2001, 2002, 2003 Ben Wing.
 
 This file is part of XEmacs.
 
@@ -326,14 +326,51 @@
   return Qnil;
 }
 
-DEFUN ("normalize-menu-item-name", Fnormalize_menu_item_name, 1, 2, 0, /*
+DEFUN ("compare-menu-text", Fcompare_menu_text, 2, 2, 0, /*
+Compare the text of two menu items, ignoring accelerator specs and case.
+Also treat %% as a single %.  Return < 0 if STRING1 is less than STRING2,
+0 if equal, > 0 if STRING1 is greater than STRING2.
+*/
+       (string1, string2))
+{
+  Ibyte *p;
+  Ibyte *q;
+
+  CHECK_STRING (string1);
+  CHECK_STRING (string2);
+
+  p = XSTRING_DATA (string1);
+  q = XSTRING_DATA (string2);
+
+  for (;;)
+    {
+      Ichar val;
+      if (*p == '%' && *(p + 1) == '%')
+	p++;
+      else if (*p == '%' && *(p + 1) == '_')
+	p += 2;
+      if (*q == '%' && *(q + 1) == '%')
+	q++;
+      else if (*q == '%' && *(q + 1) == '_')
+	q += 2;
+      if (!*p || !*q)
+	return make_int (*p - *q);
+      val = DOWNCASE (0, itext_ichar (p)) - DOWNCASE (0, itext_ichar (q));
+      if (val)
+	return make_int (val);
+      INC_IBYTEPTR (p);
+      INC_IBYTEPTR (q);
+    }
+}
+
+DEFUN ("normalize-menu-text", Fnormalize_menu_text, 1, 1, 0, /*
 Convert a menu item name string into normal form, and return the new string.
 Menu item names should be converted to normal form before being compared.
 This removes %_'s (accelerator indications) and converts %% to %.
+The returned string may be the same string as the original.
 */
-       (name, buffer))
+       (name))
 {
-  struct buffer *buf = decode_buffer (buffer, 0);
   Charcount end;
   int i;
   Ibyte *name_data;
@@ -352,7 +389,6 @@
   for (i = 0; i < end; i++)
     {
       elt = itext_ichar (name_data);
-      elt = DOWNCASE (buf, elt);
       if (expecting_underscore)
 	{
 	  expecting_underscore = 0;
@@ -400,7 +436,8 @@
   DEFSYMBOL (Qmenu_escape);
 
   DEFSUBR (Fpopup_menu);
-  DEFSUBR (Fnormalize_menu_item_name);
+  DEFSUBR (Fcompare_menu_text);
+  DEFSUBR (Fnormalize_menu_text);
   DEFSUBR (Fmenu_find_real_submenu);
 }