2 * Copyright (C) 1996-2000,2002 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "mutt_curses.h"
25 #include "mutt_menu.h"
31 extern int Charset_is_utf8; /* FIXME: bad modularisation */
33 extern size_t UngetCount;
35 char* SearchBuffers[MENU_MAX];
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));
50 SETCOLOR (MT_COLOR_TREE);
51 while (*s && *s < M_TREE_MAX)
56 if (option (OPTASCIICHARS))
58 else if (Charset_is_utf8)
59 addstr ("\342\224\224"); /* WACS_LLCORNER */
64 if (option (OPTASCIICHARS))
66 else if (Charset_is_utf8)
67 addstr ("\342\224\214"); /* WACS_ULCORNER */
72 if (option (OPTASCIICHARS))
74 else if (Charset_is_utf8)
75 addstr ("\342\224\234"); /* WACS_LTEE */
80 if (option (OPTASCIICHARS))
82 else if (Charset_is_utf8)
83 addstr ("\342\224\200"); /* WACS_HLINE */
88 if (option (OPTASCIICHARS))
90 else if (Charset_is_utf8)
91 addstr ("\342\224\202"); /* WACS_VLINE */
96 if (option (OPTASCIICHARS))
98 else if (Charset_is_utf8)
99 addstr ("\342\224\254"); /* WACS_TTEE */
104 if (option (OPTASCIICHARS))
106 else if (Charset_is_utf8)
107 addstr ("\342\224\264"); /* WACS_BTEE */
118 addch ('*'); /* fake thread indicator */
132 if (do_color) attrset(attr);
134 else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
136 addnstr ((char *)s, k);
144 static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i)
148 strncpy (s, menu->dialog[i], l);
149 menu->current = -1; /* hide menubar */
152 menu->make_entry (s, l, menu, i);
155 static void menu_pad_string (char *s, size_t n)
157 char *scratch = safe_strdup (s);
158 int shift = option (OPTARROWCURSOR) ? 3 : 0;
159 int cols = COLS - shift;
161 mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 1);
166 void menu_redraw_full (MUTTMENU *menu)
168 SETCOLOR (MT_COLOR_NORMAL);
169 /* clear() doesn't optimize screen redraws */
173 if (option (OPTHELP))
175 SETCOLOR (MT_COLOR_STATUS);
176 move (option (OPTSTATUSONTOP) ? LINES-2 : 0, 0);
177 mutt_paddstr (COLS, menu->help);
178 SETCOLOR (MT_COLOR_NORMAL);
180 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)
207 char buf[LONG_STRING];
210 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))
219 attrset (menu->color (i));
220 CLEARLINE (i - menu->top + menu->offset);
222 if (i == menu->current)
224 attrset (menu->color (i));
225 ADDCOLOR (MT_COLOR_INDICATOR);
227 attrset (menu->color (i));
232 attrset (menu->color (i));
236 print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
237 SETCOLOR (MT_COLOR_NORMAL);
241 attrset (menu->color (i));
243 if (i == menu->current)
245 ADDCOLOR (MT_COLOR_INDICATOR);
246 BKGDSET (MT_COLOR_INDICATOR);
249 CLEARLINE (i - menu->top + menu->offset);
250 print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current);
251 SETCOLOR (MT_COLOR_NORMAL);
252 BKGDSET (MT_COLOR_NORMAL);
256 CLEARLINE (i - menu->top + menu->offset);
261 void menu_redraw_motion (MUTTMENU *menu)
263 char buf[LONG_STRING];
267 menu->redraw &= ~REDRAW_MOTION;
271 move (menu->oldcurrent + menu->offset - menu->top, 0);
272 SETCOLOR (MT_COLOR_NORMAL);
273 BKGDSET (MT_COLOR_NORMAL);
275 if (option (OPTARROWCURSOR))
277 /* clear the pointer */
278 attrset (menu->color (menu->oldcurrent));
281 if (menu->redraw & REDRAW_MOTION_RESYNCH)
284 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
285 menu_pad_string (buf, sizeof (buf));
286 move (menu->oldcurrent + menu->offset - menu->top, 3);
287 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
288 SETCOLOR (MT_COLOR_NORMAL);
291 /* now draw it in the new location */
292 move (menu->current + menu->offset - menu->top, 0);
293 attrset (menu->color (menu->current));
294 ADDCOLOR (MT_COLOR_INDICATOR);
296 SETCOLOR (MT_COLOR_NORMAL);
300 /* erase the current indicator */
301 attrset (menu->color (menu->oldcurrent));
303 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
304 menu_pad_string (buf, sizeof (buf));
305 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
307 /* now draw the new one to reflect the change */
308 menu_make_entry (buf, sizeof (buf), menu, menu->current);
309 menu_pad_string (buf, sizeof (buf));
310 attrset (menu->color (menu->current));
311 ADDCOLOR (MT_COLOR_INDICATOR);
312 BKGDSET (MT_COLOR_INDICATOR);
313 CLEARLINE (menu->current - menu->top + menu->offset);
314 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
315 SETCOLOR (MT_COLOR_NORMAL);
316 BKGDSET (MT_COLOR_NORMAL);
318 menu->redraw &= REDRAW_STATUS;
321 void menu_redraw_current (MUTTMENU *menu)
323 char buf[LONG_STRING];
325 move (menu->current + menu->offset - menu->top, 0);
326 menu_make_entry (buf, sizeof (buf), menu, menu->current);
327 menu_pad_string (buf, sizeof (buf));
329 if (option (OPTARROWCURSOR))
331 int attr = menu->color (menu->current);
334 attrset (menu->color (menu->current));
335 ADDCOLOR (MT_COLOR_INDICATOR);
339 menu_pad_string (buf, sizeof (buf));
340 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 1);
341 SETCOLOR (MT_COLOR_NORMAL);
345 attrset (menu->color (menu->current));
346 ADDCOLOR (MT_COLOR_INDICATOR);
347 BKGDSET (MT_COLOR_INDICATOR);
349 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
350 SETCOLOR (MT_COLOR_NORMAL);
351 BKGDSET (MT_COLOR_NORMAL);
353 menu->redraw &= REDRAW_STATUS;
356 static void menu_redraw_prompt (MUTTMENU *menu)
360 if (option (OPTMSGERR))
363 unset_option (OPTMSGERR);
369 SETCOLOR (MT_COLOR_NORMAL);
370 mvaddstr (LINES - 1, 0, menu->prompt);
375 void menu_check_recenter (MUTTMENU *menu)
377 int c = MIN (MenuContext, menu->pagelen / 2);
378 int old_top = menu->top;
380 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) /* less entries than lines */
385 set_option (OPTNEEDREDRAW);
390 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext))
392 if (menu->current < menu->top + c)
393 menu->top = menu->current - c;
394 else if (menu->current >= menu->top + menu->pagelen - c)
395 menu->top = menu->current - menu->pagelen + c + 1;
399 if (menu->current < menu->top + c)
400 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
401 else if ((menu->current >= menu->top + menu->pagelen - c))
402 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
406 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
407 menu->top = MIN (menu->top, menu->max - menu->pagelen);
408 menu->top = MAX (menu->top, 0);
410 if (menu->top != old_top)
411 menu->redraw |= REDRAW_INDEX;
414 void menu_jump (MUTTMENU *menu)
417 char buf[SHORT_STRING];
421 mutt_ungetch (LastKey, 0);
423 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
425 if (mutt_atoi (buf, &n) == 0 && n > 0 && n < menu->max + 1)
427 n--; /* msg numbers are 0-based */
429 menu->redraw = REDRAW_MOTION;
432 mutt_error _("Invalid index number.");
436 mutt_error _("No entries.");
439 void menu_next_line (MUTTMENU *menu)
443 int c = MIN (MenuContext, menu->pagelen / 2);
445 if (menu->top + 1 < menu->max - c
446 && (option(OPTMENUMOVEOFF) || (menu->max > menu->pagelen && menu->top < menu->max - menu->pagelen)))
449 if (menu->current < menu->top + c && menu->current < menu->max - 1)
451 menu->redraw = REDRAW_INDEX;
454 mutt_error _("You cannot scroll down farther.");
457 mutt_error _("No entries.");
460 void menu_prev_line (MUTTMENU *menu)
464 int c = MIN (MenuContext, menu->pagelen / 2);
467 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
469 menu->redraw = REDRAW_INDEX;
472 mutt_error _("You cannot scroll up farther.");
476 * pageup: jumplen == -pagelen
477 * pagedown: jumplen == pagelen
478 * halfup: jumplen == -pagelen/2
479 * halfdown: jumplen == pagelen/2
481 #define DIRECTION ((neg * 2) + 1)
482 static void menu_length_jump (MUTTMENU *menu, int jumplen)
484 int tmp, neg = (jumplen >= 0) ? 0 : -1;
485 int c = MIN (MenuContext, menu->pagelen / 2);
489 /* possible to scroll? */
490 if (DIRECTION * menu->top <
491 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/))))
493 menu->top += jumplen;
495 /* jumped too long? */
496 if ((neg || !option (OPTMENUMOVEOFF)) &&
497 DIRECTION * menu->top > tmp)
500 /* need to move the cursor? */
502 (tmp = (menu->current -
503 (menu->top + (neg ? (menu->pagelen - 1) - c : c))
505 menu->current -= tmp;
507 menu->redraw = REDRAW_INDEX;
509 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog)
511 menu->current += jumplen;
512 menu->redraw = REDRAW_MOTION;
515 mutt_error (neg ? _("You are on the first page.")
516 : _("You are on the last page."));
518 menu->current = MIN (menu->current, menu->max - 1);
519 menu->current = MAX (menu->current, 0);
522 mutt_error _("No entries.");
526 void menu_next_page (MUTTMENU *menu)
528 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
531 void menu_prev_page (MUTTMENU *menu)
533 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
536 void menu_half_down (MUTTMENU *menu)
538 menu_length_jump (menu, menu->pagelen / 2);
541 void menu_half_up (MUTTMENU *menu)
543 menu_length_jump (menu, 0 - menu->pagelen / 2);
546 void menu_top_page (MUTTMENU *menu)
548 if (menu->current != menu->top)
550 menu->current = menu->top;
551 menu->redraw = REDRAW_MOTION;
555 void menu_bottom_page (MUTTMENU *menu)
559 menu->current = menu->top + menu->pagelen - 1;
560 if (menu->current > menu->max - 1)
561 menu->current = menu->max - 1;
562 menu->redraw = REDRAW_MOTION;
565 mutt_error _("No entries.");
568 void menu_middle_page (MUTTMENU *menu)
574 i = menu->top + menu->pagelen;
575 if (i > menu->max - 1)
577 menu->current = menu->top + (i - menu->top) / 2;
578 menu->redraw = REDRAW_MOTION;
581 mutt_error _("No entries.");
584 void menu_first_entry (MUTTMENU *menu)
589 menu->redraw = REDRAW_MOTION;
592 mutt_error _("No entries.");
595 void menu_last_entry (MUTTMENU *menu)
599 menu->current = menu->max - 1;
600 menu->redraw = REDRAW_MOTION;
603 mutt_error _("No entries.");
606 void menu_current_top (MUTTMENU *menu)
610 menu->top = menu->current;
611 menu->redraw = REDRAW_INDEX;
614 mutt_error _("No entries.");
617 void menu_current_middle (MUTTMENU *menu)
621 menu->top = menu->current - menu->pagelen / 2;
624 menu->redraw = REDRAW_INDEX;
627 mutt_error _("No entries.");
630 void menu_current_bottom (MUTTMENU *menu)
634 menu->top = menu->current - menu->pagelen + 1;
637 menu->redraw = REDRAW_INDEX;
640 mutt_error _("No entries.");
643 static void menu_next_entry (MUTTMENU *menu)
645 if (menu->current < menu->max - 1)
648 menu->redraw = REDRAW_MOTION;
651 mutt_error _("You are on the last entry.");
654 static void menu_prev_entry (MUTTMENU *menu)
659 menu->redraw = REDRAW_MOTION;
662 mutt_error _("You are on the first entry.");
665 static int default_color (int i)
667 return ColorDefs[MT_COLOR_NORMAL];
670 static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
672 char buf[LONG_STRING];
674 menu_make_entry (buf, sizeof (buf), m, n);
675 return (regexec (re, buf, 0, NULL, 0));
678 void mutt_menu_init (void)
682 for (i = 0; i < MENU_MAX; i++)
683 SearchBuffers[i] = NULL;
686 MUTTMENU *mutt_new_menu (int menu)
688 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
694 p->redraw = REDRAW_FULL;
695 p->pagelen = PAGELEN;
696 p->color = default_color;
697 p->search = menu_search_generic;
701 void mutt_menuDestroy (MUTTMENU **p)
707 for (i=0; i < (*p)->max; i++)
708 FREE (&(*p)->dialog[i]);
710 FREE (& (*p)->dialog);
713 FREE (p); /* __FREE_CHECKED__ */
716 #define M_SEARCH_UP 1
717 #define M_SEARCH_DOWN 2
719 static int menu_search (MUTTMENU *menu, int op)
724 char buf[SHORT_STRING];
725 char* searchBuf = menu->menu >= 0 && menu->menu < MENU_MAX ?
726 SearchBuffers[menu->menu] : NULL;
728 if (!(searchBuf && *searchBuf) ||
729 (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE))
731 strfcpy (buf, searchBuf && *searchBuf ? searchBuf : "", sizeof (buf));
732 if (mutt_get_field ((op == OP_SEARCH || op == OP_SEARCH_NEXT)
733 ? _("Search for: ") : _("Reverse search for: "),
734 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
736 if (menu->menu >= 0 && menu->menu < MENU_MAX)
738 mutt_str_replace (&SearchBuffers[menu->menu], buf);
739 searchBuf = SearchBuffers[menu->menu];
741 menu->searchDir = (op == OP_SEARCH || op == OP_SEARCH_NEXT) ?
742 M_SEARCH_DOWN : M_SEARCH_UP;
745 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
746 if (op == OP_SEARCH_OPPOSITE)
747 searchDir = -searchDir;
749 if ((r = REGCOMP (&re, searchBuf, REG_NOSUB | mutt_which_case (searchBuf))) != 0)
751 regerror (r, &re, buf, sizeof (buf));
753 mutt_error ("%s", buf);
757 r = menu->current + searchDir;
760 mutt_message (_("Search wrapped to top."));
761 while (r >= 0 && r < menu->max)
763 if (menu->search (menu, &re, r) == 0)
772 if (option (OPTWRAPSEARCH) && wrap++ == 0)
774 r = searchDir == 1 ? 0 : menu->max - 1;
778 mutt_error _("Not found.");
782 static int menu_dialog_translate_op (int i)
790 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
792 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
793 return OP_BOTTOM_PAGE;
794 case OP_CURRENT_MIDDLE:
795 return OP_MIDDLE_PAGE;
801 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
814 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
816 *ip = OP_MAX + (p - menu->keys + 1);
821 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
826 int menu_redraw (MUTTMENU *menu)
828 /* See if all or part of the screen needs to be updated. */
829 if (menu->redraw & REDRAW_FULL)
831 menu_redraw_full (menu);
832 /* allow the caller to do any local configuration */
837 menu_check_recenter (menu);
839 if (menu->redraw & REDRAW_STATUS)
840 menu_redraw_status (menu);
841 if (menu->redraw & REDRAW_INDEX)
842 menu_redraw_index (menu);
843 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
844 menu_redraw_motion (menu);
845 else if (menu->redraw == REDRAW_CURRENT)
846 menu_redraw_current (menu);
849 menu_redraw_prompt (menu);
854 int mutt_menuLoop (MUTTMENU *menu)
860 if (option (OPTMENUCALLER))
862 unset_option (OPTMENUCALLER);
869 if (menu_redraw (menu) == OP_REDRAW)
872 menu->oldcurrent = menu->current;
875 /* move the cursor out of the way */
878 if (option (OPTARROWCURSOR))
879 move (menu->current - menu->top + menu->offset, 2);
880 else if (option (OPTBRAILLEFRIENDLY))
881 move (menu->current - menu->top + menu->offset, 0);
883 move (menu->current - menu->top + menu->offset, COLS - 1);
887 /* try to catch dialog keys before ops */
888 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
891 i = km_dokey (menu->menu);
892 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
896 mvaddstr (LINES - 1, 0, "Tag-");
898 i = km_dokey (menu->menu);
900 CLEARLINE (LINES - 1);
902 else if (i == OP_TAG_PREFIX)
904 mutt_error _("No tagged entries.");
907 else /* None tagged, OP_TAG_PREFIX_COND */
913 if(tmp.op==OP_END_COND)break;
915 mutt_message _("Nothing to do.");
919 else if (menu->tagged && option (OPTAUTOTAG))
926 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
929 mutt_resize_screen ();
930 menu->redraw = REDRAW_FULL;
932 clearok(stdscr,TRUE);/*force complete redraw*/
942 /* Convert menubar movement to scrolling */
944 i = menu_dialog_translate_op (i);
949 menu_next_entry (menu);
952 menu_prev_entry (menu);
955 menu_half_down (menu);
961 menu_next_page (menu);
964 menu_prev_page (menu);
967 menu_next_line (menu);
970 menu_prev_line (menu);
973 menu_first_entry (menu);
976 menu_last_entry (menu);
979 menu_top_page (menu);
982 menu_middle_page (menu);
985 menu_bottom_page (menu);
988 menu_current_top (menu);
990 case OP_CURRENT_MIDDLE:
991 menu_current_middle (menu);
993 case OP_CURRENT_BOTTOM:
994 menu_current_bottom (menu);
997 case OP_SEARCH_REVERSE:
999 case OP_SEARCH_OPPOSITE:
1000 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
1002 menu->oldcurrent = menu->current;
1003 if ((menu->current = menu_search (menu, i)) != -1)
1004 menu->redraw = REDRAW_MOTION;
1006 menu->current = menu->oldcurrent;
1009 mutt_error _("Search is not implemented for this menu.");
1014 mutt_error _("Jumping is not implemented for dialogs.");
1019 case OP_ENTER_COMMAND:
1020 CurrentMenu = menu->menu;
1021 mutt_enter_command ();
1022 if (option (OPTFORCEREDRAWINDEX))
1024 menu->redraw = REDRAW_FULL;
1025 unset_option (OPTFORCEREDRAWINDEX);
1026 unset_option (OPTFORCEREDRAWPAGER);
1031 if (menu->tag && !menu->dialog)
1033 if (menu->tagprefix && !option (OPTAUTOTAG))
1035 for (i = 0; i < menu->max; i++)
1036 menu->tagged += menu->tag (menu, i, 0);
1037 menu->redraw = REDRAW_INDEX;
1041 int i = menu->tag (menu, menu->current, -1);
1043 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1046 menu->redraw = REDRAW_MOTION_RESYNCH;
1049 menu->redraw = REDRAW_CURRENT;
1052 mutt_error _("No entries.");
1055 mutt_error _("Tagging is not supported.");
1058 case OP_SHELL_ESCAPE:
1059 mutt_shell_escape ();
1060 MAYBE_REDRAW (menu->redraw);
1068 clearok (stdscr, TRUE);
1069 menu->redraw = REDRAW_FULL;
1073 mutt_help (menu->menu);
1074 menu->redraw = REDRAW_FULL;
1078 km_error_key (menu->menu);