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.
20 #include "mutt_curses.h"
21 #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)
39 size_t n = mutt_strlen ((char *)s);
48 pair_content(PAIR_NUMBER(ColorDefs[MT_COLOR_TREE]), &f1, &b1);
49 pair_content(PAIR_NUMBER(attr), &f2, &b2);
51 SETCOLOR (MT_COLOR_TREE);
53 while (*s && *s < M_TREE_MAX)
58 if (option (OPTASCIICHARS))
60 else if (Charset_is_utf8)
61 addstr ("\342\224\224"); /* WACS_LLCORNER */
66 if (option (OPTASCIICHARS))
68 else if (Charset_is_utf8)
69 addstr ("\342\224\214"); /* WACS_ULCORNER */
74 if (option (OPTASCIICHARS))
76 else if (Charset_is_utf8)
77 addstr ("\342\224\234"); /* WACS_LTEE */
82 if (option (OPTASCIICHARS))
84 else if (Charset_is_utf8)
85 addstr ("\342\224\200"); /* WACS_HLINE */
90 if (option (OPTASCIICHARS))
92 else if (Charset_is_utf8)
93 addstr ("\342\224\202"); /* WACS_VLINE */
98 if (option (OPTASCIICHARS))
100 else if (Charset_is_utf8)
101 addstr ("\342\224\254"); /* WACS_TTEE */
106 if (option (OPTASCIICHARS))
108 else if (Charset_is_utf8)
109 addstr ("\342\224\264"); /* WACS_BTEE */
120 addch ('*'); /* fake thread indicator */
134 if (do_color) attrset(attr);
138 addnstr ((char *)s, 1);
146 static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i)
150 strncpy (s, menu->dialog[i], l);
151 menu->current = -1; /* hide menubar */
154 menu->make_entry (s, l, menu, i);
157 void menu_pad_string (char *s, size_t n)
159 int shift = option (OPTARROWCURSOR) ? 3 : 0;
160 int cols = COLS - shift - SidebarWidth;
162 mutt_format_string (s, n, cols, cols, 0, ' ', s, strlen (s), 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)
211 for (i = menu->top; i < menu->top + menu->pagelen; i++)
215 menu_make_entry (buf, sizeof (buf), menu, i);
216 menu_pad_string (buf, sizeof (buf));
218 if (option (OPTARROWCURSOR))
220 attrset (menu->color (i));
221 CLEARLINE_WIN (i - menu->top + menu->offset);
223 if (i == menu->current)
225 attrset (menu->color (i));
226 ADDCOLOR (MT_COLOR_INDICATOR);
228 attrset (menu->color (i));
232 move (i - menu->top + menu->offset, SidebarWidth + 3);
234 print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
235 SETCOLOR (MT_COLOR_NORMAL);
239 attrset (menu->color (i));
241 if (i == menu->current)
243 ADDCOLOR (MT_COLOR_INDICATOR);
244 BKGDSET (MT_COLOR_INDICATOR);
247 CLEARLINE_WIN (i - menu->top + menu->offset);
248 print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current);
249 SETCOLOR (MT_COLOR_NORMAL);
250 BKGDSET (MT_COLOR_NORMAL);
254 CLEARLINE_WIN (i - menu->top + menu->offset);
259 void menu_redraw_motion (MUTTMENU *menu)
265 menu->redraw &= ~REDRAW_MOTION;
269 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
270 SETCOLOR (MT_COLOR_NORMAL);
271 BKGDSET (MT_COLOR_NORMAL);
273 if (option (OPTARROWCURSOR))
275 /* clear the pointer */
276 attrset (menu->color (menu->oldcurrent));
279 if (menu->redraw & REDRAW_MOTION_RESYNCH)
282 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
283 menu_pad_string (buf, sizeof (buf));
284 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
285 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
286 SETCOLOR (MT_COLOR_NORMAL);
289 /* now draw it in the new location */
290 move (menu->current + menu->offset - menu->top, SidebarWidth);
291 attrset (menu->color (menu->current));
292 ADDCOLOR (MT_COLOR_INDICATOR);
294 SETCOLOR (MT_COLOR_NORMAL);
298 /* erase the current indicator */
299 attrset (menu->color (menu->oldcurrent));
301 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
302 menu_pad_string (buf, sizeof (buf));
303 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
305 /* now draw the new one to reflect the change */
306 menu_make_entry (buf, sizeof (buf), menu, menu->current);
307 menu_pad_string (buf, sizeof (buf));
308 attrset (menu->color (menu->current));
309 ADDCOLOR (MT_COLOR_INDICATOR);
310 BKGDSET (MT_COLOR_INDICATOR);
311 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
312 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
313 SETCOLOR (MT_COLOR_NORMAL);
314 BKGDSET (MT_COLOR_NORMAL);
316 menu->redraw &= REDRAW_STATUS;
319 void menu_redraw_current (MUTTMENU *menu)
323 move (menu->current + menu->offset - menu->top, SidebarWidth);
324 menu_make_entry (buf, sizeof (buf), menu, menu->current);
325 menu_pad_string (buf, sizeof (buf));
327 if (option (OPTARROWCURSOR))
329 int attr = menu->color (menu->current);
332 attrset (menu->color (menu->current));
333 ADDCOLOR (MT_COLOR_INDICATOR);
337 menu_pad_string (buf, sizeof (buf));
338 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 1);
339 SETCOLOR (MT_COLOR_NORMAL);
343 attrset (menu->color (menu->current));
344 ADDCOLOR (MT_COLOR_INDICATOR);
345 BKGDSET (MT_COLOR_INDICATOR);
347 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
348 SETCOLOR (MT_COLOR_NORMAL);
349 BKGDSET (MT_COLOR_NORMAL);
351 menu->redraw &= REDRAW_STATUS;
354 void menu_redraw_prompt (MUTTMENU *menu)
358 if (option (OPTMSGERR))
361 unset_option (OPTMSGERR);
367 SETCOLOR (MT_COLOR_NORMAL);
368 mvaddstr (LINES - 1, 0, menu->prompt);
373 void menu_check_recenter (MUTTMENU *menu)
375 if (menu->max <= menu->pagelen && menu->top != 0)
378 set_option (OPTNEEDREDRAW);
379 menu->redraw |= REDRAW_INDEX;
381 else if (menu->current >= menu->top + menu->pagelen)
383 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
384 menu->top = menu->current - menu->pagelen + 1;
386 menu->top += menu->pagelen * ((menu->current - menu->top) / menu->pagelen);
387 menu->redraw |= REDRAW_INDEX;
389 else if (menu->current < menu->top)
391 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
392 menu->top = menu->current;
395 menu->top -= menu->pagelen * ((menu->top + menu->pagelen - 1 - menu->current) / menu->pagelen);
399 menu->redraw |= REDRAW_INDEX;
403 void menu_jump (MUTTMENU *menu)
406 char buf[SHORT_STRING];
410 mutt_ungetch (LastKey, 0);
412 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
415 if (n >= 0 && n < menu->max)
418 menu->redraw = REDRAW_MOTION;
421 mutt_error _("Invalid index number.");
425 mutt_error _("No entries.");
428 void menu_next_line (MUTTMENU *menu)
432 if (menu->top + 1 < menu->max)
435 if (menu->current < menu->top)
437 menu->redraw = REDRAW_INDEX;
440 mutt_error _("You cannot scroll down farther.");
443 mutt_error _("No entries.");
446 void menu_prev_line (MUTTMENU *menu)
451 if (menu->current >= menu->top + menu->pagelen)
453 menu->redraw = REDRAW_INDEX;
456 mutt_error _("You cannot scroll up farther.");
459 void menu_next_page (MUTTMENU *menu)
463 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)
472 menu->current = menu->max - 1;
473 menu->redraw = REDRAW_MOTION;
476 mutt_error _("You are on the last page.");
479 mutt_error _("No entries.");
482 void menu_prev_page (MUTTMENU *menu)
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)
495 menu->redraw = REDRAW_MOTION;
498 mutt_error _("You are on the first page.");
501 void menu_top_page (MUTTMENU *menu)
503 if (menu->current != menu->top)
505 menu->current = menu->top;
506 menu->redraw = REDRAW_MOTION;
510 void menu_bottom_page (MUTTMENU *menu)
514 menu->current = menu->top + menu->pagelen - 1;
515 if (menu->current > menu->max - 1)
516 menu->current = menu->max - 1;
517 menu->redraw = REDRAW_MOTION;
520 mutt_error _("No entries.");
523 void menu_middle_page (MUTTMENU *menu)
529 i = menu->top + menu->pagelen;
530 if (i > menu->max - 1)
532 menu->current = menu->top + (i - menu->top) / 2;
533 menu->redraw = REDRAW_MOTION;
536 mutt_error _("No entries.");
539 void menu_first_entry (MUTTMENU *menu)
544 menu->redraw = REDRAW_MOTION;
547 mutt_error _("No entries.");
550 void menu_last_entry (MUTTMENU *menu)
554 menu->current = menu->max - 1;
555 menu->redraw = REDRAW_MOTION;
558 mutt_error _("No entries.");
561 void menu_half_up (MUTTMENU *menu)
565 if ((menu->top -= menu->pagelen / 2) < 0)
567 if (menu->current >= menu->top + menu->pagelen)
568 menu->current = menu->top + menu->pagelen - 1;
569 menu->redraw = REDRAW_INDEX;
571 else if (menu->current && !menu->dialog)
574 menu->redraw = REDRAW_MOTION;
577 mutt_error _("First entry is shown.");
580 void menu_half_down (MUTTMENU *menu)
584 if (menu->top + menu->pagelen < menu->max)
586 menu->top += menu->pagelen / 2;
587 if (menu->current < menu->top)
588 menu->current = menu->top;
589 menu->redraw = REDRAW_INDEX;
591 else if (menu->current != menu->max - 1 && !menu->dialog)
593 menu->current = menu->max - 1;
594 menu->redraw = REDRAW_INDEX;
597 mutt_error _("Last entry is shown.");
600 mutt_error _("No entries.");
603 void menu_current_top (MUTTMENU *menu)
607 menu->top = menu->current;
608 menu->redraw = REDRAW_INDEX;
611 mutt_error _("No entries.");
614 void menu_current_middle (MUTTMENU *menu)
618 menu->top = menu->current - menu->pagelen / 2;
621 menu->redraw = REDRAW_INDEX;
624 mutt_error _("No entries.");
627 void menu_current_bottom (MUTTMENU *menu)
631 menu->top = menu->current - menu->pagelen + 1;
634 menu->redraw = REDRAW_INDEX;
637 mutt_error _("No entries.");
640 void menu_next_entry (MUTTMENU *menu)
642 if (menu->current < menu->max - 1)
645 menu->redraw = REDRAW_MOTION;
648 mutt_error _("You are on the last entry.");
651 void menu_prev_entry (MUTTMENU *menu)
656 menu->redraw = REDRAW_MOTION;
659 mutt_error _("You are on the first entry.");
662 static int default_color (int i)
664 return ColorDefs[MT_COLOR_NORMAL];
667 static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
669 char buf[LONG_STRING];
671 menu_make_entry (buf, sizeof (buf), m, n);
672 return (regexec (re, buf, 0, NULL, 0));
675 MUTTMENU *mutt_new_menu (void)
677 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
682 p->redraw = REDRAW_FULL;
683 p->pagelen = PAGELEN;
684 p->color = default_color;
685 p->search = menu_search_generic;
689 void mutt_menuDestroy (MUTTMENU **p)
693 FREE (&(*p)->searchBuf);
697 for (i=0; i < (*p)->max; i++)
698 FREE (&(*p)->dialog[i]);
706 #define M_SEARCH_UP 1
707 #define M_SEARCH_DOWN 2
709 static int menu_search (MUTTMENU *menu, int op)
714 char buf[SHORT_STRING];
716 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
718 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
719 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
720 _("Reverse search for: "),
721 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
723 mutt_str_replace (&menu->searchBuf, buf);
724 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
728 if (!menu->searchBuf)
730 mutt_error _("No search pattern.");
735 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
736 if (op == OP_SEARCH_OPPOSITE)
737 searchDir = -searchDir;
739 if ((r = REGCOMP (&re, menu->searchBuf, REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0)
741 regerror (r, &re, buf, sizeof (buf));
743 mutt_error ("%s", buf);
747 r = menu->current + searchDir;
748 while (r >= 0 && r < menu->max)
750 if (menu->search (menu, &re, r) == 0)
760 mutt_error _("Not found.");
764 static int menu_dialog_translate_op (int i)
772 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
774 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
775 return OP_BOTTOM_PAGE;
776 case OP_CURRENT_MIDDLE:
777 return OP_MIDDLE_PAGE;
783 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
796 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
798 *ip = OP_MAX + (p - menu->keys + 1);
803 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
808 int menu_redraw (MUTTMENU *menu)
810 /* See if all or part of the screen needs to be updated. */
811 if (menu->redraw & REDRAW_FULL)
813 menu_redraw_full (menu);
814 /* allow the caller to do any local configuration */
819 menu_check_recenter (menu);
821 if (menu->redraw & REDRAW_STATUS)
822 menu_redraw_status (menu);
823 if (menu->redraw & REDRAW_INDEX)
824 menu_redraw_index (menu);
825 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
826 menu_redraw_motion (menu);
827 else if (menu->redraw == REDRAW_CURRENT)
828 menu_redraw_current (menu);
831 menu_redraw_prompt (menu);
836 int mutt_menuLoop (MUTTMENU *menu)
842 if (option (OPTMENUCALLER))
844 unset_option (OPTMENUCALLER);
855 if (menu_redraw (menu) == OP_REDRAW)
858 menu->oldcurrent = menu->current;
861 /* move the cursor out of the way */
862 move (menu->current - menu->top + menu->offset,
863 (option (OPTARROWCURSOR) ? 2 : COLS-1));
867 /* try to catch dialog keys before ops */
868 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
871 i = km_dokey (menu->menu);
872 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
876 mvaddstr (LINES - 1, 0, "Tag-");
878 i = km_dokey (menu->menu);
880 CLEARLINE (LINES - 1);
882 else if (i == OP_TAG_PREFIX)
884 mutt_error _("No tagged entries.");
887 else /* None tagged, OP_TAG_PREFIX_COND */
893 if(tmp.op==OP_END_COND)break;
895 mutt_message _("Nothing to do.");
899 else if (menu->tagged && option (OPTAUTOTAG))
906 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
909 mutt_resize_screen ();
910 menu->redraw = REDRAW_FULL;
912 clearok(stdscr,TRUE);/*force complete redraw*/
922 /* Convert menubar movement to scrolling */
924 i = menu_dialog_translate_op (i);
929 menu_next_entry (menu);
932 menu_prev_entry (menu);
935 menu_half_down (menu);
941 menu_next_page (menu);
944 menu_prev_page (menu);
947 menu_next_line (menu);
950 menu_prev_line (menu);
953 menu_first_entry (menu);
956 menu_last_entry (menu);
959 menu_top_page (menu);
962 menu_middle_page (menu);
965 menu_bottom_page (menu);
968 menu_current_top (menu);
970 case OP_CURRENT_MIDDLE:
971 menu_current_middle (menu);
973 case OP_CURRENT_BOTTOM:
974 menu_current_bottom (menu);
977 case OP_SEARCH_REVERSE:
979 case OP_SEARCH_OPPOSITE:
980 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
982 menu->oldcurrent = menu->current;
983 if ((menu->current = menu_search (menu, i)) != -1)
984 menu->redraw = REDRAW_MOTION;
986 menu->current = menu->oldcurrent;
989 mutt_error _("Search is not implemented for this menu.");
994 mutt_error _("Jumping is not implemented for dialogs.");
999 case OP_ENTER_COMMAND:
1000 CurrentMenu = menu->menu;
1001 mutt_enter_command ();
1002 if (option (OPTFORCEREDRAWINDEX))
1004 menu->redraw = REDRAW_FULL;
1005 unset_option (OPTFORCEREDRAWINDEX);
1006 unset_option (OPTFORCEREDRAWPAGER);
1011 if (menu->tag && !menu->dialog)
1013 if (menu->tagprefix && !option (OPTAUTOTAG))
1015 for (i = 0; i < menu->max; i++)
1016 menu->tagged += menu->tag (menu, i, 0);
1017 menu->redraw = REDRAW_INDEX;
1021 int i = menu->tag (menu, menu->current, -1);
1023 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1026 menu->redraw = REDRAW_MOTION_RESYNCH;
1029 menu->redraw = REDRAW_CURRENT;
1032 mutt_error _("No entries.");
1035 mutt_error _("Tagging is not supported.");
1038 case OP_SHELL_ESCAPE:
1039 mutt_shell_escape ();
1040 MAYBE_REDRAW (menu->redraw);
1048 clearok (stdscr, TRUE);
1049 menu->redraw = REDRAW_FULL;
1053 mutt_help (menu->menu);
1054 menu->redraw = REDRAW_FULL;
1058 km_error_key (menu->menu);