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>
22 /* redraw flags for mutt_enter_string() */
24 M_REDRAW_INIT = 1, /* go to end of line and redraw */
25 M_REDRAW_LINE /* redraw entire line */
28 static int my_wcwidth (wchar_t wc)
32 if (iswprint(wc) && n > 0)
41 /* combining mark / non-spacing character */
42 #define COMB_CHAR(wc) (iswprint(wc) && !wcwidth(wc))
44 static int my_wcswidth (const wchar_t * s, size_t n)
49 w += my_wcwidth (*s++);
53 static int my_addwch (wchar_t wc)
57 if (iswprint(wc) && n > 0)
58 return waddwch(stdscr, wc);
60 return wprintw (stdscr, "^%c", ((int) wc + 0x40) & 0x7f);
62 return wprintw (stdscr, "\\u%04x", (int) wc);
63 return wprintw (stdscr, "\\u%08x", (int) wc);
66 static size_t width_ceiling (const wchar_t * s, size_t n, int w1)
68 const wchar_t *s0 = s;
72 if ((w += my_wcwidth (*s)) > w1)
77 static void my_wcstombs (char *dest, ssize_t dlen, const wchar_t * src,
83 /* First convert directly into the destination buffer */
85 for (; slen && dlen >= MB_LEN_MAX; dest += k, dlen -= k, src++, slen--)
86 if ((k = wcrtomb (dest, *src, &st)) == (size_t) (-1))
89 /* If this works, we can stop now */
90 if (dlen >= MB_LEN_MAX) {
91 wcrtomb (dest, 0, &st);
95 /* Otherwise convert any remaining data into a local buffer */
97 char buf[3 * MB_LEN_MAX];
100 for (; slen && p - buf < dlen; p += k, src++, slen--)
101 if ((k = wcrtomb (p, *src, &st)) == (size_t) (-1))
103 p += wcrtomb (p, 0, &st);
105 /* If it fits into the destination buffer, we can stop now */
106 if (p - buf <= dlen) {
107 memcpy (dest, buf, p - buf);
111 /* Otherwise we truncate the string in an ugly fashion */
112 memcpy (dest, buf, dlen);
113 dest[dlen - 1] = '\0'; /* assume original dlen > 0 */
117 static size_t my_mbstowcs (wchar_t ** pwbuf, size_t * pwbuflen, size_t i, const char *buf)
125 wbuf = *pwbuf, wbuflen = *pwbuflen;
127 for (; (k = mbrtowc (&wc, buf, MB_LEN_MAX, &st)) &&
128 k != (size_t) (-1) && k != (size_t) (-2); buf += k) {
131 p_realloc(&wbuf, wbuflen);
135 *pwbuf = wbuf, *pwbuflen = wbuflen;
140 * Replace part of the wchar_t buffer, from FROM to CURPOS, by BUF.
143 static void replace_part (ENTER_STATE *state, size_t from, const char *buf)
145 /* Save the suffix */
146 size_t savelen = state->lastchar - state->curpos;
147 wchar_t *savebuf = p_new(wchar_t, savelen);
149 memcpy (savebuf, state->wbuf + state->curpos, savelen * sizeof (wchar_t));
151 /* Convert to wide characters */
152 state->curpos = my_mbstowcs(&state->wbuf, &state->wbuflen, from, buf);
154 /* Make space for suffix */
155 if (state->curpos + savelen > state->wbuflen) {
156 state->wbuflen = state->curpos + savelen;
157 p_realloc(&state->wbuf, state->wbuflen);
161 memcpy (state->wbuf + state->curpos, savebuf, savelen * sizeof (wchar_t));
162 state->lastchar = state->curpos + savelen;
169 * 1 need to redraw the screen and call me again
170 * 0 if input was given
174 int mutt_enter_string (char *buf, size_t buflen, int y, int x, int flags)
177 ENTER_STATE *es = mutt_new_enter_state ();
179 rv = _mutt_enter_string(buf, buflen, y, x, flags, 0, NULL, NULL, es);
180 mutt_free_enter_state (&es);
184 int _mutt_enter_string (char *buf, size_t buflen, int y, int x,
185 int flags, int multiple, char ***files, int *numfiles,
188 int width = COLS - x - 1;
190 int pass = (flags & M_PASS);
194 wchar_t *tempbuf = 0;
196 history_class_t hclass;
202 p_clear(&mbstate, 1);
205 /* Coming back after return 1 */
206 redraw = M_REDRAW_LINE;
209 /* Initialise wbuf from buf */
211 state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
212 redraw = M_REDRAW_INIT;
215 if (flags & (M_FILE | M_EFILE))
217 else if (flags & M_CMD)
219 else if (flags & M_ALIAS)
221 else if (flags & M_COMMAND)
223 else if (flags & M_PATTERN)
229 if (redraw && !pass) {
230 if (redraw == M_REDRAW_INIT) {
231 /* Go to end of line */
232 state->curpos = state->lastchar;
234 width_ceiling (state->wbuf, state->lastchar,
235 my_wcswidth (state->wbuf,
236 state->lastchar) - width + 1);
238 if (state->curpos < state->begin ||
239 my_wcswidth (state->wbuf + state->begin,
240 state->curpos - state->begin) >= width)
242 width_ceiling (state->wbuf, state->lastchar,
243 my_wcswidth (state->wbuf,
244 state->curpos) - width / 2);
245 wmove (stdscr, y, x);
247 for (i = state->begin; i < state->lastchar; i++) {
248 w += my_wcwidth (state->wbuf[i]);
251 my_addwch (state->wbuf[i]);
255 x + my_wcswidth (state->wbuf + state->begin,
256 state->curpos - state->begin));
260 if ((ch = km_dokey (MENU_EDITOR)) == -1) {
267 if (ch != OP_EDITOR_COMPLETE && ch != OP_EDITOR_COMPLETE_QUERY)
269 redraw = M_REDRAW_LINE;
271 case OP_EDITOR_HISTORY_UP:
272 state->curpos = state->lastchar;
273 replace_part(state, 0, mutt_history_prev(hclass));
274 redraw = M_REDRAW_INIT;
277 case OP_EDITOR_HISTORY_DOWN:
278 state->curpos = state->lastchar;
279 replace_part(state, 0, mutt_history_next(hclass));
280 redraw = M_REDRAW_INIT;
283 case OP_EDITOR_BACKSPACE:
284 if (state->curpos == 0)
288 while (i && COMB_CHAR (state->wbuf[i - 1]))
292 memmove (state->wbuf + i, state->wbuf + state->curpos,
293 (state->lastchar - state->curpos) * sizeof (wchar_t));
294 state->lastchar -= state->curpos - i;
304 redraw = M_REDRAW_INIT;
307 case OP_EDITOR_KILL_LINE:
308 state->curpos = state->lastchar = 0;
311 case OP_EDITOR_KILL_EOL:
312 state->lastchar = state->curpos;
315 case OP_EDITOR_BACKWARD_CHAR:
316 if (state->curpos == 0)
319 while (state->curpos && COMB_CHAR (state->wbuf[state->curpos - 1]))
326 case OP_EDITOR_FORWARD_CHAR:
327 if (state->curpos == state->lastchar)
331 while (state->curpos < state->lastchar
332 && COMB_CHAR (state->wbuf[state->curpos]))
337 case OP_EDITOR_BACKWARD_WORD:
338 if (state->curpos == 0)
341 while (state->curpos && iswspace (state->wbuf[state->curpos - 1]))
343 while (state->curpos && !iswspace (state->wbuf[state->curpos - 1]))
348 case OP_EDITOR_FORWARD_WORD:
349 if (state->curpos == state->lastchar)
352 while (state->curpos < state->lastchar
353 && iswspace (state->wbuf[state->curpos]))
355 while (state->curpos < state->lastchar
356 && !iswspace (state->wbuf[state->curpos]))
361 case OP_EDITOR_CAPITALIZE_WORD:
362 case OP_EDITOR_UPCASE_WORD:
363 case OP_EDITOR_DOWNCASE_WORD:
364 if (state->curpos == state->lastchar) {
368 while (state->curpos && !iswspace (state->wbuf[state->curpos]))
370 while (state->curpos < state->lastchar
371 && iswspace (state->wbuf[state->curpos]))
373 while (state->curpos < state->lastchar
374 && !iswspace (state->wbuf[state->curpos])) {
375 if (ch == OP_EDITOR_DOWNCASE_WORD)
376 state->wbuf[state->curpos] =
377 towlower (state->wbuf[state->curpos]);
379 state->wbuf[state->curpos] =
380 towupper (state->wbuf[state->curpos]);
381 if (ch == OP_EDITOR_CAPITALIZE_WORD)
382 ch = OP_EDITOR_DOWNCASE_WORD;
388 case OP_EDITOR_DELETE_CHAR:
389 if (state->curpos == state->lastchar)
393 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
395 if (i < state->lastchar)
397 while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
399 memmove (state->wbuf + state->curpos, state->wbuf + i,
400 (state->lastchar - i) * sizeof (wchar_t));
401 state->lastchar -= i - state->curpos;
405 case OP_EDITOR_KILL_WORD:
406 /* delete to begining of word */
407 if (state->curpos != 0) {
409 while (i && iswspace (state->wbuf[i - 1]))
412 if (iswalnum (state->wbuf[i - 1])) {
413 for (--i; i && iswalnum (state->wbuf[i - 1]); i--);
418 memmove (state->wbuf + i, state->wbuf + state->curpos,
419 (state->lastchar - state->curpos) * sizeof (wchar_t));
420 state->lastchar += i - state->curpos;
425 case OP_EDITOR_KILL_EOW:
426 /* delete to end of word */
427 for (i = state->curpos;
428 i < state->lastchar && iswspace (state->wbuf[i]); i++);
429 for (; i < state->lastchar && !iswspace (state->wbuf[i]); i++);
430 memmove (state->wbuf + state->curpos, state->wbuf + i,
431 (state->lastchar - i) * sizeof (wchar_t));
432 state->lastchar += state->curpos - i;
435 case OP_EDITOR_BUFFY_CYCLE:
436 if (flags & M_EFILE) {
437 first = 1; /* clear input if user types a real key later */
438 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
439 buffy_next (buf, buflen);
440 state->curpos = state->lastchar =
441 my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
444 else if (!(flags & M_FILE))
446 /* fall through to completion routine (M_FILE) */
448 case OP_EDITOR_COMPLETE:
449 case OP_EDITOR_COMPLETE_QUERY:
452 for (i = state->curpos; i && state->wbuf[i - 1] != ' '; i--);
453 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
454 if (tempbuf && templen == state->lastchar - i &&
455 !memcmp (tempbuf, state->wbuf + i,
456 (state->lastchar - i) * sizeof (wchar_t))) {
457 mutt_select_file(buf, buflen, flags & M_EFILE ? M_SEL_FOLDER : 0,
459 set_option (OPTNEEDREDRAW);
461 replace_part (state, i, buf);
465 if (!mutt_complete (buf, buflen)) {
466 templen = state->lastchar - i;
467 p_realloc(&tempbuf, templen);
472 replace_part (state, i, buf);
474 else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE) {
475 /* invoke the alias-menu to get more addresses */
476 for (i = state->curpos; i && state->wbuf[i - 1] != ',' &&
477 state->wbuf[i - 1] != ':'; i--);
478 for (; i < state->lastchar && state->wbuf[i] == ' '; i++);
479 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
480 r = mutt_alias_complete (buf, buflen);
481 replace_part (state, i, buf);
487 } else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY) {
488 /* invoke the query-menu to get more addresses */
489 if ((i = state->curpos)) {
490 for (; i && state->wbuf[i - 1] != ','; i--);
491 for (; i < state->curpos && state->wbuf[i] == ' '; i++);
493 my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
494 mutt_query_complete (buf, buflen);
495 replace_part (state, i, buf);
498 } else if (flags & M_COMMAND) {
499 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
501 if (i && buf[i - 1] == '=' &&
502 mutt_var_value_complete (buf, buflen, i))
504 else if (!mutt_command_complete (buf, buflen, i, state->tabs))
506 replace_part (state, 0, buf);
508 else if (flags & (M_FILE | M_EFILE)) {
509 my_wcstombs (buf, buflen, state->wbuf, state->curpos);
511 /* see if the path has changed from the last time */
512 if ((!tempbuf && !state->lastchar)
513 || (tempbuf && templen == state->lastchar
514 && !memcmp (tempbuf, state->wbuf,
515 state->lastchar * sizeof (wchar_t)))) {
516 mutt_select_file(buf, buflen,
517 ((flags & M_EFILE) ? M_SEL_FOLDER : 0) |
518 (multiple ? M_SEL_MULTI : 0), files, numfiles);
519 set_option (OPTNEEDREDRAW);
521 mutt_pretty_mailbox (buf);
523 mutt_history_add (hclass, buf);
528 /* file selection cancelled */
533 if (!mutt_complete (buf, buflen)) {
534 templen = state->lastchar;
535 p_realloc(&tempbuf, templen);
536 memcpy (tempbuf, state->wbuf, templen * sizeof (wchar_t));
539 BEEP (); /* let the user know that nothing matched */
540 replace_part (state, 0, buf);
546 case OP_EDITOR_QUOTE_CHAR:
550 /*ADDCH (LastKey); */
551 event = mutt_getch ();
552 if (event.ch != -1) {
558 case OP_EDITOR_TRANSPOSE_CHARS:
559 if (state->lastchar < 2)
564 if (state->curpos == 0)
566 else if (state->curpos < state->lastchar)
569 t = state->wbuf[state->curpos - 2];
570 state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
571 state->wbuf[state->curpos - 1] = t;
584 /* use the raw keypress */
587 if ((ch == '#') && (flags & M_LASTFOLDER)) {
589 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
594 /* treat ENTER the same as RETURN */
599 /* quietly ignore all other function keys */
603 /* gather the octets into a wide character */
609 k = mbrtowc (&wc, &c, 1, &mbstate);
610 if (k == (size_t) (-2))
612 else if (k && k != 1) {
613 p_clear(&mbstate, 1);
618 if (first && (flags & M_CLEAR)) {
620 if (iswprint(wc)) /* why? */
621 state->curpos = state->lastchar = 0;
624 if (wc == '\r' || wc == '\n') {
625 /* Convert from wide characters */
626 my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
628 mutt_history_add (hclass, buf);
634 tfiles = p_new(char *, *numfiles);
635 mutt_expand_path (buf, buflen);
636 tfiles[0] = m_strdup(buf);
642 else if (wc && (wc < ' ' || iswprint(wc))) { /* why? */
643 if (state->lastchar >= state->wbuflen) {
644 state->wbuflen = state->lastchar + 20;
645 p_realloc(&state->wbuf, state->wbuflen);
647 memmove (state->wbuf + state->curpos + 1, state->wbuf + state->curpos,
648 (state->lastchar - state->curpos) * sizeof (wchar_t));
649 state->wbuf[state->curpos++] = wc;
665 void mutt_free_enter_state (ENTER_STATE ** esp)
670 p_delete(&(*esp)->wbuf);
676 * very narrow screen might crash it
677 * sort out the input side