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.
15 #include <lib-lib/mem.h>
19 #include "mutt_menu.h"
20 #include "mutt_curses.h"
28 /* redraw flags for mutt_enter_string() */
30 M_REDRAW_INIT = 1, /* go to end of line and redraw */
31 M_REDRAW_LINE /* redraw entire line */
34 static int my_wcwidth (wchar_t wc)
38 if (IsWPrint (wc) && n > 0)
47 /* combining mark / non-spacing character */
48 #define COMB_CHAR(wc) (IsWPrint (wc) && !wcwidth (wc))
50 static int my_wcswidth (const wchar_t * s, size_t n)
55 w += my_wcwidth (*s++);
59 static int my_addwch (wchar_t wc)
63 if (IsWPrint (wc) && n > 0)
64 return mutt_addwch (wc);
66 return printw ("^%c", ((int) wc + 0x40) & 0x7f);
68 return printw ("\\u%04x", (int) wc);
69 return printw ("\\u%08x", (int) wc);
72 static size_t width_ceiling (const wchar_t * s, size_t n, int w1)
74 const wchar_t *s0 = s;
78 if ((w += my_wcwidth (*s)) > w1)
83 static void my_wcstombs (char *dest, size_t dlen, const wchar_t * src,
89 /* First convert directly into the destination buffer */
91 for (; slen && dlen >= MB_LEN_MAX; dest += k, dlen -= k, src++, slen--)
92 if ((k = wcrtomb (dest, *src, &st)) == (size_t) (-1))
95 /* If this works, we can stop now */
96 if (dlen >= MB_LEN_MAX) {
97 wcrtomb (dest, 0, &st);
101 /* Otherwise convert any remaining data into a local buffer */
103 char buf[3 * MB_LEN_MAX];
106 for (; slen && p - buf < dlen; p += k, src++, slen--)
107 if ((k = wcrtomb (p, *src, &st)) == (size_t) (-1))
109 p += wcrtomb (p, 0, &st);
111 /* If it fits into the destination buffer, we can stop now */
112 if (p - buf <= dlen) {
113 memcpy (dest, buf, p - buf);
117 /* Otherwise we truncate the string in an ugly fashion */
118 memcpy (dest, buf, dlen);
119 dest[dlen - 1] = '\0'; /* assume original dlen > 0 */
123 size_t my_mbstowcs (wchar_t ** pwbuf, size_t * pwbuflen, size_t i, const char *buf)
131 wbuf = *pwbuf, wbuflen = *pwbuflen;
133 for (; (k = mbrtowc (&wc, buf, MB_LEN_MAX, &st)) &&
134 k != (size_t) (-1) && k != (size_t) (-2); buf += k) {
137 p_realloc(&wbuf, wbuflen);
141 *pwbuf = wbuf, *pwbuflen = wbuflen;
146 * Replace part of the wchar_t buffer, from FROM to CURPOS, by BUF.
149 static void replace_part (ENTER_STATE *state, size_t from, const char *buf)
151 /* Save the suffix */
152 size_t savelen = state->lastchar - state->curpos;
153 wchar_t *savebuf = p_new(wchar_t, savelen);
155 memcpy (savebuf, state->wbuf + state->curpos, savelen * sizeof (wchar_t));
157 /* Convert to wide characters */
158 state->curpos = my_mbstowcs(&state->wbuf, &state->wbuflen, from, buf);
160 /* Make space for suffix */
161 if (state->curpos + savelen > state->wbuflen) {
162 state->wbuflen = state->curpos + savelen;
163 p_realloc(&state->wbuf, state->wbuflen);
167 memcpy (state->wbuf + state->curpos, savebuf, savelen * sizeof (wchar_t));
168 state->lastchar = state->curpos + savelen;
175 * 1 need to redraw the screen and call me again
176 * 0 if input was given
180 int mutt_enter_string (char *buf, size_t buflen, int y, int x, int flags)
183 ENTER_STATE *es = mutt_new_enter_state ();
185 rv = _mutt_enter_string (buf, buflen, y, x, flags, 0, NULL, NULL, es);
186 mutt_free_enter_state (&es);
190 int _mutt_enter_string (char *buf, size_t buflen, int y, int x,
191 int flags, int multiple, char ***files, int *numfiles,
194 int width = COLS - x - 1;
196 int pass = (flags & M_PASS);
200 wchar_t *tempbuf = 0;
202 history_class_t hclass;
208 p_clear(&mbstate, 1);
211 /* Coming back after return 1 */
212 redraw = M_REDRAW_LINE;
215 /* Initialise wbuf from buf */
217 state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
218 redraw = M_REDRAW_INIT;
221 if (flags & (M_FILE | M_EFILE))
223 else if (flags & M_CMD)
225 else if (flags & M_ALIAS)
227 else if (flags & M_COMMAND)
229 else if (flags & M_PATTERN)
235 if (redraw && !pass) {
236 if (redraw == M_REDRAW_INIT) {
237 /* Go to end of line */
238 state->curpos = state->lastchar;
240 width_ceiling (state->wbuf, state->lastchar,
241 my_wcswidth (state->wbuf,
242 state->lastchar) - width + 1);
244 if (state->curpos < state->begin ||
245 my_wcswidth (state->wbuf + state->begin,
246 state->curpos - state->begin) >= width)
248 width_ceiling (state->wbuf, state->lastchar,
249 my_wcswidth (state->wbuf,
250 state->curpos) - width / 2);
253 for (i = state->begin; i < state->lastchar; i++) {
254 w += my_wcwidth (state->wbuf[i]);
257 my_addwch (state->wbuf[i]);
261 x + my_wcswidth (state->wbuf + state->begin,
262 state->curpos - state->begin));
266 if ((ch = km_dokey (MENU_EDITOR)) == -1) {
273 if (ch != OP_EDITOR_COMPLETE && ch != OP_EDITOR_COMPLETE_QUERY)
275 redraw = M_REDRAW_LINE;
277 case OP_EDITOR_HISTORY_UP:
278 state->curpos = state->lastchar;
279 replace_part(state, 0, mutt_history_prev(hclass));
280 redraw = M_REDRAW_INIT;
283 case OP_EDITOR_HISTORY_DOWN:
284 state->curpos = state->lastchar;
285 replace_part(state, 0, mutt_history_next(hclass));
286 redraw = M_REDRAW_INIT;
289 case OP_EDITOR_BACKSPACE:
290 if (state->curpos == 0)
294 while (i && COMB_CHAR (state->wbuf[i - 1]))
298 memmove (state->wbuf + i, state->wbuf + state->curpos,
299 (state->lastchar - state->curpos) * sizeof (wchar_t));
300 state->lastchar -= state->curpos - i;
310 redraw = M_REDRAW_INIT;
313 case OP_EDITOR_KILL_LINE:
314 state->curpos = state->lastchar = 0;
317 case OP_EDITOR_KILL_EOL:
318 state->lastchar = state->curpos;
321 case OP_EDITOR_BACKWARD_CHAR:
322 if (state->curpos == 0)
325 while (state->curpos && COMB_CHAR (state->wbuf[state->curpos - 1]))
332 case OP_EDITOR_FORWARD_CHAR:
333 if (state->curpos == state->lastchar)
337 while (state->curpos < state->lastchar
338 && COMB_CHAR (state->wbuf[state->curpos]))
343 case OP_EDITOR_BACKWARD_WORD:
344 if (state->curpos == 0)
347 while (state->curpos && iswspace (state->wbuf[state->curpos - 1]))
349 while (state->curpos && !iswspace (state->wbuf[state->curpos - 1]))
354 case OP_EDITOR_FORWARD_WORD:
355 if (state->curpos == state->lastchar)
358 while (state->curpos < state->lastchar
359 && iswspace (state->wbuf[state->curpos]))
361 while (state->curpos < state->lastchar
362 && !iswspace (state->wbuf[state->curpos]))
367 case OP_EDITOR_CAPITALIZE_WORD:
368 case OP_EDITOR_UPCASE_WORD:
369 case OP_EDITOR_DOWNCASE_WORD:
370 if (state->curpos == state->lastchar) {
374 while (state->curpos && !iswspace (state->wbuf[state->curpos]))
376 while (state->curpos < state->lastchar
377 && iswspace (state->wbuf[state->curpos]))
379 while (state->curpos < state->lastchar
380 && !iswspace (state->wbuf[state->curpos])) {
381 if (ch == OP_EDITOR_DOWNCASE_WORD)
382 state->wbuf[state->curpos] =
383 towlower (state->wbuf[state->curpos]);
385 state->wbuf[state->curpos] =
386 towupper (state->wbuf[state->curpos]);
387 if (ch == OP_EDITOR_CAPITALIZE_WORD)
388 ch = OP_EDITOR_DOWNCASE_WORD;
394 case OP_EDITOR_DELETE_CHAR:
395 if (state->curpos == state->lastchar)
399 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
401 if (i < state->lastchar)
403 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
405 memmove (state->wbuf + state->curpos, state->wbuf + i,
406 (state->lastchar - i) * sizeof (wchar_t));
407 state->lastchar -= i - state->curpos;
411 case OP_EDITOR_KILL_WORD:
412 /* delete to begining of word */
413 if (state->curpos != 0) {
415 while (i && iswspace (state->wbuf[i - 1]))
418 if (iswalnum (state->wbuf[i - 1])) {
419 for (--i; i && iswalnum (state->wbuf[i - 1]); i--);
424 memmove (state->wbuf + i, state->wbuf + state->curpos,
425 (state->lastchar - state->curpos) * sizeof (wchar_t));
426 state->lastchar += i - state->curpos;
431 case OP_EDITOR_KILL_EOW:
432 /* delete to end of word */
433 for (i = state->curpos;
434 i < state->lastchar && iswspace (state->wbuf[i]); i++);
435 for (; i < state->lastchar && !iswspace (state->wbuf[i]); i++);
436 memmove (state->wbuf + state->curpos, state->wbuf + i,
437 (state->lastchar - i) * sizeof (wchar_t));
438 state->lastchar += state->curpos - i;
441 case OP_EDITOR_BUFFY_CYCLE:
442 if (flags & M_EFILE) {
443 first = 1; /* clear input if user types a real key later */
444 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
445 buffy_next (buf, buflen);
446 state->curpos = state->lastchar =
447 my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
450 else if (!(flags & M_FILE))
452 /* fall through to completion routine (M_FILE) */
454 case OP_EDITOR_COMPLETE:
455 case OP_EDITOR_COMPLETE_QUERY:
458 for (i = state->curpos; i && state->wbuf[i - 1] != ' '; i--);
459 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
460 if (tempbuf && templen == state->lastchar - i &&
461 !memcmp (tempbuf, state->wbuf + i,
462 (state->lastchar - i) * sizeof (wchar_t))) {
463 mutt_select_file (buf, buflen,
464 (flags & M_EFILE) ? M_SEL_FOLDER : 0);
465 set_option (OPTNEEDREDRAW);
467 replace_part (state, i, buf);
471 if (!mutt_complete (buf, buflen)) {
472 templen = state->lastchar - i;
473 p_realloc(&tempbuf, templen);
478 replace_part (state, i, buf);
480 else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE) {
481 /* invoke the alias-menu to get more addresses */
482 for (i = state->curpos; i && state->wbuf[i - 1] != ',' &&
483 state->wbuf[i - 1] != ':'; i--);
484 for (; i < state->lastchar && state->wbuf[i] == ' '; i++);
485 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
486 r = mutt_alias_complete (buf, buflen);
487 replace_part (state, i, buf);
493 } else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY) {
494 /* invoke the query-menu to get more addresses */
495 if ((i = state->curpos)) {
496 for (; i && state->wbuf[i - 1] != ','; i--);
497 for (; i < state->curpos && state->wbuf[i] == ' '; i++);
499 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
500 mutt_query_complete (buf, buflen);
501 replace_part (state, i, buf);
504 } else if (flags & M_COMMAND) {
505 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
507 if (i && buf[i - 1] == '=' &&
508 mutt_var_value_complete (buf, buflen, i))
510 else if (!mutt_command_complete (buf, buflen, i, state->tabs))
512 replace_part (state, 0, buf);
514 else if (flags & (M_FILE | M_EFILE)) {
515 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
517 /* see if the path has changed from the last time */
518 if ((!tempbuf && !state->lastchar)
519 || (tempbuf && templen == state->lastchar
520 && !memcmp (tempbuf, state->wbuf,
521 state->lastchar * sizeof (wchar_t)))) {
522 _mutt_select_file (buf, buflen,
523 ((flags & M_EFILE) ? M_SEL_FOLDER : 0) |
524 (multiple ? M_SEL_MULTI : 0), files, numfiles);
525 set_option (OPTNEEDREDRAW);
527 mutt_pretty_mailbox (buf);
529 mutt_history_add (hclass, buf);
534 /* file selection cancelled */
539 if (!mutt_complete (buf, buflen)) {
540 templen = state->lastchar;
541 p_realloc(&tempbuf, templen);
542 memcpy (tempbuf, state->wbuf, templen * sizeof (wchar_t));
545 BEEP (); /* let the user know that nothing matched */
546 replace_part (state, 0, buf);
552 case OP_EDITOR_QUOTE_CHAR:
556 /*ADDCH (LastKey); */
557 event = mutt_getch ();
558 if (event.ch != -1) {
564 case OP_EDITOR_TRANSPOSE_CHARS:
565 if (state->lastchar < 2)
570 if (state->curpos == 0)
572 else if (state->curpos < state->lastchar)
575 t = state->wbuf[state->curpos - 2];
576 state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
577 state->wbuf[state->curpos - 1] = t;
590 /* use the raw keypress */
593 if ((ch == '#') && (flags & M_LASTFOLDER)) {
595 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
600 /* treat ENTER the same as RETURN */
605 /* quietly ignore all other function keys */
609 /* gather the octets into a wide character */
615 k = mbrtowc (&wc, &c, 1, &mbstate);
616 if (k == (size_t) (-2))
618 else if (k && k != 1) {
619 p_clear(&mbstate, 1);
624 if (first && (flags & M_CLEAR)) {
626 if (IsWPrint (wc)) /* why? */
627 state->curpos = state->lastchar = 0;
630 if (wc == '\r' || wc == '\n') {
631 /* Convert from wide characters */
632 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
634 mutt_history_add (hclass, buf);
640 tfiles = p_new(char *, *numfiles);
641 mutt_expand_path (buf, buflen);
642 tfiles[0] = m_strdup(buf);
648 else if (wc && (wc < ' ' || IsWPrint (wc))) { /* why? */
649 if (state->lastchar >= state->wbuflen) {
650 state->wbuflen = state->lastchar + 20;
651 p_realloc(&state->wbuf, state->wbuflen);
653 memmove (state->wbuf + state->curpos + 1, state->wbuf + state->curpos,
654 (state->lastchar - state->curpos) * sizeof (wchar_t));
655 state->wbuf[state->curpos++] = wc;
671 void mutt_free_enter_state (ENTER_STATE ** esp)
676 p_delete(&(*esp)->wbuf);
682 * very narrow screen might crash it
683 * sort out the input side