2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * * Parts of it were written/modified by:
5 * Nico Golde <nico@ngolde.de>
7 * This file is part of mutt-ng, see http://www.muttng.org/.
8 * It's licensed under the GNU General Public License,
9 * please see the file GPL in the top level source directory.
17 #include "mutt_curses.h"
18 #include "mutt_menu.h"
33 extern int Charset_is_utf8; /* FIXME: bad modularisation */
35 extern size_t UngetCount;
37 static void print_enriched_string (int attr, unsigned char *s, int do_color)
41 size_t n = mutt_strlen ((char *) s);
44 memset (&mbstate, 0, sizeof (mbstate));
46 if (*s < M_TREE_MAX) {
48 SETCOLOR (MT_COLOR_TREE);
49 while (*s && *s < M_TREE_MAX) {
52 if (option (OPTASCIICHARS))
54 else if (Charset_is_utf8)
55 addstr ("\342\224\224"); /* WACS_LLCORNER */
60 if (option (OPTASCIICHARS))
62 else if (Charset_is_utf8)
63 addstr ("\342\224\214"); /* WACS_ULCORNER */
68 if (option (OPTASCIICHARS))
70 else if (Charset_is_utf8)
71 addstr ("\342\224\234"); /* WACS_LTEE */
76 if (option (OPTASCIICHARS))
78 else if (Charset_is_utf8)
79 addstr ("\342\224\200"); /* WACS_HLINE */
84 if (option (OPTASCIICHARS))
86 else if (Charset_is_utf8)
87 addstr ("\342\224\202"); /* WACS_VLINE */
92 if (option (OPTASCIICHARS))
94 else if (Charset_is_utf8)
95 addstr ("\342\224\254"); /* WACS_TTEE */
100 if (option (OPTASCIICHARS))
102 else if (Charset_is_utf8)
103 addstr ("\342\224\264"); /* WACS_BTEE */
114 addch ('*'); /* fake thread indicator */
131 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
132 addnstr ((char *) s, k);
140 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
143 strncpy (s, menu->dialog[i], l);
144 menu->current = -1; /* hide menubar */
147 menu->make_entry (s, l, menu, i);
150 void menu_pad_string (char *s, size_t n)
152 int shift = option (OPTARROWCURSOR) ? 3 : 0;
154 char *tmpbuf = safe_malloc (n);
156 if (option (OPTMBOXPANE))
157 cols = COLS - shift - SidebarWidth;
160 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, mutt_strlen (s), 1);
162 snprintf (s, n, "%s", tmpbuf); /* overkill */
166 void menu_redraw_full (MUTTMENU * menu)
168 SETCOLOR (MT_COLOR_NORMAL);
169 /* clear() doesn't optimize screen redraws */
173 if (!option(OPTMBOXPANE)) SidebarWidth=0;
175 if (option (OPTHELP)) {
176 SETCOLOR (MT_COLOR_STATUS);
177 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, 0);
178 mutt_paddstr (COLS, menu->help);
179 SETCOLOR (MT_COLOR_NORMAL);
181 menu->pagelen = LINES - 3;
184 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
185 menu->pagelen = LINES - 2;
190 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
193 void menu_redraw_status (MUTTMENU * menu)
197 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
198 SETCOLOR (MT_COLOR_STATUS);
199 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
200 mutt_paddstr (COLS, buf);
201 SETCOLOR (MT_COLOR_NORMAL);
202 menu->redraw &= ~REDRAW_STATUS;
205 void menu_redraw_index (MUTTMENU * menu)
210 if (!option(OPTMBOXPANE)) SidebarWidth=0;
212 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
214 menu_make_entry (buf, sizeof (buf), menu, i);
215 menu_pad_string (buf, sizeof (buf));
217 if (option (OPTARROWCURSOR)) {
218 attrset (menu->color (i));
219 CLEARLINE_WIN (i - menu->top + menu->offset);
221 if (i == menu->current) {
222 attrset (menu->color (i));
223 ADDCOLOR (MT_COLOR_INDICATOR);
225 attrset (menu->color (i));
229 attrset (menu->color (i));
230 move (i - menu->top + menu->offset, SidebarWidth);
234 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
235 SETCOLOR (MT_COLOR_NORMAL);
238 attrset (menu->color (i));
240 if (i == menu->current) {
241 ADDCOLOR (MT_COLOR_INDICATOR);
242 BKGDSET (MT_COLOR_INDICATOR);
245 CLEARLINE_WIN (i - menu->top + menu->offset);
246 print_enriched_string (menu->color (i), (unsigned char *) buf,
248 SETCOLOR (MT_COLOR_NORMAL);
249 BKGDSET (MT_COLOR_NORMAL);
253 CLEARLINE_WIN (i - menu->top + menu->offset);
258 void menu_redraw_motion (MUTTMENU * menu)
263 menu->redraw &= ~REDRAW_MOTION;
267 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
268 SETCOLOR (MT_COLOR_NORMAL);
269 BKGDSET (MT_COLOR_NORMAL);
271 if (option (OPTARROWCURSOR)) {
272 /* clear the pointer */
273 attrset (menu->color (menu->oldcurrent));
276 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
278 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
279 menu_pad_string (buf, sizeof (buf));
280 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
281 print_enriched_string (menu->color (menu->oldcurrent),
282 (unsigned char *) buf, 1);
283 SETCOLOR (MT_COLOR_NORMAL);
286 /* now draw it in the new location */
287 move (menu->current + menu->offset - menu->top, SidebarWidth);
288 attrset (menu->color (menu->current));
289 ADDCOLOR (MT_COLOR_INDICATOR);
291 SETCOLOR (MT_COLOR_NORMAL);
294 /* erase the current indicator */
295 attrset (menu->color (menu->oldcurrent));
297 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
298 menu_pad_string (buf, sizeof (buf));
299 print_enriched_string (menu->color (menu->oldcurrent),
300 (unsigned char *) buf, 1);
302 /* now draw the new one to reflect the change */
303 menu_make_entry (buf, sizeof (buf), menu, menu->current);
304 menu_pad_string (buf, sizeof (buf));
305 attrset (menu->color (menu->current));
306 ADDCOLOR (MT_COLOR_INDICATOR);
307 BKGDSET (MT_COLOR_INDICATOR);
308 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
309 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
311 SETCOLOR (MT_COLOR_NORMAL);
312 BKGDSET (MT_COLOR_NORMAL);
314 menu->redraw &= REDRAW_STATUS;
317 void menu_redraw_current (MUTTMENU * menu)
321 move (menu->current + menu->offset - menu->top, SidebarWidth);
322 menu_make_entry (buf, sizeof (buf), menu, menu->current);
323 menu_pad_string (buf, sizeof (buf));
325 if (!option(OPTMBOXPANE)) SidebarWidth=0;
327 if (option (OPTARROWCURSOR)) {
328 int attr = menu->color (menu->current);
332 attrset (menu->color (menu->current));
333 ADDCOLOR (MT_COLOR_INDICATOR);
337 menu_pad_string (buf, sizeof (buf));
338 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
340 SETCOLOR (MT_COLOR_NORMAL);
343 attrset (menu->color (menu->current));
344 ADDCOLOR (MT_COLOR_INDICATOR);
345 BKGDSET (MT_COLOR_INDICATOR);
347 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
349 SETCOLOR (MT_COLOR_NORMAL);
350 BKGDSET (MT_COLOR_NORMAL);
352 menu->redraw &= REDRAW_STATUS;
355 void menu_redraw_prompt (MUTTMENU * menu)
358 if (option (OPTMSGERR)) {
360 unset_option (OPTMSGERR);
366 SETCOLOR (MT_COLOR_NORMAL);
367 mvaddstr (LINES - 1, 0, menu->prompt);
372 void menu_check_recenter (MUTTMENU * menu)
374 int c = MIN (MenuContext, menu->pagelen / 2);
375 int old_top = menu->top;
377 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
378 if (menu->top != 0) {
380 set_option (OPTNEEDREDRAW);
383 else if (menu->current >= menu->top + menu->pagelen - c) { /* indicator below bottom threshold */
384 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
385 menu->top = menu->current - menu->pagelen + c + 1;
389 c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
391 else if (menu->current < menu->top + c) { /* indicator above top threshold */
392 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
393 menu->top = menu->current - c;
397 c) * ((menu->top + menu->pagelen - 1 -
398 menu->current) / (menu->pagelen - c)) - c;
401 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
402 menu->top = MIN (menu->top, menu->max - menu->pagelen);
403 menu->top = MAX (menu->top, 0);
405 if (menu->top != old_top)
406 menu->redraw |= REDRAW_INDEX;
409 void menu_jump (MUTTMENU * menu)
412 char buf[SHORT_STRING];
415 mutt_ungetch (LastKey, 0);
417 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
419 if (n >= 0 && n < menu->max) {
421 menu->redraw = REDRAW_MOTION;
424 mutt_error _("Invalid index number.");
428 mutt_error _("No entries.");
431 void menu_next_line (MUTTMENU * menu)
434 int c = MIN (MenuContext, menu->pagelen / 2);
436 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
437 || (menu->max > menu->pagelen
439 menu->max - menu->pagelen))) {
441 if (menu->current < menu->top + c && menu->current < menu->max - 1)
443 menu->redraw = REDRAW_INDEX;
446 mutt_error _("You cannot scroll down farther.");
449 mutt_error _("No entries.");
452 void menu_prev_line (MUTTMENU * menu)
455 int c = MIN (MenuContext, menu->pagelen / 2);
458 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
460 menu->redraw = REDRAW_INDEX;
463 mutt_error _("You cannot scroll up farther.");
466 void menu_next_page (MUTTMENU * menu)
469 if (menu->top + menu->pagelen < menu->max) {
470 menu->top += menu->pagelen;
471 if (menu->current < menu->top)
472 menu->current = menu->top;
473 menu->redraw = REDRAW_INDEX;
475 else if (menu->current != menu->max - 1 && !menu->dialog) {
476 menu->current = menu->max - 1;
477 menu->redraw = REDRAW_MOTION;
480 mutt_error _("You are on the last page.");
483 mutt_error _("No entries.");
486 void menu_prev_page (MUTTMENU * menu)
488 int c = MIN (MenuContext, menu->pagelen / 2);
491 if ((menu->top -= menu->pagelen) < 0)
493 if (menu->current >= menu->top + menu->pagelen)
494 menu->current = menu->top + menu->pagelen - 1;
495 menu->redraw = REDRAW_INDEX;
497 else if (menu->current && !menu->dialog) {
499 menu->redraw = REDRAW_MOTION;
502 mutt_error _("You are on the first page.");
505 void menu_top_page (MUTTMENU * menu)
507 if (menu->current != menu->top) {
508 menu->current = menu->top;
509 menu->redraw = REDRAW_MOTION;
513 void menu_bottom_page (MUTTMENU * menu)
516 menu->current = menu->top + menu->pagelen - 1;
517 if (menu->current > menu->max - 1)
518 menu->current = menu->max - 1;
519 menu->redraw = REDRAW_MOTION;
522 mutt_error _("No entries.");
525 void menu_middle_page (MUTTMENU * menu)
530 i = menu->top + menu->pagelen;
531 if (i > menu->max - 1)
533 menu->current = menu->top + (i - menu->top) / 2;
534 menu->redraw = REDRAW_MOTION;
537 mutt_error _("No entries.");
540 void menu_first_entry (MUTTMENU * menu)
544 menu->redraw = REDRAW_MOTION;
547 mutt_error _("No entries.");
550 void menu_last_entry (MUTTMENU * menu)
553 menu->current = menu->max - 1;
554 menu->redraw = REDRAW_MOTION;
557 mutt_error _("No entries.");
560 void menu_half_up (MUTTMENU * menu)
563 if ((menu->top -= menu->pagelen / 2) < 0)
565 if (menu->current >= menu->top + menu->pagelen)
566 menu->current = menu->top + menu->pagelen - 1;
567 menu->redraw = REDRAW_INDEX;
569 else if (menu->current && !menu->dialog) {
571 menu->redraw = REDRAW_MOTION;
574 mutt_error _("First entry is shown.");
577 void menu_half_down (MUTTMENU * menu)
580 if (menu->top + menu->pagelen < menu->max) {
581 menu->top += menu->pagelen / 2;
582 if (menu->current < menu->top)
583 menu->current = menu->top;
584 menu->redraw = REDRAW_INDEX;
586 else if (menu->current != menu->max - 1 && !menu->dialog) {
587 menu->current = menu->max - 1;
588 menu->redraw = REDRAW_INDEX;
591 mutt_error _("Last entry is shown.");
594 mutt_error _("No entries.");
597 void menu_current_top (MUTTMENU * menu)
600 menu->top = menu->current;
601 menu->redraw = REDRAW_INDEX;
604 mutt_error _("No entries.");
607 void menu_current_middle (MUTTMENU * menu)
610 menu->top = menu->current - menu->pagelen / 2;
613 menu->redraw = REDRAW_INDEX;
616 mutt_error _("No entries.");
619 void menu_current_bottom (MUTTMENU * menu)
622 menu->top = menu->current - menu->pagelen + 1;
625 menu->redraw = REDRAW_INDEX;
628 mutt_error _("No entries.");
631 void menu_next_entry (MUTTMENU * menu)
633 if (menu->current < menu->max - 1) {
635 menu->redraw = REDRAW_MOTION;
638 mutt_error _("You are on the last entry.");
641 void menu_prev_entry (MUTTMENU * menu)
645 menu->redraw = REDRAW_MOTION;
648 mutt_error _("You are on the first entry.");
651 static int default_color (int i)
653 return ColorDefs[MT_COLOR_NORMAL];
656 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
658 char buf[LONG_STRING];
660 menu_make_entry (buf, sizeof (buf), m, n);
661 return (regexec (re, buf, 0, NULL, 0));
664 MUTTMENU *mutt_new_menu (void)
666 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
671 p->redraw = REDRAW_FULL;
672 p->pagelen = PAGELEN;
673 p->color = default_color;
674 p->search = menu_search_generic;
678 void mutt_menuDestroy (MUTTMENU ** p)
682 FREE (&(*p)->searchBuf);
685 for (i = 0; i < (*p)->max; i++)
686 FREE (&(*p)->dialog[i]);
688 FREE (&(*p)->dialog);
694 #define M_SEARCH_UP 1
695 #define M_SEARCH_DOWN 2
697 static int menu_search (MUTTMENU * menu, int op)
702 char buf[SHORT_STRING];
704 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
705 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
706 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
707 _("Reverse search for: "),
708 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
710 str_replace (&menu->searchBuf, buf);
711 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
714 if (!menu->searchBuf) {
715 mutt_error _("No search pattern.");
721 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
722 if (op == OP_SEARCH_OPPOSITE)
723 searchDir = -searchDir;
726 REGCOMP (&re, menu->searchBuf,
727 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
728 regerror (r, &re, buf, sizeof (buf));
730 mutt_error ("%s", buf);
734 r = menu->current + searchDir;
735 while (r >= 0 && r < menu->max) {
736 if (menu->search (menu, &re, r) == 0) {
745 mutt_error _("Not found.");
750 static int menu_dialog_translate_op (int i)
760 case OP_CURRENT_BOTTOM:
762 return OP_BOTTOM_PAGE;
763 case OP_CURRENT_MIDDLE:
764 return OP_MIDDLE_PAGE;
770 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
782 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
783 *ip = OP_MAX + (p - menu->keys + 1);
787 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
792 int menu_redraw (MUTTMENU * menu)
794 /* See if all or part of the screen needs to be updated. */
795 if (menu->redraw & REDRAW_FULL) {
796 menu_redraw_full (menu);
797 /* allow the caller to do any local configuration */
802 menu_check_recenter (menu);
804 if (menu->redraw & REDRAW_STATUS)
805 menu_redraw_status (menu);
806 if (menu->redraw & REDRAW_INDEX)
807 menu_redraw_index (menu);
808 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
809 menu_redraw_motion (menu);
810 else if (menu->redraw == REDRAW_CURRENT)
811 menu_redraw_current (menu);
814 menu_redraw_prompt (menu);
819 int mutt_menuLoop (MUTTMENU * menu)
824 if (option (OPTMENUCALLER)) {
825 unset_option (OPTMENUCALLER);
836 if (menu_redraw (menu) == OP_REDRAW)
839 menu->oldcurrent = menu->current;
841 if (option (OPTARROWCURSOR))
842 move (menu->current - menu->top + menu->offset, 2);
843 else if (option (OPTBRAILLEFRIENDLY))
844 move (menu->current - menu->top + menu->offset, 0);
846 move (menu->current - menu->top + menu->offset, COLS - 1);
851 /* try to catch dialog keys before ops */
852 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
855 i = km_dokey (menu->menu);
856 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
858 mvaddstr (LINES - 1, 0, "Tag-");
860 i = km_dokey (menu->menu);
862 CLEARLINE (LINES - 1);
864 else if (i == OP_TAG_PREFIX) {
865 mutt_error _("No tagged entries.");
869 else { /* None tagged, OP_TAG_PREFIX_COND */
873 while (UngetCount > 0) {
875 if (tmp.op == OP_END_COND)
878 mutt_message _("Nothing to do.");
883 else if (menu->tagged && option (OPTAUTOTAG))
890 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
892 mutt_resize_screen ();
893 menu->redraw = REDRAW_FULL;
895 clearok (stdscr, TRUE); /*force complete redraw */
905 /* Convert menubar movement to scrolling */
907 i = menu_dialog_translate_op (i);
911 menu_next_entry (menu);
914 menu_prev_entry (menu);
917 menu_half_down (menu);
923 menu_next_page (menu);
926 menu_prev_page (menu);
929 menu_next_line (menu);
932 menu_prev_line (menu);
935 menu_first_entry (menu);
938 menu_last_entry (menu);
941 menu_top_page (menu);
944 menu_middle_page (menu);
947 menu_bottom_page (menu);
950 menu_current_top (menu);
952 case OP_CURRENT_MIDDLE:
953 menu_current_middle (menu);
955 case OP_CURRENT_BOTTOM:
956 menu_current_bottom (menu);
959 case OP_SEARCH_REVERSE:
961 case OP_SEARCH_OPPOSITE:
962 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
963 menu->oldcurrent = menu->current;
964 if ((menu->current = menu_search (menu, i)) != -1)
965 menu->redraw = REDRAW_MOTION;
967 menu->current = menu->oldcurrent;
970 mutt_error _("Search is not implemented for this menu.");
975 mutt_error (_("Jumping is not implemented for dialogs."));
981 case OP_ENTER_COMMAND:
982 CurrentMenu = menu->menu;
983 mutt_enter_command ();
984 if (option (OPTFORCEREDRAWINDEX)) {
985 menu->redraw = REDRAW_FULL;
986 unset_option (OPTFORCEREDRAWINDEX);
987 unset_option (OPTFORCEREDRAWPAGER);
992 if (menu->tag && !menu->dialog) {
993 if (menu->tagprefix && !option (OPTAUTOTAG)) {
994 for (i = 0; i < menu->max; i++)
995 menu->tagged += menu->tag (menu, i, 0);
996 menu->redraw = REDRAW_INDEX;
998 else if (menu->max) {
999 int i = menu->tag (menu, menu->current, -1);
1002 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) {
1004 menu->redraw = REDRAW_MOTION_RESYNCH;
1007 menu->redraw = REDRAW_CURRENT;
1010 mutt_error _("No entries.");
1013 mutt_error _("Tagging is not supported.");
1016 case OP_SHELL_ESCAPE:
1017 mutt_shell_escape ();
1018 MAYBE_REDRAW (menu->redraw);
1026 clearok (stdscr, TRUE);
1027 menu->redraw = REDRAW_FULL;
1031 mutt_help (menu->menu);
1032 menu->redraw = REDRAW_FULL;
1036 km_error_key (menu->menu);