2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
15 #include "mutt_curses.h"
16 #include "mutt_menu.h"
27 extern int Charset_is_utf8; /* FIXME: bad modularisation */
29 extern size_t UngetCount;
31 static void print_enriched_string (int attr, unsigned char *s, int do_color)
35 size_t n = mutt_strlen ((char *) s);
38 memset (&mbstate, 0, sizeof (mbstate));
40 if (*s < M_TREE_MAX) {
42 SETCOLOR (MT_COLOR_TREE);
43 while (*s && *s < M_TREE_MAX) {
46 if (option (OPTASCIICHARS))
48 else if (Charset_is_utf8)
49 addstr ("\342\224\224"); /* WACS_LLCORNER */
54 if (option (OPTASCIICHARS))
56 else if (Charset_is_utf8)
57 addstr ("\342\224\214"); /* WACS_ULCORNER */
62 if (option (OPTASCIICHARS))
64 else if (Charset_is_utf8)
65 addstr ("\342\224\234"); /* WACS_LTEE */
70 if (option (OPTASCIICHARS))
72 else if (Charset_is_utf8)
73 addstr ("\342\224\200"); /* WACS_HLINE */
78 if (option (OPTASCIICHARS))
80 else if (Charset_is_utf8)
81 addstr ("\342\224\202"); /* WACS_VLINE */
86 if (option (OPTASCIICHARS))
88 else if (Charset_is_utf8)
89 addstr ("\342\224\254"); /* WACS_TTEE */
94 if (option (OPTASCIICHARS))
96 else if (Charset_is_utf8)
97 addstr ("\342\224\264"); /* WACS_BTEE */
108 addch ('*'); /* fake thread indicator */
125 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
126 addnstr ((char *) s, k);
134 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
137 strncpy (s, menu->dialog[i], l);
138 menu->current = -1; /* hide menubar */
141 menu->make_entry (s, l, menu, i);
144 void menu_pad_string (char *s, size_t n)
146 int shift = option (OPTARROWCURSOR) ? 3 : 0;
148 char *tmpbuf = safe_malloc (n);
150 if (option (OPTMBOXPANE))
151 cols = COLS - shift - SidebarWidth;
154 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, mutt_strlen (s), 1);
156 snprintf (s, n, "%s", tmpbuf); /* overkill */
160 void menu_redraw_full (MUTTMENU * menu)
162 SETCOLOR (MT_COLOR_NORMAL);
163 /* clear() doesn't optimize screen redraws */
167 if (option (OPTHELP)) {
168 SETCOLOR (MT_COLOR_STATUS);
169 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, 0);
170 mutt_paddstr (COLS, menu->help);
171 SETCOLOR (MT_COLOR_NORMAL);
173 menu->pagelen = LINES - 3;
176 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
177 menu->pagelen = LINES - 2;
182 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
185 void menu_redraw_status (MUTTMENU * menu)
189 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
190 SETCOLOR (MT_COLOR_STATUS);
191 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
192 mutt_paddstr (COLS, buf);
193 SETCOLOR (MT_COLOR_NORMAL);
194 menu->redraw &= ~REDRAW_STATUS;
197 void menu_redraw_index (MUTTMENU * menu)
203 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
205 menu_make_entry (buf, sizeof (buf), menu, i);
206 menu_pad_string (buf, sizeof (buf));
208 if (option (OPTARROWCURSOR)) {
209 attrset (menu->color (i));
210 CLEARLINE_WIN (i - menu->top + menu->offset);
212 if (i == menu->current) {
213 attrset (menu->color (i));
214 ADDCOLOR (MT_COLOR_INDICATOR);
216 attrset (menu->color (i));
220 move (i - menu->top + menu->offset, SidebarWidth + 3);
222 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
223 SETCOLOR (MT_COLOR_NORMAL);
226 attrset (menu->color (i));
228 if (i == menu->current) {
229 ADDCOLOR (MT_COLOR_INDICATOR);
230 BKGDSET (MT_COLOR_INDICATOR);
233 CLEARLINE_WIN (i - menu->top + menu->offset);
234 print_enriched_string (menu->color (i), (unsigned char *) buf,
236 SETCOLOR (MT_COLOR_NORMAL);
237 BKGDSET (MT_COLOR_NORMAL);
241 CLEARLINE_WIN (i - menu->top + menu->offset);
246 void menu_redraw_motion (MUTTMENU * menu)
251 menu->redraw &= ~REDRAW_MOTION;
255 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
256 SETCOLOR (MT_COLOR_NORMAL);
257 BKGDSET (MT_COLOR_NORMAL);
259 if (option (OPTARROWCURSOR)) {
260 /* clear the pointer */
261 attrset (menu->color (menu->oldcurrent));
264 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
266 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
267 menu_pad_string (buf, sizeof (buf));
268 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
269 print_enriched_string (menu->color (menu->oldcurrent),
270 (unsigned char *) buf, 1);
271 SETCOLOR (MT_COLOR_NORMAL);
274 /* now draw it in the new location */
275 move (menu->current + menu->offset - menu->top, SidebarWidth);
276 attrset (menu->color (menu->current));
277 ADDCOLOR (MT_COLOR_INDICATOR);
279 SETCOLOR (MT_COLOR_NORMAL);
282 /* erase the current indicator */
283 attrset (menu->color (menu->oldcurrent));
285 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
286 menu_pad_string (buf, sizeof (buf));
287 print_enriched_string (menu->color (menu->oldcurrent),
288 (unsigned char *) buf, 1);
290 /* now draw the new one to reflect the change */
291 menu_make_entry (buf, sizeof (buf), menu, menu->current);
292 menu_pad_string (buf, sizeof (buf));
293 attrset (menu->color (menu->current));
294 ADDCOLOR (MT_COLOR_INDICATOR);
295 BKGDSET (MT_COLOR_INDICATOR);
296 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
297 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
299 SETCOLOR (MT_COLOR_NORMAL);
300 BKGDSET (MT_COLOR_NORMAL);
302 menu->redraw &= REDRAW_STATUS;
305 void menu_redraw_current (MUTTMENU * menu)
309 move (menu->current + menu->offset - menu->top, SidebarWidth);
310 menu_make_entry (buf, sizeof (buf), menu, menu->current);
311 menu_pad_string (buf, sizeof (buf));
313 if (option (OPTARROWCURSOR)) {
314 int attr = menu->color (menu->current);
318 attrset (menu->color (menu->current));
319 ADDCOLOR (MT_COLOR_INDICATOR);
323 menu_pad_string (buf, sizeof (buf));
324 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
326 SETCOLOR (MT_COLOR_NORMAL);
329 attrset (menu->color (menu->current));
330 ADDCOLOR (MT_COLOR_INDICATOR);
331 BKGDSET (MT_COLOR_INDICATOR);
333 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
335 SETCOLOR (MT_COLOR_NORMAL);
336 BKGDSET (MT_COLOR_NORMAL);
338 menu->redraw &= REDRAW_STATUS;
341 void menu_redraw_prompt (MUTTMENU * menu)
344 if (option (OPTMSGERR)) {
346 unset_option (OPTMSGERR);
352 SETCOLOR (MT_COLOR_NORMAL);
353 mvaddstr (LINES - 1, 0, menu->prompt);
358 void menu_check_recenter (MUTTMENU * menu)
360 int c = MIN (MenuContext, menu->pagelen / 2);
361 int old_top = menu->top;
363 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
364 if (menu->top != 0) {
366 set_option (OPTNEEDREDRAW);
369 else if (menu->current >= menu->top + menu->pagelen - c) { /* indicator below bottom threshold */
370 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
371 menu->top = menu->current - menu->pagelen + c + 1;
375 c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
377 else if (menu->current < menu->top + c) { /* indicator above top threshold */
378 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
379 menu->top = menu->current - c;
383 c) * ((menu->top + menu->pagelen - 1 -
384 menu->current) / (menu->pagelen - c)) - c;
387 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
388 menu->top = MIN (menu->top, menu->max - menu->pagelen);
389 menu->top = MAX (menu->top, 0);
391 if (menu->top != old_top)
392 menu->redraw |= REDRAW_INDEX;
395 void menu_jump (MUTTMENU * menu)
398 char buf[SHORT_STRING];
401 mutt_ungetch (LastKey, 0);
403 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
405 if (n >= 0 && n < menu->max) {
407 menu->redraw = REDRAW_MOTION;
410 mutt_error _("Invalid index number.");
414 mutt_error _("No entries.");
417 void menu_next_line (MUTTMENU * menu)
420 int c = MIN (MenuContext, menu->pagelen / 2);
422 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
423 || (menu->max > menu->pagelen
425 menu->max - menu->pagelen))) {
427 if (menu->current < menu->top + c && menu->current < menu->max - 1)
429 menu->redraw = REDRAW_INDEX;
432 mutt_error _("You cannot scroll down farther.");
435 mutt_error _("No entries.");
438 void menu_prev_line (MUTTMENU * menu)
441 int c = MIN (MenuContext, menu->pagelen / 2);
444 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
446 menu->redraw = REDRAW_INDEX;
449 mutt_error _("You cannot scroll up farther.");
452 void menu_next_page (MUTTMENU * menu)
455 if (menu->top + menu->pagelen < menu->max) {
456 menu->top += menu->pagelen;
457 if (menu->current < menu->top)
458 menu->current = menu->top;
459 menu->redraw = REDRAW_INDEX;
461 else if (menu->current != menu->max - 1 && !menu->dialog) {
462 menu->current = menu->max - 1;
463 menu->redraw = REDRAW_MOTION;
466 mutt_error _("You are on the last page.");
469 mutt_error _("No entries.");
472 void menu_prev_page (MUTTMENU * menu)
474 int c = MIN (MenuContext, menu->pagelen / 2);
477 if ((menu->top -= menu->pagelen) < 0)
479 if (menu->current >= menu->top + menu->pagelen)
480 menu->current = menu->top + menu->pagelen - 1;
481 menu->redraw = REDRAW_INDEX;
483 else if (menu->current && !menu->dialog) {
485 menu->redraw = REDRAW_MOTION;
488 mutt_error _("You are on the first page.");
491 void menu_top_page (MUTTMENU * menu)
493 if (menu->current != menu->top) {
494 menu->current = menu->top;
495 menu->redraw = REDRAW_MOTION;
499 void menu_bottom_page (MUTTMENU * menu)
502 menu->current = menu->top + menu->pagelen - 1;
503 if (menu->current > menu->max - 1)
504 menu->current = menu->max - 1;
505 menu->redraw = REDRAW_MOTION;
508 mutt_error _("No entries.");
511 void menu_middle_page (MUTTMENU * menu)
516 i = menu->top + menu->pagelen;
517 if (i > menu->max - 1)
519 menu->current = menu->top + (i - menu->top) / 2;
520 menu->redraw = REDRAW_MOTION;
523 mutt_error _("No entries.");
526 void menu_first_entry (MUTTMENU * menu)
530 menu->redraw = REDRAW_MOTION;
533 mutt_error _("No entries.");
536 void menu_last_entry (MUTTMENU * menu)
539 menu->current = menu->max - 1;
540 menu->redraw = REDRAW_MOTION;
543 mutt_error _("No entries.");
546 void menu_half_up (MUTTMENU * menu)
549 if ((menu->top -= menu->pagelen / 2) < 0)
551 if (menu->current >= menu->top + menu->pagelen)
552 menu->current = menu->top + menu->pagelen - 1;
553 menu->redraw = REDRAW_INDEX;
555 else if (menu->current && !menu->dialog) {
557 menu->redraw = REDRAW_MOTION;
560 mutt_error _("First entry is shown.");
563 void menu_half_down (MUTTMENU * menu)
566 if (menu->top + menu->pagelen < menu->max) {
567 menu->top += menu->pagelen / 2;
568 if (menu->current < menu->top)
569 menu->current = menu->top;
570 menu->redraw = REDRAW_INDEX;
572 else if (menu->current != menu->max - 1 && !menu->dialog) {
573 menu->current = menu->max - 1;
574 menu->redraw = REDRAW_INDEX;
577 mutt_error _("Last entry is shown.");
580 mutt_error _("No entries.");
583 void menu_current_top (MUTTMENU * menu)
586 menu->top = menu->current;
587 menu->redraw = REDRAW_INDEX;
590 mutt_error _("No entries.");
593 void menu_current_middle (MUTTMENU * menu)
596 menu->top = menu->current - menu->pagelen / 2;
599 menu->redraw = REDRAW_INDEX;
602 mutt_error _("No entries.");
605 void menu_current_bottom (MUTTMENU * menu)
608 menu->top = menu->current - menu->pagelen + 1;
611 menu->redraw = REDRAW_INDEX;
614 mutt_error _("No entries.");
617 void menu_next_entry (MUTTMENU * menu)
619 if (menu->current < menu->max - 1) {
621 menu->redraw = REDRAW_MOTION;
624 mutt_error _("You are on the last entry.");
627 void menu_prev_entry (MUTTMENU * menu)
631 menu->redraw = REDRAW_MOTION;
634 mutt_error _("You are on the first entry.");
637 static int default_color (int i)
639 return ColorDefs[MT_COLOR_NORMAL];
642 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
644 char buf[LONG_STRING];
646 menu_make_entry (buf, sizeof (buf), m, n);
647 return (regexec (re, buf, 0, NULL, 0));
650 MUTTMENU *mutt_new_menu (void)
652 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
657 p->redraw = REDRAW_FULL;
658 p->pagelen = PAGELEN;
659 p->color = default_color;
660 p->search = menu_search_generic;
664 void mutt_menuDestroy (MUTTMENU ** p)
668 FREE (&(*p)->searchBuf);
671 for (i = 0; i < (*p)->max; i++)
672 FREE (&(*p)->dialog[i]);
674 FREE (&(*p)->dialog);
680 #define M_SEARCH_UP 1
681 #define M_SEARCH_DOWN 2
683 static int menu_search (MUTTMENU * menu, int op)
688 char buf[SHORT_STRING];
690 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
691 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
692 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
693 _("Reverse search for: "),
694 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
696 mutt_str_replace (&menu->searchBuf, buf);
697 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
700 if (!menu->searchBuf) {
701 mutt_error _("No search pattern.");
707 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
708 if (op == OP_SEARCH_OPPOSITE)
709 searchDir = -searchDir;
712 REGCOMP (&re, menu->searchBuf,
713 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
714 regerror (r, &re, buf, sizeof (buf));
716 mutt_error ("%s", buf);
720 r = menu->current + searchDir;
721 while (r >= 0 && r < menu->max) {
722 if (menu->search (menu, &re, r) == 0) {
731 mutt_error _("Not found.");
736 static int menu_dialog_translate_op (int i)
746 case OP_CURRENT_BOTTOM:
748 return OP_BOTTOM_PAGE;
749 case OP_CURRENT_MIDDLE:
750 return OP_MIDDLE_PAGE;
756 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
768 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
769 *ip = OP_MAX + (p - menu->keys + 1);
773 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
778 int menu_redraw (MUTTMENU * menu)
780 /* See if all or part of the screen needs to be updated. */
781 if (menu->redraw & REDRAW_FULL) {
782 menu_redraw_full (menu);
783 /* allow the caller to do any local configuration */
788 menu_check_recenter (menu);
790 if (menu->redraw & REDRAW_STATUS)
791 menu_redraw_status (menu);
792 if (menu->redraw & REDRAW_INDEX)
793 menu_redraw_index (menu);
794 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
795 menu_redraw_motion (menu);
796 else if (menu->redraw == REDRAW_CURRENT)
797 menu_redraw_current (menu);
800 menu_redraw_prompt (menu);
805 int mutt_menuLoop (MUTTMENU * menu)
810 if (option (OPTMENUCALLER)) {
811 unset_option (OPTMENUCALLER);
822 if (menu_redraw (menu) == OP_REDRAW)
825 menu->oldcurrent = menu->current;
828 /* move the cursor out of the way */
829 move (menu->current - menu->top + menu->offset,
830 (option (OPTARROWCURSOR) ? 2 : COLS - 1));
834 /* try to catch dialog keys before ops */
835 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
838 i = km_dokey (menu->menu);
839 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
841 mvaddstr (LINES - 1, 0, "Tag-");
843 i = km_dokey (menu->menu);
845 CLEARLINE (LINES - 1);
847 else if (i == OP_TAG_PREFIX) {
848 mutt_error _("No tagged entries.");
852 else { /* None tagged, OP_TAG_PREFIX_COND */
856 while (UngetCount > 0) {
858 if (tmp.op == OP_END_COND)
861 mutt_message _("Nothing to do.");
866 else if (menu->tagged && option (OPTAUTOTAG))
873 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
875 mutt_resize_screen ();
876 menu->redraw = REDRAW_FULL;
878 clearok (stdscr, TRUE); /*force complete redraw */
888 /* Convert menubar movement to scrolling */
890 i = menu_dialog_translate_op (i);
894 menu_next_entry (menu);
897 menu_prev_entry (menu);
900 menu_half_down (menu);
906 menu_next_page (menu);
909 menu_prev_page (menu);
912 menu_next_line (menu);
915 menu_prev_line (menu);
918 menu_first_entry (menu);
921 menu_last_entry (menu);
924 menu_top_page (menu);
927 menu_middle_page (menu);
930 menu_bottom_page (menu);
933 menu_current_top (menu);
935 case OP_CURRENT_MIDDLE:
936 menu_current_middle (menu);
938 case OP_CURRENT_BOTTOM:
939 menu_current_bottom (menu);
942 case OP_SEARCH_REVERSE:
944 case OP_SEARCH_OPPOSITE:
945 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
946 menu->oldcurrent = menu->current;
947 if ((menu->current = menu_search (menu, i)) != -1)
948 menu->redraw = REDRAW_MOTION;
950 menu->current = menu->oldcurrent;
953 mutt_error _("Search is not implemented for this menu.");
958 mutt_error (_("Jumping is not implemented for dialogs."));
964 case OP_ENTER_COMMAND:
965 CurrentMenu = menu->menu;
966 mutt_enter_command ();
967 if (option (OPTFORCEREDRAWINDEX)) {
968 menu->redraw = REDRAW_FULL;
969 unset_option (OPTFORCEREDRAWINDEX);
970 unset_option (OPTFORCEREDRAWPAGER);
975 if (menu->tag && !menu->dialog) {
976 if (menu->tagprefix && !option (OPTAUTOTAG)) {
977 for (i = 0; i < menu->max; i++)
978 menu->tagged += menu->tag (menu, i, 0);
979 menu->redraw = REDRAW_INDEX;
981 else if (menu->max) {
982 int i = menu->tag (menu, menu->current, -1);
985 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) {
987 menu->redraw = REDRAW_MOTION_RESYNCH;
990 menu->redraw = REDRAW_CURRENT;
993 mutt_error _("No entries.");
996 mutt_error _("Tagging is not supported.");
999 case OP_SHELL_ESCAPE:
1000 mutt_shell_escape ();
1001 MAYBE_REDRAW (menu->redraw);
1009 clearok (stdscr, TRUE);
1010 menu->redraw = REDRAW_FULL;
1014 mutt_help (menu->menu);
1015 menu->redraw = REDRAW_FULL;
1019 km_error_key (menu->menu);