2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 #include "mutt_curses.h"
25 #include "mutt_menu.h"
36 extern int Charset_is_utf8; /* FIXME: bad modularisation */
38 extern size_t UngetCount;
40 static void print_enriched_string (int attr, unsigned char *s, int do_color)
44 size_t n = mutt_strlen ((char *) s);
47 memset (&mbstate, 0, sizeof (mbstate));
49 if (*s < M_TREE_MAX) {
51 SETCOLOR (MT_COLOR_TREE);
52 while (*s && *s < M_TREE_MAX) {
55 if (option (OPTASCIICHARS))
57 else if (Charset_is_utf8)
58 addstr ("\342\224\224"); /* WACS_LLCORNER */
63 if (option (OPTASCIICHARS))
65 else if (Charset_is_utf8)
66 addstr ("\342\224\214"); /* WACS_ULCORNER */
71 if (option (OPTASCIICHARS))
73 else if (Charset_is_utf8)
74 addstr ("\342\224\234"); /* WACS_LTEE */
79 if (option (OPTASCIICHARS))
81 else if (Charset_is_utf8)
82 addstr ("\342\224\200"); /* WACS_HLINE */
87 if (option (OPTASCIICHARS))
89 else if (Charset_is_utf8)
90 addstr ("\342\224\202"); /* WACS_VLINE */
95 if (option (OPTASCIICHARS))
97 else if (Charset_is_utf8)
98 addstr ("\342\224\254"); /* WACS_TTEE */
103 if (option (OPTASCIICHARS))
105 else if (Charset_is_utf8)
106 addstr ("\342\224\264"); /* WACS_BTEE */
117 addch ('*'); /* fake thread indicator */
134 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
135 addnstr ((char *) s, k);
143 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
146 strncpy (s, menu->dialog[i], l);
147 menu->current = -1; /* hide menubar */
150 menu->make_entry (s, l, menu, i);
153 void menu_pad_string (char *s, size_t n)
155 int shift = option (OPTARROWCURSOR) ? 3 : 0;
157 char *tmpbuf = safe_malloc (n);
159 if (option (OPTMBOXPANE))
160 cols = COLS - shift - SidebarWidth;
163 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, strlen (s), 1);
165 snprintf (s, n, "%s", tmpbuf); /* overkill */
169 void menu_redraw_full (MUTTMENU * menu)
171 SETCOLOR (MT_COLOR_NORMAL);
172 /* clear() doesn't optimize screen redraws */
176 if (option (OPTHELP)) {
177 SETCOLOR (MT_COLOR_STATUS);
178 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, 0);
179 mutt_paddstr (COLS, menu->help);
180 SETCOLOR (MT_COLOR_NORMAL);
182 menu->pagelen = LINES - 3;
185 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
186 menu->pagelen = LINES - 2;
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, 0);
201 mutt_paddstr (COLS, buf);
202 SETCOLOR (MT_COLOR_NORMAL);
203 menu->redraw &= ~REDRAW_STATUS;
206 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);
225 attrset (menu->color (i));
229 move (i - menu->top + menu->offset, SidebarWidth + 3);
231 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
232 SETCOLOR (MT_COLOR_NORMAL);
235 attrset (menu->color (i));
237 if (i == menu->current) {
238 ADDCOLOR (MT_COLOR_INDICATOR);
239 BKGDSET (MT_COLOR_INDICATOR);
242 CLEARLINE_WIN (i - menu->top + menu->offset);
243 print_enriched_string (menu->color (i), (unsigned char *) buf,
245 SETCOLOR (MT_COLOR_NORMAL);
246 BKGDSET (MT_COLOR_NORMAL);
250 CLEARLINE_WIN (i - menu->top + menu->offset);
255 void menu_redraw_motion (MUTTMENU * menu)
260 menu->redraw &= ~REDRAW_MOTION;
264 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
265 SETCOLOR (MT_COLOR_NORMAL);
266 BKGDSET (MT_COLOR_NORMAL);
268 if (option (OPTARROWCURSOR)) {
269 /* clear the pointer */
270 attrset (menu->color (menu->oldcurrent));
273 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
275 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
276 menu_pad_string (buf, sizeof (buf));
277 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
278 print_enriched_string (menu->color (menu->oldcurrent),
279 (unsigned char *) buf, 1);
280 SETCOLOR (MT_COLOR_NORMAL);
283 /* now draw it in the new location */
284 move (menu->current + menu->offset - menu->top, SidebarWidth);
285 attrset (menu->color (menu->current));
286 ADDCOLOR (MT_COLOR_INDICATOR);
288 SETCOLOR (MT_COLOR_NORMAL);
291 /* erase the current indicator */
292 attrset (menu->color (menu->oldcurrent));
294 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
295 menu_pad_string (buf, sizeof (buf));
296 print_enriched_string (menu->color (menu->oldcurrent),
297 (unsigned char *) buf, 1);
299 /* now draw the new one to reflect the change */
300 menu_make_entry (buf, sizeof (buf), menu, menu->current);
301 menu_pad_string (buf, sizeof (buf));
302 attrset (menu->color (menu->current));
303 ADDCOLOR (MT_COLOR_INDICATOR);
304 BKGDSET (MT_COLOR_INDICATOR);
305 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
306 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
308 SETCOLOR (MT_COLOR_NORMAL);
309 BKGDSET (MT_COLOR_NORMAL);
311 menu->redraw &= REDRAW_STATUS;
314 void menu_redraw_current (MUTTMENU * menu)
318 move (menu->current + menu->offset - menu->top, SidebarWidth);
319 menu_make_entry (buf, sizeof (buf), menu, menu->current);
320 menu_pad_string (buf, sizeof (buf));
322 if (option (OPTARROWCURSOR)) {
323 int attr = menu->color (menu->current);
327 attrset (menu->color (menu->current));
328 ADDCOLOR (MT_COLOR_INDICATOR);
332 menu_pad_string (buf, sizeof (buf));
333 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
335 SETCOLOR (MT_COLOR_NORMAL);
338 attrset (menu->color (menu->current));
339 ADDCOLOR (MT_COLOR_INDICATOR);
340 BKGDSET (MT_COLOR_INDICATOR);
342 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
344 SETCOLOR (MT_COLOR_NORMAL);
345 BKGDSET (MT_COLOR_NORMAL);
347 menu->redraw &= REDRAW_STATUS;
350 void menu_redraw_prompt (MUTTMENU * menu)
353 if (option (OPTMSGERR)) {
355 unset_option (OPTMSGERR);
361 SETCOLOR (MT_COLOR_NORMAL);
362 mvaddstr (LINES - 1, 0, menu->prompt);
367 void menu_check_recenter (MUTTMENU * menu)
369 int c = MIN (MenuContext, menu->pagelen / 2);
370 int old_top = menu->top;
372 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
373 if (menu->top != 0) {
375 set_option (OPTNEEDREDRAW);
378 else if (menu->current >= menu->top + menu->pagelen - c) { /* indicator below bottom threshold */
379 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
380 menu->top = menu->current - menu->pagelen + c + 1;
384 c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
386 else if (menu->current < menu->top + c) { /* indicator above top threshold */
387 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
388 menu->top = menu->current - c;
392 c) * ((menu->top + menu->pagelen - 1 -
393 menu->current) / (menu->pagelen - c)) - c;
396 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
397 menu->top = MIN (menu->top, menu->max - menu->pagelen);
398 menu->top = MAX (menu->top, 0);
400 if (menu->top != old_top)
401 menu->redraw |= REDRAW_INDEX;
404 void menu_jump (MUTTMENU * menu)
407 char buf[SHORT_STRING];
410 mutt_ungetch (LastKey, 0);
412 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
414 if (n >= 0 && n < menu->max) {
416 menu->redraw = REDRAW_MOTION;
419 mutt_error _("Invalid index number.");
423 mutt_error _("No entries.");
426 void menu_next_line (MUTTMENU * menu)
429 int c = MIN (MenuContext, menu->pagelen / 2);
431 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
432 || (menu->max > menu->pagelen
434 menu->max - menu->pagelen))) {
436 if (menu->current < menu->top + c && menu->current < menu->max - 1)
438 menu->redraw = REDRAW_INDEX;
441 mutt_error _("You cannot scroll down farther.");
444 mutt_error _("No entries.");
447 void menu_prev_line (MUTTMENU * menu)
450 int c = MIN (MenuContext, menu->pagelen / 2);
453 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
455 menu->redraw = REDRAW_INDEX;
458 mutt_error _("You cannot scroll up farther.");
461 void menu_next_page (MUTTMENU * menu)
464 if (menu->top + menu->pagelen < menu->max) {
465 menu->top += menu->pagelen;
466 if (menu->current < menu->top)
467 menu->current = menu->top;
468 menu->redraw = REDRAW_INDEX;
470 else if (menu->current != menu->max - 1 && !menu->dialog) {
471 menu->current = menu->max - 1;
472 menu->redraw = REDRAW_MOTION;
475 mutt_error _("You are on the last page.");
478 mutt_error _("No entries.");
481 void menu_prev_page (MUTTMENU * menu)
483 int c = MIN (MenuContext, menu->pagelen / 2);
486 if ((menu->top -= menu->pagelen) < 0)
488 if (menu->current >= menu->top + menu->pagelen)
489 menu->current = menu->top + menu->pagelen - 1;
490 menu->redraw = REDRAW_INDEX;
492 else if (menu->current && !menu->dialog) {
494 menu->redraw = REDRAW_MOTION;
497 mutt_error _("You are on the first page.");
500 void menu_top_page (MUTTMENU * menu)
502 if (menu->current != menu->top) {
503 menu->current = menu->top;
504 menu->redraw = REDRAW_MOTION;
508 void menu_bottom_page (MUTTMENU * menu)
511 menu->current = menu->top + menu->pagelen - 1;
512 if (menu->current > menu->max - 1)
513 menu->current = menu->max - 1;
514 menu->redraw = REDRAW_MOTION;
517 mutt_error _("No entries.");
520 void menu_middle_page (MUTTMENU * menu)
525 i = menu->top + menu->pagelen;
526 if (i > menu->max - 1)
528 menu->current = menu->top + (i - menu->top) / 2;
529 menu->redraw = REDRAW_MOTION;
532 mutt_error _("No entries.");
535 void menu_first_entry (MUTTMENU * menu)
539 menu->redraw = REDRAW_MOTION;
542 mutt_error _("No entries.");
545 void menu_last_entry (MUTTMENU * menu)
548 menu->current = menu->max - 1;
549 menu->redraw = REDRAW_MOTION;
552 mutt_error _("No entries.");
555 void menu_half_up (MUTTMENU * menu)
558 if ((menu->top -= menu->pagelen / 2) < 0)
560 if (menu->current >= menu->top + menu->pagelen)
561 menu->current = menu->top + menu->pagelen - 1;
562 menu->redraw = REDRAW_INDEX;
564 else if (menu->current && !menu->dialog) {
566 menu->redraw = REDRAW_MOTION;
569 mutt_error _("First entry is shown.");
572 void menu_half_down (MUTTMENU * menu)
575 if (menu->top + menu->pagelen < menu->max) {
576 menu->top += menu->pagelen / 2;
577 if (menu->current < menu->top)
578 menu->current = menu->top;
579 menu->redraw = REDRAW_INDEX;
581 else if (menu->current != menu->max - 1 && !menu->dialog) {
582 menu->current = menu->max - 1;
583 menu->redraw = REDRAW_INDEX;
586 mutt_error _("Last entry is shown.");
589 mutt_error _("No entries.");
592 void menu_current_top (MUTTMENU * menu)
595 menu->top = menu->current;
596 menu->redraw = REDRAW_INDEX;
599 mutt_error _("No entries.");
602 void menu_current_middle (MUTTMENU * menu)
605 menu->top = menu->current - menu->pagelen / 2;
608 menu->redraw = REDRAW_INDEX;
611 mutt_error _("No entries.");
614 void menu_current_bottom (MUTTMENU * menu)
617 menu->top = menu->current - menu->pagelen + 1;
620 menu->redraw = REDRAW_INDEX;
623 mutt_error _("No entries.");
626 void menu_next_entry (MUTTMENU * menu)
628 if (menu->current < menu->max - 1) {
630 menu->redraw = REDRAW_MOTION;
633 mutt_error _("You are on the last entry.");
636 void menu_prev_entry (MUTTMENU * menu)
640 menu->redraw = REDRAW_MOTION;
643 mutt_error _("You are on the first entry.");
646 static int default_color (int i)
648 return ColorDefs[MT_COLOR_NORMAL];
651 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
653 char buf[LONG_STRING];
655 menu_make_entry (buf, sizeof (buf), m, n);
656 return (regexec (re, buf, 0, NULL, 0));
659 MUTTMENU *mutt_new_menu (void)
661 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
666 p->redraw = REDRAW_FULL;
667 p->pagelen = PAGELEN;
668 p->color = default_color;
669 p->search = menu_search_generic;
673 void mutt_menuDestroy (MUTTMENU ** p)
677 FREE (&(*p)->searchBuf);
680 for (i = 0; i < (*p)->max; i++)
681 FREE (&(*p)->dialog[i]);
683 FREE (&(*p)->dialog);
689 #define M_SEARCH_UP 1
690 #define M_SEARCH_DOWN 2
692 static int menu_search (MUTTMENU * menu, int op)
697 char buf[SHORT_STRING];
699 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
700 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
701 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
702 _("Reverse search for: "),
703 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
705 mutt_str_replace (&menu->searchBuf, buf);
706 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
709 if (!menu->searchBuf) {
710 mutt_error _("No search pattern.");
716 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
717 if (op == OP_SEARCH_OPPOSITE)
718 searchDir = -searchDir;
721 REGCOMP (&re, menu->searchBuf,
722 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
723 regerror (r, &re, buf, sizeof (buf));
725 mutt_error ("%s", buf);
729 r = menu->current + searchDir;
730 while (r >= 0 && r < menu->max) {
731 if (menu->search (menu, &re, r) == 0) {
740 mutt_error _("Not found.");
745 static int menu_dialog_translate_op (int i)
755 case OP_CURRENT_BOTTOM:
757 return OP_BOTTOM_PAGE;
758 case OP_CURRENT_MIDDLE:
759 return OP_MIDDLE_PAGE;
765 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
777 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
778 *ip = OP_MAX + (p - menu->keys + 1);
782 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
787 int menu_redraw (MUTTMENU * menu)
789 /* See if all or part of the screen needs to be updated. */
790 if (menu->redraw & REDRAW_FULL) {
791 menu_redraw_full (menu);
792 /* allow the caller to do any local configuration */
797 menu_check_recenter (menu);
799 if (menu->redraw & REDRAW_STATUS)
800 menu_redraw_status (menu);
801 if (menu->redraw & REDRAW_INDEX)
802 menu_redraw_index (menu);
803 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
804 menu_redraw_motion (menu);
805 else if (menu->redraw == REDRAW_CURRENT)
806 menu_redraw_current (menu);
809 menu_redraw_prompt (menu);
814 int mutt_menuLoop (MUTTMENU * menu)
819 if (option (OPTMENUCALLER)) {
820 unset_option (OPTMENUCALLER);
831 if (menu_redraw (menu) == OP_REDRAW)
834 menu->oldcurrent = menu->current;
837 /* move the cursor out of the way */
838 move (menu->current - menu->top + menu->offset,
839 (option (OPTARROWCURSOR) ? 2 : COLS - 1));
843 /* try to catch dialog keys before ops */
844 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
847 i = km_dokey (menu->menu);
848 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
850 mvaddstr (LINES - 1, 0, "Tag-");
852 i = km_dokey (menu->menu);
854 CLEARLINE (LINES - 1);
856 else if (i == OP_TAG_PREFIX) {
857 mutt_error _("No tagged entries.");
861 else { /* None tagged, OP_TAG_PREFIX_COND */
865 while (UngetCount > 0) {
867 if (tmp.op == OP_END_COND)
870 mutt_message _("Nothing to do.");
875 else if (menu->tagged && option (OPTAUTOTAG))
882 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
884 mutt_resize_screen ();
885 menu->redraw = REDRAW_FULL;
887 clearok (stdscr, TRUE); /*force complete redraw */
897 /* Convert menubar movement to scrolling */
899 i = menu_dialog_translate_op (i);
903 menu_next_entry (menu);
906 menu_prev_entry (menu);
909 menu_half_down (menu);
915 menu_next_page (menu);
918 menu_prev_page (menu);
921 menu_next_line (menu);
924 menu_prev_line (menu);
927 menu_first_entry (menu);
930 menu_last_entry (menu);
933 menu_top_page (menu);
936 menu_middle_page (menu);
939 menu_bottom_page (menu);
942 menu_current_top (menu);
944 case OP_CURRENT_MIDDLE:
945 menu_current_middle (menu);
947 case OP_CURRENT_BOTTOM:
948 menu_current_bottom (menu);
951 case OP_SEARCH_REVERSE:
953 case OP_SEARCH_OPPOSITE:
954 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
955 menu->oldcurrent = menu->current;
956 if ((menu->current = menu_search (menu, i)) != -1)
957 menu->redraw = REDRAW_MOTION;
959 menu->current = menu->oldcurrent;
962 mutt_error _("Search is not implemented for this menu.");
967 mutt_error (_("Jumping is not implemented for dialogs."));
973 case OP_ENTER_COMMAND:
974 CurrentMenu = menu->menu;
975 mutt_enter_command ();
976 if (option (OPTFORCEREDRAWINDEX)) {
977 menu->redraw = REDRAW_FULL;
978 unset_option (OPTFORCEREDRAWINDEX);
979 unset_option (OPTFORCEREDRAWPAGER);
984 if (menu->tag && !menu->dialog) {
985 if (menu->tagprefix && !option (OPTAUTOTAG)) {
986 for (i = 0; i < menu->max; i++)
987 menu->tagged += menu->tag (menu, i, 0);
988 menu->redraw = REDRAW_INDEX;
990 else if (menu->max) {
991 int i = menu->tag (menu, menu->current, -1);
994 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) {
996 menu->redraw = REDRAW_MOTION_RESYNCH;
999 menu->redraw = REDRAW_CURRENT;
1002 mutt_error _("No entries.");
1005 mutt_error _("Tagging is not supported.");
1008 case OP_SHELL_ESCAPE:
1009 mutt_shell_escape ();
1010 MAYBE_REDRAW (menu->redraw);
1018 clearok (stdscr, TRUE);
1019 menu->redraw = REDRAW_FULL;
1023 mutt_help (menu->menu);
1024 menu->redraw = REDRAW_FULL;
1028 km_error_key (menu->menu);