1 /* Minibuffer handling
2
3 Copyright (c) 1997-2011 Free Software Foundation, Inc.
4
5 This file is part of GNU Zile.
6
7 GNU Zile is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 GNU Zile is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Zile; see the file COPYING. If not, write to the
19 Free Software Foundation, Fifth Floor, 51 Franklin Street, Boston,
20 MA 02111-1301, USA. */
21
22 #include <config.h>
23
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include "main.h"
29 #include "extern.h"
30
31 void
term_minibuf_write(const char * s)32 term_minibuf_write (const char *s)
33 {
34 term_move (term_height () - 1, 0);
35 term_clrtoeol ();
36 term_addstr (s);
37 }
38
39 static void
draw_minibuf_read(const char * prompt,const char * value,size_t prompt_len,const char * match,size_t pointo)40 draw_minibuf_read (const char *prompt, const char *value,
41 size_t prompt_len, const char *match, size_t pointo)
42 {
43 int margin = 1, n = 0;
44
45 term_minibuf_write (prompt);
46
47 if (prompt_len + pointo + 1 >= term_width ())
48 {
49 margin++;
50 term_addstr ("$");
51 n = pointo - pointo % (term_width () - prompt_len - 2);
52 }
53
54 term_addstr (value + n);
55 term_addstr (match);
56
57 if (strlen (value + n) >= term_width () - prompt_len - margin)
58 {
59 term_move (term_height () - 1, term_width () - 1);
60 term_addstr ("$");
61 }
62
63 term_move (term_height () - 1,
64 prompt_len + margin - 1 + pointo % (term_width () - prompt_len -
65 margin));
66
67 term_refresh ();
68 }
69
70 static void
maybe_close_popup(Completion cp)71 maybe_close_popup (Completion cp)
72 {
73 Window wp, old_wp = cur_wp;
74 if (cp != NULL && (get_completion_flags (cp) & CFLAG_POPPEDUP)
75 && (wp = find_window ("*Completions*")) != NULL)
76 {
77 set_current_window (wp);
78 if (get_completion_flags (cp) & CFLAG_CLOSE)
79 FUNCALL (delete_window);
80 else if (get_completion_old_bp (cp))
81 switch_to_buffer (get_completion_old_bp (cp));
82 set_current_window (old_wp);
83 term_redisplay ();
84 }
85 }
86
87 const_astr
term_minibuf_read(const char * prompt,const char * value,size_t pos,Completion cp,History hp)88 term_minibuf_read (const char *prompt, const char *value, size_t pos, Completion cp, History hp)
89 {
90 if (hp)
91 prepare_history (hp);
92
93 size_t c;
94 int thistab, lasttab = -1;
95 astr as = astr_new_cstr (value), saved = NULL;
96
97 size_t prompt_len = strlen (prompt);
98 if (pos == SIZE_MAX)
99 pos = astr_len (as);
100
101 do
102 {
103 const char *s;
104 switch (lasttab)
105 {
106 case completion_matchednonunique:
107 s = " [Complete, but not unique]";
108 break;
109 case completion_notmatched:
110 s = " [No match]";
111 break;
112 case completion_matched:
113 s = " [Sole completion]";
114 break;
115 default:
116 s = "";
117 }
118 draw_minibuf_read (prompt, astr_cstr (as), prompt_len, s, pos);
119
120 thistab = -1;
121
122 switch (c = getkeystroke (GETKEY_DEFAULT))
123 {
124 case KBD_NOKEY:
125 case KBD_RET:
126 break;
127 case KBD_CTRL | 'z':
128 FUNCALL (suspend_emacs);
129 break;
130 case KBD_CANCEL:
131 as = NULL;
132 break;
133 case KBD_CTRL | 'a':
134 case KBD_HOME:
135 pos = 0;
136 break;
137 case KBD_CTRL | 'e':
138 case KBD_END:
139 pos = astr_len (as);
140 break;
141 case KBD_CTRL | 'b':
142 case KBD_LEFT:
143 if (pos > 0)
144 --pos;
145 else
146 ding ();
147 break;
148 case KBD_CTRL | 'f':
149 case KBD_RIGHT:
150 if (pos < astr_len (as))
151 ++pos;
152 else
153 ding ();
154 break;
155 case KBD_CTRL | 'k':
156 /* FIXME: do kill-register save. */
157 if (pos < astr_len (as))
158 astr_truncate (as, pos);
159 else
160 ding ();
161 break;
162 case KBD_BS:
163 if (pos > 0)
164 astr_remove (as, --pos, 1);
165 else
166 ding ();
167 break;
168 case KBD_CTRL | 'd':
169 case KBD_DEL:
170 if (pos < astr_len (as))
171 astr_remove (as, pos, 1);
172 else
173 ding ();
174 break;
175 case KBD_META | 'v':
176 case KBD_PGUP:
177 if (cp == NULL)
178 {
179 ding ();
180 break;
181 }
182
183 if (get_completion_flags (cp) & CFLAG_POPPEDUP)
184 {
185 completion_scroll_down ();
186 thistab = lasttab;
187 }
188 break;
189 case KBD_CTRL | 'v':
190 case KBD_PGDN:
191 if (cp == NULL)
192 {
193 ding ();
194 break;
195 }
196
197 if (get_completion_flags (cp) & CFLAG_POPPEDUP)
198 {
199 completion_scroll_up ();
200 thistab = lasttab;
201 }
202 break;
203 case KBD_UP:
204 case KBD_META | 'p':
205 if (hp)
206 {
207 const char *elem = previous_history_element (hp);
208 if (elem)
209 {
210 if (!saved)
211 saved = astr_cpy (astr_new (), as);
212
213 astr_cpy_cstr (as, elem);
214 }
215 }
216 break;
217 case KBD_DOWN:
218 case KBD_META | 'n':
219 if (hp)
220 {
221 const char *elem = next_history_element (hp);
222 if (elem)
223 astr_cpy_cstr (as, elem);
224 else if (saved)
225 {
226 astr_cpy (as, saved);
227 saved = NULL;
228 }
229 }
230 break;
231 case KBD_TAB:
232 got_tab:
233 if (cp == NULL)
234 {
235 ding ();
236 break;
237 }
238
239 if (lasttab != -1 && lasttab != completion_notmatched
240 && get_completion_flags (cp) & CFLAG_POPPEDUP)
241 {
242 completion_scroll_up ();
243 thistab = lasttab;
244 }
245 else
246 {
247 astr bs = astr_new ();
248 astr_cpy (bs, as);
249 thistab = completion_try (cp, bs, true);
250 switch (thistab)
251 {
252 case completion_matched:
253 maybe_close_popup (cp);
254 set_completion_flags (cp, get_completion_flags (cp) & ~CFLAG_POPPEDUP);
255 /* FALLTHROUGH */
256 case completion_matchednonunique:
257 case completion_nonunique:
258 {
259 bs = astr_new ();
260 if (get_completion_flags (cp) & CFLAG_FILENAME)
261 astr_cat (bs, get_completion_path (cp));
262 astr_cat_nstr (bs, get_completion_match (cp), get_completion_matchsize (cp));
263 if (strncmp (astr_cstr (as), astr_cstr (bs),
264 astr_len (bs)) != 0)
265 thistab = -1;
266 as = bs;
267 pos = astr_len (as);
268 break;
269 }
270 case completion_notmatched:
271 ding ();
272 break;
273 default:
274 break;
275 }
276 }
277 break;
278 case ' ':
279 if (cp != NULL)
280 goto got_tab;
281 /* FALLTHROUGH */
282 default:
283 if (c > 255 || !isprint (c))
284 ding ();
285 else
286 {
287 astr_cpy (as, astr_fmt ("%s%c%s", astr_cstr (astr_substr (as, 0, pos)), (int) c, astr_cstr (astr_substr (as, pos, astr_len (as) - pos))));
288 pos++;
289 }
290 }
291
292 lasttab = thistab;
293 } while (c != KBD_RET && c != KBD_CANCEL);
294
295 minibuf_clear ();
296 maybe_close_popup (cp);
297 return as;
298 }
299