1 ;;; menudiag.el --- Minibuffer Menu System
3 ;; Copyright (C) 1997 Mule Project, Powered by Electrotechnical
5 ;; Project Leader: Satoru Tomura <tomura@etl.go.jp>
7 ;; Author: NIIBE Yutaka <gniibe@mri.co.jp>
8 ;; Maintainer: NIIBE Yutaka <gniibe@mri.co.jp>
10 ;; This file will be part of GNU Emacs (in future).
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
28 ;; Inspired by the menu subsystem of EGG V3.0
30 ;; Completely different implementation, using keymap and recursive edit.
35 ;; Data structure of MENU
37 ;; <menu> ::= ( menu <prompt> <item-list> )
38 ;; <prompt> ::= STRING
39 ;; <item-list> ::= ( <item> ... )
40 ;; <item> ::= <string> | ( <string> . <value> )
42 ;; <value> ::= <menu> | INTEGER | STRING (Must *NOT* cons cell)
47 ;; <selection-list> ::= ( <line>... )
48 ;; <line> ::= ( <item>... )
51 (defvar menudiag-mode-map
52 (let ((map (make-keymap))
55 (define-key map (char-to-string ch) 'undefined)
59 (define-key map (char-to-string ch) 'menudiag-goto-item)
63 (define-key map (char-to-string ch) 'menudiag-goto-item)
67 (define-key map (char-to-string ch) 'menudiag-goto-item)
69 (define-key map "\C-a" 'menudiag-beginning-of-line)
70 (define-key map "\C-e" 'menudiag-end-of-line)
71 (define-key map "\C-f" 'menudiag-forward-item)
72 (define-key map "\C-b" 'menudiag-backward-item)
73 (define-key map "\C-n" 'menudiag-next-line)
74 (define-key map "\C-p" 'menudiag-previous-line)
75 (define-key map "\C-]" 'menudiag-exit)
76 (define-key map "\C-g" 'menudiag-exit-one-level)
77 (define-key map "\C-l" 'menudiag-redraw)
78 (define-key map "\C-m" 'menudiag-select-this-item)
79 (define-key map [return] 'menudiag-select-this-item)
80 (define-key map [left] 'menudiag-backward-item)
81 (define-key map [right] 'menudiag-forward-item)
82 (define-key map [up] 'menudiag-previous-line)
83 (define-key map [down] 'menudiag-next-line)
84 (define-key map [menudiag-continuation] 'menudiag-follow-continuation)
88 (defun menudiag-menu-p (item)
89 (and (consp item) (eq 'menu (car item))))
91 (defun menudiag-item-string (item)
94 (format "%s" (car item))))
96 (defun menudiag-item-value (item)
101 (defsubst menudiag-item-width (item)
102 (+ 4 (string-width (menudiag-item-string item))))
104 (defun menudiag-make-selection-list (item-list line-width)
109 (let* ((item (car item-list))
110 (item-width (menudiag-item-width item)))
111 (if (and line (>= (+ width item-width) line-width))
112 (setq l (cons (reverse line) l)
115 (setq line (cons item line)
116 width (+ width (menudiag-item-width item))
117 item-list (cdr item-list))))
119 (reverse (cons (reverse line) l))
123 (defun menudiag-select (menu &optional menudiag-continuation return-contin)
125 (if menudiag-continuation
126 (setq unread-command-events (cons 'menudiag-continuation
127 unread-command-events)))
128 (if (not return-contin)
130 (if (catch 'menudiag-exit
131 (menudiag-select-internal menu))
136 (defun menudiag-get-value (continuation)
137 (menudiag-item-value (nth (1- (length continuation)) continuation)))
139 (defun menudiag-follow-continuation ()
141 (let ((item (car menudiag-continuation)))
142 (setq menudiag-continuation (cdr menudiag-continuation))
143 (if menudiag-continuation
144 (setq unread-command-events (cons 'menudiag-continuation
145 unread-command-events)))
148 (if (eq item (nth pos-in-line line))
150 (menudiag-forward-item))))
151 (let ((v (menudiag-item-value item)))
152 (if (menudiag-menu-p v)
155 (menudiag-select-internal v)
158 (setq value (cons item value))))))))
160 (defun menudiag-select-internal (menu)
161 (let* ((minibuf-prompt (nth 1 menu))
163 (menudiag-make-selection-list (nth 2 menu)
164 (- (window-width (minibuffer-window))
165 (string-width minibuf-prompt))))
166 (line (car selection-list))
168 (menudiag-make-menu-formatted-string line)))
171 (read-from-minibuffer minibuf-prompt
172 (cons minibuf-contents 3)
173 menudiag-mode-map))))
175 (defun menudiag-make-menu-formatted-string (item-list)
178 (function (lambda (item)
180 (format " %x.%s" i (menudiag-item-string item))))
183 (defun menudiag-goto-item ()
185 (let ((ch last-command-char)
187 (cond ((and (<= ?0 ch) (<= ch ?9))
189 ((and (<= ?a ch) (<= ch ?z))
190 (setq n (+ 10 (- ch ?a))))
191 ((and (<= ?A ch) (<= ch ?Z))
192 (setq n (+ 10 (- ch ?A)))))
193 (if (>= n (length line))
194 (error "No such item")
195 (menudiag-goto-item-internal n))))
197 (defun menudiag-goto-item-internal (n)
198 (let ((old-pos-in-line pos-in-line)
202 (while (< i pos-in-line)
203 (setq p (+ p (length (menudiag-item-string (nth i line))) 4))
207 (defun menudiag-beginning-of-line ()
209 (menudiag-goto-item-internal 0))
211 (defun menudiag-end-of-line ()
213 (menudiag-goto-item-internal (1- (length line))))
215 (defun menudiag-forward-item ()
217 (if (< pos-in-line (1- (length line)))
218 (menudiag-goto-item-internal (1+ pos-in-line))
219 (if (>= linepos (1- (length selection-list)))
220 (signal 'end-of-buffer "")
221 (menudiag-goto-line (1+ linepos))
222 (menudiag-beginning-of-line))))
224 (defun menudiag-backward-item ()
226 (if (< 0 pos-in-line)
227 (menudiag-goto-item-internal (1- pos-in-line))
229 (signal 'beginning-of-buffer "")
230 (menudiag-goto-line (1- linepos))
231 (menudiag-end-of-line))))
233 (defun menudiag-goto-line (n)
234 (if (or (>= n (length selection-list)) (< n 0))
236 (setq line (nth n selection-list)
238 (delete-region (point-min) (point-max))
239 (insert (menudiag-make-menu-formatted-string line))))
241 (defun menudiag-next-line ()
243 (menudiag-goto-line (1+ linepos))
244 (if (< pos-in-line (length line))
245 (menudiag-goto-item-internal pos-in-line)
246 (menudiag-end-of-line)))
248 (defun menudiag-previous-line ()
250 (menudiag-goto-line (1- linepos))
251 (if (< pos-in-line (length line))
252 (menudiag-goto-item-internal pos-in-line)
253 (menudiag-end-of-line)))
255 (defun menudiag-redraw ()
257 (menudiag-goto-line linepos)
258 (menudiag-goto-item-internal pos-in-line))
260 (defun menudiag-exit-one-level ()
264 (defun menudiag-exit ()
266 (throw 'menudiag-exit t))
268 (defun menudiag-select-this-item ()
270 (let* ((item (nth pos-in-line line))
271 (v (menudiag-item-value item)))
272 (if (menudiag-menu-p v)
275 (menudiag-select-internal v)
278 (setq value (cons item value))))
280 (setq value (menudiag-item-value item))
281 (setq value (cons item nil)))
282 (throw 'menudiag-exit nil))))
285 ;;; menudiag.el ends here.