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 extern int Charset_is_utf8; /* FIXME: bad modularisation */
34 extern size_t UngetCount;
36 static void print_enriched_string (int attr, unsigned char *s, int do_color)
40 size_t n = str_len ((char *) s);
43 memset (&mbstate, 0, sizeof (mbstate));
45 if (*s < M_TREE_MAX) {
47 SETCOLOR (MT_COLOR_TREE);
48 while (*s && *s < M_TREE_MAX) {
51 if (option (OPTASCIICHARS))
53 else if (Charset_is_utf8)
54 addstr ("\342\224\224"); /* WACS_LLCORNER */
59 if (option (OPTASCIICHARS))
61 else if (Charset_is_utf8)
62 addstr ("\342\224\214"); /* WACS_ULCORNER */
67 if (option (OPTASCIICHARS))
69 else if (Charset_is_utf8)
70 addstr ("\342\224\234"); /* WACS_LTEE */
75 if (option (OPTASCIICHARS))
77 else if (Charset_is_utf8)
78 addstr ("\342\224\200"); /* WACS_HLINE */
83 if (option (OPTASCIICHARS))
85 else if (Charset_is_utf8)
86 addstr ("\342\224\202"); /* WACS_VLINE */
91 if (option (OPTASCIICHARS))
93 else if (Charset_is_utf8)
94 addstr ("\342\224\254"); /* WACS_TTEE */
99 if (option (OPTASCIICHARS))
101 else if (Charset_is_utf8)
102 addstr ("\342\224\264"); /* WACS_BTEE */
113 addch ('*'); /* fake thread indicator */
130 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
131 addnstr ((char *) s, k);
139 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
142 strncpy (s, menu->dialog[i], l);
143 menu->current = -1; /* hide menubar */
146 menu->make_entry (s, l, menu, i);
149 void menu_pad_string (char *s, size_t n)
151 int shift = option (OPTARROWCURSOR) ? 3 : 0;
153 char *tmpbuf = mem_malloc (n);
155 if (option (OPTMBOXPANE))
156 cols = COLS - shift - SidebarWidth;
159 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, str_len (s), 1);
161 snprintf (s, n, "%s", tmpbuf); /* overkill */
165 void menu_redraw_full (MUTTMENU * menu)
167 SETCOLOR (MT_COLOR_NORMAL);
168 /* clear() doesn't optimize screen redraws */
172 if (option (OPTHELP)) {
173 SETCOLOR (MT_COLOR_STATUS);
174 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, 0);
175 mutt_paddstr (COLS, menu->help);
176 SETCOLOR (MT_COLOR_NORMAL);
178 menu->pagelen = LINES - 3;
181 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
182 menu->pagelen = LINES - 2;
187 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
190 void menu_redraw_status (MUTTMENU * menu)
194 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
195 SETCOLOR (MT_COLOR_STATUS);
196 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
197 mutt_paddstr (COLS, buf);
198 SETCOLOR (MT_COLOR_NORMAL);
199 menu->redraw &= ~REDRAW_STATUS;
202 void menu_redraw_index (MUTTMENU * menu)
208 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
210 menu_make_entry (buf, sizeof (buf), menu, i);
211 menu_pad_string (buf, sizeof (buf));
213 if (option (OPTARROWCURSOR)) {
214 attrset (menu->color (i));
215 CLEARLINE_WIN (i - menu->top + menu->offset);
217 if (i == menu->current) {
218 attrset (menu->color (i));
219 ADDCOLOR (MT_COLOR_INDICATOR);
221 attrset (menu->color (i));
225 attrset (menu->color (i));
226 move (i - menu->top + menu->offset, SidebarWidth);
230 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
231 SETCOLOR (MT_COLOR_NORMAL);
234 attrset (menu->color (i));
236 if (i == menu->current) {
237 ADDCOLOR (MT_COLOR_INDICATOR);
238 BKGDSET (MT_COLOR_INDICATOR);
241 CLEARLINE_WIN (i - menu->top + menu->offset);
242 print_enriched_string (menu->color (i), (unsigned char *) buf,
244 SETCOLOR (MT_COLOR_NORMAL);
245 BKGDSET (MT_COLOR_NORMAL);
249 CLEARLINE_WIN (i - menu->top + menu->offset);
254 void menu_redraw_motion (MUTTMENU * menu)
259 menu->redraw &= ~REDRAW_MOTION;
263 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
264 SETCOLOR (MT_COLOR_NORMAL);
265 BKGDSET (MT_COLOR_NORMAL);
267 if (option (OPTARROWCURSOR)) {
268 /* clear the pointer */
269 attrset (menu->color (menu->oldcurrent));
272 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
274 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
275 menu_pad_string (buf, sizeof (buf));
276 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
277 print_enriched_string (menu->color (menu->oldcurrent),
278 (unsigned char *) buf, 1);
279 SETCOLOR (MT_COLOR_NORMAL);
282 /* now draw it in the new location */
283 move (menu->current + menu->offset - menu->top, SidebarWidth);
284 attrset (menu->color (menu->current));
285 ADDCOLOR (MT_COLOR_INDICATOR);
287 SETCOLOR (MT_COLOR_NORMAL);
290 /* erase the current indicator */
291 attrset (menu->color (menu->oldcurrent));
293 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
294 menu_pad_string (buf, sizeof (buf));
295 print_enriched_string (menu->color (menu->oldcurrent),
296 (unsigned char *) buf, 1);
298 /* now draw the new one to reflect the change */
299 menu_make_entry (buf, sizeof (buf), menu, menu->current);
300 menu_pad_string (buf, sizeof (buf));
301 attrset (menu->color (menu->current));
302 ADDCOLOR (MT_COLOR_INDICATOR);
303 BKGDSET (MT_COLOR_INDICATOR);
304 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
305 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
307 SETCOLOR (MT_COLOR_NORMAL);
308 BKGDSET (MT_COLOR_NORMAL);
310 menu->redraw &= REDRAW_STATUS;
313 void menu_redraw_current (MUTTMENU * menu)
317 move (menu->current + menu->offset - menu->top, SidebarWidth);
318 menu_make_entry (buf, sizeof (buf), menu, menu->current);
319 menu_pad_string (buf, sizeof (buf));
321 if (option (OPTARROWCURSOR)) {
322 int attr = menu->color (menu->current);
326 attrset (menu->color (menu->current));
327 ADDCOLOR (MT_COLOR_INDICATOR);
331 menu_pad_string (buf, sizeof (buf));
332 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
334 SETCOLOR (MT_COLOR_NORMAL);
337 attrset (menu->color (menu->current));
338 ADDCOLOR (MT_COLOR_INDICATOR);
339 BKGDSET (MT_COLOR_INDICATOR);
341 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
343 SETCOLOR (MT_COLOR_NORMAL);
344 BKGDSET (MT_COLOR_NORMAL);
346 menu->redraw &= REDRAW_STATUS;
349 void menu_redraw_prompt (MUTTMENU * menu)
352 if (option (OPTMSGERR)) {
354 unset_option (OPTMSGERR);
360 SETCOLOR (MT_COLOR_NORMAL);
361 mvaddstr (LINES - 1, 0, menu->prompt);
366 void menu_check_recenter (MUTTMENU * menu)
368 int c = MIN (MenuContext, menu->pagelen / 2);
369 int old_top = menu->top;
371 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
372 if (menu->top != 0) {
374 set_option (OPTNEEDREDRAW);
377 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c <= MenuContext)) {
378 if (menu->current < menu->top + c)
379 menu->top = menu->current - c;
380 else if (menu->current >= menu->top + menu->pagelen - c)
381 menu->top = menu->current - menu->pagelen + c + 1;
383 if (menu->current < menu->top + c)
384 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
385 else if ((menu->current >= menu->top + menu->pagelen - c))
386 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
390 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
391 menu->top = MIN (menu->top, menu->max - menu->pagelen);
392 menu->top = MAX (menu->top, 0);
394 if (menu->top != old_top)
395 menu->redraw |= REDRAW_INDEX;
398 void menu_jump (MUTTMENU * menu)
401 char buf[SHORT_STRING];
404 mutt_ungetch (LastKey, 0);
406 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
408 if (n >= 0 && n < menu->max) {
410 menu->redraw = REDRAW_MOTION;
413 mutt_error _("Invalid index number.");
417 mutt_error _("No entries.");
420 void menu_next_line (MUTTMENU * menu)
423 int c = MIN (MenuContext, menu->pagelen / 2);
425 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
426 || (menu->max > menu->pagelen
428 menu->max - menu->pagelen))) {
430 if (menu->current < menu->top + c && menu->current < menu->max - 1)
432 menu->redraw = REDRAW_INDEX;
435 mutt_error _("You cannot scroll down farther.");
438 mutt_error _("No entries.");
441 void menu_prev_line (MUTTMENU * menu)
444 int c = MIN (MenuContext, menu->pagelen / 2);
447 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
449 menu->redraw = REDRAW_INDEX;
452 mutt_error _("You cannot scroll up farther.");
456 * pageup: jumplen == -pagelen
457 * pagedown: jumplen == pagelen
458 * halfup: jumplen == -pagelen/2
459 * halfdown: jumplen == pagelen/2
461 #define DIRECTION ((neg * 2) + 1)
462 void menu_length_jump (MUTTMENU *menu, int jumplen) {
463 int tmp, neg = (jumplen >= 0) ? 0 : -1;
464 int c = MIN (MenuContext, menu->pagelen / 2);
467 /* possible to scroll? */
468 if (DIRECTION * menu->top <
469 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
470 menu->top += jumplen;
472 /* jumped too long? */
473 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
476 /* need to move the cursor? */
478 (tmp = (menu->current - (menu->top +
479 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
480 menu->current -= tmp;
482 menu->redraw = REDRAW_INDEX;
484 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
485 menu->current += jumplen;
486 menu->redraw = REDRAW_MOTION;
489 mutt_error (neg ? _("You are on the first page.")
490 : _("You are on the last page."));
492 menu->current = MIN (menu->current, menu->max - 1);
493 menu->current = MAX (menu->current, 0);
496 mutt_error _("No entries.");
500 void menu_next_page (MUTTMENU *menu) {
501 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
504 void menu_prev_page (MUTTMENU *menu) {
505 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
508 void menu_half_down (MUTTMENU *menu) {
509 menu_length_jump (menu, menu->pagelen / 2);
512 void menu_half_up (MUTTMENU *menu) {
513 menu_length_jump (menu, 0 - menu->pagelen / 2);
516 void menu_top_page (MUTTMENU *menu) {
517 if (menu->current != menu->top) {
518 menu->current = menu->top;
519 menu->redraw = REDRAW_MOTION;
523 void menu_bottom_page (MUTTMENU *menu) {
525 menu->current = menu->top + menu->pagelen - 1;
526 if (menu->current > menu->max - 1)
527 menu->current = menu->max - 1;
528 menu->redraw = REDRAW_MOTION;
531 mutt_error _("No entries.");
534 void menu_middle_page (MUTTMENU *menu) {
538 i = menu->top + menu->pagelen;
539 if (i > menu->max - 1)
541 menu->current = menu->top + (i - menu->top) / 2;
542 menu->redraw = REDRAW_MOTION;
545 mutt_error _("No entries.");
548 void menu_first_entry (MUTTMENU *menu) {
551 menu->redraw = REDRAW_MOTION;
554 mutt_error _("No entries.");
557 void menu_last_entry (MUTTMENU *menu) {
559 menu->current = menu->max - 1;
560 menu->redraw = REDRAW_MOTION;
563 mutt_error _("No entries.");
566 void menu_current_top (MUTTMENU * menu)
569 menu->top = menu->current;
570 menu->redraw = REDRAW_INDEX;
573 mutt_error _("No entries.");
576 void menu_current_middle (MUTTMENU * menu)
579 menu->top = menu->current - menu->pagelen / 2;
582 menu->redraw = REDRAW_INDEX;
585 mutt_error _("No entries.");
588 void menu_current_bottom (MUTTMENU * menu)
591 menu->top = menu->current - menu->pagelen + 1;
594 menu->redraw = REDRAW_INDEX;
597 mutt_error _("No entries.");
600 void menu_next_entry (MUTTMENU * menu)
602 if (menu->current < menu->max - 1) {
604 menu->redraw = REDRAW_MOTION;
607 mutt_error _("You are on the last entry.");
610 void menu_prev_entry (MUTTMENU * menu)
614 menu->redraw = REDRAW_MOTION;
617 mutt_error _("You are on the first entry.");
620 static int default_color (int i)
622 return ColorDefs[MT_COLOR_NORMAL];
625 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
627 char buf[LONG_STRING];
629 menu_make_entry (buf, sizeof (buf), m, n);
630 return (regexec (re, buf, 0, NULL, 0));
633 MUTTMENU *mutt_new_menu (void)
635 MUTTMENU *p = (MUTTMENU *) mem_calloc (1, sizeof (MUTTMENU));
640 p->redraw = REDRAW_FULL;
641 p->pagelen = PAGELEN;
642 p->color = default_color;
643 p->search = menu_search_generic;
647 void mutt_menuDestroy (MUTTMENU ** p)
651 mem_free (&(*p)->searchBuf);
654 for (i = 0; i < (*p)->max; i++)
655 mem_free (&(*p)->dialog[i]);
657 mem_free (&(*p)->dialog);
663 #define M_SEARCH_UP 1
664 #define M_SEARCH_DOWN 2
666 static int menu_search (MUTTMENU * menu, int op)
671 char buf[SHORT_STRING];
673 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
674 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
675 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
676 _("Reverse search for: "),
677 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
679 str_replace (&menu->searchBuf, buf);
680 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
683 if (!menu->searchBuf) {
684 mutt_error _("No search pattern.");
690 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
691 if (op == OP_SEARCH_OPPOSITE)
692 searchDir = -searchDir;
695 REGCOMP (&re, menu->searchBuf,
696 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
697 regerror (r, &re, buf, sizeof (buf));
699 mutt_error ("%s", buf);
703 r = menu->current + searchDir;
704 while (r >= 0 && r < menu->max) {
705 if (menu->search (menu, &re, r) == 0) {
714 mutt_error _("Not found.");
719 static int menu_dialog_translate_op (int i)
729 case OP_CURRENT_BOTTOM:
731 return OP_BOTTOM_PAGE;
732 case OP_CURRENT_MIDDLE:
733 return OP_MIDDLE_PAGE;
739 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
751 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
752 *ip = OP_MAX + (p - menu->keys + 1);
756 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
761 int menu_redraw (MUTTMENU * menu)
763 /* See if all or part of the screen needs to be updated. */
764 if (menu->redraw & REDRAW_FULL) {
765 menu_redraw_full (menu);
766 /* allow the caller to do any local configuration */
771 menu_check_recenter (menu);
773 if (menu->redraw & REDRAW_STATUS)
774 menu_redraw_status (menu);
775 if (menu->redraw & REDRAW_INDEX)
776 menu_redraw_index (menu);
777 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
778 menu_redraw_motion (menu);
779 else if (menu->redraw == REDRAW_CURRENT)
780 menu_redraw_current (menu);
783 menu_redraw_prompt (menu);
788 int mutt_menuLoop (MUTTMENU * menu)
793 if (option (OPTMENUCALLER)) {
794 unset_option (OPTMENUCALLER);
805 if (menu_redraw (menu) == OP_REDRAW)
808 menu->oldcurrent = menu->current;
810 if (option (OPTARROWCURSOR))
811 move (menu->current - menu->top + menu->offset, 2);
812 else if (option (OPTBRAILLEFRIENDLY))
813 move (menu->current - menu->top + menu->offset, 0);
815 move (menu->current - menu->top + menu->offset, COLS - 1);
820 /* try to catch dialog keys before ops */
821 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
824 i = km_dokey (menu->menu);
825 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
827 mvaddstr (LINES - 1, 0, "Tag-");
829 i = km_dokey (menu->menu);
831 CLEARLINE (LINES - 1);
833 else if (i == OP_TAG_PREFIX) {
834 mutt_error _("No tagged entries.");
838 else { /* None tagged, OP_TAG_PREFIX_COND */
842 while (UngetCount > 0) {
844 if (tmp.op == OP_END_COND)
847 mutt_message _("Nothing to do.");
852 else if (menu->tagged && option (OPTAUTOTAG))
859 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
861 mutt_resize_screen ();
862 menu->redraw = REDRAW_FULL;
864 clearok (stdscr, TRUE); /*force complete redraw */
874 /* Convert menubar movement to scrolling */
876 i = menu_dialog_translate_op (i);
880 menu_next_entry (menu);
883 menu_prev_entry (menu);
886 menu_half_down (menu);
892 menu_next_page (menu);
895 menu_prev_page (menu);
898 menu_next_line (menu);
901 menu_prev_line (menu);
904 menu_first_entry (menu);
907 menu_last_entry (menu);
910 menu_top_page (menu);
913 menu_middle_page (menu);
916 menu_bottom_page (menu);
919 menu_current_top (menu);
921 case OP_CURRENT_MIDDLE:
922 menu_current_middle (menu);
924 case OP_CURRENT_BOTTOM:
925 menu_current_bottom (menu);
928 case OP_SEARCH_REVERSE:
930 case OP_SEARCH_OPPOSITE:
931 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
932 menu->oldcurrent = menu->current;
933 if ((menu->current = menu_search (menu, i)) != -1)
934 menu->redraw = REDRAW_MOTION;
936 menu->current = menu->oldcurrent;
939 mutt_error _("Search is not implemented for this menu.");
944 mutt_error (_("Jumping is not implemented for dialogs."));
950 case OP_ENTER_COMMAND:
951 CurrentMenu = menu->menu;
952 mutt_enter_command ();
953 if (option (OPTFORCEREDRAWINDEX)) {
954 menu->redraw = REDRAW_FULL;
955 unset_option (OPTFORCEREDRAWINDEX);
956 unset_option (OPTFORCEREDRAWPAGER);
961 if (menu->tag && !menu->dialog) {
962 if (menu->tagprefix && !option (OPTAUTOTAG)) {
963 for (i = 0; i < menu->max; i++)
964 menu->tagged += menu->tag (menu, i, 0);
965 menu->redraw = REDRAW_INDEX;
967 else if (menu->max) {
968 int i = menu->tag (menu, menu->current, -1);
971 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) {
973 menu->redraw = REDRAW_MOTION_RESYNCH;
976 menu->redraw = REDRAW_CURRENT;
979 mutt_error _("No entries.");
982 mutt_error _("Tagging is not supported.");
985 case OP_SHELL_ESCAPE:
986 mutt_shell_escape ();
987 MAYBE_REDRAW (menu->redraw);
994 case OP_REBUILD_CACHE:
999 clearok (stdscr, TRUE);
1000 menu->redraw = REDRAW_FULL;
1004 mutt_help (menu->menu);
1005 menu->redraw = REDRAW_FULL;
1009 km_error_key (menu->menu);