1 ;;; menudiag.el --- Minibuffer Menu System
3 ;; Copyright (C) 1999, 2000 Free Software Foundation, Inc
5 ;; Author: NIIBE Yutaka <gniibe@chroot.org>
7 ;; Maintainer: TOMURA Satoru <tomura@etl.go.jp>
9 ;; Keywords: mule, multilingual, input method
11 ;; This file is part of EGG.
13 ;; EGG is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; EGG is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
29 ;; Inspired by the menu subsystem of EGG V3.0
31 ;; Completely different implementation, using keymap and recursive edit.
36 ;; Data structure of MENU
38 ;; <menu> ::= ( menu <prompt> <item-list> )
39 ;; <prompt> ::= STRING
40 ;; <item-list> ::= ( <item> ... )
41 ;; <item> ::= <string> | ( <string> . <value> )
43 ;; <value> ::= <menu> | INTEGER | STRING (Must *NOT* cons cell)
48 ;; <selection-list> ::= ( <line>... )
49 ;; <line> ::= ( <item>... )
52 (defgroup menudiag nil
53 "Input Translation System of Tamagotchy"
56 (defcustom menudiag-select-without-return nil
57 "*Number keys not only goes the item, but also select the item, if non-NIL."
58 :group 'menudiag :type 'boolean)
60 (defvar menudiag-mode-map
61 (let ((map (make-sparse-keymap))
65 (define-key map (char-to-string ch) 'menudiag-goto-item)
69 (define-key map (char-to-string ch) 'menudiag-goto-item)
73 (define-key map (char-to-string ch) 'menudiag-goto-item)
75 (define-key map "\C-a" 'menudiag-beginning-of-line)
76 (define-key map "\C-e" 'menudiag-end-of-line)
77 (define-key map "\M-<" 'menudiag-beginning-of-items)
78 (define-key map "\M->" 'menudiag-end-of-items)
79 (define-key map "\C-f" 'menudiag-forward-item)
80 (define-key map "\C-b" 'menudiag-backward-item)
81 (define-key map "\C-n" 'menudiag-next-line)
82 (define-key map "\C-p" 'menudiag-previous-line)
83 (define-key map "\C-]" 'menudiag-exit)
84 (define-key map "\C-g" 'menudiag-exit-one-level)
85 (define-key map "\C-l" 'menudiag-redraw)
86 (define-key map "\C-m" 'menudiag-select-this-item)
87 (define-key map "?" 'menudiag-list-other-window)
88 (define-key map [return] 'menudiag-select-this-item)
89 (define-key map [left] 'menudiag-backward-item)
90 (define-key map [right] 'menudiag-forward-item)
91 (define-key map [up] 'menudiag-previous-line)
92 (define-key map [down] 'menudiag-next-line)
93 (define-key map [menudiag-continuation] 'menudiag-follow-continuation)
94 (define-key map [t] 'undefined)
98 (defun menudiag-menu-p (item)
99 (and (consp item) (eq 'menu (car item))))
101 (defun menudiag-item-string (item)
104 (format "%s" (car item))))
106 (defun menudiag-item-value (item)
111 (defsubst menudiag-item-width (item)
112 (+ 4 (string-width (menudiag-item-string item))))
114 (defvar menudiag-window-conf nil)
116 (defun menudiag-make-selection-list (item-list line-width)
122 (let* ((item (car item-list))
123 (item-width (menudiag-item-width item)))
124 (if (and line (or (>= (+ width item-width) line-width)
126 (setq l (cons (reverse line) l)
130 (setq line (cons item line)
131 width (+ width (menudiag-item-width item))
133 item-list (cdr item-list))))
135 (reverse (cons (reverse line) l))
139 (defun menudiag-select (menu &optional menudiag-continuation return-contin)
140 (let ((enable-recursive-minibuffers t)
142 (setq menudiag-window-conf nil)
143 (if menudiag-continuation
144 (setq unread-command-events (cons 'menudiag-continuation
145 unread-command-events)))
146 (if (not return-contin)
148 (menudiag-select-internal menu)
154 (defun menudiag-get-value (continuation)
155 (menudiag-item-value (nth (1- (length continuation)) continuation)))
157 (defun menudiag-follow-continuation ()
159 (let ((item (car menudiag-continuation)))
160 (setq menudiag-continuation (cdr menudiag-continuation))
161 (if menudiag-continuation
162 (setq unread-command-events (cons 'menudiag-continuation
163 unread-command-events)))
164 (if (eq item 'menudiag-list-all)
165 (menudiag-list-other-window)
168 (if (eq item (nth pos-in-line line))
170 (menudiag-forward-item)
171 (if (and (= linepos 0) (= pos-in-line 0))
172 (error "no such item: %s" (menudiag-item-string item))))))
173 (let ((v (menudiag-item-value item)))
174 (if (menudiag-menu-p v)
177 (menudiag-select-internal v)
180 (setq value (cons item value)))
181 (if done (menudiag-exit-minibuffer))))))))
183 (defun menudiag-select-internal (menu)
184 (let* ((minibuf-prompt (nth 1 menu))
185 (current-items (nth 2 menu))
187 (menudiag-make-selection-list current-items
188 (- (window-width (minibuffer-window))
189 (string-width minibuf-prompt))))
190 (line (car selection-list))
192 (menudiag-make-menu-formatted-string line)))
195 (read-from-minibuffer minibuf-prompt
196 (cons minibuf-contents 3)
197 menudiag-mode-map))))
199 (defun menudiag-make-menu-formatted-string (item-list)
202 (function (lambda (item)
204 (format " %c.%s" (menudiag-item-num-to-char i)
205 (menudiag-item-string item))))
209 ;; ITEM No --> Character
210 (defun menudiag-item-num-to-char (num)
213 (setq char (+ ?0 num)))
215 (setq char (+ ?a (- num 10))))
219 ;; Character --> ITEM No
220 (defun menudiag-char-to-item-num (char)
222 (cond ((and (<= ?0 ch) (<= ch ?9))
223 (setq num (- ch ?0)))
224 ((and (<= ?a ch) (<= ch ?z))
225 (setq num (+ 10 (- ch ?a))))
226 ((and (<= ?A ch) (<= ch ?Z))
227 (setq num (+ 10 (- ch ?A))))
231 (defun menudiag-goto-item ()
233 (let ((ch last-command-char)
235 (setq n (menudiag-char-to-item-num ch))
236 (if (>= n (length line))
237 (error "No such item")
238 (menudiag-goto-item-internal n)
239 (if menudiag-select-without-return
240 (menudiag-select-this-item)))))
242 (defun menudiag-goto-item-internal (n)
243 (let ((old-pos-in-line pos-in-line)
247 (while (< i pos-in-line)
248 (setq p (+ p (length (menudiag-item-string (nth i line))) 4))
252 (defun menudiag-beginning-of-items ()
254 (menudiag-goto-line 0)
255 (menudiag-beginning-of-line))
257 (defun menudiag-end-of-items ()
259 (menudiag-goto-line (1- (length selection-list)))
260 (menudiag-end-of-line))
262 (defun menudiag-beginning-of-line ()
264 (menudiag-goto-item-internal 0))
266 (defun menudiag-end-of-line ()
268 (menudiag-goto-item-internal (1- (length line))))
270 ;; Should retain compatibility. Must.
272 ;;(defun menudiag-forward-item ()
274 ;; (if (< pos-in-line (1- (length line)))
275 ;; (menudiag-goto-item-internal (1+ pos-in-line))
276 ;; (if (>= linepos (1- (length selection-list)))
277 ;; (signal 'end-of-buffer "")
278 ;; (menudiag-goto-line (1+ linepos))
279 ;; (menudiag-beginning-of-line))))
281 ;;(defun menudiag-backward-item ()
283 ;; (if (< 0 pos-in-line)
284 ;; (menudiag-goto-item-internal (1- pos-in-line))
286 ;; (signal 'beginning-of-buffer "")
287 ;; (menudiag-goto-line (1- linepos))
288 ;; (menudiag-end-of-line))))
290 ;;(defun menudiag-goto-line (n)
291 ;; (if (or (>= n (length selection-list)) (< n 0))
293 ;; (setq line (nth n selection-list)
295 ;; (delete-region (point-min) (point-max))
296 ;; (insert (menudiag-make-menu-formatted-string line))))
299 (defun menudiag-forward-item ()
301 (if (< pos-in-line (1- (length line)))
302 (menudiag-goto-item-internal (1+ pos-in-line))
303 (if (>= linepos (1- (length selection-list)))
304 (menudiag-goto-line 0)
305 (menudiag-goto-line (1+ linepos)))
306 (menudiag-beginning-of-line)))
308 (defun menudiag-backward-item ()
310 (if (< 0 pos-in-line)
311 (menudiag-goto-item-internal (1- pos-in-line))
313 (menudiag-goto-line (1- (length selection-list)))
314 (menudiag-goto-line (1- linepos)))
315 (menudiag-end-of-line)))
317 (defun menudiag-goto-line (n)
319 ((>= n (length selection-list))
322 (setq n (1- (length selection-list)))))
323 (setq line (nth n selection-list)
325 (delete-region (point-min) (point-max))
326 (insert (menudiag-make-menu-formatted-string line)))
328 (defun menudiag-next-line ()
330 (menudiag-goto-line (1+ linepos))
331 (if (< pos-in-line (length line))
332 (menudiag-goto-item-internal pos-in-line)
333 (menudiag-end-of-line)))
335 (defun menudiag-previous-line ()
337 (menudiag-goto-line (1- linepos))
338 (if (< pos-in-line (length line))
339 (menudiag-goto-item-internal pos-in-line)
340 (menudiag-end-of-line)))
342 (defun menudiag-redraw ()
344 (menudiag-goto-line linepos)
345 (menudiag-goto-item-internal pos-in-line))
347 (defun menudiag-exit-one-level ()
349 (menudiag-exit-minibuffer))
351 (defun menudiag-exit ()
354 (menudiag-exit-minibuffer))
356 (defun menudiag-select-this-item ()
358 (let* ((item (nth pos-in-line line))
359 (v (menudiag-item-value item)))
360 (if (menudiag-menu-p v)
363 (menudiag-restore-window)
364 (menudiag-select-internal v)
367 (setq value (cons item value)))
368 (if done (menudiag-exit-minibuffer)))
370 (setq value (menudiag-item-value item))
371 (setq value (cons item nil)))
373 (menudiag-exit-minibuffer))))
375 (defconst menudiag-selection-map
376 (let ((map (make-sparse-keymap)))
377 (define-key map [right] 'next-completion)
378 (define-key map [left] 'previous-completion)
379 (define-key map "\r" 'menudiag-choose-item)
380 (define-key map [mouse-2] 'menudiag-mouse-choose-item)
383 (defvar menudiag-selection-buffer nil)
384 (make-variable-buffer-local 'menudiag-selection-buffer)
385 (put 'menudiag-selection-buffer 'permanent-local t)
387 (defvar menudiag-selection-main-buffer nil)
388 (make-variable-buffer-local 'menudiag-selection-main-buffer)
389 (put 'menudiag-selection-main-buffer 'permanent-local t)
391 (defun menudiag-selection-mode ()
393 (kill-all-local-variables)
394 (make-local-variable 'inhibit-read-only)
395 (setq buffer-read-only t
396 inhibit-read-only nil)
397 (use-local-map menudiag-selection-map)
398 (setq mode-name "Menudiag Selection")
399 (setq major-mode 'menudiag-selection-mode))
401 (defun menudiag-max-item-width (item-list)
404 (setq max (max max (menudiag-item-width (car item-list)))
405 item-list (cdr item-list)))
408 (defun menudiag-buffer-show-function ()
409 (let* ((items current-items)
410 (digits (length (concat (length items))))
411 (columns (max 1 (/ (window-width (minibuffer-window))
412 (+ digits (menudiag-max-item-width items)))))
413 (width (/ (window-width (minibuffer-window)) columns))
418 str (format (concat "%" digits "d. %s")
419 n (menudiag-item-string (car items))))
421 (set-text-properties p (point) '(mouse-face highlight))
427 (insert (make-string (- width (string-width str)) ?\ ))
430 (goto-char (point-min))
431 (set-buffer-modified-p nil)
432 (menudiag-selection-mode)))
434 (defun menudiag-buffer-name (prompt)
435 (let ((len (1- (length prompt))))
436 (if (= (aref prompt len) ?:) (substring prompt 0 len) prompt)))
438 (defun menudiag-list-other-window ()
440 (let ((temp-buffer-show-hook 'menudiag-buffer-show-function)
441 (main-buf (current-buffer)))
442 (setq menudiag-window-conf (current-window-configuration))
443 (with-output-to-temp-buffer (menudiag-buffer-name minibuf-prompt)
444 (setq menudiag-selection-buffer standard-output))
445 (set-buffer menudiag-selection-buffer)
446 (setq menudiag-selection-main-buffer main-buf)))
448 (defun menudiag-choose-item ()
450 (let ((org-buf menudiag-selection-main-buffer)
451 (sel-buf (current-buffer))
452 (item-list selection-list)
456 (setq tmp-buf (current-buffer))
458 (setq completion-reference-buffer tmp-buf)
461 (setq n (string-to-int (buffer-string))))
462 (pop-to-buffer org-buf)
463 (while (and item-list (>= (- n (length (car item-list))) 0))
465 n (- n (length (car item-list)))
466 item-list (cdr item-list)))
467 (menudiag-goto-line l)
468 (menudiag-goto-item-internal n)
469 (menudiag-select-this-item)))
471 (defun menudiag-mouse-choose-item (event)
473 (set-buffer (window-buffer (car (nth 1 event))))
474 (let ((org-buf menudiag-selection-main-buffer)
475 (sel-buf (current-buffer))
476 (item-list selection-list)
480 (setq tmp-buf (current-buffer))
482 (setq completion-reference-buffer tmp-buf)
483 (mouse-choose-completion event)
485 (setq n (string-to-int (buffer-string))))
486 (pop-to-buffer org-buf)
487 (while (and item-list (>= (- n (length (car item-list))) 0))
489 n (- n (length (car item-list)))
490 item-list (cdr item-list)))
491 (menudiag-goto-line l)
492 (menudiag-goto-item-internal n)
493 (menudiag-select-this-item)))
495 (defun menudiag-restore-window ()
496 (if menudiag-window-conf
498 (set-window-configuration menudiag-window-conf)
499 (setq menudiag-window-conf nil)
500 (kill-buffer menudiag-selection-buffer))))
502 (defun menudiag-exit-minibuffer ()
503 (and menudiag-window-conf (menudiag-restore-window))
507 ;;; menudiag.el ends here.