2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * Copyright (C) 2000 Edmund Grimley Evans <edmundo@rano.org>
6 * This file is part of mutt-ng, see http://www.muttng.org/.
7 * It's licensed under the GNU General Public License,
8 * please see the file GPL in the top level source directory.
11 #include <lib-ui/lib-ui.h>
31 /* redraw flags for mutt_enter_string() */
33 M_REDRAW_INIT = 1, /* go to end of line and redraw */
34 M_REDRAW_LINE /* redraw entire line */
37 static int my_wcwidth (wchar_t wc)
41 if (iswprint(wc) && n > 0)
50 /* combining mark / non-spacing character */
51 #define COMB_CHAR(wc) (iswprint(wc) && !wcwidth(wc))
53 static int my_wcswidth (const wchar_t * s, size_t n)
58 w += my_wcwidth (*s++);
62 static int my_addwch (wchar_t wc)
66 if (iswprint(wc) && n > 0)
67 return waddwch(stdscr, wc);
69 return wprintw (stdscr, "^%c", ((int) wc + 0x40) & 0x7f);
71 return wprintw (stdscr, "\\u%04x", (int) wc);
72 return wprintw (stdscr, "\\u%08x", (int) wc);
75 static size_t width_ceiling (const wchar_t * s, size_t n, int w1)
77 const wchar_t *s0 = s;
81 if ((w += my_wcwidth (*s)) > w1)
86 static void my_wcstombs (char *dest, ssize_t dlen, const wchar_t * src,
92 /* First convert directly into the destination buffer */
94 for (; slen && dlen >= MB_LEN_MAX; dest += k, dlen -= k, src++, slen--)
95 if ((k = wcrtomb (dest, *src, &st)) == (size_t) (-1))
98 /* If this works, we can stop now */
99 if (dlen >= MB_LEN_MAX) {
100 wcrtomb (dest, 0, &st);
104 /* Otherwise convert any remaining data into a local buffer */
106 char buf[3 * MB_LEN_MAX];
109 for (; slen && p - buf < dlen; p += k, src++, slen--)
110 if ((k = wcrtomb (p, *src, &st)) == (size_t) (-1))
112 p += wcrtomb (p, 0, &st);
114 /* If it fits into the destination buffer, we can stop now */
115 if (p - buf <= dlen) {
116 memcpy (dest, buf, p - buf);
120 /* Otherwise we truncate the string in an ugly fashion */
121 memcpy (dest, buf, dlen);
122 dest[dlen - 1] = '\0'; /* assume original dlen > 0 */
126 static size_t my_mbstowcs (wchar_t ** pwbuf, size_t * pwbuflen, size_t i, const char *buf)
134 wbuf = *pwbuf, wbuflen = *pwbuflen;
136 for (; (k = mbrtowc (&wc, buf, MB_LEN_MAX, &st)) &&
137 k != (size_t) (-1) && k != (size_t) (-2); buf += k) {
140 p_realloc(&wbuf, wbuflen);
144 *pwbuf = wbuf, *pwbuflen = wbuflen;
149 * Replace part of the wchar_t buffer, from FROM to CURPOS, by BUF.
152 static void replace_part (ENTER_STATE *state, size_t from, const char *buf)
154 /* Save the suffix */
155 size_t savelen = state->lastchar - state->curpos;
156 wchar_t *savebuf = p_new(wchar_t, savelen);
158 memcpy (savebuf, state->wbuf + state->curpos, savelen * sizeof (wchar_t));
160 /* Convert to wide characters */
161 state->curpos = my_mbstowcs(&state->wbuf, &state->wbuflen, from, buf);
163 /* Make space for suffix */
164 if (state->curpos + savelen > state->wbuflen) {
165 state->wbuflen = state->curpos + savelen;
166 p_realloc(&state->wbuf, state->wbuflen);
170 memcpy (state->wbuf + state->curpos, savebuf, savelen * sizeof (wchar_t));
171 state->lastchar = state->curpos + savelen;
177 mutt_enter_string(char *buf, size_t buflen, int y, int x, int flags,
178 int multiple, char ***files, int *numfiles,
181 int width = COLS - x - 1;
183 int pass = (flags & M_PASS);
187 wchar_t *tempbuf = 0;
189 history_class_t hclass;
195 p_clear(&mbstate, 1);
198 /* Coming back after return 1 */
199 redraw = M_REDRAW_LINE;
202 /* Initialise wbuf from buf */
204 state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
205 redraw = M_REDRAW_INIT;
208 if (flags & (M_FILE | M_EFILE))
210 else if (flags & M_CMD)
212 else if (flags & M_ALIAS)
214 else if (flags & M_COMMAND)
216 else if (flags & M_PATTERN)
222 if (redraw && !pass) {
223 if (redraw == M_REDRAW_INIT) {
224 /* Go to end of line */
225 state->curpos = state->lastchar;
227 width_ceiling (state->wbuf, state->lastchar,
228 my_wcswidth (state->wbuf,
229 state->lastchar) - width + 1);
231 if (state->curpos < state->begin ||
232 my_wcswidth (state->wbuf + state->begin,
233 state->curpos - state->begin) >= width)
235 width_ceiling (state->wbuf, state->lastchar,
236 my_wcswidth (state->wbuf,
237 state->curpos) - width / 2);
238 wmove (stdscr, y, x);
240 for (i = state->begin; i < state->lastchar; i++) {
241 w += my_wcwidth (state->wbuf[i]);
244 my_addwch (state->wbuf[i]);
248 x + my_wcswidth (state->wbuf + state->begin,
249 state->curpos - state->begin));
253 if ((ch = km_dokey (MENU_EDITOR)) == -1) {
260 if (ch != OP_EDITOR_COMPLETE && ch != OP_EDITOR_COMPLETE_QUERY)
262 redraw = M_REDRAW_LINE;
264 case OP_EDITOR_HISTORY_UP:
265 state->curpos = state->lastchar;
266 replace_part(state, 0, mutt_history_prev(hclass));
267 redraw = M_REDRAW_INIT;
270 case OP_EDITOR_HISTORY_DOWN:
271 state->curpos = state->lastchar;
272 replace_part(state, 0, mutt_history_next(hclass));
273 redraw = M_REDRAW_INIT;
276 case OP_EDITOR_BACKSPACE:
277 if (state->curpos == 0)
281 while (i && COMB_CHAR (state->wbuf[i - 1]))
285 memmove (state->wbuf + i, state->wbuf + state->curpos,
286 (state->lastchar - state->curpos) * sizeof (wchar_t));
287 state->lastchar -= state->curpos - i;
297 redraw = M_REDRAW_INIT;
300 case OP_EDITOR_KILL_LINE:
301 state->curpos = state->lastchar = 0;
304 case OP_EDITOR_KILL_EOL:
305 state->lastchar = state->curpos;
308 case OP_EDITOR_BACKWARD_CHAR:
309 if (state->curpos == 0)
312 while (state->curpos && COMB_CHAR (state->wbuf[state->curpos - 1]))
319 case OP_EDITOR_FORWARD_CHAR:
320 if (state->curpos == state->lastchar)
324 while (state->curpos < state->lastchar
325 && COMB_CHAR (state->wbuf[state->curpos]))
330 case OP_EDITOR_BACKWARD_WORD:
331 if (state->curpos == 0)
334 while (state->curpos && iswspace (state->wbuf[state->curpos - 1]))
336 while (state->curpos && !iswspace (state->wbuf[state->curpos - 1]))
341 case OP_EDITOR_FORWARD_WORD:
342 if (state->curpos == state->lastchar)
345 while (state->curpos < state->lastchar
346 && iswspace (state->wbuf[state->curpos]))
348 while (state->curpos < state->lastchar
349 && !iswspace (state->wbuf[state->curpos]))
354 case OP_EDITOR_CAPITALIZE_WORD:
355 case OP_EDITOR_UPCASE_WORD:
356 case OP_EDITOR_DOWNCASE_WORD:
357 if (state->curpos == state->lastchar) {
361 while (state->curpos && !iswspace (state->wbuf[state->curpos]))
363 while (state->curpos < state->lastchar
364 && iswspace (state->wbuf[state->curpos]))
366 while (state->curpos < state->lastchar
367 && !iswspace (state->wbuf[state->curpos])) {
368 if (ch == OP_EDITOR_DOWNCASE_WORD)
369 state->wbuf[state->curpos] =
370 towlower (state->wbuf[state->curpos]);
372 state->wbuf[state->curpos] =
373 towupper (state->wbuf[state->curpos]);
374 if (ch == OP_EDITOR_CAPITALIZE_WORD)
375 ch = OP_EDITOR_DOWNCASE_WORD;
381 case OP_EDITOR_DELETE_CHAR:
382 if (state->curpos == state->lastchar)
386 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
388 if (i < state->lastchar)
390 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
392 memmove (state->wbuf + state->curpos, state->wbuf + i,
393 (state->lastchar - i) * sizeof (wchar_t));
394 state->lastchar -= i - state->curpos;
398 case OP_EDITOR_KILL_WORD:
399 /* delete to begining of word */
400 if (state->curpos != 0) {
402 while (i && iswspace (state->wbuf[i - 1]))
405 if (iswalnum (state->wbuf[i - 1])) {
406 for (--i; i && iswalnum (state->wbuf[i - 1]); i--);
411 memmove (state->wbuf + i, state->wbuf + state->curpos,
412 (state->lastchar - state->curpos) * sizeof (wchar_t));
413 state->lastchar += i - state->curpos;
418 case OP_EDITOR_KILL_EOW:
419 /* delete to end of word */
420 for (i = state->curpos;
421 i < state->lastchar && iswspace (state->wbuf[i]); i++);
422 for (; i < state->lastchar && !iswspace (state->wbuf[i]); i++);
423 memmove (state->wbuf + state->curpos, state->wbuf + i,
424 (state->lastchar - i) * sizeof (wchar_t));
425 state->lastchar += state->curpos - i;
428 case OP_EDITOR_BUFFY_CYCLE:
429 if (flags & M_EFILE) {
430 first = 1; /* clear input if user types a real key later */
431 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
432 buffy_next (buf, buflen);
433 state->curpos = state->lastchar =
434 my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
437 else if (!(flags & M_FILE))
439 /* fall through to completion routine (M_FILE) */
441 case OP_EDITOR_COMPLETE:
442 case OP_EDITOR_COMPLETE_QUERY:
445 for (i = state->curpos; i && state->wbuf[i - 1] != ' '; i--);
446 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
447 if (tempbuf && templen == state->lastchar - i &&
448 !memcmp (tempbuf, state->wbuf + i,
449 (state->lastchar - i) * sizeof (wchar_t))) {
450 mutt_select_file(buf, buflen, flags & M_EFILE ? M_SEL_FOLDER : 0,
452 set_option (OPTNEEDREDRAW);
454 replace_part (state, i, buf);
458 if (!mutt_complete (buf, buflen)) {
459 templen = state->lastchar - i;
460 p_realloc(&tempbuf, templen);
465 replace_part (state, i, buf);
467 else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE) {
468 /* invoke the alias-menu to get more addresses */
469 for (i = state->curpos; i && state->wbuf[i - 1] != ',' &&
470 state->wbuf[i - 1] != ':'; i--);
471 for (; i < state->lastchar && state->wbuf[i] == ' '; i++);
472 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
473 r = mutt_alias_complete (buf, buflen);
474 replace_part (state, i, buf);
480 } else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY) {
481 /* invoke the query-menu to get more addresses */
482 if ((i = state->curpos)) {
483 for (; i && state->wbuf[i - 1] != ','; i--);
484 for (; i < state->curpos && state->wbuf[i] == ' '; i++);
486 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
487 mutt_query_complete (buf, buflen);
488 replace_part (state, i, buf);
491 } else if (flags & M_COMMAND) {
492 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
494 if (i && buf[i - 1] == '=' &&
495 mutt_var_value_complete (buf, buflen, i))
497 else if (!mutt_command_complete (buf, buflen, i, state->tabs))
499 replace_part (state, 0, buf);
501 else if (flags & (M_FILE | M_EFILE)) {
502 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
504 /* see if the path has changed from the last time */
505 if ((!tempbuf && !state->lastchar)
506 || (tempbuf && templen == state->lastchar
507 && !memcmp (tempbuf, state->wbuf,
508 state->lastchar * sizeof (wchar_t)))) {
509 mutt_select_file(buf, buflen,
510 ((flags & M_EFILE) ? M_SEL_FOLDER : 0) |
511 (multiple ? M_SEL_MULTI : 0), files, numfiles);
512 set_option (OPTNEEDREDRAW);
514 mutt_pretty_mailbox (buf);
516 mutt_history_add (hclass, buf);
521 /* file selection cancelled */
526 if (!mutt_complete (buf, buflen)) {
527 templen = state->lastchar;
528 p_realloc(&tempbuf, templen);
529 memcpy (tempbuf, state->wbuf, templen * sizeof (wchar_t));
532 BEEP (); /* let the user know that nothing matched */
533 replace_part (state, 0, buf);
539 case OP_EDITOR_QUOTE_CHAR:
543 /*ADDCH (LastKey); */
544 event = mutt_getch ();
545 if (event.ch != -1) {
551 case OP_EDITOR_TRANSPOSE_CHARS:
552 if (state->lastchar < 2)
557 if (state->curpos == 0)
559 else if (state->curpos < state->lastchar)
562 t = state->wbuf[state->curpos - 2];
563 state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
564 state->wbuf[state->curpos - 1] = t;
577 /* use the raw keypress */
580 if ((ch == '#') && (flags & M_LASTFOLDER)) {
582 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
587 /* treat ENTER the same as RETURN */
592 /* quietly ignore all other function keys */
596 /* gather the octets into a wide character */
602 k = mbrtowc (&wc, &c, 1, &mbstate);
603 if (k == (size_t) (-2))
605 else if (k && k != 1) {
606 p_clear(&mbstate, 1);
611 if (first && (flags & M_CLEAR)) {
613 if (iswprint(wc)) /* why? */
614 state->curpos = state->lastchar = 0;
617 if (wc == '\r' || wc == '\n') {
618 /* Convert from wide characters */
619 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
621 mutt_history_add (hclass, buf);
627 tfiles = p_new(char *, *numfiles);
628 mutt_expand_path (buf, buflen);
629 tfiles[0] = m_strdup(buf);
635 else if (wc && (wc < ' ' || iswprint(wc))) { /* why? */
636 if (state->lastchar >= state->wbuflen) {
637 state->wbuflen = state->lastchar + 20;
638 p_realloc(&state->wbuf, state->wbuflen);
640 memmove (state->wbuf + state->curpos + 1, state->wbuf + state->curpos,
641 (state->lastchar - state->curpos) * sizeof (wchar_t));
642 state->wbuf[state->curpos++] = wc;
658 int _mutt_get_field(const char *field, char *buf, ssize_t buflen, int flags,
659 int multiple, char ***files, int *numfiles)
668 CLEARLINE(stdscr, LINES - 1);
669 waddstr (stdscr, field);
671 getyx (stdscr, y, x);
672 ret = mutt_enter_string(buf, buflen, y, x, flags, multiple, files,
675 CLEARLINE(stdscr, LINES - 1);
683 * very narrow screen might crash it
684 * sort out the input side