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.
16 #include "mutt_menu.h"
17 #include "mutt_curses.h"
25 /* redraw flags for mutt_enter_string() */
27 M_REDRAW_INIT = 1, /* go to end of line and redraw */
28 M_REDRAW_LINE /* redraw entire line */
31 static int my_wcwidth (wchar_t wc)
35 if (IsWPrint (wc) && n > 0)
44 /* combining mark / non-spacing character */
45 #define COMB_CHAR(wc) (IsWPrint (wc) && !wcwidth (wc))
47 static int my_wcswidth (const wchar_t * s, size_t n)
52 w += my_wcwidth (*s++);
56 static int my_addwch (wchar_t wc)
60 if (IsWPrint (wc) && n > 0)
61 return mutt_addwch (wc);
63 return printw ("^%c", ((int) wc + 0x40) & 0x7f);
65 return printw ("\\u%04x", (int) wc);
66 return printw ("\\u%08x", (int) wc);
69 static size_t width_ceiling (const wchar_t * s, size_t n, int w1)
71 const wchar_t *s0 = s;
75 if ((w += my_wcwidth (*s)) > w1)
80 static void my_wcstombs (char *dest, size_t dlen, const wchar_t * src,
86 /* First convert directly into the destination buffer */
87 memset (&st, 0, sizeof (st));
88 for (; slen && dlen >= MB_LEN_MAX; dest += k, dlen -= k, src++, slen--)
89 if ((k = wcrtomb (dest, *src, &st)) == (size_t) (-1))
92 /* If this works, we can stop now */
93 if (dlen >= MB_LEN_MAX) {
94 wcrtomb (dest, 0, &st);
98 /* Otherwise convert any remaining data into a local buffer */
100 char buf[3 * MB_LEN_MAX];
103 for (; slen && p - buf < dlen; p += k, src++, slen--)
104 if ((k = wcrtomb (p, *src, &st)) == (size_t) (-1))
106 p += wcrtomb (p, 0, &st);
108 /* If it fits into the destination buffer, we can stop now */
109 if (p - buf <= dlen) {
110 memcpy (dest, buf, p - buf);
114 /* Otherwise we truncate the string in an ugly fashion */
115 memcpy (dest, buf, dlen);
116 dest[dlen - 1] = '\0'; /* assume original dlen > 0 */
120 size_t my_mbstowcs (wchar_t ** pwbuf, size_t * pwbuflen, size_t i, char *buf)
128 wbuf = *pwbuf, wbuflen = *pwbuflen;
129 memset (&st, 0, sizeof (st));
130 for (; (k = mbrtowc (&wc, buf, MB_LEN_MAX, &st)) &&
131 k != (size_t) (-1) && k != (size_t) (-2); buf += k) {
134 safe_realloc (&wbuf, wbuflen * sizeof (*wbuf));
138 *pwbuf = wbuf, *pwbuflen = wbuflen;
143 * Replace part of the wchar_t buffer, from FROM to CURPOS, by BUF.
146 static void replace_part (ENTER_STATE * state, size_t from, char *buf)
148 /* Save the suffix */
149 size_t savelen = state->lastchar - state->curpos;
150 wchar_t *savebuf = safe_calloc (savelen, sizeof (wchar_t));
152 memcpy (savebuf, state->wbuf + state->curpos, savelen * sizeof (wchar_t));
154 /* Convert to wide characters */
155 state->curpos = my_mbstowcs (&state->wbuf, &state->wbuflen, from, buf);
157 /* Make space for suffix */
158 if (state->curpos + savelen > state->wbuflen) {
159 state->wbuflen = state->curpos + savelen;
160 safe_realloc (&state->wbuf, state->wbuflen * sizeof (wchar_t));
164 memcpy (state->wbuf + state->curpos, savebuf, savelen * sizeof (wchar_t));
165 state->lastchar = state->curpos + savelen;
172 * 1 need to redraw the screen and call me again
173 * 0 if input was given
177 int mutt_enter_string (char *buf, size_t buflen, int y, int x, int flags)
180 ENTER_STATE *es = mutt_new_enter_state ();
182 rv = _mutt_enter_string (buf, buflen, y, x, flags, 0, NULL, NULL, es);
183 mutt_free_enter_state (&es);
187 int _mutt_enter_string (char *buf, size_t buflen, int y, int x,
188 int flags, int multiple, char ***files, int *numfiles,
191 int width = COLS - x - 1;
193 int pass = (flags & M_PASS);
197 wchar_t *tempbuf = 0;
199 history_class_t hclass;
205 memset (&mbstate, 0, sizeof (mbstate));
208 /* Coming back after return 1 */
209 redraw = M_REDRAW_LINE;
212 /* Initialise wbuf from buf */
214 state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
215 redraw = M_REDRAW_INIT;
218 if (flags & (M_FILE | M_EFILE))
220 else if (flags & M_CMD)
222 else if (flags & M_ALIAS)
224 else if (flags & M_COMMAND)
226 else if (flags & M_PATTERN)
232 if (redraw && !pass) {
233 if (redraw == M_REDRAW_INIT) {
234 /* Go to end of line */
235 state->curpos = state->lastchar;
237 width_ceiling (state->wbuf, state->lastchar,
238 my_wcswidth (state->wbuf,
239 state->lastchar) - width + 1);
241 if (state->curpos < state->begin ||
242 my_wcswidth (state->wbuf + state->begin,
243 state->curpos - state->begin) >= width)
245 width_ceiling (state->wbuf, state->lastchar,
246 my_wcswidth (state->wbuf,
247 state->curpos) - width / 2);
250 for (i = state->begin; i < state->lastchar; i++) {
251 w += my_wcwidth (state->wbuf[i]);
254 my_addwch (state->wbuf[i]);
258 x + my_wcswidth (state->wbuf + state->begin,
259 state->curpos - state->begin));
263 if ((ch = km_dokey (MENU_EDITOR)) == -1) {
270 if (ch != OP_EDITOR_COMPLETE)
272 redraw = M_REDRAW_LINE;
274 case OP_EDITOR_HISTORY_UP:
275 state->curpos = state->lastchar;
276 replace_part (state, 0, mutt_history_prev (hclass));
277 redraw = M_REDRAW_INIT;
280 case OP_EDITOR_HISTORY_DOWN:
281 state->curpos = state->lastchar;
282 replace_part (state, 0, mutt_history_next (hclass));
283 redraw = M_REDRAW_INIT;
286 case OP_EDITOR_BACKSPACE:
287 if (state->curpos == 0)
291 while (i && COMB_CHAR (state->wbuf[i - 1]))
295 memmove (state->wbuf + i, state->wbuf + state->curpos,
296 (state->lastchar - state->curpos) * sizeof (wchar_t));
297 state->lastchar -= state->curpos - i;
307 redraw = M_REDRAW_INIT;
310 case OP_EDITOR_KILL_LINE:
311 state->curpos = state->lastchar = 0;
314 case OP_EDITOR_KILL_EOL:
315 state->lastchar = state->curpos;
318 case OP_EDITOR_BACKWARD_CHAR:
319 if (state->curpos == 0)
322 while (state->curpos && COMB_CHAR (state->wbuf[state->curpos - 1]))
329 case OP_EDITOR_FORWARD_CHAR:
330 if (state->curpos == state->lastchar)
334 while (state->curpos < state->lastchar
335 && COMB_CHAR (state->wbuf[state->curpos]))
340 case OP_EDITOR_BACKWARD_WORD:
341 if (state->curpos == 0)
344 while (state->curpos && iswspace (state->wbuf[state->curpos - 1]))
346 while (state->curpos && !iswspace (state->wbuf[state->curpos - 1]))
351 case OP_EDITOR_FORWARD_WORD:
352 if (state->curpos == state->lastchar)
355 while (state->curpos < state->lastchar
356 && iswspace (state->wbuf[state->curpos]))
358 while (state->curpos < state->lastchar
359 && !iswspace (state->wbuf[state->curpos]))
364 case OP_EDITOR_CAPITALIZE_WORD:
365 case OP_EDITOR_UPCASE_WORD:
366 case OP_EDITOR_DOWNCASE_WORD:
367 if (state->curpos == state->lastchar) {
371 while (state->curpos && !iswspace (state->wbuf[state->curpos]))
373 while (state->curpos < state->lastchar
374 && iswspace (state->wbuf[state->curpos]))
376 while (state->curpos < state->lastchar
377 && !iswspace (state->wbuf[state->curpos])) {
378 if (ch == OP_EDITOR_DOWNCASE_WORD)
379 state->wbuf[state->curpos] =
380 towlower (state->wbuf[state->curpos]);
382 state->wbuf[state->curpos] =
383 towupper (state->wbuf[state->curpos]);
384 if (ch == OP_EDITOR_CAPITALIZE_WORD)
385 ch = OP_EDITOR_DOWNCASE_WORD;
391 case OP_EDITOR_DELETE_CHAR:
392 if (state->curpos == state->lastchar)
396 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
398 if (i < state->lastchar)
400 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
402 memmove (state->wbuf + state->curpos, state->wbuf + i,
403 (state->lastchar - i) * sizeof (wchar_t));
404 state->lastchar -= i - state->curpos;
408 case OP_EDITOR_KILL_WORD:
409 /* delete to begining of word */
410 if (state->curpos != 0) {
412 while (i && iswspace (state->wbuf[i - 1]))
415 if (iswalnum (state->wbuf[i - 1])) {
416 for (--i; i && iswalnum (state->wbuf[i - 1]); i--);
421 memmove (state->wbuf + i, state->wbuf + state->curpos,
422 (state->lastchar - state->curpos) * sizeof (wchar_t));
423 state->lastchar += i - state->curpos;
428 case OP_EDITOR_KILL_EOW:
429 /* delete to end of word */
430 for (i = state->curpos;
431 i < state->lastchar && iswspace (state->wbuf[i]); i++);
432 for (; i < state->lastchar && !iswspace (state->wbuf[i]); i++);
433 memmove (state->wbuf + state->curpos, state->wbuf + i,
434 (state->lastchar - i) * sizeof (wchar_t));
435 state->lastchar += state->curpos - i;
438 case OP_EDITOR_BUFFY_CYCLE:
439 if (flags & M_EFILE) {
440 first = 1; /* clear input if user types a real key later */
441 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
442 mutt_buffy (buf, buflen);
443 state->curpos = state->lastchar =
444 my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
447 else if (!(flags & M_FILE))
449 /* fall through to completion routine (M_FILE) */
451 case OP_EDITOR_COMPLETE:
454 for (i = state->curpos; i && state->wbuf[i - 1] != ' '; i--);
455 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
456 if (tempbuf && templen == state->lastchar - i &&
457 !memcmp (tempbuf, state->wbuf + i,
458 (state->lastchar - i) * sizeof (wchar_t))) {
459 mutt_select_file (buf, buflen,
460 (flags & M_EFILE) ? M_SEL_FOLDER : 0);
461 set_option (OPTNEEDREDRAW);
463 replace_part (state, i, buf);
467 if (!mutt_complete (buf, buflen)) {
468 templen = state->lastchar - i;
469 safe_realloc (&tempbuf, templen * sizeof (wchar_t));
474 replace_part (state, i, buf);
476 else if (flags & M_ALIAS) {
477 /* invoke the alias-menu to get more addresses */
478 for (i = state->curpos; i && state->wbuf[i - 1] != ',' &&
479 state->wbuf[i - 1] != ':'; i--);
480 for (; i < state->lastchar && state->wbuf[i] == ' '; i++);
481 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
482 r = mutt_alias_complete (buf, buflen);
483 replace_part (state, i, buf);
490 else if (flags & M_COMMAND) {
491 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
492 i = mutt_strlen (buf);
493 if (i && buf[i - 1] == '=' &&
494 mutt_var_value_complete (buf, buflen, i))
496 else if (!mutt_command_complete (buf, buflen, i, state->tabs))
498 replace_part (state, 0, buf);
500 else if (flags & (M_FILE | M_EFILE)) {
501 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
503 /* see if the path has changed from the last time */
504 if ((!tempbuf && !state->lastchar)
505 || (tempbuf && templen == state->lastchar
506 && !memcmp (tempbuf, state->wbuf,
507 state->lastchar * sizeof (wchar_t)))) {
508 _mutt_select_file (buf, buflen,
509 ((flags & M_EFILE) ? M_SEL_FOLDER : 0) |
510 (multiple ? M_SEL_MULTI : 0), files, numfiles);
511 set_option (OPTNEEDREDRAW);
513 mutt_pretty_mailbox (buf);
515 mutt_history_add (hclass, buf);
520 /* file selection cancelled */
525 if (!mutt_complete (buf, buflen)) {
526 templen = state->lastchar;
527 safe_realloc (&tempbuf, templen * sizeof (wchar_t));
528 memcpy (tempbuf, state->wbuf, templen * sizeof (wchar_t));
531 BEEP (); /* let the user know that nothing matched */
532 replace_part (state, 0, buf);
538 case OP_EDITOR_COMPLETE_QUERY:
539 if (flags & M_ALIAS) {
540 /* invoke the query-menu to get more addresses */
541 if ((i = state->curpos)) {
542 for (; i && state->wbuf[i - 1] != ','; i--);
543 for (; i < state->curpos && state->wbuf[i] == ' '; i++);
546 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
547 mutt_query_complete (buf, buflen);
548 replace_part (state, i, buf);
556 case OP_EDITOR_QUOTE_CHAR:
560 /*ADDCH (LastKey); */
561 event = mutt_getch ();
562 if (event.ch != -1) {
568 case OP_EDITOR_TRANSPOSE_CHARS:
569 if (state->lastchar < 2)
574 if (state->curpos == 0)
576 else if (state->curpos < state->lastchar)
579 t = state->wbuf[state->curpos - 2];
580 state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
581 state->wbuf[state->curpos - 1] = t;
594 /* use the raw keypress */
597 if ((ch == '#') && (flags & M_LASTFOLDER)) {
599 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
604 /* treat ENTER the same as RETURN */
609 /* quietly ignore all other function keys */
613 /* gather the octets into a wide character */
619 k = mbrtowc (&wc, &c, 1, &mbstate);
620 if (k == (size_t) (-2))
622 else if (k && k != 1) {
623 memset (&mbstate, 0, sizeof (mbstate));
628 if (first && (flags & M_CLEAR)) {
630 if (IsWPrint (wc)) /* why? */
631 state->curpos = state->lastchar = 0;
634 if (wc == '\r' || wc == '\n') {
635 /* Convert from wide characters */
636 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
638 mutt_history_add (hclass, buf);
644 tfiles = safe_calloc (*numfiles, sizeof (char *));
645 mutt_expand_path (buf, buflen);
646 tfiles[0] = safe_strdup (buf);
652 else if (wc && (wc < ' ' || IsWPrint (wc))) { /* why? */
653 if (state->lastchar >= state->wbuflen) {
654 state->wbuflen = state->lastchar + 20;
655 safe_realloc (&state->wbuf, state->wbuflen * sizeof (wchar_t));
657 memmove (state->wbuf + state->curpos + 1, state->wbuf + state->curpos,
658 (state->lastchar - state->curpos) * sizeof (wchar_t));
659 state->wbuf[state->curpos++] = wc;
675 void mutt_free_enter_state (ENTER_STATE ** esp)
680 FREE (&(*esp)->wbuf);
686 * very narrow screen might crash it
687 * sort out the input side