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.
16 #include "mutt_curses.h"
17 #include "mutt_menu.h"
32 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
34 extern int Charset_is_utf8; /* FIXME: bad modularisation */
36 extern size_t UngetCount;
38 static void print_enriched_string (int attr, unsigned char *s, int do_color)
42 size_t n = str_len ((char *) s);
45 memset (&mbstate, 0, sizeof (mbstate));
47 if (*s < M_TREE_MAX) {
49 SETCOLOR (MT_COLOR_TREE);
50 while (*s && *s < M_TREE_MAX) {
53 if (option (OPTASCIICHARS))
55 else if (Charset_is_utf8)
56 addstr ("\342\224\224"); /* WACS_LLCORNER */
61 if (option (OPTASCIICHARS))
63 else if (Charset_is_utf8)
64 addstr ("\342\224\214"); /* WACS_ULCORNER */
69 if (option (OPTASCIICHARS))
71 else if (Charset_is_utf8)
72 addstr ("\342\224\234"); /* WACS_LTEE */
77 if (option (OPTASCIICHARS))
79 else if (Charset_is_utf8)
80 addstr ("\342\224\200"); /* WACS_HLINE */
85 if (option (OPTASCIICHARS))
87 else if (Charset_is_utf8)
88 addstr ("\342\224\202"); /* WACS_VLINE */
93 if (option (OPTASCIICHARS))
95 else if (Charset_is_utf8)
96 addstr ("\342\224\254"); /* WACS_TTEE */
101 if (option (OPTASCIICHARS))
103 else if (Charset_is_utf8)
104 addstr ("\342\224\264"); /* WACS_BTEE */
115 addch ('*'); /* fake thread indicator */
132 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
133 addnstr ((char *) s, k);
141 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
144 strncpy (s, menu->dialog[i], l);
145 menu->current = -1; /* hide menubar */
148 menu->make_entry (s, l, menu, i);
151 void menu_pad_string (char *s, size_t n)
153 int shift = option (OPTARROWCURSOR) ? 3 : 0;
155 char *tmpbuf = mem_malloc (n);
157 if (option (OPTMBOXPANE))
158 cols = COLS - shift - SidebarWidth;
161 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, str_len (s), 1);
163 snprintf (s, n, "%s", tmpbuf); /* overkill */
167 void menu_redraw_full (MUTTMENU * menu)
169 SETCOLOR (MT_COLOR_NORMAL);
170 /* clear() doesn't optimize screen redraws */
174 if (option (OPTHELP)) {
175 SETCOLOR (MT_COLOR_STATUS);
176 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, SW);
177 mutt_paddstr (COLS-SW, menu->help);
178 SETCOLOR (MT_COLOR_NORMAL);
180 menu->pagelen = LINES - 3;
183 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
184 menu->pagelen = LINES - 2;
187 sidebar_draw_frames();
191 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
194 void menu_redraw_status (MUTTMENU * menu)
198 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
199 SETCOLOR (MT_COLOR_STATUS);
200 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, SW);
201 mutt_paddstr (COLS-SW, buf);
202 SETCOLOR (MT_COLOR_NORMAL);
203 menu->redraw &= ~REDRAW_STATUS;
204 sidebar_draw_frames();
207 void menu_redraw_index (MUTTMENU * menu)
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);
224 BKGDSET (MT_COLOR_INDICATOR);
226 attrset (menu->color (i));
230 attrset (menu->color (i));
231 move (i - menu->top + menu->offset, SW);
235 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
236 SETCOLOR (MT_COLOR_NORMAL);
237 BKGDSET (MT_COLOR_NORMAL);
240 attrset (menu->color (i));
242 if (i == menu->current) {
243 ADDCOLOR (MT_COLOR_INDICATOR);
244 BKGDSET (MT_COLOR_INDICATOR);
247 CLEARLINE_WIN (i - menu->top + menu->offset);
249 move (i - menu->top + menu->offset, SW);
250 print_enriched_string (menu->color (i), (unsigned char *) buf,
252 SETCOLOR (MT_COLOR_NORMAL);
253 BKGDSET (MT_COLOR_NORMAL);
257 CLEARLINE_WIN (i - menu->top + menu->offset);
260 /* sidebar_draw_frames(); */
265 void menu_redraw_motion (MUTTMENU * menu)
270 menu->redraw &= ~REDRAW_MOTION;
274 move (menu->oldcurrent + menu->offset - menu->top, SW);
275 SETCOLOR (MT_COLOR_NORMAL);
276 BKGDSET (MT_COLOR_NORMAL);
278 if (option (OPTARROWCURSOR)) {
279 /* clear the pointer */
280 attrset (menu->color (menu->oldcurrent));
283 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
285 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
286 menu_pad_string (buf, sizeof (buf));
287 move (menu->oldcurrent + menu->offset - menu->top, SW + 3);
288 print_enriched_string (menu->color (menu->oldcurrent),
289 (unsigned char *) buf, 1);
290 SETCOLOR (MT_COLOR_NORMAL);
293 /* now draw it in the new location */
294 move (menu->current + menu->offset - menu->top, SW);
295 attrset (menu->color (menu->current));
296 ADDCOLOR (MT_COLOR_INDICATOR);
298 SETCOLOR (MT_COLOR_NORMAL);
301 /* erase the current indicator */
302 attrset (menu->color (menu->oldcurrent));
304 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
305 menu_pad_string (buf, sizeof (buf));
306 print_enriched_string (menu->color (menu->oldcurrent),
307 (unsigned char *) buf, 1);
309 /* now draw the new one to reflect the change */
310 menu_make_entry (buf, sizeof (buf), menu, menu->current);
311 menu_pad_string (buf, sizeof (buf));
312 attrset (menu->color (menu->current));
313 ADDCOLOR (MT_COLOR_INDICATOR);
314 BKGDSET (MT_COLOR_INDICATOR);
315 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
316 move (menu->current + menu->offset - menu->top, SW);
317 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
319 SETCOLOR (MT_COLOR_NORMAL);
320 BKGDSET (MT_COLOR_NORMAL);
322 menu->redraw &= REDRAW_STATUS;
325 void menu_redraw_current (MUTTMENU * menu)
329 move (menu->current + menu->offset - menu->top, SW);
330 menu_make_entry (buf, sizeof (buf), menu, menu->current);
331 menu_pad_string (buf, sizeof (buf));
333 if (option (OPTARROWCURSOR)) {
334 int attr = menu->color (menu->current);
338 attrset (menu->color (menu->current));
339 ADDCOLOR (MT_COLOR_INDICATOR);
343 menu_pad_string (buf, sizeof (buf));
344 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
346 SETCOLOR (MT_COLOR_NORMAL);
349 attrset (menu->color (menu->current));
350 ADDCOLOR (MT_COLOR_INDICATOR);
351 BKGDSET (MT_COLOR_INDICATOR);
353 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
355 SETCOLOR (MT_COLOR_NORMAL);
356 BKGDSET (MT_COLOR_NORMAL);
358 menu->redraw &= REDRAW_STATUS;
361 void menu_redraw_prompt (MUTTMENU * menu)
364 if (option (OPTMSGERR)) {
366 unset_option (OPTMSGERR);
372 SETCOLOR (MT_COLOR_NORMAL);
373 mvaddstr (LINES - 1, 0, menu->prompt);
378 void menu_check_recenter (MUTTMENU * menu)
380 int c = MIN (MenuContext, menu->pagelen / 2);
381 int old_top = menu->top;
383 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
384 if (menu->top != 0) {
386 set_option (OPTNEEDREDRAW);
389 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
390 if (menu->current < menu->top + c)
391 menu->top = menu->current - c;
392 else if (menu->current >= menu->top + menu->pagelen - c)
393 menu->top = menu->current - menu->pagelen + c + 1;
395 if (menu->current < menu->top + c)
396 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
397 else if ((menu->current >= menu->top + menu->pagelen - c))
398 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
402 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
403 menu->top = MIN (menu->top, menu->max - menu->pagelen);
404 menu->top = MAX (menu->top, 0);
406 if (menu->top != old_top)
407 menu->redraw |= REDRAW_INDEX;
410 void menu_jump (MUTTMENU * menu)
413 char buf[SHORT_STRING];
416 mutt_ungetch (LastKey, 0);
418 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
420 if (n >= 0 && n < menu->max) {
422 menu->redraw = REDRAW_MOTION;
425 mutt_error _("Invalid index number.");
429 mutt_error _("No entries.");
432 void menu_next_line (MUTTMENU * menu)
435 int c = MIN (MenuContext, menu->pagelen / 2);
437 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
438 || (menu->max > menu->pagelen
440 menu->max - menu->pagelen))) {
442 if (menu->current < menu->top + c && menu->current < menu->max - 1)
444 menu->redraw = REDRAW_INDEX;
447 mutt_error _("You cannot scroll down farther.");
450 mutt_error _("No entries.");
453 void menu_prev_line (MUTTMENU * menu)
456 int c = MIN (MenuContext, menu->pagelen / 2);
459 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
461 menu->redraw = REDRAW_INDEX;
464 mutt_error _("You cannot scroll up farther.");
468 * pageup: jumplen == -pagelen
469 * pagedown: jumplen == pagelen
470 * halfup: jumplen == -pagelen/2
471 * halfdown: jumplen == pagelen/2
473 #define DIRECTION ((neg * 2) + 1)
474 void menu_length_jump (MUTTMENU *menu, int jumplen) {
475 int tmp, neg = (jumplen >= 0) ? 0 : -1;
476 int c = MIN (MenuContext, menu->pagelen / 2);
479 /* possible to scroll? */
480 if (DIRECTION * menu->top <
481 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
482 menu->top += jumplen;
484 /* jumped too long? */
485 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
488 /* need to move the cursor? */
490 (tmp = (menu->current - (menu->top +
491 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
492 menu->current -= tmp;
494 menu->redraw = REDRAW_INDEX;
496 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
497 menu->current += jumplen;
498 menu->redraw = REDRAW_MOTION;
501 mutt_error (neg ? _("You are on the first page.")
502 : _("You are on the last page."));
504 menu->current = MIN (menu->current, menu->max - 1);
505 menu->current = MAX (menu->current, 0);
508 mutt_error _("No entries.");
512 void menu_next_page (MUTTMENU *menu) {
513 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
516 void menu_prev_page (MUTTMENU *menu) {
517 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
520 void menu_half_down (MUTTMENU *menu) {
521 menu_length_jump (menu, menu->pagelen / 2);
524 void menu_half_up (MUTTMENU *menu) {
525 menu_length_jump (menu, 0 - menu->pagelen / 2);
528 void menu_top_page (MUTTMENU *menu) {
529 if (menu->current != menu->top) {
530 menu->current = menu->top;
531 menu->redraw = REDRAW_MOTION;
535 void menu_bottom_page (MUTTMENU *menu) {
537 menu->current = menu->top + menu->pagelen - 1;
538 if (menu->current > menu->max - 1)
539 menu->current = menu->max - 1;
540 menu->redraw = REDRAW_MOTION;
543 mutt_error _("No entries.");
546 void menu_middle_page (MUTTMENU *menu) {
550 i = menu->top + menu->pagelen;
551 if (i > menu->max - 1)
553 menu->current = menu->top + (i - menu->top) / 2;
554 menu->redraw = REDRAW_MOTION;
557 mutt_error _("No entries.");
560 void menu_first_entry (MUTTMENU *menu) {
563 menu->redraw = REDRAW_MOTION;
566 mutt_error _("No entries.");
569 void menu_last_entry (MUTTMENU *menu) {
571 menu->current = menu->max - 1;
572 menu->redraw = REDRAW_MOTION;
575 mutt_error _("No entries.");
578 void menu_current_top (MUTTMENU * menu)
581 menu->top = menu->current;
582 menu->redraw = REDRAW_INDEX;
585 mutt_error _("No entries.");
588 void menu_current_middle (MUTTMENU * menu)
591 menu->top = menu->current - menu->pagelen / 2;
594 menu->redraw = REDRAW_INDEX;
597 mutt_error _("No entries.");
600 void menu_current_bottom (MUTTMENU * menu)
603 menu->top = menu->current - menu->pagelen + 1;
606 menu->redraw = REDRAW_INDEX;
609 mutt_error _("No entries.");
612 void menu_next_entry (MUTTMENU * menu)
614 if (menu->current < menu->max - 1) {
616 menu->redraw = REDRAW_MOTION;
619 mutt_error _("You are on the last entry.");
622 void menu_prev_entry (MUTTMENU * menu)
626 menu->redraw = REDRAW_MOTION;
629 mutt_error _("You are on the first entry.");
632 static int default_color (int i)
634 return ColorDefs[MT_COLOR_NORMAL];
637 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
639 char buf[LONG_STRING];
641 menu_make_entry (buf, sizeof (buf), m, n);
642 return (regexec (re, buf, 0, NULL, 0));
645 MUTTMENU *mutt_new_menu (void)
647 MUTTMENU *p = (MUTTMENU *) mem_calloc (1, sizeof (MUTTMENU));
652 p->redraw = REDRAW_FULL;
653 p->pagelen = PAGELEN;
654 p->color = default_color;
655 p->search = menu_search_generic;
659 void mutt_menuDestroy (MUTTMENU ** p)
663 mem_free (&(*p)->searchBuf);
666 for (i = 0; i < (*p)->max; i++)
667 mem_free (&(*p)->dialog[i]);
669 mem_free (&(*p)->dialog);
675 #define M_SEARCH_UP 1
676 #define M_SEARCH_DOWN 2
678 static int menu_search (MUTTMENU * menu, int op)
683 char buf[SHORT_STRING];
685 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
686 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
687 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
688 _("Reverse search for: "),
689 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
691 str_replace (&menu->searchBuf, buf);
692 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
695 if (!menu->searchBuf) {
696 mutt_error _("No search pattern.");
702 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
703 if (op == OP_SEARCH_OPPOSITE)
704 searchDir = -searchDir;
707 REGCOMP (&re, menu->searchBuf,
708 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
709 regerror (r, &re, buf, sizeof (buf));
711 mutt_error ("%s", buf);
715 r = menu->current + searchDir;
716 while (r >= 0 && r < menu->max) {
717 if (menu->search (menu, &re, r) == 0) {
726 mutt_error _("Not found.");
731 static int menu_dialog_translate_op (int i)
741 case OP_CURRENT_BOTTOM:
743 return OP_BOTTOM_PAGE;
744 case OP_CURRENT_MIDDLE:
745 return OP_MIDDLE_PAGE;
751 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
763 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
764 *ip = OP_MAX + (p - menu->keys + 1);
768 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
773 int menu_redraw (MUTTMENU * menu)
775 /* See if all or part of the screen needs to be updated. */
776 if (menu->redraw & REDRAW_FULL) {
777 menu_redraw_full (menu);
778 /* allow the caller to do any local configuration */
783 menu_check_recenter (menu);
785 if (menu->redraw & REDRAW_STATUS)
786 menu_redraw_status (menu);
787 if (menu->redraw & REDRAW_INDEX)
788 menu_redraw_index (menu);
789 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
790 menu_redraw_motion (menu);
791 else if (menu->redraw == REDRAW_CURRENT)
792 menu_redraw_current (menu);
795 menu_redraw_prompt (menu);
800 int mutt_menuLoop (MUTTMENU * menu)
805 if (option (OPTMENUCALLER)) {
806 unset_option (OPTMENUCALLER);
817 if (menu_redraw (menu) == OP_REDRAW)
820 menu->oldcurrent = menu->current;
822 if (option (OPTARROWCURSOR))
823 move (menu->current - menu->top + menu->offset, SW + 2);
824 else if (option (OPTBRAILLEFRIENDLY))
825 move (menu->current - menu->top + menu->offset, SW);
827 move (menu->current - menu->top + menu->offset, COLS - 1);
832 /* try to catch dialog keys before ops */
833 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
836 i = km_dokey (menu->menu);
837 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
839 mvaddstr (LINES - 1, 0, "Tag-");
841 i = km_dokey (menu->menu);
843 CLEARLINE (LINES - 1);
845 else if (i == OP_TAG_PREFIX) {
846 mutt_error _("No tagged entries.");
850 else { /* None tagged, OP_TAG_PREFIX_COND */
854 while (UngetCount > 0) {
856 if (tmp.op == OP_END_COND)
859 mutt_message _("Nothing to do.");
864 else if (menu->tagged && option (OPTAUTOTAG))
871 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
873 mutt_resize_screen ();
874 menu->redraw = REDRAW_FULL;
876 clearok (stdscr, TRUE); /*force complete redraw */
886 /* Convert menubar movement to scrolling */
888 i = menu_dialog_translate_op (i);
892 menu_next_entry (menu);
895 menu_prev_entry (menu);
898 menu_half_down (menu);
904 menu_next_page (menu);
907 menu_prev_page (menu);
910 menu_next_line (menu);
913 menu_prev_line (menu);
916 menu_first_entry (menu);
919 menu_last_entry (menu);
922 menu_top_page (menu);
925 menu_middle_page (menu);
928 menu_bottom_page (menu);
931 menu_current_top (menu);
933 case OP_CURRENT_MIDDLE:
934 menu_current_middle (menu);
936 case OP_CURRENT_BOTTOM:
937 menu_current_bottom (menu);
940 case OP_SEARCH_REVERSE:
942 case OP_SEARCH_OPPOSITE:
943 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
944 menu->oldcurrent = menu->current;
945 if ((menu->current = menu_search (menu, i)) != -1)
946 menu->redraw = REDRAW_MOTION;
948 menu->current = menu->oldcurrent;
951 mutt_error _("Search is not implemented for this menu.");
956 mutt_error (_("Jumping is not implemented for dialogs."));
962 case OP_ENTER_COMMAND:
963 CurrentMenu = menu->menu;
964 mutt_enter_command ();
965 if (option (OPTFORCEREDRAWINDEX)) {
966 menu->redraw = REDRAW_FULL;
967 unset_option (OPTFORCEREDRAWINDEX);
968 unset_option (OPTFORCEREDRAWPAGER);
973 if (menu->tag && !menu->dialog) {
974 if (menu->tagprefix && !option (OPTAUTOTAG)) {
975 for (i = 0; i < menu->max; i++)
976 menu->tagged += menu->tag (menu, i, 0);
977 menu->redraw = REDRAW_INDEX;
979 else if (menu->max) {
980 int i = menu->tag (menu, menu->current, -1);
983 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) {
985 menu->redraw = REDRAW_MOTION_RESYNCH;
988 menu->redraw = REDRAW_CURRENT;
991 mutt_error _("No entries.");
994 mutt_error _("Tagging is not supported.");
997 case OP_SHELL_ESCAPE:
998 mutt_shell_escape ();
999 MAYBE_REDRAW (menu->redraw);
1006 case OP_REBUILD_CACHE:
1007 mx_rebuild_cache ();
1011 clearok (stdscr, TRUE);
1012 menu->redraw = REDRAW_FULL;
1016 mutt_help (menu->menu);
1017 menu->redraw = REDRAW_FULL;
1021 km_error_key (menu->menu);