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-lib/lib-lib.h>
23 /* redraw flags for mutt_enter_string() */
25 M_REDRAW_INIT = 1, /* go to end of line and redraw */
26 M_REDRAW_LINE /* redraw entire line */
29 static int my_wcwidth (wchar_t wc)
33 if (iswprint(wc) && n > 0)
42 /* combining mark / non-spacing character */
43 #define COMB_CHAR(wc) (iswprint(wc) && !wcwidth(wc))
45 static int my_wcswidth (const wchar_t * s, size_t n)
50 w += my_wcwidth (*s++);
54 static int my_addwch (wchar_t wc)
58 if (iswprint(wc) && n > 0)
59 return mutt_addwch (wc);
61 return printw ("^%c", ((int) wc + 0x40) & 0x7f);
63 return printw ("\\u%04x", (int) wc);
64 return printw ("\\u%08x", (int) wc);
67 static size_t width_ceiling (const wchar_t * s, size_t n, int w1)
69 const wchar_t *s0 = s;
73 if ((w += my_wcwidth (*s)) > w1)
78 static void my_wcstombs (char *dest, ssize_t dlen, const wchar_t * src,
84 /* First convert directly into the destination buffer */
86 for (; slen && dlen >= MB_LEN_MAX; dest += k, dlen -= k, src++, slen--)
87 if ((k = wcrtomb (dest, *src, &st)) == (size_t) (-1))
90 /* If this works, we can stop now */
91 if (dlen >= MB_LEN_MAX) {
92 wcrtomb (dest, 0, &st);
96 /* Otherwise convert any remaining data into a local buffer */
98 char buf[3 * MB_LEN_MAX];
101 for (; slen && p - buf < dlen; p += k, src++, slen--)
102 if ((k = wcrtomb (p, *src, &st)) == (size_t) (-1))
104 p += wcrtomb (p, 0, &st);
106 /* If it fits into the destination buffer, we can stop now */
107 if (p - buf <= dlen) {
108 memcpy (dest, buf, p - buf);
112 /* Otherwise we truncate the string in an ugly fashion */
113 memcpy (dest, buf, dlen);
114 dest[dlen - 1] = '\0'; /* assume original dlen > 0 */
118 static size_t my_mbstowcs (wchar_t ** pwbuf, size_t * pwbuflen, size_t i, const char *buf)
126 wbuf = *pwbuf, wbuflen = *pwbuflen;
128 for (; (k = mbrtowc (&wc, buf, MB_LEN_MAX, &st)) &&
129 k != (size_t) (-1) && k != (size_t) (-2); buf += k) {
132 p_realloc(&wbuf, wbuflen);
136 *pwbuf = wbuf, *pwbuflen = wbuflen;
141 * Replace part of the wchar_t buffer, from FROM to CURPOS, by BUF.
144 static void replace_part (ENTER_STATE *state, size_t from, const char *buf)
146 /* Save the suffix */
147 size_t savelen = state->lastchar - state->curpos;
148 wchar_t *savebuf = p_new(wchar_t, savelen);
150 memcpy (savebuf, state->wbuf + state->curpos, savelen * sizeof (wchar_t));
152 /* Convert to wide characters */
153 state->curpos = my_mbstowcs(&state->wbuf, &state->wbuflen, from, buf);
155 /* Make space for suffix */
156 if (state->curpos + savelen > state->wbuflen) {
157 state->wbuflen = state->curpos + savelen;
158 p_realloc(&state->wbuf, state->wbuflen);
162 memcpy (state->wbuf + state->curpos, savebuf, savelen * sizeof (wchar_t));
163 state->lastchar = state->curpos + savelen;
170 * 1 need to redraw the screen and call me again
171 * 0 if input was given
175 int mutt_enter_string (char *buf, size_t buflen, int y, int x, int flags)
178 ENTER_STATE *es = mutt_new_enter_state ();
180 rv = _mutt_enter_string(buf, buflen, y, x, flags, 0, NULL, NULL, es);
181 mutt_free_enter_state (&es);
185 int _mutt_enter_string (char *buf, size_t buflen, int y, int x,
186 int flags, int multiple, char ***files, int *numfiles,
189 int width = COLS - x - 1;
191 int pass = (flags & M_PASS);
195 wchar_t *tempbuf = 0;
197 history_class_t hclass;
203 p_clear(&mbstate, 1);
206 /* Coming back after return 1 */
207 redraw = M_REDRAW_LINE;
210 /* Initialise wbuf from buf */
212 state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
213 redraw = M_REDRAW_INIT;
216 if (flags & (M_FILE | M_EFILE))
218 else if (flags & M_CMD)
220 else if (flags & M_ALIAS)
222 else if (flags & M_COMMAND)
224 else if (flags & M_PATTERN)
230 if (redraw && !pass) {
231 if (redraw == M_REDRAW_INIT) {
232 /* Go to end of line */
233 state->curpos = state->lastchar;
235 width_ceiling (state->wbuf, state->lastchar,
236 my_wcswidth (state->wbuf,
237 state->lastchar) - width + 1);
239 if (state->curpos < state->begin ||
240 my_wcswidth (state->wbuf + state->begin,
241 state->curpos - state->begin) >= width)
243 width_ceiling (state->wbuf, state->lastchar,
244 my_wcswidth (state->wbuf,
245 state->curpos) - width / 2);
248 for (i = state->begin; i < state->lastchar; i++) {
249 w += my_wcwidth (state->wbuf[i]);
252 my_addwch (state->wbuf[i]);
256 x + my_wcswidth (state->wbuf + state->begin,
257 state->curpos - state->begin));
261 if ((ch = km_dokey (MENU_EDITOR)) == -1) {
268 if (ch != OP_EDITOR_COMPLETE && ch != OP_EDITOR_COMPLETE_QUERY)
270 redraw = M_REDRAW_LINE;
272 case OP_EDITOR_HISTORY_UP:
273 state->curpos = state->lastchar;
274 replace_part(state, 0, mutt_history_prev(hclass));
275 redraw = M_REDRAW_INIT;
278 case OP_EDITOR_HISTORY_DOWN:
279 state->curpos = state->lastchar;
280 replace_part(state, 0, mutt_history_next(hclass));
281 redraw = M_REDRAW_INIT;
284 case OP_EDITOR_BACKSPACE:
285 if (state->curpos == 0)
289 while (i && COMB_CHAR (state->wbuf[i - 1]))
293 memmove (state->wbuf + i, state->wbuf + state->curpos,
294 (state->lastchar - state->curpos) * sizeof (wchar_t));
295 state->lastchar -= state->curpos - i;
305 redraw = M_REDRAW_INIT;
308 case OP_EDITOR_KILL_LINE:
309 state->curpos = state->lastchar = 0;
312 case OP_EDITOR_KILL_EOL:
313 state->lastchar = state->curpos;
316 case OP_EDITOR_BACKWARD_CHAR:
317 if (state->curpos == 0)
320 while (state->curpos && COMB_CHAR (state->wbuf[state->curpos - 1]))
327 case OP_EDITOR_FORWARD_CHAR:
328 if (state->curpos == state->lastchar)
332 while (state->curpos < state->lastchar
333 && COMB_CHAR (state->wbuf[state->curpos]))
338 case OP_EDITOR_BACKWARD_WORD:
339 if (state->curpos == 0)
342 while (state->curpos && iswspace (state->wbuf[state->curpos - 1]))
344 while (state->curpos && !iswspace (state->wbuf[state->curpos - 1]))
349 case OP_EDITOR_FORWARD_WORD:
350 if (state->curpos == state->lastchar)
353 while (state->curpos < state->lastchar
354 && iswspace (state->wbuf[state->curpos]))
356 while (state->curpos < state->lastchar
357 && !iswspace (state->wbuf[state->curpos]))
362 case OP_EDITOR_CAPITALIZE_WORD:
363 case OP_EDITOR_UPCASE_WORD:
364 case OP_EDITOR_DOWNCASE_WORD:
365 if (state->curpos == state->lastchar) {
369 while (state->curpos && !iswspace (state->wbuf[state->curpos]))
371 while (state->curpos < state->lastchar
372 && iswspace (state->wbuf[state->curpos]))
374 while (state->curpos < state->lastchar
375 && !iswspace (state->wbuf[state->curpos])) {
376 if (ch == OP_EDITOR_DOWNCASE_WORD)
377 state->wbuf[state->curpos] =
378 towlower (state->wbuf[state->curpos]);
380 state->wbuf[state->curpos] =
381 towupper (state->wbuf[state->curpos]);
382 if (ch == OP_EDITOR_CAPITALIZE_WORD)
383 ch = OP_EDITOR_DOWNCASE_WORD;
389 case OP_EDITOR_DELETE_CHAR:
390 if (state->curpos == state->lastchar)
394 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
396 if (i < state->lastchar)
398 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
400 memmove (state->wbuf + state->curpos, state->wbuf + i,
401 (state->lastchar - i) * sizeof (wchar_t));
402 state->lastchar -= i - state->curpos;
406 case OP_EDITOR_KILL_WORD:
407 /* delete to begining of word */
408 if (state->curpos != 0) {
410 while (i && iswspace (state->wbuf[i - 1]))
413 if (iswalnum (state->wbuf[i - 1])) {
414 for (--i; i && iswalnum (state->wbuf[i - 1]); i--);
419 memmove (state->wbuf + i, state->wbuf + state->curpos,
420 (state->lastchar - state->curpos) * sizeof (wchar_t));
421 state->lastchar += i - state->curpos;
426 case OP_EDITOR_KILL_EOW:
427 /* delete to end of word */
428 for (i = state->curpos;
429 i < state->lastchar && iswspace (state->wbuf[i]); i++);
430 for (; i < state->lastchar && !iswspace (state->wbuf[i]); i++);
431 memmove (state->wbuf + state->curpos, state->wbuf + i,
432 (state->lastchar - i) * sizeof (wchar_t));
433 state->lastchar += state->curpos - i;
436 case OP_EDITOR_BUFFY_CYCLE:
437 if (flags & M_EFILE) {
438 first = 1; /* clear input if user types a real key later */
439 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
440 buffy_next (buf, buflen);
441 state->curpos = state->lastchar =
442 my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
445 else if (!(flags & M_FILE))
447 /* fall through to completion routine (M_FILE) */
449 case OP_EDITOR_COMPLETE:
450 case OP_EDITOR_COMPLETE_QUERY:
453 for (i = state->curpos; i && state->wbuf[i - 1] != ' '; i--);
454 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
455 if (tempbuf && templen == state->lastchar - i &&
456 !memcmp (tempbuf, state->wbuf + i,
457 (state->lastchar - i) * sizeof (wchar_t))) {
458 mutt_select_file(buf, buflen, flags & M_EFILE ? M_SEL_FOLDER : 0,
460 set_option (OPTNEEDREDRAW);
462 replace_part (state, i, buf);
466 if (!mutt_complete (buf, buflen)) {
467 templen = state->lastchar - i;
468 p_realloc(&tempbuf, templen);
473 replace_part (state, i, buf);
475 else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE) {
476 /* invoke the alias-menu to get more addresses */
477 for (i = state->curpos; i && state->wbuf[i - 1] != ',' &&
478 state->wbuf[i - 1] != ':'; i--);
479 for (; i < state->lastchar && state->wbuf[i] == ' '; i++);
480 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
481 r = mutt_alias_complete (buf, buflen);
482 replace_part (state, i, buf);
488 } else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY) {
489 /* invoke the query-menu to get more addresses */
490 if ((i = state->curpos)) {
491 for (; i && state->wbuf[i - 1] != ','; i--);
492 for (; i < state->curpos && state->wbuf[i] == ' '; i++);
494 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
495 mutt_query_complete (buf, buflen);
496 replace_part (state, i, buf);
499 } else if (flags & M_COMMAND) {
500 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
502 if (i && buf[i - 1] == '=' &&
503 mutt_var_value_complete (buf, buflen, i))
505 else if (!mutt_command_complete (buf, buflen, i, state->tabs))
507 replace_part (state, 0, buf);
509 else if (flags & (M_FILE | M_EFILE)) {
510 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
512 /* see if the path has changed from the last time */
513 if ((!tempbuf && !state->lastchar)
514 || (tempbuf && templen == state->lastchar
515 && !memcmp (tempbuf, state->wbuf,
516 state->lastchar * sizeof (wchar_t)))) {
517 mutt_select_file(buf, buflen,
518 ((flags & M_EFILE) ? M_SEL_FOLDER : 0) |
519 (multiple ? M_SEL_MULTI : 0), files, numfiles);
520 set_option (OPTNEEDREDRAW);
522 mutt_pretty_mailbox (buf);
524 mutt_history_add (hclass, buf);
529 /* file selection cancelled */
534 if (!mutt_complete (buf, buflen)) {
535 templen = state->lastchar;
536 p_realloc(&tempbuf, templen);
537 memcpy (tempbuf, state->wbuf, templen * sizeof (wchar_t));
540 BEEP (); /* let the user know that nothing matched */
541 replace_part (state, 0, buf);
547 case OP_EDITOR_QUOTE_CHAR:
551 /*ADDCH (LastKey); */
552 event = mutt_getch ();
553 if (event.ch != -1) {
559 case OP_EDITOR_TRANSPOSE_CHARS:
560 if (state->lastchar < 2)
565 if (state->curpos == 0)
567 else if (state->curpos < state->lastchar)
570 t = state->wbuf[state->curpos - 2];
571 state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
572 state->wbuf[state->curpos - 1] = t;
585 /* use the raw keypress */
588 if ((ch == '#') && (flags & M_LASTFOLDER)) {
590 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
595 /* treat ENTER the same as RETURN */
600 /* quietly ignore all other function keys */
604 /* gather the octets into a wide character */
610 k = mbrtowc (&wc, &c, 1, &mbstate);
611 if (k == (size_t) (-2))
613 else if (k && k != 1) {
614 p_clear(&mbstate, 1);
619 if (first && (flags & M_CLEAR)) {
621 if (iswprint(wc)) /* why? */
622 state->curpos = state->lastchar = 0;
625 if (wc == '\r' || wc == '\n') {
626 /* Convert from wide characters */
627 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
629 mutt_history_add (hclass, buf);
635 tfiles = p_new(char *, *numfiles);
636 mutt_expand_path (buf, buflen);
637 tfiles[0] = m_strdup(buf);
643 else if (wc && (wc < ' ' || iswprint(wc))) { /* why? */
644 if (state->lastchar >= state->wbuflen) {
645 state->wbuflen = state->lastchar + 20;
646 p_realloc(&state->wbuf, state->wbuflen);
648 memmove (state->wbuf + state->curpos + 1, state->wbuf + state->curpos,
649 (state->lastchar - state->curpos) * sizeof (wchar_t));
650 state->wbuf[state->curpos++] = wc;
666 void mutt_free_enter_state (ENTER_STATE ** esp)
671 p_delete(&(*esp)->wbuf);
677 * very narrow screen might crash it
678 * sort out the input side