1 ;;; lacarte.el --- Execute menu items as commands, with completion.
3 ;; Filename: lacarte.el
4 ;; Description: Execute menu items as commands, with completion.
6 ;; Maintainer: Drew Adams
7 ;; Copyright (C) 2005-2011, Drew Adams, all rights reserved.
8 ;; Created: Fri Aug 12 17:18:02 2005
10 ;; Last-Updated: Tue Jan 4 10:59:52 2011 (-0800)
13 ;; URL: http://www.emacswiki.org/cgi-bin/wiki/lacarte.el
14 ;; Keywords: menu-bar, menu, command, help, abbrev, minibuffer, keys,
15 ;; completion, matching, local, internal, extensions,
16 ;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x
18 ;; Features that might be required by this library:
22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26 ;; Q. When is a menu not a menu? A. When it's a la carte.
28 ;; Library La Carte lets you execute menu items as commands, with
29 ;; completion. You can use it as an alternative to standard library
32 ;; Type a menu item. Completion is available. Completion candidates
33 ;; are of the form menu > submenu > subsubmenu > ... > menu item.
36 ;; File > Open Recent > Cleanup list
37 ;; File > Open Recent > Edit list...
39 ;; When you choose a menu-item candidate, the corresponding command
42 ;; Put this in your init file (~/.emacs):
46 ;; Suggested key bindings:
48 ;; (global-set-key [?\e ?\M-x] 'lacarte-execute-command)
49 ;; (global-set-key [?\M-`] 'lacarte-execute-menu-command)
50 ;; (global-set-key [f10] 'lacarte-execute-menu-command)
52 ;; (The latter two replace standard bindings for `tmm-menubar'. On
53 ;; MS Windows, `f10' is normally bound to `menu-bar-open', which uses
54 ;; the Windows native keyboard access to menus.)
56 ;; To really take advantage of La Carte, use it together with
57 ;; Icicles. Icicles is not required to be able to use La Carte, but
58 ;; it enhances the functionality of `lacarte.el' considerably.
59 ;; (Note: `lacarte.el' was originally called `icicles-menu.el'.)
61 ;; If you use MS Windows keyboard accelerators, consider using
62 ;; `lacarte-remove-w32-keybd-accelerators' as the value of
63 ;; `lacarte-convert-menu-item-function'. It removes any unescaped
64 ;; `&' characters (indicating an accelerator) from the menu items.
65 ;; One library that adds keyboard accelerators to your menu items is
66 ;; `menuacc.el', by Lennart Borgman (< l e n n a r t . b o r g m a n
67 ;; @ g m a i l . c o m >).
70 ;; Commands defined here:
72 ;; `lacarte-execute-command', `lacarte-execute-menu-command'.
74 ;; Options defined here: `lacarte-convert-menu-item-function'.
76 ;; Non-interactive functions defined here:
78 ;; `lacarte-escape-w32-accel', `lacarte-get-a-menu-item-alist',
79 ;; `lacarte-get-a-menu-item-alist-1',
80 ;; `lacarte-get-overall-menu-item-alist', `lacarte-menu-first-p',
81 ;; `lacarte-remove-w32-keybd-accelerators'.
83 ;; Internal variables defined here:
85 ;; `lacarte-history', `lacarte-menu-items-alist'.
91 ;; In your init file (`~/.emacs'), bind `ESC M-x' as suggested above:
93 ;; (global-set-key [?\e ?\M-x] 'lacarte-execute-command)
95 ;; Type `ESC M-x' (or `ESC ESC x', which is the same thing). You are
96 ;; prompted for a command or menu command to execute. Just start
97 ;; typing its name. Each menu item's full name, for completion, has
98 ;; its parent menu names as prefixes.
104 ;; Command: Tools > Compa [TAB]
105 ;; Command: Tools > Compare (Ediff) > Two F [TAB]
106 ;; Command: Tools > Compare (Ediff) > Two Files... [RET]
109 ;; Not Just for Wimps and Noobs Anymore
110 ;; ------------------------------------
112 ;; *You* don't use menus. Nah, they're too slow! Only newbies and
113 ;; wimps use menus. Well not any more. Use the keyboard to access
114 ;; any menu item, without knowing where it is or what its full name
115 ;; is. Type just part of its name and use completion to get the
116 ;; rest: the complete path and item name.
119 ;; Commands and Menu Commands
120 ;; --------------------------
122 ;; You can bind either `lacarte-execute-menu-command' or
123 ;; `lacarte-execute-command' to a key such as `ESC M-x'.
125 ;; `lacarte-execute-menu-command' uses only menu commands.
126 ;; `lacarte-execute-command' lets you choose among ordinary Emacs
127 ;; commands, in addition to menu commands. You can use a prefix arg
128 ;; with `lacarte-execute-command' to get the same effect as
129 ;; `lacarte-execute-menu-command'.
131 ;; Use `lacarte-execute-command' if you don't care whether a command
132 ;; is on a menu. Then, if you want a command that affects a buffer,
133 ;; just type `buf'. This is especially useful if you use Icicles -
136 ;; By default, in Icicle mode, `ESC M-x' is bound to
137 ;; `lacarte-execute-command', and `M-`' is bound to
138 ;; `lacarte-execute-menu-command'.
141 ;; Icicles Enhances Dining A La Carte
142 ;; ----------------------------------
144 ;; Use Icicles with La Carte to get more power and convenience.
146 ;; It is Icicles that lets you choose menu items a la carte, in fact.
147 ;; That is, you can access them directly, wherever they might be in
148 ;; the menu hierachy. Without Icicles, you are limited to choosing
149 ;; items by their menu-hierarchy prefixes, and you must complete the
150 ;; entire menu prefix to the item, from the top of the menu on down.
151 ;; With Icicles, you can directly match any parts of a menu item and
152 ;; its hierarchy path. Icicles is here:
153 ;; http://www.emacswiki.org/cgi-bin/wiki/Icicles.
155 ;; Type any part of a menu-item, then use the Page Up and Page Down
156 ;; keys (`prior' and `next') to cycle through all menu commands that
157 ;; contain the text you typed somewhere in their name. You can match
158 ;; within any menu or within all menus; that is, you can match any
159 ;; part(s) of the menu-hierachy prefix.
161 ;; You can use `S-TAB' to show and choose from all such "apropos
162 ;; completions", just as you normally use `TAB' to show all prefix
163 ;; completions (that is, ordinary completions). Vanilla, prefix
164 ;; completion is still available using `TAB', and you can cycle
165 ;; through the prefix completions using the arrow keys.
167 ;; You can use Icicles "progressive completion" to match multiple
168 ;; parts of a menu item separately, in any order. For example, if
169 ;; you want a menu command that has to do with buffers and
170 ;; highlighting, type `buf M-SPC hig S-TAB'.
172 ;; Icicles apropos completion also lets you type a regular expression
173 ;; (regexp) - it is matched against all of the possible menu items.
174 ;; So, for instance, you could type `^e.+buff [next] [next]...' to
175 ;; quickly cycle to menu command `Edit > Go To > Goto End of Buffer'.
176 ;; Or type `.*print.*buf S-TAB' to choose from the list of all menu
177 ;; commands that match `print' followed somewhere by `buf'.
179 ;; If you know how to use regexps, you can easily and quickly get to
180 ;; a menu command you want, or at least narrow the list of candidates
181 ;; for completion and cycling.
183 ;; Additional benefits of using Icicles with La Carte:
185 ;; * When you cycle to a candidate menu item, or you complete to one
186 ;; (entirely), the Emacs command associated with the menu item is
187 ;; shown in the mode line of buffer `*Completions*'.
189 ;; * You can use `M-h' to complete your minibuffer input against
190 ;; commands, including menu-item commands, that you have entered
191 ;; previously. You can also use the standard history keys
192 ;; (e.g. `M-p', `M-r') to access these commands.
195 ;; Menu Organization Helps You Find a Command
196 ;; ------------------------------------------
198 ;; Unlike commands listed in a flat `*Apropos*' page, menu items are
199 ;; organized, grouped logically by common area of application
200 ;; (`File', `Edit',...). This grouping is also available when
201 ;; cycling completion candidates using Icicles, and you can take
202 ;; advantage of it to hasten your search for the right command.
204 ;; You want to execute a command that puts the cursor at the end of a
205 ;; buffer, but you don't remember its name, what menu it might be a
206 ;; part of, or where it might appear in that (possibly complex) menu.
207 ;; With Icicles and La Carte, you type `ESC M-x' and then type
208 ;; `buffer' at the prompt. You use the Page Up and Page Down keys to
209 ;; cycle through all menu items that contain the word `buffer'.
211 ;; There are lots of such menu items. But all items from the same
212 ;; menu (e.g. `File') are grouped together. You cycle quickly (not
213 ;; reading) to the `Edit' menu, because you guess that moving the
214 ;; cursor has more to do with editing than with file operations, tool
215 ;; use, buffer choice, help, etc. Then you cycle more slowly among
216 ;; the `buffer' menu items in the `Edit' menu. You quickly find
217 ;; `Edit > Go To > Goto End of Buffer'. QED.
220 ;; Learn About Menu Items By Exploring Them
221 ;; ----------------------------------------
223 ;; With Icicles, you can display the complete documentation (doc
224 ;; string) for the command corresponding to each menu item, as the
225 ;; item appears in the minibuffer. To do this, just cycle menu-item
226 ;; candidates using `C-down' or `C-next', instead of `[down]' or
227 ;; `[next]'. The documentation appears in buffer `*Help*'.
229 ;; In sum, if you use La Carte, you will want to use it with Icicles
236 ;; 1. Provide sorting by menu-bar order, instead of alphabetically.
237 ;; 2. Echo key bindings for each completed menu item.
239 ;; 3. Maybe use tmm-get-bind?
243 ;; If you have library `linkd.el' and Emacs 22 or later, load
244 ;; `linkd.el' and turn on `linkd-mode' now. It lets you easily
245 ;; navigate around the sections of this doc. Linkd mode will
246 ;; highlight this Index, as well as the cross-references and section
247 ;; headings throughout this file. You can get `linkd.el' here:
248 ;; http://dto.freeshell.org/notebook/Linkd.html.
251 ;; (@> "User Options")
252 ;; (@> "Internal Variables")
255 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
262 ;; Added autoload cookies for defgroup, defcustom, and commands.
264 ;; lacarte-execute-command: Protected Icicles vars with boundp. Thx to Alexey Romanov.
266 ;; lacarte-get-a-menu-item-alist-1: Add keyboard shortcuts to item names.
267 ;; Applied Icicles renamings (belatedly):
268 ;; icicle-sort-functions-alist to icicle-sort-orders-alist,
269 ;; icicle-sort-function to icicle-sort-comparer.
271 ;; Added: lacarte-execute-command, lacarte-menu-first-p.
272 ;; lacarte-get-a-menu-item-alist-1: Handle :filter (e.g. File > Open Recent submenus).
273 ;; lacarte-execute-menu-command:
274 ;; Just let-bind lacarte-menu-items-alist - don't use unwind-protect.
275 ;; lacarte-get-overall-menu-item-alist: Reset lacarte-menu-items-alist to nil.
276 ;; lacarte-get-a-menu-item-alist: Set to the return value.
278 ;; Added: lacarte-history.
279 ;; lacarte-execute-menu-command:
280 ;; Use lacarte-history as the history list. Use strict completion.
282 ;; lacarte-execute-menu-command: Use icicle-interactive-history as the history list.
284 ;; Renamed from alacarte to lacarte. Confusion with alacarte Ubuntu source package.
286 ;; Renamed library icicles-menu.el to alacarte.el.
287 ;; alacarte-execute-menu-command: Case-insensitive completion, by default.
289 ;; icicle-get-a-menu-item-alist-1: Don't add non-selectable item to alist.
291 ;; icicle-convert-menu-item-function: Use choice as :type, allowing nil.
292 ;; :group 'icicles -> :group 'Icicles.
294 ;; icicle-get-overall-menu-item-alist: Include minor-mode keymaps.
296 ;; Added to Commentary.
298 ;; icicle-execute-menu-command: \s -> \\s. (Thx to dslcustomer-211-74.vivodi.gr.)
300 ;; Added :link for sending bug reports.
302 ;; Changed defgroup to icicles-menu from icicles.
305 ;; icicle-execute-menu-command:
306 ;; Reset icicle-menu-items-alist in unwind-protect.
307 ;; Fix for dynamic menus Select and Paste, Buffers, and Frames:
308 ;; Treat special cases of last-command-event.
309 ;; icicle-get-overall-menu-item-alist: setq result of sort.
311 ;; Replaced icicle-menu-items with icicle-menu-items-alist (no need for both).
312 ;; icicle-execute-menu-command: Set, don't bind icicle-menu-items-alist.
314 ;; icicle-execute-menu-command: renamed alist to icicle-menu-items-alist, so can
315 ;; refer to it unambiguously in icicle-help-on-candidate (in icicles.el).
317 ;; Added: icicle-convert-menu-item-function, icicle-remove-w32-keybd-accelerators,
318 ;; icicle-escape-w32-accel.
320 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
322 ;; This program is free software; you can redistribute it and/or modify
323 ;; it under the terms of the GNU General Public License as published by
324 ;; the Free Software Foundation; either version 3, or (at your option)
325 ;; any later version.
327 ;; This program is distributed in the hope that it will be useful,
328 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
329 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
330 ;; GNU General Public License for more details.
332 ;; You should have received a copy of the GNU General Public License
333 ;; along with this program; see the file COPYING. If not, write to
334 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
335 ;; Floor, Boston, MA 02110-1301, USA.
337 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
341 (unless (fboundp 'replace-regexp-in-string) (require 'subr-21 nil t))
343 ;;;;;;;;;;;;;;;;;;;;;;;;;
345 ;;(@* "User Options")
347 ;;; User Options -------------------------------------------
350 (defgroup lacarte nil
351 "Execute menu items as commands, with completion."
352 :prefix "lacarte-" :group 'menu
353 :link `(url-link :tag "Send Bug Report"
354 ,(concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=
356 &body=Describe bug here, starting with `emacs -q'. \
357 Don't forget to mention your Emacs and library versions."))
358 :link '(url-link :tag "Other Libraries by Drew"
359 "http://www.emacswiki.org/cgi-bin/wiki/DrewsElispLibraries")
360 :link '(url-link :tag "Download" "http://www.emacswiki.org/cgi-bin/wiki/lacarte.el")
361 :link '(url-link :tag "Description" "http://www.emacswiki.org/cgi-bin/wiki/LaCarte")
362 :link '(emacs-commentary-link :tag "Commentary" "lacarte.el")
366 (defcustom lacarte-convert-menu-item-function nil
367 "*Function to call to convert a menu item.
368 Used by `lacarte-execute-menu-command'. A typical use would be to
369 remove the `&' characters used in MS Windows menus to define keyboard
370 accelerators. See `lacarte-remove-w32-keybd-accelerators'."
371 :type '(choice (const :tag "None" nil) function) :group 'lacarte)
373 ;; $$$ NOT YET IMPLEMENTED
374 ;; (defcustom lacarte-sort-menu-bar-order-flag nil
375 ;; "*Non-nil means that `lacarte-execute-menu-command' uses menu-bar order.
376 ;; Nil means use alphabetic order.
377 ;; The order is what is used for completion.
378 ;; Note: Using a non-nil value imposes an extra sorting operation, which
379 ;; slows down the creation of the completion-candidates list."
380 ;; :type 'boolean :group 'lacarte)
382 ;;; Internal Variables -------------------------------------
384 (defvar lacarte-history nil "History for menu items read using La Carte completion.")
386 ;; This is used also in `icicle-help-on-candidate', which is defined in Icicles
387 ;; (library `icicles-mcmd.el').
388 (defvar lacarte-menu-items-alist nil
389 "Alist of pairs (MENU-ITEM . COMMAND).
390 The pairs are defined by the current local and global keymaps.
391 MENU-ITEM is a menu item, with ancestor-menu prefixes.
392 Example: `(\"Files > Insert File...\" . insert-file)'.
393 COMMAND is the command bound to the menu item.")
395 ;;; Functions -------------------------------
398 (defun lacarte-execute-command (&optional no-commands-p)
399 "Execute a menu-bar menu command or an ordinary command.
400 Type a menu item or a command name. Completion is available.
401 With a prefix arg, only menu items are available.
402 Completion is not case-sensitive. However, if you use Icicles, then
403 you can use `C-A' in the minibuffer to toggle case-sensitivity.
405 If you use Icicles, then you can also sort the completion candidates
406 in different ways, using `C-,'. With Icicles, by default menu items
407 are sorted before non-menu commands, and menu items are highlighted
408 using face `icicle-special-candidate'."
410 (let ((lacarte-menu-items-alist (lacarte-get-overall-menu-item-alist))
411 (completion-ignore-case t) ; Not case-sensitive, by default.
412 (icicle-special-candidate-regexp (and (not no-commands-p) ".* > \\(.\\|\n\\)*"))
413 (icicle-sort-orders-alist (and (boundp 'icicle-sort-orders-alist)
415 icicle-sort-orders-alist
416 (cons '("menu items first"
417 . lacarte-menu-first-p)
418 icicle-sort-orders-alist))))
419 (icicle-sort-comparer (and (boundp 'icicle-sort-comparer)
422 'lacarte-menu-first-p)))
424 (unless no-commands-p
425 (mapatoms (lambda (symb)
426 (when (commandp symb)
427 (push (cons (symbol-name symb) symb) lacarte-menu-items-alist)))))
428 (setq choice (completing-read (if no-commands-p "Menu command: " "Command: ")
429 lacarte-menu-items-alist nil t nil 'lacarte-history)
430 cmd (cdr (assoc choice lacarte-menu-items-alist)))
431 (unless cmd (error "No such menu command"))
432 ;; Treat special cases of `last-command-event', reconstructing it for
433 ;; menu items that get their meaning from the click itself.
434 (cond ((eq cmd 'menu-bar-select-buffer)
435 (string-match " >\\s-+\\(.+\\)\\s-+\\*?%?\\s-+\\S-*\\s-*$" choice)
436 (setq choice (substring choice (match-beginning 1) (match-end 1)))
437 (when (string-match " \\*?%?" choice)
438 (setq choice (substring choice 0 (match-beginning 0))))
439 (setq last-command-event choice))
440 ((eq cmd 'menu-bar-select-yank)
441 (string-match "Edit > Select and Paste > \\(.*\\)$" choice)
442 (setq last-command-event
443 (substring choice (match-beginning 1) (match-end 1))))
444 ((eq cmd 'menu-bar-select-frame)
445 (string-match " >\\s-[^>]+>\\s-+\\(.+\\)$" choice)
446 (setq choice (substring choice (match-beginning 1) (match-end 1)))
447 (setq last-command-event choice)))
448 (call-interactively cmd)))
450 (defun lacarte-menu-first-p (s1 s2)
451 "Return non-nil if S1 is a menu item and S2 is not."
453 (and (string-match " > " s1) (not (string-match " > " s2)))))
456 (defun lacarte-execute-menu-command ()
457 "Execute a menu-bar menu command.
458 Type a menu item. Completion is available.
459 Completion is not case-sensitive. However, if you use Icicles, then
460 you can use `C-A' in the minibuffer to toggle case-sensitivity.
461 If you use Icicles, then you can also sort the completion candidates
462 in different ways, using `C-,'."
464 (let* ((lacarte-menu-items-alist (lacarte-get-overall-menu-item-alist))
465 (completion-ignore-case t) ; Not case-sensitive, by default.
466 (menu-item (completing-read "Menu command: "
467 lacarte-menu-items-alist
468 nil t nil 'lacarte-history))
469 (cmd (cdr (assoc menu-item lacarte-menu-items-alist))))
470 (unless cmd (error "No such menu command"))
471 ;; Treat special cases of `last-command-event', reconstructing it for
472 ;; menu items that get their meaning from the click itself.
473 (cond ((eq cmd 'menu-bar-select-buffer)
474 (string-match " >\\s-+\\(.+\\)\\s-+\\*?%?\\s-+\\S-*\\s-*$"
476 (setq menu-item (substring menu-item (match-beginning 1) (match-end 1)))
477 (when (string-match " \\*?%?" menu-item)
478 (setq menu-item (substring menu-item 0 (match-beginning 0))))
479 (setq last-command-event menu-item))
480 ((eq cmd 'menu-bar-select-yank)
481 (string-match "Edit > Select and Paste > \\(.*\\)$" menu-item)
482 (setq last-command-event
483 (substring menu-item (match-beginning 1) (match-end 1))))
484 ((eq cmd 'menu-bar-select-frame)
485 (string-match " >\\s-[^>]+>\\s-+\\(.+\\)$" menu-item)
486 (setq menu-item (substring menu-item (match-beginning 1) (match-end 1)))
487 (setq last-command-event menu-item)))
488 (call-interactively cmd)))
490 (defun lacarte-get-overall-menu-item-alist ()
491 "Alist formed from menu items in current active keymaps.
492 See `lacarte-get-a-menu-item-alist' for the structure.
493 As a side effect, this modifies `lacarte-get-a-menu-item-alist' and
494 then resets it to ()"
497 (lacarte-get-a-menu-item-alist (assq 'menu-bar (current-local-map)))
498 (lacarte-get-a-menu-item-alist (assq 'menu-bar (current-global-map)))
499 (mapcar (lambda (map) (lacarte-get-a-menu-item-alist (assq 'menu-bar map)))
500 (current-minor-mode-maps)))))
501 (setq lacarte-menu-items-alist ())
502 (if nil;; `lacarte-sort-menu-bar-order-flag' ; Not yet implemented.
503 (setq alist (sort alist SOME-PREDICATE))
506 (defun lacarte-get-a-menu-item-alist (keymap)
507 "Alist of pairs (MENU-ITEM . COMMAND) defined by KEYMAP.
508 KEYMAP is any keymap that has menu items.
509 MENU-ITEM is a menu item, with ancestor-menu prefixes.
510 Example: `(\"Files > Insert File...\" . insert-file)'.
511 COMMAND is the command bound to the menu item.
512 Returns `lacarte-menu-items-alist' which it modifies."
513 (setq lacarte-menu-items-alist ())
514 (lacarte-get-a-menu-item-alist-1 keymap)
515 (setq lacarte-menu-items-alist (nreverse lacarte-menu-items-alist)))
517 (defun lacarte-get-a-menu-item-alist-1 (keymap &optional root)
518 "Helper function for `lacarte-get-a-menu-item-alist'.
519 This calls itself recursively, to process submenus.
520 Returns `lacarte-menu-items-alist', which it modifies."
522 (setq root (or root)) ; nil, for top level.
524 (if (atom (car scan))
525 (setq scan (cdr scan))
526 (let ((defn (cdr (car scan)))
528 ;; Get REAL-BINDING for the menu item.
530 ;; (menu-item ITEM-STRING): non-selectable item - skip it.
531 ((and (eq 'menu-item (car-safe defn))
532 (null (cdr-safe (cdr-safe defn))))
533 (setq defn nil)) ; So `keymapp' test, below, fails.
535 ;; (ITEM-STRING): non-selectable item - skip it.
536 ((and (stringp (car-safe defn)) (null (cdr-safe defn)))
537 (setq defn nil)) ; So `keymapp' test, below, fails.
539 ;; (menu-item ITEM-STRING REAL-BINDING . PROPERTIES), with `:filter'
540 ((and (eq 'menu-item (car-safe defn))
541 (member :filter (cdr (cddr defn))))
542 (let ((filt (cadr (member :filter (cdr (cddr defn))))))
544 (concat root (and root " > ") (eval (cadr defn))
545 (let ((keys (car-safe (cdr-safe (cdr-safe (cdr-safe defn))))))
546 (and (consp keys) (stringp (cdr keys)) (cdr keys)))))
547 (setq defn (if (functionp filt) ; Apply the filter to REAL-BINDING.
548 (funcall filt (car (cddr defn)))
549 (car (cddr defn))))))
551 ;; (menu-item ITEM-STRING REAL-BINDING . PROPERTIES)
552 ((eq 'menu-item (car-safe defn))
554 (concat root (and root " > ") (eval (cadr defn))
555 (let ((keys (car-safe (cdr-safe (cdr-safe (cdr-safe defn))))))
556 (and (consp keys) (stringp (cdr keys)) (cdr keys)))))
557 (setq defn (car (cddr defn))))
559 ;; (ITEM-STRING . REAL-BINDING) or
560 ;; (ITEM-STRING [HELP-STRING] (KEYBD-SHORTCUTS) . REAL-BINDING)
561 ((stringp (car-safe defn))
562 (setq composite-name (concat root (and root " > ") (eval (car defn))))
563 (setq defn (cdr defn))
565 (when (stringp (car-safe defn)) (setq defn (cdr defn)))
566 ;; Skip (KEYBD-SHORTCUTS): cached key-equivalence data for menu items.
567 ;; But first add shortcuts to composite name.
568 (when (and (consp defn) (consp (car defn)))
569 (when (stringp (cdar defn)) ; Add shortcuts to name.
570 (setq composite-name (concat composite-name (cdar defn))))
571 (setq defn (cdr defn)))))
573 ;; If REAL-BINDING is a keymap, then recurse on it.
575 ;; Follow indirections to ultimate symbol naming a command.
576 (while (and (symbolp defn) (fboundp defn) (keymapp (symbol-function defn)))
577 (setq defn (symbol-function defn)))
578 (if (eq 'keymap (car-safe defn))
579 (lacarte-get-a-menu-item-alist-1 (cdr defn) composite-name)
580 (lacarte-get-a-menu-item-alist-1 (symbol-function defn) composite-name)))
582 ;; Add menu item + command pair to `lacarte-menu-items-alist' alist.
583 ;; Don't add it if `composite-name' is nil - that's a non-selectable item.
584 (when (and root composite-name (not (keymapp defn)))
585 (setq lacarte-menu-items-alist
587 (cons (if (and (functionp lacarte-convert-menu-item-function)
588 (stringp composite-name)) ; Could be nil
589 (funcall lacarte-convert-menu-item-function composite-name)
592 lacarte-menu-items-alist))))
593 (when (consp scan) (setq scan (cdr scan)))))
594 lacarte-menu-items-alist))
596 (defun lacarte-remove-w32-keybd-accelerators (menu-item)
597 "Remove `&' characters that define keyboard accelerators in MS Windows.
598 \"&&\" is an escaped `&' - it is replaced by a single `&'.
599 This is a candidate value for `lacarte-convert-menu-item-function'."
600 (replace-regexp-in-string "&&?" 'lacarte-escape-w32-accel menu-item))
602 (defun lacarte-escape-w32-accel (match-string)
603 "If STRING is \"&&\", then return \"&\". Else return \"\"."
604 (if (> (length match-string) 1) "&" ""))
606 ;;;;;;;;;;;;;;;;;;;;;;;
610 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
611 ;;; lacarte.el ends here