Initial Commit
[packages] / xemacs-packages / vm / lisp / vm-minibuf.el
1 ;;; vm-minibuf.el --- Minibuffer read functions for VM
2 ;;
3 ;; Copyright (C) 1993, 1994 Kyle E. Jones
4 ;; Copyright (C) 2003-2006 Robert Widhopf-Fenk
5 ;;
6 ;; This program is free software; you can redistribute it and/or modify
7 ;; it under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation; either version 2 of the License, or
9 ;; (at your option) any later version.
10 ;;
11 ;; This program is distributed in the hope that it will be useful,
12 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ;; GNU General Public License for more details.
15 ;;
16 ;; You should have received a copy of the GNU General Public License along
17 ;; with this program; if not, write to the Free Software Foundation, Inc.,
18 ;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 ;;; Code:
21
22 (defun vm-minibuffer-complete-word (&optional exiting)
23   (interactive)
24   (let ((opoint (point))
25         ;; In Emacs 21, during a minibuffer read the minibuffer
26         ;; contains the prompt as buffer text and that text is
27         ;; read only.  So we can no longer assume that (point-min)
28         ;; is where the user-entered text starts and we must avoid
29         ;; modifying that prompt text.  The value we want instead
30         ;; of (point-min) is (minibuffer-prompt-end).
31         (point-min (if (fboundp 'minibuffer-prompt-end)
32                        (minibuffer-prompt-end)
33                      (point-min)))
34         (case-fold-search completion-ignore-case)
35         trimmed-c-list c-list beg end diff word word-prefix-regexp completion)
36     ;; find the beginning and end of the word we're trying to complete
37     (if (or (eobp) (memq (following-char) '(?\t ?\n ?\ )))
38         (progn
39           (skip-chars-backward " \t\n")
40           (and (not (eobp)) (forward-char))
41           (setq end (point)))
42       (skip-chars-forward "^ \t\n")
43       (setq end (point)))
44     ;; if there can't be multiple words in the input the beginning
45     ;; of the word must be at point-min.
46     (if (not vm-completion-auto-space)
47         (setq beg point-min)
48       (skip-chars-backward "^ \t\n")
49       (setq beg (point)))
50     (goto-char opoint)
51     ;; copy the word into a string
52     (setq word (buffer-substring beg end))
53     ;; trim the completion list down to just likely candidates
54     ;; then convert it to an alist.
55     (setq word-prefix-regexp (concat "^" (regexp-quote word))
56           trimmed-c-list (vm-delete-non-matching-strings
57                           word-prefix-regexp
58                           vm-minibuffer-completion-table)
59           trimmed-c-list (sort trimmed-c-list (function string-lessp))
60           trimmed-c-list (mapcar 'list trimmed-c-list)
61           c-list (mapcar 'list vm-minibuffer-completion-table))
62     ;; Try the word against the completion list.
63     (and trimmed-c-list
64          (setq completion (try-completion word trimmed-c-list)))
65     ;; If completion is nil, figure out what prefix of the word would prefix
66     ;; something in the completion list... but only if the user is interested.
67     (if (and (null completion) vm-completion-auto-correct c-list)
68         (let ((i -1))
69           (while (null (setq completion
70                              (try-completion (substring word 0 i) c-list)))
71             (vm-decrement i))
72           (setq completion (substring word 0 i))))
73     ;; If completion is t, we had a perfect match already.
74     (if (eq completion t)
75         (cond (vm-completion-auto-space
76                (goto-char end)
77                (insert " "))
78               (t
79                (and (not exiting)
80                     (vm-minibuffer-completion-message "[Sole completion]"))))
81       ;; Compute the difference in length between the completion and the
82       ;; word.  A negative difference means no match and the magnitude
83       ;; indicates the number of chars that need to be shaved off the end
84       ;; before a match will occur.  A positive difference means a match
85       ;; occurred and the magnitude specifies the number of new chars that
86       ;; can be appended to the word as a completion.
87       ;;
88       ;; `completion' can be nil here, but the code works anyway because
89       ;; (length nil) still equals 0!
90       (setq diff (- (length completion) (length word)))
91       (cond
92        ;; We have some completion chars.  Insert them.
93        ((or (> diff 0) 
94             (and completion (zerop diff) (not (string-equal completion word))))
95         (goto-char end)
96         (delete-char (- (length word)))
97         (insert completion)
98         (if (and vm-completion-auto-space
99                  (null (cdr trimmed-c-list)))
100             (insert " ")))
101        ((null completion)
102         (vm-minibuffer-completion-message "[No completion available]"))
103        ;; The word prefixed more than one string, but we can't complete
104        ;; any further.  Either give help or say "Ambiguous".
105        ((zerop diff)
106         (and (not exiting)
107              (cond ((> (length (car (car trimmed-c-list))) (length word))
108                     (if (null completion-auto-help)
109                         (vm-minibuffer-completion-message "[Ambiguous]")
110                       (vm-minibuffer-show-completions (sort
111                                                        (mapcar 'car
112                                                                trimmed-c-list)
113                                                        'string-lessp))))
114                    ((not (eq last-command 'vm-minibuffer-complete-word))
115                     (vm-minibuffer-completion-message
116                      "[Complete, but not unique]"))
117                    (vm-completion-auto-space
118                     (insert " ")))))
119        ;; The word didn't prefix anything... if vm-completion-auto-correct is
120        ;; non-nil strip the offending characters and try again.
121        (vm-completion-auto-correct
122         (goto-char end)
123         (delete-char diff)
124         (vm-minibuffer-complete-word exiting))
125        ;; if we're not auto-correcting and we're doing
126        ;; multi-word, just let the user insert a space.
127        (vm-completion-auto-space
128         (insert " "))
129        ;; completion utterly failed, tell the user so.
130        (t
131         (and (not exiting)
132              (vm-minibuffer-completion-message "[No match]")))))))
133
134 (defun vm-minibuffer-complete-word-and-exit ()
135   (interactive)
136   (vm-minibuffer-complete-word t)
137   (exit-minibuffer))
138
139 (defun vm-minibuffer-completion-message (string &optional seconds)
140   "Briefly display STRING to the right of the current minibuffer input.
141 Optional second arg SECONDS specifies how long to keep the message visible;
142 the default is 2 seconds.
143
144 A keypress causes the immediate erasure of the STRING, and return of control
145 to the calling program."
146   (let (omax (inhibit-quit t))
147     (save-excursion
148       (goto-char (point-max))
149       (setq omax (point))
150       (insert " " string))
151     (sit-for (or seconds 2))
152     (delete-region omax (point-max))))
153
154 (defun vm-minibuffer-replace-word (word)
155   (goto-char (point-max))
156   (skip-chars-backward "^ \t\n")
157   (delete-region (point) (point-max))
158   (insert word))
159
160 (defun vm-minibuffer-show-completions (list)
161   "Display LIST in a multi-column listing in the \" *Completions*\" buffer.
162 LIST should be a list of strings."
163   (save-excursion
164     (set-buffer (get-buffer-create " *Completions*"))
165     (setq buffer-read-only nil)
166     (use-local-map (make-sparse-keymap))
167     ;; ignore vm-mutable-* here.  the user shouldn't mind
168     ;; because when they exit the minibuffer the windows will be
169     ;; set right again.
170     (display-buffer (current-buffer))
171     (erase-buffer)
172     (insert "Possible completions are:\n")
173     (setq buffer-read-only t)
174     (vm-show-list list 'vm-minibuffer-replace-word
175                   (list (current-local-map) minibuffer-local-map))
176     (goto-char (point-min))))
177
178 (defun vm-show-list (list &optional function keymaps)
179   "Display LIST in a multi-column listing in the current buffer at point.
180 The current buffer must be displayed in some window at the time
181 this function is called.
182
183 LIST should be a list of strings.
184
185 Optional second argument FUNCTION will be called if the mouse is
186 clicked on one of the strings in the current buffer.  The string
187 clicked upon will be passed to FUNCTION as its sole argument.
188
189 Optional third argument KEYMAPS specifies a lists of keymaps
190 where the FUNCTION should be bound to the mouse clicks.  By
191 default the local keymap of the current buffer is used."
192   (or keymaps (setq keymaps (and (current-local-map)
193                                  (list (current-local-map)))))
194   (save-excursion
195     (let ((buffer-read-only nil)
196           (separation 3)
197           tabs longest columns list-length q i w start command
198           keymap)
199       (cond ((and function keymaps (vm-mouse-support-possible-p))
200              (setq command
201                    (list 'lambda '(e) '(interactive "e")
202                          (list 'let
203                                '((string (vm-mouse-get-mouse-track-string e)))
204                                (list 'and 'string (list function 'string)))))
205              (while keymaps
206                (setq keymap (car keymaps))
207                (cond ((vm-mouse-xemacs-mouse-p)
208                       (define-key keymap 'button1 command)
209                       (define-key keymap 'button2 command))
210                      ((vm-mouse-fsfemacs-mouse-p)
211                       (define-key keymap [down-mouse-1] 'ignore)
212                       (define-key keymap [drag-mouse-1] 'ignore)
213                       (define-key keymap [mouse-1] command)
214                       (define-key keymap [drag-mouse-2] 'ignore)
215                       (define-key keymap [down-mouse-2] 'ignore)
216                       (define-key keymap [mouse-2] command)))
217                (setq keymaps (cdr keymaps)))))
218       (setq list (sort (copy-sequence list) (function string-lessp))
219             w (vm-get-buffer-window (current-buffer))
220             q list
221             list-length 0
222             longest 0
223             columns (1- (window-width w)))
224       (while q
225         (setq longest (max longest (length (car q)))
226               list-length (1+ list-length)
227               q (cdr q)))
228       (setq tabs (/ list-length (/ columns (+ longest separation)))
229             tabs (+ tabs
230                     (if (zerop (% list-length
231                                   (/ columns (+ longest separation))))
232                         0
233                       1)))
234       (setq i 0)
235       (while (< i tabs)
236         (setq q (nthcdr i list))
237         (while q
238           (setq start (point))
239           (insert (car q))
240           (and function (vm-mouse-set-mouse-track-highlight start (point)))
241           (insert-char ?  (+ separation (- longest (length (car q)))))
242           (setq q (nthcdr tabs q)))
243         (setq i (1+ i))
244         (insert "\n")))))
245
246 (defun vm-minibuffer-completion-help ()
247   (interactive)
248   (let ((opoint (point))
249         c-list beg end word word-prefix-regexp)
250     ;; find the beginning and end of the word we're trying to complete
251     (if (or (eobp) (memq (following-char) '(?\t ?\n ?\ )))
252         (progn
253           (skip-chars-backward " \t\n")
254           (and (not (eobp)) (forward-char))
255           (setq end (point)))
256       (skip-chars-forward "^ \t\n")
257       (setq end (point)))
258     (skip-chars-backward "^ \t\n")
259     (setq beg (point))
260     (goto-char opoint)
261     ;; copy the word into a string
262     (setq word (buffer-substring beg end))
263     ;; trim the completion list down to just likely candidates
264     ;; then convert it to an alist.
265     (setq word-prefix-regexp (concat "^" (regexp-quote word))
266           c-list (vm-delete-non-matching-strings
267                   word-prefix-regexp
268                   vm-minibuffer-completion-table)
269           c-list (sort c-list (function string-lessp)))
270     (if c-list
271         (vm-minibuffer-show-completions c-list)
272       (vm-minibuffer-completion-message " [No match]"))))
273
274 (defun vm-keyboard-read-string (prompt completion-list &optional multi-word)
275   (let ((minibuffer-local-map (copy-keymap minibuffer-local-map))
276         (vm-completion-auto-space multi-word)
277         (vm-minibuffer-completion-table completion-list))
278     (define-key minibuffer-local-map "\t" 'vm-minibuffer-complete-word)
279     (define-key minibuffer-local-map " " 'vm-minibuffer-complete-word)
280     (define-key minibuffer-local-map "?" 'vm-minibuffer-completion-help)
281     (if (not multi-word)
282         (define-key minibuffer-local-map "\r"
283           'vm-minibuffer-complete-word-and-exit))
284     ;; evade the XEmacs dialog box, yeccch.
285     (let ((use-dialog-box nil))
286       (read-string prompt))))
287
288 (defvar last-nonmenu-event)
289
290 (defun vm-read-string (prompt completion-list &optional multi-word)
291   ;; handle alist
292   (if (consp (car completion-list))
293       (setq completion-list (nreverse (mapcar 'car completion-list))))
294   (if (and completion-list (vm-mouse-support-possible-here-p))
295       (cond ((and (vm-mouse-xemacs-mouse-p)
296                   (or (button-press-event-p last-command-event)
297                       (button-release-event-p last-command-event)
298                       (menu-event-p last-command-event)))
299              (vm-mouse-read-string prompt completion-list multi-word))
300             ((and (vm-mouse-fsfemacs-mouse-p)
301                   (listp last-nonmenu-event))
302              (vm-mouse-read-string prompt completion-list multi-word))
303             (t
304              (vm-keyboard-read-string prompt completion-list multi-word)))
305     (vm-keyboard-read-string prompt completion-list multi-word)))
306
307 (defun vm-read-number (prompt)
308   (let (result)
309     (while
310         (null
311          (string-match "^[ \t]*-?[0-9]+" (setq result (read-string prompt)))))
312     (string-to-number result)))
313
314 (defun vm-keyboard-read-file-name (prompt &optional dir default
315                                           must-match initial history)
316   "Like read-file-name, except HISTORY's value is unaltered."
317   (let ((oldvalue (symbol-value history))
318         ;; evade the XEmacs dialog box, yeccch.
319         (use-dialog-box nil))
320     (unwind-protect
321         (condition-case nil
322             (read-file-name prompt dir default must-match initial history)
323           ((wrong-number-of-arguments void-function)
324            (if history
325                (let ((file-name-history (symbol-value history))
326                      file)
327                  (setq file
328                        (read-file-name prompt dir default must-match initial))
329                  file )
330              (read-file-name prompt dir default must-match initial))))
331       (and history (set history oldvalue)))))
332
333 (defun vm-read-file-name (prompt &optional dir default
334                                  must-match initial history)
335   "Like read-file-name, except a mouse interface is used if a mouse
336 click mouse triggered the current command."
337   (if (vm-mouse-support-possible-here-p)
338       (cond ((and (vm-mouse-xemacs-mouse-p)
339                   (or (button-press-event-p last-command-event)
340                       (button-release-event-p last-command-event)
341                       (menu-event-p last-command-event)))
342              (vm-mouse-read-file-name prompt dir default
343                                       must-match initial history))
344             ((and (vm-mouse-fsfemacs-mouse-p)
345                   (listp last-nonmenu-event))
346              (vm-mouse-read-file-name prompt dir default
347                                       must-match initial history))
348             (t
349              (vm-keyboard-read-file-name prompt dir default
350                                          must-match initial history)))
351     (vm-keyboard-read-file-name prompt dir default
352                                 must-match initial history)))
353
354 (defun vm-folder-list (&optional non-virtual)
355   (save-excursion
356     (let ((buffers (buffer-list))
357           (modes (if non-virtual '(vm-mode) '(vm-mode vm-virtual-mode)))
358           folders)
359       (while buffers
360         (set-buffer (car buffers))
361         (if (member major-mode modes)
362             (setq folders (cons (buffer-name) folders)))
363         (setq buffers (cdr buffers)))
364       folders)))
365
366 (defun vm-read-folder-name ()
367   (completing-read
368    "VM Folder: "
369    (mapcar (lambda (f) (list f)) (vm-folder-list))
370    nil t nil nil))
371
372 (provide 'vm-minibuf)
373
374 ;;; vm-minibuf.el ends here