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