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-2010, Drew Adams, all rights reserved.
8 ;; Created: Fri Aug 12 17:18:02 2005
10 ;; Last-Updated: Fri Jun 25 21:05:15 2010 (-0700)
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 ;; lacarte-execute-command: Protected Icicles vars with boundp. Thx to Alexey Romanov.
264 ;; lacarte-get-a-menu-item-alist-1: Add keyboard shortcuts to item names.
265 ;; Applied Icicles renamings (belatedly):
266 ;; icicle-sort-functions-alist to icicle-sort-orders-alist,
267 ;; icicle-sort-function to icicle-sort-comparer.
269 ;; Added: lacarte-execute-command, lacarte-menu-first-p.
270 ;; lacarte-get-a-menu-item-alist-1: Handle :filter (e.g. File > Open Recent submenus).
271 ;; lacarte-execute-menu-command:
272 ;; Just let-bind lacarte-menu-items-alist - don't use unwind-protect.
273 ;; lacarte-get-overall-menu-item-alist: Reset lacarte-menu-items-alist to nil.
274 ;; lacarte-get-a-menu-item-alist: Set to the return value.
276 ;; Added: lacarte-history.
277 ;; lacarte-execute-menu-command:
278 ;; Use lacarte-history as the history list. Use strict completion.
280 ;; lacarte-execute-menu-command: Use icicle-interactive-history as the history list.
282 ;; Renamed from alacarte to lacarte. Confusion with alacarte Ubuntu source package.
284 ;; Renamed library icicles-menu.el to alacarte.el.
285 ;; alacarte-execute-menu-command: Case-insensitive completion, by default.
287 ;; icicle-get-a-menu-item-alist-1: Don't add non-selectable item to alist.
289 ;; icicle-convert-menu-item-function: Use choice as :type, allowing nil.
290 ;; :group 'icicles -> :group 'Icicles.
292 ;; icicle-get-overall-menu-item-alist: Include minor-mode keymaps.
294 ;; Added to Commentary.
296 ;; icicle-execute-menu-command: \s -> \\s. (Thx to dslcustomer-211-74.vivodi.gr.)
298 ;; Added :link for sending bug reports.
300 ;; Changed defgroup to icicles-menu from icicles.
303 ;; icicle-execute-menu-command:
304 ;; Reset icicle-menu-items-alist in unwind-protect.
305 ;; Fix for dynamic menus Select and Paste, Buffers, and Frames:
306 ;; Treat special cases of last-command-event.
307 ;; icicle-get-overall-menu-item-alist: setq result of sort.
309 ;; Replaced icicle-menu-items with icicle-menu-items-alist (no need for both).
310 ;; icicle-execute-menu-command: Set, don't bind icicle-menu-items-alist.
312 ;; icicle-execute-menu-command: renamed alist to icicle-menu-items-alist, so can
313 ;; refer to it unambiguously in icicle-help-on-candidate (in icicles.el).
315 ;; Added: icicle-convert-menu-item-function, icicle-remove-w32-keybd-accelerators,
316 ;; icicle-escape-w32-accel.
318 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
320 ;; This program is free software; you can redistribute it and/or modify
321 ;; it under the terms of the GNU General Public License as published by
322 ;; the Free Software Foundation; either version 3, or (at your option)
323 ;; any later version.
325 ;; This program is distributed in the hope that it will be useful,
326 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
327 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
328 ;; GNU General Public License for more details.
330 ;; You should have received a copy of the GNU General Public License
331 ;; along with this program; see the file COPYING. If not, write to
332 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
333 ;; Floor, Boston, MA 02110-1301, USA.
335 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
339 (unless (fboundp 'replace-regexp-in-string) (require 'subr-21 nil t))
341 ;;;;;;;;;;;;;;;;;;;;;;;;;
343 ;;(@* "User Options")
345 ;;; User Options -------------------------------------------
347 (defgroup lacarte nil
348 "Execute menu items as commands, with completion."
349 :prefix "lacarte-" :group 'menu
350 :link `(url-link :tag "Send Bug Report"
351 ,(concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=
353 &body=Describe bug here, starting with `emacs -q'. \
354 Don't forget to mention your Emacs and library versions."))
355 :link '(url-link :tag "Other Libraries by Drew"
356 "http://www.emacswiki.org/cgi-bin/wiki/DrewsElispLibraries")
357 :link '(url-link :tag "Download" "http://www.emacswiki.org/cgi-bin/wiki/lacarte.el")
358 :link '(url-link :tag "Description" "http://www.emacswiki.org/cgi-bin/wiki/LaCarte")
359 :link '(emacs-commentary-link :tag "Commentary" "lacarte.el")
362 (defcustom lacarte-convert-menu-item-function nil
363 "*Function to call to convert a menu item.
364 Used by `lacarte-execute-menu-command'. A typical use would be to
365 remove the `&' characters used in MS Windows menus to define keyboard
366 accelerators. See `lacarte-remove-w32-keybd-accelerators'."
367 :type '(choice (const :tag "None" nil) function) :group 'lacarte)
369 ;; $$$ NOT YET IMPLEMENTED
370 ;; (defcustom lacarte-sort-menu-bar-order-flag nil
371 ;; "*Non-nil means that `lacarte-execute-menu-command' uses menu-bar order.
372 ;; Nil means use alphabetic order.
373 ;; The order is what is used for completion.
374 ;; Note: Using a non-nil value imposes an extra sorting operation, which
375 ;; slows down the creation of the completion-candidates list."
376 ;; :type 'boolean :group 'lacarte)
378 ;;; Internal Variables -------------------------------------
380 (defvar lacarte-history nil "History for menu items read using La Carte completion.")
382 ;; This is used also in `icicle-help-on-candidate', which is defined in Icicles
383 ;; (library `icicles-mcmd.el').
384 (defvar lacarte-menu-items-alist nil
385 "Alist of pairs (MENU-ITEM . COMMAND).
386 The pairs are defined by the current local and global keymaps.
387 MENU-ITEM is a menu item, with ancestor-menu prefixes.
388 Example: `(\"Files > Insert File...\" . insert-file)'.
389 COMMAND is the command bound to the menu item.")
391 ;;; Functions -------------------------------
393 (defun lacarte-execute-command (&optional no-commands-p)
394 "Execute a menu-bar menu command or an ordinary command.
395 Type a menu item or a command name. Completion is available.
396 With a prefix arg, only menu items are available.
397 Completion is not case-sensitive. However, if you use Icicles, then
398 you can use `C-A' in the minibuffer to toggle case-sensitivity.
400 If you use Icicles, then you can also sort the completion candidates
401 in different ways, using `C-,'. With Icicles, by default menu items
402 are sorted before non-menu commands, and menu items are highlighted
403 using face `icicle-special-candidate'."
405 (let ((lacarte-menu-items-alist (lacarte-get-overall-menu-item-alist))
406 (completion-ignore-case t) ; Not case-sensitive, by default.
407 (icicle-special-candidate-regexp (and (not no-commands-p) ".* > \\(.\\|\n\\)*"))
408 (icicle-sort-orders-alist (and (boundp 'icicle-sort-orders-alist)
410 icicle-sort-orders-alist
411 (cons '("menu items first"
412 . lacarte-menu-first-p)
413 icicle-sort-orders-alist))))
414 (icicle-sort-comparer (and (boundp 'icicle-sort-comparer)
417 'lacarte-menu-first-p)))
419 (unless no-commands-p
420 (mapatoms (lambda (symb)
421 (when (commandp symb)
422 (push (cons (symbol-name symb) symb) lacarte-menu-items-alist)))))
423 (setq choice (completing-read (if no-commands-p "Menu command: " "Command: ")
424 lacarte-menu-items-alist nil t nil 'lacarte-history)
425 cmd (cdr (assoc choice lacarte-menu-items-alist)))
426 (unless cmd (error "No such menu command"))
427 ;; Treat special cases of `last-command-event', reconstructing it for
428 ;; menu items that get their meaning from the click itself.
429 (cond ((eq cmd 'menu-bar-select-buffer)
430 (string-match " >\\s-+\\(.+\\)\\s-+\\*?%?\\s-+\\S-*\\s-*$" choice)
431 (setq choice (substring choice (match-beginning 1) (match-end 1)))
432 (when (string-match " \\*?%?" choice)
433 (setq choice (substring choice 0 (match-beginning 0))))
434 (setq last-command-event choice))
435 ((eq cmd 'menu-bar-select-yank)
436 (string-match "Edit > Select and Paste > \\(.*\\)$" choice)
437 (setq last-command-event
438 (substring choice (match-beginning 1) (match-end 1))))
439 ((eq cmd 'menu-bar-select-frame)
440 (string-match " >\\s-[^>]+>\\s-+\\(.+\\)$" choice)
441 (setq choice (substring choice (match-beginning 1) (match-end 1)))
442 (setq last-command-event choice)))
443 (call-interactively cmd)))
445 (defun lacarte-menu-first-p (s1 s2)
446 "Return non-nil if S1 is a menu item and S2 is not."
448 (and (string-match " > " s1) (not (string-match " > " s2)))))
450 (defun lacarte-execute-menu-command ()
451 "Execute a menu-bar menu command.
452 Type a menu item. Completion is available.
453 Completion is not case-sensitive. However, if you use Icicles, then
454 you can use `C-A' in the minibuffer to toggle case-sensitivity.
455 If you use Icicles, then you can also sort the completion candidates
456 in different ways, using `C-,'."
458 (let* ((lacarte-menu-items-alist (lacarte-get-overall-menu-item-alist))
459 (completion-ignore-case t) ; Not case-sensitive, by default.
460 (menu-item (completing-read "Menu command: "
461 lacarte-menu-items-alist
462 nil t nil 'lacarte-history))
463 (cmd (cdr (assoc menu-item lacarte-menu-items-alist))))
464 (unless cmd (error "No such menu command"))
465 ;; Treat special cases of `last-command-event', reconstructing it for
466 ;; menu items that get their meaning from the click itself.
467 (cond ((eq cmd 'menu-bar-select-buffer)
468 (string-match " >\\s-+\\(.+\\)\\s-+\\*?%?\\s-+\\S-*\\s-*$"
470 (setq menu-item (substring menu-item (match-beginning 1) (match-end 1)))
471 (when (string-match " \\*?%?" menu-item)
472 (setq menu-item (substring menu-item 0 (match-beginning 0))))
473 (setq last-command-event menu-item))
474 ((eq cmd 'menu-bar-select-yank)
475 (string-match "Edit > Select and Paste > \\(.*\\)$" menu-item)
476 (setq last-command-event
477 (substring menu-item (match-beginning 1) (match-end 1))))
478 ((eq cmd 'menu-bar-select-frame)
479 (string-match " >\\s-[^>]+>\\s-+\\(.+\\)$" menu-item)
480 (setq menu-item (substring menu-item (match-beginning 1) (match-end 1)))
481 (setq last-command-event menu-item)))
482 (call-interactively cmd)))
484 (defun lacarte-get-overall-menu-item-alist ()
485 "Alist formed from menu items in current active keymaps.
486 See `lacarte-get-a-menu-item-alist' for the structure.
487 As a side effect, this modifies `lacarte-get-a-menu-item-alist' and
488 then resets it to ()"
491 (lacarte-get-a-menu-item-alist (assq 'menu-bar (current-local-map)))
492 (lacarte-get-a-menu-item-alist (assq 'menu-bar (current-global-map)))
493 (mapcar (lambda (map) (lacarte-get-a-menu-item-alist (assq 'menu-bar map)))
494 (current-minor-mode-maps)))))
495 (setq lacarte-menu-items-alist ())
496 (if nil;; `lacarte-sort-menu-bar-order-flag' ; Not yet implemented.
497 (setq alist (sort alist SOME-PREDICATE))
500 (defun lacarte-get-a-menu-item-alist (keymap)
501 "Alist of pairs (MENU-ITEM . COMMAND) defined by KEYMAP.
502 KEYMAP is any keymap that has menu items.
503 MENU-ITEM is a menu item, with ancestor-menu prefixes.
504 Example: `(\"Files > Insert File...\" . insert-file)'.
505 COMMAND is the command bound to the menu item.
506 Returns `lacarte-menu-items-alist' which it modifies."
507 (setq lacarte-menu-items-alist ())
508 (lacarte-get-a-menu-item-alist-1 keymap)
509 (setq lacarte-menu-items-alist (nreverse lacarte-menu-items-alist)))
511 (defun lacarte-get-a-menu-item-alist-1 (keymap &optional root)
512 "Helper function for `lacarte-get-a-menu-item-alist'.
513 This calls itself recursively, to process submenus.
514 Returns `lacarte-menu-items-alist', which it modifies."
516 (setq root (or root)) ; nil, for top level.
518 (if (atom (car scan))
519 (setq scan (cdr scan))
520 (let ((defn (cdr (car scan)))
522 ;; Get REAL-BINDING for the menu item.
524 ;; (menu-item ITEM-STRING): non-selectable item - skip it.
525 ((and (eq 'menu-item (car-safe defn))
526 (null (cdr-safe (cdr-safe defn))))
527 (setq defn nil)) ; So `keymapp' test, below, fails.
529 ;; (ITEM-STRING): non-selectable item - skip it.
530 ((and (stringp (car-safe defn)) (null (cdr-safe defn)))
531 (setq defn nil)) ; So `keymapp' test, below, fails.
533 ;; (menu-item ITEM-STRING REAL-BINDING . PROPERTIES), with `:filter'
534 ((and (eq 'menu-item (car-safe defn))
535 (member :filter (cdr (cddr defn))))
536 (let ((filt (cadr (member :filter (cdr (cddr defn))))))
538 (concat root (and root " > ") (eval (cadr defn))
539 (let ((keys (car-safe (cdr-safe (cdr-safe (cdr-safe defn))))))
540 (and (consp keys) (stringp (cdr keys)) (cdr keys)))))
541 (setq defn (if (functionp filt) ; Apply the filter to REAL-BINDING.
542 (funcall filt (car (cddr defn)))
543 (car (cddr defn))))))
545 ;; (menu-item ITEM-STRING REAL-BINDING . PROPERTIES)
546 ((eq 'menu-item (car-safe defn))
548 (concat root (and root " > ") (eval (cadr defn))
549 (let ((keys (car-safe (cdr-safe (cdr-safe (cdr-safe defn))))))
550 (and (consp keys) (stringp (cdr keys)) (cdr keys)))))
551 (setq defn (car (cddr defn))))
553 ;; (ITEM-STRING . REAL-BINDING) or
554 ;; (ITEM-STRING [HELP-STRING] (KEYBD-SHORTCUTS) . REAL-BINDING)
555 ((stringp (car-safe defn))
556 (setq composite-name (concat root (and root " > ") (eval (car defn))))
557 (setq defn (cdr defn))
559 (when (stringp (car-safe defn)) (setq defn (cdr defn)))
560 ;; Skip (KEYBD-SHORTCUTS): cached key-equivalence data for menu items.
561 ;; But first add shortcuts to composite name.
562 (when (and (consp defn) (consp (car defn)))
563 (when (stringp (cdar defn)) ; Add shortcuts to name.
564 (setq composite-name (concat composite-name (cdar defn))))
565 (setq defn (cdr defn)))))
567 ;; If REAL-BINDING is a keymap, then recurse on it.
569 ;; Follow indirections to ultimate symbol naming a command.
570 (while (and (symbolp defn) (fboundp defn) (keymapp (symbol-function defn)))
571 (setq defn (symbol-function defn)))
572 (if (eq 'keymap (car-safe defn))
573 (lacarte-get-a-menu-item-alist-1 (cdr defn) composite-name)
574 (lacarte-get-a-menu-item-alist-1 (symbol-function defn) composite-name)))
576 ;; Add menu item + command pair to `lacarte-menu-items-alist' alist.
577 ;; Don't add it if `composite-name' is nil - that's a non-selectable item.
578 (when (and root composite-name (not (keymapp defn)))
579 (setq lacarte-menu-items-alist
581 (cons (if (and (functionp lacarte-convert-menu-item-function)
582 (stringp composite-name)) ; Could be nil
583 (funcall lacarte-convert-menu-item-function composite-name)
586 lacarte-menu-items-alist))))
587 (when (consp scan) (setq scan (cdr scan)))))
588 lacarte-menu-items-alist))
590 (defun lacarte-remove-w32-keybd-accelerators (menu-item)
591 "Remove `&' characters that define keyboard accelerators in MS Windows.
592 \"&&\" is an escaped `&' - it is replaced by a single `&'.
593 This is a candidate value for `lacarte-convert-menu-item-function'."
594 (replace-regexp-in-string "&&?" 'lacarte-escape-w32-accel menu-item))
596 (defun lacarte-escape-w32-accel (match-string)
597 "If STRING is \"&&\", then return \"&\". Else return \"\"."
598 (if (> (length match-string) 1) "&" ""))
600 ;;;;;;;;;;;;;;;;;;;;;;;
604 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
605 ;;; lacarte.el ends here