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.
17 #include "mutt_menu.h"
18 #include "mutt_curses.h"
27 /* redraw flags for mutt_enter_string() */
29 M_REDRAW_INIT = 1, /* go to end of line and redraw */
30 M_REDRAW_LINE /* redraw entire line */
33 static int my_wcwidth (wchar_t wc)
37 if (IsWPrint (wc) && n > 0)
46 /* combining mark / non-spacing character */
47 #define COMB_CHAR(wc) (IsWPrint (wc) && !wcwidth (wc))
49 static int my_wcswidth (const wchar_t * s, size_t n)
54 w += my_wcwidth (*s++);
58 static int my_addwch (wchar_t wc)
62 if (IsWPrint (wc) && n > 0)
63 return mutt_addwch (wc);
65 return printw ("^%c", ((int) wc + 0x40) & 0x7f);
67 return printw ("\\u%04x", (int) wc);
68 return printw ("\\u%08x", (int) wc);
71 static size_t width_ceiling (const wchar_t * s, size_t n, int w1)
73 const wchar_t *s0 = s;
77 if ((w += my_wcwidth (*s)) > w1)
82 static void my_wcstombs (char *dest, size_t dlen, const wchar_t * src,
88 /* First convert directly into the destination buffer */
89 memset (&st, 0, sizeof (st));
90 for (; slen && dlen >= MB_LEN_MAX; dest += k, dlen -= k, src++, slen--)
91 if ((k = wcrtomb (dest, *src, &st)) == (size_t) (-1))
94 /* If this works, we can stop now */
95 if (dlen >= MB_LEN_MAX) {
96 wcrtomb (dest, 0, &st);
100 /* Otherwise convert any remaining data into a local buffer */
102 char buf[3 * MB_LEN_MAX];
105 for (; slen && p - buf < dlen; p += k, src++, slen--)
106 if ((k = wcrtomb (p, *src, &st)) == (size_t) (-1))
108 p += wcrtomb (p, 0, &st);
110 /* If it fits into the destination buffer, we can stop now */
111 if (p - buf <= dlen) {
112 memcpy (dest, buf, p - buf);
116 /* Otherwise we truncate the string in an ugly fashion */
117 memcpy (dest, buf, dlen);
118 dest[dlen - 1] = '\0'; /* assume original dlen > 0 */
122 size_t my_mbstowcs (wchar_t ** pwbuf, size_t * pwbuflen, size_t i, char *buf)
130 wbuf = *pwbuf, wbuflen = *pwbuflen;
131 memset (&st, 0, sizeof (st));
132 for (; (k = mbrtowc (&wc, buf, MB_LEN_MAX, &st)) &&
133 k != (size_t) (-1) && k != (size_t) (-2); buf += k) {
136 mem_realloc (&wbuf, wbuflen * sizeof (*wbuf));
140 *pwbuf = wbuf, *pwbuflen = wbuflen;
145 * Replace part of the wchar_t buffer, from FROM to CURPOS, by BUF.
148 static void replace_part (ENTER_STATE * state, size_t from, char *buf)
150 /* Save the suffix */
151 size_t savelen = state->lastchar - state->curpos;
152 wchar_t *savebuf = mem_calloc (savelen, sizeof (wchar_t));
154 memcpy (savebuf, state->wbuf + state->curpos, savelen * sizeof (wchar_t));
156 /* Convert to wide characters */
157 state->curpos = my_mbstowcs (&state->wbuf, &state->wbuflen, from, buf);
159 /* Make space for suffix */
160 if (state->curpos + savelen > state->wbuflen) {
161 state->wbuflen = state->curpos + savelen;
162 mem_realloc (&state->wbuf, state->wbuflen * sizeof (wchar_t));
166 memcpy (state->wbuf + state->curpos, savebuf, savelen * sizeof (wchar_t));
167 state->lastchar = state->curpos + savelen;
174 * 1 need to redraw the screen and call me again
175 * 0 if input was given
179 int mutt_enter_string (char *buf, size_t buflen, int y, int x, int flags)
182 ENTER_STATE *es = mutt_new_enter_state ();
184 rv = _mutt_enter_string (buf, buflen, y, x, flags, 0, NULL, NULL, es);
185 mutt_free_enter_state (&es);
189 int _mutt_enter_string (char *buf, size_t buflen, int y, int x,
190 int flags, int multiple, char ***files, int *numfiles,
193 int width = COLS - x - 1;
195 int pass = (flags & M_PASS);
199 wchar_t *tempbuf = 0;
201 history_class_t hclass;
207 memset (&mbstate, 0, sizeof (mbstate));
210 /* Coming back after return 1 */
211 redraw = M_REDRAW_LINE;
214 /* Initialise wbuf from buf */
216 state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
217 redraw = M_REDRAW_INIT;
220 if (flags & (M_FILE | M_EFILE))
222 else if (flags & M_CMD)
224 else if (flags & M_ALIAS)
226 else if (flags & M_COMMAND)
228 else if (flags & M_PATTERN)
234 if (redraw && !pass) {
235 if (redraw == M_REDRAW_INIT) {
236 /* Go to end of line */
237 state->curpos = state->lastchar;
239 width_ceiling (state->wbuf, state->lastchar,
240 my_wcswidth (state->wbuf,
241 state->lastchar) - width + 1);
243 if (state->curpos < state->begin ||
244 my_wcswidth (state->wbuf + state->begin,
245 state->curpos - state->begin) >= width)
247 width_ceiling (state->wbuf, state->lastchar,
248 my_wcswidth (state->wbuf,
249 state->curpos) - width / 2);
252 for (i = state->begin; i < state->lastchar; i++) {
253 w += my_wcwidth (state->wbuf[i]);
256 my_addwch (state->wbuf[i]);
260 x + my_wcswidth (state->wbuf + state->begin,
261 state->curpos - state->begin));
265 if ((ch = km_dokey (MENU_EDITOR)) == -1) {
272 if (ch != OP_EDITOR_COMPLETE && ch != OP_EDITOR_COMPLETE_QUERY)
274 redraw = M_REDRAW_LINE;
276 case OP_EDITOR_HISTORY_UP:
277 state->curpos = state->lastchar;
278 replace_part (state, 0, mutt_history_prev (hclass));
279 redraw = M_REDRAW_INIT;
282 case OP_EDITOR_HISTORY_DOWN:
283 state->curpos = state->lastchar;
284 replace_part (state, 0, mutt_history_next (hclass));
285 redraw = M_REDRAW_INIT;
288 case OP_EDITOR_BACKSPACE:
289 if (state->curpos == 0)
293 while (i && COMB_CHAR (state->wbuf[i - 1]))
297 memmove (state->wbuf + i, state->wbuf + state->curpos,
298 (state->lastchar - state->curpos) * sizeof (wchar_t));
299 state->lastchar -= state->curpos - i;
309 redraw = M_REDRAW_INIT;
312 case OP_EDITOR_KILL_LINE:
313 state->curpos = state->lastchar = 0;
316 case OP_EDITOR_KILL_EOL:
317 state->lastchar = state->curpos;
320 case OP_EDITOR_BACKWARD_CHAR:
321 if (state->curpos == 0)
324 while (state->curpos && COMB_CHAR (state->wbuf[state->curpos - 1]))
331 case OP_EDITOR_FORWARD_CHAR:
332 if (state->curpos == state->lastchar)
336 while (state->curpos < state->lastchar
337 && COMB_CHAR (state->wbuf[state->curpos]))
342 case OP_EDITOR_BACKWARD_WORD:
343 if (state->curpos == 0)
346 while (state->curpos && iswspace (state->wbuf[state->curpos - 1]))
348 while (state->curpos && !iswspace (state->wbuf[state->curpos - 1]))
353 case OP_EDITOR_FORWARD_WORD:
354 if (state->curpos == state->lastchar)
357 while (state->curpos < state->lastchar
358 && iswspace (state->wbuf[state->curpos]))
360 while (state->curpos < state->lastchar
361 && !iswspace (state->wbuf[state->curpos]))
366 case OP_EDITOR_CAPITALIZE_WORD:
367 case OP_EDITOR_UPCASE_WORD:
368 case OP_EDITOR_DOWNCASE_WORD:
369 if (state->curpos == state->lastchar) {
373 while (state->curpos && !iswspace (state->wbuf[state->curpos]))
375 while (state->curpos < state->lastchar
376 && iswspace (state->wbuf[state->curpos]))
378 while (state->curpos < state->lastchar
379 && !iswspace (state->wbuf[state->curpos])) {
380 if (ch == OP_EDITOR_DOWNCASE_WORD)
381 state->wbuf[state->curpos] =
382 towlower (state->wbuf[state->curpos]);
384 state->wbuf[state->curpos] =
385 towupper (state->wbuf[state->curpos]);
386 if (ch == OP_EDITOR_CAPITALIZE_WORD)
387 ch = OP_EDITOR_DOWNCASE_WORD;
393 case OP_EDITOR_DELETE_CHAR:
394 if (state->curpos == state->lastchar)
398 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
400 if (i < state->lastchar)
402 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
404 memmove (state->wbuf + state->curpos, state->wbuf + i,
405 (state->lastchar - i) * sizeof (wchar_t));
406 state->lastchar -= i - state->curpos;
410 case OP_EDITOR_KILL_WORD:
411 /* delete to begining of word */
412 if (state->curpos != 0) {
414 while (i && iswspace (state->wbuf[i - 1]))
417 if (iswalnum (state->wbuf[i - 1])) {
418 for (--i; i && iswalnum (state->wbuf[i - 1]); i--);
423 memmove (state->wbuf + i, state->wbuf + state->curpos,
424 (state->lastchar - state->curpos) * sizeof (wchar_t));
425 state->lastchar += i - state->curpos;
430 case OP_EDITOR_KILL_EOW:
431 /* delete to end of word */
432 for (i = state->curpos;
433 i < state->lastchar && iswspace (state->wbuf[i]); i++);
434 for (; i < state->lastchar && !iswspace (state->wbuf[i]); i++);
435 memmove (state->wbuf + state->curpos, state->wbuf + i,
436 (state->lastchar - i) * sizeof (wchar_t));
437 state->lastchar += state->curpos - i;
440 case OP_EDITOR_BUFFY_CYCLE:
441 if (flags & M_EFILE) {
442 first = 1; /* clear input if user types a real key later */
443 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
444 buffy_next (buf, buflen);
445 state->curpos = state->lastchar =
446 my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
449 else if (!(flags & M_FILE))
451 /* fall through to completion routine (M_FILE) */
453 case OP_EDITOR_COMPLETE:
454 case OP_EDITOR_COMPLETE_QUERY:
457 for (i = state->curpos; i && state->wbuf[i - 1] != ' '; i--);
458 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
459 if (tempbuf && templen == state->lastchar - i &&
460 !memcmp (tempbuf, state->wbuf + i,
461 (state->lastchar - i) * sizeof (wchar_t))) {
462 mutt_select_file (buf, buflen,
463 (flags & M_EFILE) ? M_SEL_FOLDER : 0);
464 set_option (OPTNEEDREDRAW);
466 replace_part (state, i, buf);
470 if (!mutt_complete (buf, buflen)) {
471 templen = state->lastchar - i;
472 mem_realloc (&tempbuf, templen * sizeof (wchar_t));
477 replace_part (state, i, buf);
479 else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE) {
480 /* invoke the alias-menu to get more addresses */
481 for (i = state->curpos; i && state->wbuf[i - 1] != ',' &&
482 state->wbuf[i - 1] != ':'; i--);
483 for (; i < state->lastchar && state->wbuf[i] == ' '; i++);
484 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
485 r = mutt_alias_complete (buf, buflen);
486 replace_part (state, i, buf);
492 } else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY) {
493 /* invoke the query-menu to get more addresses */
494 if ((i = state->curpos)) {
495 for (; i && state->wbuf[i - 1] != ','; i--);
496 for (; i < state->curpos && state->wbuf[i] == ' '; i++);
498 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
499 mutt_query_complete (buf, buflen);
500 replace_part (state, i, buf);
503 } else if (flags & M_COMMAND) {
504 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
506 if (i && buf[i - 1] == '=' &&
507 mutt_var_value_complete (buf, buflen, i))
509 else if (!mutt_command_complete (buf, buflen, i, state->tabs))
511 replace_part (state, 0, buf);
513 else if (flags & (M_FILE | M_EFILE)) {
514 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
516 /* see if the path has changed from the last time */
517 if ((!tempbuf && !state->lastchar)
518 || (tempbuf && templen == state->lastchar
519 && !memcmp (tempbuf, state->wbuf,
520 state->lastchar * sizeof (wchar_t)))) {
521 _mutt_select_file (buf, buflen,
522 ((flags & M_EFILE) ? M_SEL_FOLDER : 0) |
523 (multiple ? M_SEL_MULTI : 0), files, numfiles);
524 set_option (OPTNEEDREDRAW);
526 mutt_pretty_mailbox (buf);
528 mutt_history_add (hclass, buf);
533 /* file selection cancelled */
538 if (!mutt_complete (buf, buflen)) {
539 templen = state->lastchar;
540 mem_realloc (&tempbuf, templen * sizeof (wchar_t));
541 memcpy (tempbuf, state->wbuf, templen * sizeof (wchar_t));
544 BEEP (); /* let the user know that nothing matched */
545 replace_part (state, 0, buf);
551 case OP_EDITOR_QUOTE_CHAR:
555 /*ADDCH (LastKey); */
556 event = mutt_getch ();
557 if (event.ch != -1) {
563 case OP_EDITOR_TRANSPOSE_CHARS:
564 if (state->lastchar < 2)
569 if (state->curpos == 0)
571 else if (state->curpos < state->lastchar)
574 t = state->wbuf[state->curpos - 2];
575 state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
576 state->wbuf[state->curpos - 1] = t;
589 /* use the raw keypress */
592 if ((ch == '#') && (flags & M_LASTFOLDER)) {
594 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
599 /* treat ENTER the same as RETURN */
604 /* quietly ignore all other function keys */
608 /* gather the octets into a wide character */
614 k = mbrtowc (&wc, &c, 1, &mbstate);
615 if (k == (size_t) (-2))
617 else if (k && k != 1) {
618 memset (&mbstate, 0, sizeof (mbstate));
623 if (first && (flags & M_CLEAR)) {
625 if (IsWPrint (wc)) /* why? */
626 state->curpos = state->lastchar = 0;
629 if (wc == '\r' || wc == '\n') {
630 /* Convert from wide characters */
631 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
633 mutt_history_add (hclass, buf);
639 tfiles = mem_calloc (*numfiles, sizeof (char *));
640 mutt_expand_path (buf, buflen);
641 tfiles[0] = str_dup (buf);
647 else if (wc && (wc < ' ' || IsWPrint (wc))) { /* why? */
648 if (state->lastchar >= state->wbuflen) {
649 state->wbuflen = state->lastchar + 20;
650 mem_realloc (&state->wbuf, state->wbuflen * sizeof (wchar_t));
652 memmove (state->wbuf + state->curpos + 1, state->wbuf + state->curpos,
653 (state->lastchar - state->curpos) * sizeof (wchar_t));
654 state->wbuf[state->curpos++] = wc;
670 void mutt_free_enter_state (ENTER_STATE ** esp)
675 mem_free (&(*esp)->wbuf);
681 * very narrow screen might crash it
682 * sort out the input side