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);
46 SETCOLOR (MT_COLOR_TREE);
47 while (*s && *s < M_TREE_MAX)
52 if (option (OPTASCIICHARS))
54 else if (Charset_is_utf8)
55 addstr ("\342\224\224"); /* WACS_LLCORNER */
60 if (option (OPTASCIICHARS))
62 else if (Charset_is_utf8)
63 addstr ("\342\224\214"); /* WACS_ULCORNER */
68 if (option (OPTASCIICHARS))
70 else if (Charset_is_utf8)
71 addstr ("\342\224\234"); /* WACS_LTEE */
76 if (option (OPTASCIICHARS))
78 else if (Charset_is_utf8)
79 addstr ("\342\224\200"); /* WACS_HLINE */
84 if (option (OPTASCIICHARS))
86 else if (Charset_is_utf8)
87 addstr ("\342\224\202"); /* WACS_VLINE */
92 if (option (OPTASCIICHARS))
94 else if (Charset_is_utf8)
95 addstr ("\342\224\254"); /* WACS_TTEE */
100 if (option (OPTASCIICHARS))
102 else if (Charset_is_utf8)
103 addstr ("\342\224\264"); /* WACS_BTEE */
114 addch ('*'); /* fake thread indicator */
128 if (do_color) attrset(attr);
132 addnstr ((char *)s, 1);
140 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;
154 int cols = COLS - shift - SidebarWidth;
156 mutt_format_string (s, n, cols, cols, 0, ' ', s, strlen (s), 1);
160 void menu_redraw_full (MUTTMENU *menu)
162 SETCOLOR (MT_COLOR_NORMAL);
163 /* clear() doesn't optimize screen redraws */
167 if (option (OPTHELP))
169 SETCOLOR (MT_COLOR_STATUS);
170 move (option (OPTSTATUSONTOP) ? LINES-2 : 0, 0);
171 mutt_paddstr (COLS, menu->help);
172 SETCOLOR (MT_COLOR_NORMAL);
174 menu->pagelen = LINES - 3;
178 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
179 menu->pagelen = LINES - 2;
184 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
187 void menu_redraw_status (MUTTMENU *menu)
191 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
192 SETCOLOR (MT_COLOR_STATUS);
193 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
194 mutt_paddstr (COLS, buf);
195 SETCOLOR (MT_COLOR_NORMAL);
196 menu->redraw &= ~REDRAW_STATUS;
199 void menu_redraw_index (MUTTMENU *menu)
205 for (i = menu->top; i < menu->top + menu->pagelen; i++)
209 menu_make_entry (buf, sizeof (buf), menu, i);
210 menu_pad_string (buf, sizeof (buf));
212 if (option (OPTARROWCURSOR))
214 attrset (menu->color (i));
215 CLEARLINE_WIN (i - menu->top + menu->offset);
217 if (i == menu->current)
219 attrset (menu->color (i));
220 ADDCOLOR (MT_COLOR_INDICATOR);
222 attrset (menu->color (i));
226 move (i - menu->top + menu->offset, SidebarWidth + 3);
228 print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
229 SETCOLOR (MT_COLOR_NORMAL);
233 attrset (menu->color (i));
235 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, i != menu->current);
243 SETCOLOR (MT_COLOR_NORMAL);
244 BKGDSET (MT_COLOR_NORMAL);
248 CLEARLINE_WIN (i - menu->top + menu->offset);
253 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))
269 /* clear the pointer */
270 attrset (menu->color (menu->oldcurrent));
273 if (menu->redraw & REDRAW_MOTION_RESYNCH)
276 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
277 menu_pad_string (buf, sizeof (buf));
278 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
279 print_enriched_string (menu->color(menu->oldcurrent), (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);
292 /* erase the current indicator */
293 attrset (menu->color (menu->oldcurrent));
295 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
296 menu_pad_string (buf, sizeof (buf));
297 print_enriched_string (menu->color(menu->oldcurrent), (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, 0);
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))
323 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, 1);
333 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, 0);
342 SETCOLOR (MT_COLOR_NORMAL);
343 BKGDSET (MT_COLOR_NORMAL);
345 menu->redraw &= REDRAW_STATUS;
348 void menu_redraw_prompt (MUTTMENU *menu)
352 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 if (menu->max <= menu->pagelen && menu->top != 0)
372 set_option (OPTNEEDREDRAW);
373 menu->redraw |= REDRAW_INDEX;
375 else if (menu->current >= menu->top + menu->pagelen)
377 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
378 menu->top = menu->current - menu->pagelen + 1;
380 menu->top += menu->pagelen * ((menu->current - menu->top) / menu->pagelen);
381 menu->redraw |= REDRAW_INDEX;
383 else if (menu->current < menu->top)
385 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
386 menu->top = menu->current;
389 menu->top -= menu->pagelen * ((menu->top + menu->pagelen - 1 - menu->current) / menu->pagelen);
393 menu->redraw |= REDRAW_INDEX;
397 void menu_jump (MUTTMENU *menu)
400 char buf[SHORT_STRING];
404 mutt_ungetch (LastKey, 0);
406 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
409 if (n >= 0 && n < menu->max)
412 menu->redraw = REDRAW_MOTION;
415 mutt_error _("Invalid index number.");
419 mutt_error _("No entries.");
422 void menu_next_line (MUTTMENU *menu)
426 if (menu->top + 1 < menu->max)
429 if (menu->current < menu->top)
431 menu->redraw = REDRAW_INDEX;
434 mutt_error _("You cannot scroll down farther.");
437 mutt_error _("No entries.");
440 void menu_prev_line (MUTTMENU *menu)
445 if (menu->current >= menu->top + menu->pagelen)
447 menu->redraw = REDRAW_INDEX;
450 mutt_error _("You cannot scroll up farther.");
453 void menu_next_page (MUTTMENU *menu)
457 if (menu->top + menu->pagelen < menu->max)
459 menu->top += menu->pagelen;
460 if (menu->current < menu->top)
461 menu->current = menu->top;
462 menu->redraw = REDRAW_INDEX;
464 else if (menu->current != menu->max - 1 && !menu->dialog)
466 menu->current = menu->max - 1;
467 menu->redraw = REDRAW_MOTION;
470 mutt_error _("You are on the last page.");
473 mutt_error _("No entries.");
476 void menu_prev_page (MUTTMENU *menu)
480 if ((menu->top -= menu->pagelen) < 0)
482 if (menu->current >= menu->top + menu->pagelen)
483 menu->current = menu->top + menu->pagelen - 1;
484 menu->redraw = REDRAW_INDEX;
486 else if (menu->current && !menu->dialog)
489 menu->redraw = REDRAW_MOTION;
492 mutt_error _("You are on the first page.");
495 void menu_top_page (MUTTMENU *menu)
497 if (menu->current != menu->top)
499 menu->current = menu->top;
500 menu->redraw = REDRAW_MOTION;
504 void menu_bottom_page (MUTTMENU *menu)
508 menu->current = menu->top + menu->pagelen - 1;
509 if (menu->current > menu->max - 1)
510 menu->current = menu->max - 1;
511 menu->redraw = REDRAW_MOTION;
514 mutt_error _("No entries.");
517 void menu_middle_page (MUTTMENU *menu)
523 i = menu->top + menu->pagelen;
524 if (i > menu->max - 1)
526 menu->current = menu->top + (i - menu->top) / 2;
527 menu->redraw = REDRAW_MOTION;
530 mutt_error _("No entries.");
533 void menu_first_entry (MUTTMENU *menu)
538 menu->redraw = REDRAW_MOTION;
541 mutt_error _("No entries.");
544 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)
559 if ((menu->top -= menu->pagelen / 2) < 0)
561 if (menu->current >= menu->top + menu->pagelen)
562 menu->current = menu->top + menu->pagelen - 1;
563 menu->redraw = REDRAW_INDEX;
565 else if (menu->current && !menu->dialog)
568 menu->redraw = REDRAW_MOTION;
571 mutt_error _("First entry is shown.");
574 void menu_half_down (MUTTMENU *menu)
578 if (menu->top + menu->pagelen < menu->max)
580 menu->top += menu->pagelen / 2;
581 if (menu->current < menu->top)
582 menu->current = menu->top;
583 menu->redraw = REDRAW_INDEX;
585 else if (menu->current != menu->max - 1 && !menu->dialog)
587 menu->current = menu->max - 1;
588 menu->redraw = REDRAW_INDEX;
591 mutt_error _("Last entry is shown.");
594 mutt_error _("No entries.");
597 void menu_current_top (MUTTMENU *menu)
601 menu->top = menu->current;
602 menu->redraw = REDRAW_INDEX;
605 mutt_error _("No entries.");
608 void menu_current_middle (MUTTMENU *menu)
612 menu->top = menu->current - menu->pagelen / 2;
615 menu->redraw = REDRAW_INDEX;
618 mutt_error _("No entries.");
621 void menu_current_bottom (MUTTMENU *menu)
625 menu->top = menu->current - menu->pagelen + 1;
628 menu->redraw = REDRAW_INDEX;
631 mutt_error _("No entries.");
634 void menu_next_entry (MUTTMENU *menu)
636 if (menu->current < menu->max - 1)
639 menu->redraw = REDRAW_MOTION;
642 mutt_error _("You are on the last entry.");
645 void menu_prev_entry (MUTTMENU *menu)
650 menu->redraw = REDRAW_MOTION;
653 mutt_error _("You are on the first entry.");
656 static int default_color (int i)
658 return ColorDefs[MT_COLOR_NORMAL];
661 static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
663 char buf[LONG_STRING];
665 menu_make_entry (buf, sizeof (buf), m, n);
666 return (regexec (re, buf, 0, NULL, 0));
669 MUTTMENU *mutt_new_menu (void)
671 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
676 p->redraw = REDRAW_FULL;
677 p->pagelen = PAGELEN;
678 p->color = default_color;
679 p->search = menu_search_generic;
683 void mutt_menuDestroy (MUTTMENU **p)
687 FREE (&(*p)->searchBuf);
691 for (i=0; i < (*p)->max; i++)
692 FREE (&(*p)->dialog[i]);
694 FREE (& (*p)->dialog);
700 #define M_SEARCH_UP 1
701 #define M_SEARCH_DOWN 2
703 static int menu_search (MUTTMENU *menu, int op)
708 char buf[SHORT_STRING];
710 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
712 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
713 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
714 _("Reverse search for: "),
715 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
717 mutt_str_replace (&menu->searchBuf, buf);
718 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
722 if (!menu->searchBuf)
724 mutt_error _("No search pattern.");
729 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
730 if (op == OP_SEARCH_OPPOSITE)
731 searchDir = -searchDir;
733 if ((r = REGCOMP (&re, menu->searchBuf, REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0)
735 regerror (r, &re, buf, sizeof (buf));
737 mutt_error ("%s", buf);
741 r = menu->current + searchDir;
742 while (r >= 0 && r < menu->max)
744 if (menu->search (menu, &re, r) == 0)
754 mutt_error _("Not found.");
758 static int menu_dialog_translate_op (int i)
766 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
768 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
769 return OP_BOTTOM_PAGE;
770 case OP_CURRENT_MIDDLE:
771 return OP_MIDDLE_PAGE;
777 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
790 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
792 *ip = OP_MAX + (p - menu->keys + 1);
797 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
802 int menu_redraw (MUTTMENU *menu)
804 /* See if all or part of the screen needs to be updated. */
805 if (menu->redraw & REDRAW_FULL)
807 menu_redraw_full (menu);
808 /* allow the caller to do any local configuration */
813 menu_check_recenter (menu);
815 if (menu->redraw & REDRAW_STATUS)
816 menu_redraw_status (menu);
817 if (menu->redraw & REDRAW_INDEX)
818 menu_redraw_index (menu);
819 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
820 menu_redraw_motion (menu);
821 else if (menu->redraw == REDRAW_CURRENT)
822 menu_redraw_current (menu);
825 menu_redraw_prompt (menu);
830 int mutt_menuLoop (MUTTMENU *menu)
836 if (option (OPTMENUCALLER))
838 unset_option (OPTMENUCALLER);
849 if (menu_redraw (menu) == OP_REDRAW)
852 menu->oldcurrent = menu->current;
855 /* move the cursor out of the way */
856 move (menu->current - menu->top + menu->offset,
857 (option (OPTARROWCURSOR) ? 2 : COLS-1));
861 /* try to catch dialog keys before ops */
862 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
865 i = km_dokey (menu->menu);
866 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
870 mvaddstr (LINES - 1, 0, "Tag-");
872 i = km_dokey (menu->menu);
874 CLEARLINE (LINES - 1);
876 else if (i == OP_TAG_PREFIX)
878 mutt_error _("No tagged entries.");
881 else /* None tagged, OP_TAG_PREFIX_COND */
887 if(tmp.op==OP_END_COND)break;
889 mutt_message _("Nothing to do.");
893 else if (menu->tagged && option (OPTAUTOTAG))
900 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
903 mutt_resize_screen ();
904 menu->redraw = REDRAW_FULL;
906 clearok(stdscr,TRUE);/*force complete redraw*/
916 /* Convert menubar movement to scrolling */
918 i = menu_dialog_translate_op (i);
923 menu_next_entry (menu);
926 menu_prev_entry (menu);
929 menu_half_down (menu);
935 menu_next_page (menu);
938 menu_prev_page (menu);
941 menu_next_line (menu);
944 menu_prev_line (menu);
947 menu_first_entry (menu);
950 menu_last_entry (menu);
953 menu_top_page (menu);
956 menu_middle_page (menu);
959 menu_bottom_page (menu);
962 menu_current_top (menu);
964 case OP_CURRENT_MIDDLE:
965 menu_current_middle (menu);
967 case OP_CURRENT_BOTTOM:
968 menu_current_bottom (menu);
971 case OP_SEARCH_REVERSE:
973 case OP_SEARCH_OPPOSITE:
974 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
976 menu->oldcurrent = menu->current;
977 if ((menu->current = menu_search (menu, i)) != -1)
978 menu->redraw = REDRAW_MOTION;
980 menu->current = menu->oldcurrent;
983 mutt_error _("Search is not implemented for this menu.");
988 mutt_error _("Jumping is not implemented for dialogs.");
993 case OP_ENTER_COMMAND:
994 CurrentMenu = menu->menu;
995 mutt_enter_command ();
996 if (option (OPTFORCEREDRAWINDEX))
998 menu->redraw = REDRAW_FULL;
999 unset_option (OPTFORCEREDRAWINDEX);
1000 unset_option (OPTFORCEREDRAWPAGER);
1005 if (menu->tag && !menu->dialog)
1007 if (menu->tagprefix && !option (OPTAUTOTAG))
1009 for (i = 0; i < menu->max; i++)
1010 menu->tagged += menu->tag (menu, i, 0);
1011 menu->redraw = REDRAW_INDEX;
1015 int i = menu->tag (menu, menu->current, -1);
1017 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1020 menu->redraw = REDRAW_MOTION_RESYNCH;
1023 menu->redraw = REDRAW_CURRENT;
1026 mutt_error _("No entries.");
1029 mutt_error _("Tagging is not supported.");
1032 case OP_SHELL_ESCAPE:
1033 mutt_shell_escape ();
1034 MAYBE_REDRAW (menu->redraw);
1042 clearok (stdscr, TRUE);
1043 menu->redraw = REDRAW_FULL;
1047 mutt_help (menu->menu);
1048 menu->redraw = REDRAW_FULL;
1052 km_error_key (menu->menu);