1 ;;; tinylibmenu.el --- Library for echo-area menu
3 ;; This file is not part of Emacs
7 ;; Copyright (C) 1996-2007 Jari Aalto
8 ;; Keywords: extensions
10 ;; Maintainer: Jari Aalto
12 ;; To get information on this program, call M-x ti::menu-version.
13 ;; Look at the code with folding.el
17 ;; This program is free software; you can redistribute it and/or modify it
18 ;; under the terms of the GNU General Public License as published by the Free
19 ;; Software Foundation; either version 2 of the License, or (at your option)
22 ;; This program is distributed in the hope that it will be useful, but
23 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with program; see the file COPYING. If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
37 ;; ....................................................... &t-install ...
38 ;; Put this file on your Emacs-Lisp load path, add following into your
39 ;; ~/.emacs startup file
41 ;; (require 'tinylibmenu)
43 ;; Or use autoload, which is prefered. Your ~/.emacs loads faster
45 ;; (autoload 'ti::menu-menu-default "tinylibmenu" "" t)
47 ;; To bring up the menu (or menus), bind the main function into some key.
48 ;; This s only a demonstration. Configure your own menus using the
49 ;; example in this file.
51 ;; (global-set-key "\C-cM" 'ti::menu-menu-default)
53 ;; Make sure you have defined the variables `my-menu1' and `my-menu2'
54 ;; which hold the menu information.
56 ;; If you have any questions, use this function to contact author
58 ;; M-x ti::menu-submit-bug-report
65 ;; Overview of features
67 ;; o This package is a library.
68 ;; Store key bindings behind echo area menu, which is similar to
70 ;; o Regular Emacs user can also put less used binding to guided
71 ;; echo menu by just defining couple of menu variables.
75 ;; If some package defines echo area menus and you only want to make
76 ;; small modifications and not to copy the whole 'defvar MENU' to your
77 ;; .emacs, you can use following functions to manipulate the menu
81 ;; ti::menu-set-doc-string
83 ;; For example, if there is menu item:
85 ;; (defconst my-menu-sample
86 ;; '("?)help, 1)test1, 2)test2"
87 ;; ((?1 . ( (some-menu-test1 1 2 3)))
88 ;; (?2 . ( (some-menu-test2 1 2 3))))))
90 ;; and you don't like keybinding '?2'. You first delete the menu item,
91 ;; then add yours and lastly you update the doc string that is printed
92 ;; in echo area. Here is how you do all these three steps.
94 ;; (ti::menu-add 'my-menu-sample ?2 nil 'delete)
95 ;; (ti::menu-add 'my-menu-sample ?t '( (my-test 1 2 3)))
96 ;; (ti::menu-set-doc-string 'my-menu-sample
97 ;; "?)help, 1)test1, t)myTest")
99 ;; And the modified menu looks like this
101 ;; (defconst my-menu-sample
102 ;; '("?)help, 1)test1, t)myTest"
103 ;; ((?1 . ( (some-menu-test1 1 2 3)))
104 ;; (?t . ( (my-test2 1 2 3))))))
106 ;; If you want to replace _many_ commands from the menu, it is lot
107 ;; easier if you copy the menu `defvar' and make direct changes there.
108 ;; If you want to make it all with lisp, here is example which
109 ;; replaces 2 items from the menu
113 ;; (let ((key (car x)))
115 ;; 'ti::menu-:menu-sample nil 'delete) ;; Remove old
117 ;; (ti::menu-add 'ti::menu-:menu-sample key (cdr x))))
118 ;; '((?1 . ( (my-1 1 2 3))) ;; New menu item replacements
119 ;; (?2 . ( (my-2 1 2 3)))))
121 ;; (ti::menu-set-doc-string
122 ;; 'ti::menu-:menu-sample "?)help, 1)my1 2)my2")
126 ;; The easiest way to get a hold on the echo menu is that you try it.
127 ;; Follow these steps. Then you're ready to make your own menus.
129 ;; . Load this file. M-x load-library tinylibmenu.el
130 ;; . Start menu with `M-x' `ti::menu-menu-default'
131 ;; . Press key `?' or `h' to get help and `q' to quit menu.
132 ;; . Try offered choices
142 (autoload 'ignore-errors "cl-macs" nil 'macro))
145 ;;{{{ setup: variables
147 (defvar ti::menu-:load-hook nil
148 "*Hook that is run when package has been loaded.")
150 (defvar ti::menu-:prefix-arg nil
151 "Prefix arg when menu is called.")
153 ;; This is just an example, not a user variable.
154 ;; This is how you use the package
155 ;; NOTE: put the help into the documentation string. Like
156 ;; in variable ti::menu-:menu-mode.
158 (defconst ti::menu-:menu-sample
159 '("?)help, 1)test1, 2)test2, m)ode, u)ndefined , e)val. q)uit"
160 ((?1 . ( (ti::menu-test1 1 2 3))) ;this does not have FLAG
161 (?2 . (t (ti::menu-test2))) ;FLAG used.
162 (?m . ti::menu-:menu-mode)
163 (?u . ti::menu-:menu-not-exist) ;this variable does not exist :-)
165 (message "menu item evaled. Pfx: '%s' "
166 (prin1-to-string ti::menu-:prefix-arg))
168 "*This is documentation string of variable `ti::menu-:menu-sample'.
169 The menu help is put here.
171 Reserved menu keys (characters)
173 `q' and `Q' are reserved for quitting the menu prompt.
174 `?' anf `h' are reserved for help.
176 Menu structure is as follows
178 FLAG is optional. If non-nil, menu should be shown after
179 function has completed. If FLAG is missing, the menu is not displayed
180 after the function call. (that is: call function and exit menu)
182 The DISPLAYED-MENU-STRING is evaled, so it can contain any lisp expression
185 Below you see 3 different ways to define one menu element.
189 DISPLAYED-MENU-STRING
190 ((CHARACTER-KEY . ANOTHER-MENU-VARIABLE-SYMBOL)
191 (CHARACTER-KEY . ([FLAG] (FUNCTION-NAME PARAMETER PARAMETER...)))
192 (CHARACTER-KEY . ([FLAG] (FORM-TO-EVAL)))
194 \" MENU HELP RESIDES IN THE DOCUMENTATION STRING\")")
196 ;; This is just an example how you could utilize the prefix arguments.
198 ;;(defconst ti::menu-:menu-mail
199 ;; '((if current-prefix-arg
200 ;; "View mailbox read-only: E)macs M)ailbox P)erl R)ead/write"
201 ;; "View mailbox: E)macs M)ailbox P)erl R)ead-only")
202 ;; ((?e . ( (vm-visit-folder "~/mail/emacs" current-prefix-arg)))
203 ;; (?m . ( (call-interactively 'vm)))
204 ;; (?p . ( (vm-visit-folder "~/mail/perl" current-prefix-arg)))
205 ;; (?r . (t(setq current-prefix-arg (if current-prefix-arg nil '(4)))))
207 ;; "Select a mailbox to visit")
209 ;; This is just an example, not a user variable.
211 (defconst ti::menu-:menu-mode
212 '("Press ?/ cC)++ l)isp tT)ext f)undamental p)icture F0ill O)font"
215 (?l . ( (lisp-mode)))
216 (?t . ( (text-mode)))
217 (?T . ( (indented-text-mode)))
218 (?f . ( (fundamental-mode)))
219 (?p . ( (picture-mode)))
220 (?F . (t (auto-fill-mode)))
221 (?O . (t (font-lock-mode)))
222 (?/ . ti::menu-:menu-sample))) ;back to ROOT menu
228 l = turn on lisp mode
229 t = turn on text mode
230 T = turn on indented text mode
231 f = turn on fundamental mode
232 p = turn on picture mode
236 F = turn on auto fill mode
237 O = turn on f(o)nt lock mode
240 / = Return to root menu")
242 (defvar ti::menu-:menu 'ti::menu-:menu-sample
243 "*Variable holding the default root menu.")
247 ;;; ########################################################### &Funcs ###
249 ;;{{{ code: test funcs
251 (defun ti::menu-test1 (&optional arg1 arg2 arg3)
252 "Sample Menu test function with ARG1 ARG2 ARG3."
253 (message (format "function 1 called with args: %s %s %s" arg1 arg2 arg3)))
255 (defun ti::menu-test2 ()
256 "Sample Menu test function."
257 (message (format "function 2 called"))
261 ;;{{{ menu item add, delete
263 ;;; ------------------------------------------------------------- &add ---
266 (defun ti::menu-add (menu-symbol ch cell &optional delete)
267 "Add to menu MENU-SYMBOL elt (CH . CELL). Optionally DELETE.
271 (ti::menu-add 'ti::menu-:menu-sample ?2 nil 'delete)
272 (ti::menu-add 'ti::menu-:menu-sample ?t '( (my-test 1 2 3)))
276 nil no add done due to existing CELL
277 no remove due to non-existing CELL"
278 (let* ((menu (symbol-value menu-symbol))
283 (setq elt (assq ch list))
288 (setq list (delete elt list))
289 (set menu-symbol (list doc list))))
291 (not elt)) ;not already exist?
292 (setq ret (cons ch cell))
294 (set menu-symbol (list doc list))))
297 ;;; ----------------------------------------------------------------------
300 (defun ti::menu-set-doc-string (menu-symbol doc-string)
301 "Use MENU-SYMBOL and set its DOC-STRING.
305 (ti::menu-set-doc-string 'ti::menu-:menu-sample \"?=help, 1=test1, t=myTest\")"
306 (let* ((menu (symbol-value menu-symbol)))
307 ;; It's better to check that the arg is right; setcar won't
309 (if (not (stringp doc-string))
310 (error "timu: need string."))
311 (setcar menu doc-string)
312 (set menu-symbol menu)))
318 ;;; ----------------------------------------------------------------------
320 (defun ti::menu-read-char-exclusive (msg)
321 "Aa `read-char-exclusive', but for Emacs and XEmacs. Display MSG."
322 (if (fboundp 'read-char-exclusive)
326 (read-char-exclusive))
328 (read-char-exclusive msg)))
331 ;;; ----------------------------------------------------------------------
333 (defun ti::menu-help-output (variable-symbol)
334 "Write doctring, ie Menu help, to the *Help* buffer"
335 (with-output-to-temp-buffer "*Help*"
337 (documentation-property
339 'variable-documentation))))
341 ;;; ----------------------------------------------------------------------
342 ;;; - This is only simple help. You can't resize the window etc...
344 (defun ti::menu-help (menu-sym)
345 "Show menu help of MENU-SYM.
346 MENU-SYM can variable symbol, whose documentaion is displayed or
349 The help commands are:
353 (let* ((msg "Help: space or n = next, backspace or p = prev, q = quit")
354 (oframe (selected-frame))
356 (docs (or (documentation-property
357 menu-sym 'variable-documentation)
358 (and (fboundp menu-sym)
359 (documentation menu-sym))))
364 (unwind-protect ;make sure the help buffer is deleted
366 (save-window-excursion
367 ;; We have to save the source window config above
368 ;; Be sure this frame is non-dedicated.
369 (if (window-dedicated-p (selected-window))
374 (get-buffer-window (current-buffer)))
376 ;; now we may be in another frame; save it's configuration
378 (save-window-excursion
379 (with-output-to-temp-buffer buffer (princ docs))
380 (select-window (get-buffer-window buffer))
381 ;; This is simplest way to resize help window
383 (setq step (1- (window-height)))
384 ;; Now scroll the help
387 (setq ch (char-to-string
388 (ti::menu-read-char-exclusive msg)))
391 ;; 127 = backspace in windowed
393 ((member ch '("p" "P" "\177" "\b"))
394 (ignore-errors (scroll-down step)))
396 ((member ch '("n" "N" " "))
397 (ignore-errors (scroll-up step))))))))
398 (if (and (not (null oframe))
400 (if (framep (setq oframe (raise-frame oframe)))
401 (select-frame oframe)))
402 (kill-buffer buffer)))
404 (message "Sorry, no help defined.")
409 ;;; ----------------------------------------------------------------------
412 (defun ti::menu-menu (menu-symbol &optional pfx-arg)
413 "The menu navigation engine.
417 MENU-SYMBOL variable symbol containing menu items
418 PFX-ARG the prefix arg user ppossibly passed to menu
422 `ti::menu-:menu-sample' Show how the menu is constructed.
423 `ti::menu-:prefix-arg' Copy of current prefix arg"
424 (let* ((var menu-symbol)
425 (m (eval var)) ;menu content
427 (current-prefix-arg pfx-arg) ;set for menu functions
433 (setq ti::menu-:prefix-arg pfx-arg)
435 (setq prompt (eval (nth 0 m))
437 (replace-regexp-in-string "\r" "" prompt))
439 (when (or (not (stringp prompt))
440 (not (string-match "[^ \t\r\n]" prompt)))
441 (error "Menu structure error; no prompt: %s" m))
442 ;; moving the mouse and reading with read-char would break. Use above.
443 (setq ch (char-to-string (ti::menu-read-char-exclusive prompt)))
444 (setq eval-form nil) ;clear this always
445 ;; .................................................. what ch ? ...
447 ((member ch '("q" "Q" "\e")) ;quit
449 ((string= ch "?") ;handle help
451 ((string= ch "h") ;handle help
452 (ti::menu-help-output var)
454 ((setq elt (assq (string-to-char ch) alist))
456 ;; ................................. new menu or call function ...
459 (if (not (boundp elt))
460 (error (format "Menu variable does not exist: %s" elt))
461 ;; replace with another menu
463 m (symbol-value elt))))
464 ;; ..................................................... list ...
465 ((and (not (null elt))
467 (cond ;See if there is flag.
468 ((and (eq 2 (length elt))
469 (equal 'quote (car elt)))
470 ;; A menu entry is not right
472 ;; '(?x . 'my-symbol)
473 ;; --> (quote my-symbol)
475 "Menu error, not a symbol. Use cons or list: %s" elt))
478 (setq elt (nth 1 elt)))
481 (setq elt (nth 0 elt))))
483 ((fboundp (car elt)) ;is first element a function ?
487 (error "Menu structure error %s %s"
488 (assq (string-to-char ch) alist)
491 ;; ch not found from list, keep looping
492 (sit-for 0.3))) ;flash the echo area
493 (message "") ;clear echo area
497 ;;; ----------------------------------------------------------------------
498 ;;; - This is user function
500 (defun ti::menu-menu-default (&optional arg)
501 "Call echo area menu with prefix ARG.
502 Please read the documentation of variable `ti::menu-:menu-sample' to see
503 the structure of menu.
505 Menu pointed by `ti::menu-:menu' is used and PREFIX-ARG is passed to menu engine
509 `ti::menu-:menu-sample'"
511 (ti::menu-menu ti::menu-:menu arg))
515 (provide 'tinylibmenu)
516 (run-hooks 'ti::menu-:load-hook)
518 ;;; tinylibmenu.el ends here