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)
43 size_t n = mutt_strlen ((char *)s);
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);
136 addnstr ((char *)s, 1);
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 void menu_pad_string (char *s, size_t n)
157 int shift = option (OPTARROWCURSOR) ? 3 : 0;
158 int cols = COLS - shift - SidebarWidth;
162 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, strlen (s), 1);
165 snprintf(s,n,"%s",tmpbuf); /* overkill */
168 void menu_redraw_full (MUTTMENU *menu)
170 SETCOLOR (MT_COLOR_NORMAL);
171 /* clear() doesn't optimize screen redraws */
175 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;
186 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
187 menu->pagelen = LINES - 2;
192 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
195 void menu_redraw_status (MUTTMENU *menu)
199 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
200 SETCOLOR (MT_COLOR_STATUS);
201 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
202 mutt_paddstr (COLS, buf);
203 SETCOLOR (MT_COLOR_NORMAL);
204 menu->redraw &= ~REDRAW_STATUS;
207 void menu_redraw_index (MUTTMENU *menu)
213 for (i = menu->top; i < menu->top + menu->pagelen; i++)
217 menu_make_entry (buf, sizeof (buf), menu, i);
218 menu_pad_string (buf, sizeof (buf));
220 if (option (OPTARROWCURSOR))
222 attrset (menu->color (i));
223 CLEARLINE_WIN (i - menu->top + menu->offset);
225 if (i == menu->current)
227 attrset (menu->color (i));
228 ADDCOLOR (MT_COLOR_INDICATOR);
230 attrset (menu->color (i));
234 move (i - menu->top + menu->offset, SidebarWidth + 3);
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_WIN (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_WIN (i - menu->top + menu->offset);
261 void menu_redraw_motion (MUTTMENU *menu)
267 menu->redraw &= ~REDRAW_MOTION;
271 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
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, SidebarWidth + 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, SidebarWidth);
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_WIN (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)
325 move (menu->current + menu->offset - menu->top, SidebarWidth);
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 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 (menu->max <= menu->pagelen) /* less entries than lines */
382 if (menu->top != 0) {
384 set_option (OPTNEEDREDRAW);
387 else if (menu->current >= menu->top + menu->pagelen - c) /* indicator below bottom threshold */
389 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
390 menu->top = menu->current - menu->pagelen + c + 1;
392 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
394 else if (menu->current < menu->top + c) /* indicator above top threshold */
396 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
397 menu->top = menu->current - c;
399 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
402 /* make entries stick to bottom */
403 menu->top = MIN (menu->top, menu->max - menu->pagelen);
404 menu->top = MAX (menu->top, 0);
406 if (menu->top != old_top)
407 menu->redraw |= REDRAW_INDEX;
410 void menu_jump (MUTTMENU *menu)
413 char buf[SHORT_STRING];
417 mutt_ungetch (LastKey, 0);
419 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
422 if (n >= 0 && n < menu->max)
425 menu->redraw = REDRAW_MOTION;
428 mutt_error _("Invalid index number.");
432 mutt_error _("No entries.");
435 void menu_next_line (MUTTMENU *menu)
439 if (menu->top + 1 < menu->max)
442 if (menu->current < menu->top)
444 menu->redraw = REDRAW_INDEX;
447 mutt_error _("You cannot scroll down farther.");
450 mutt_error _("No entries.");
453 void menu_prev_line (MUTTMENU *menu)
458 if (menu->current >= menu->top + menu->pagelen)
460 menu->redraw = REDRAW_INDEX;
463 mutt_error _("You cannot scroll up farther.");
466 void menu_next_page (MUTTMENU *menu)
470 if (menu->top + menu->pagelen < menu->max)
472 menu->top += menu->pagelen;
473 if (menu->current < menu->top)
474 menu->current = menu->top;
475 menu->redraw = REDRAW_INDEX;
477 else if (menu->current != menu->max - 1 && !menu->dialog)
479 menu->current = menu->max - 1;
480 menu->redraw = REDRAW_MOTION;
483 mutt_error _("You are on the last page.");
486 mutt_error _("No entries.");
489 void menu_prev_page (MUTTMENU *menu)
491 int c = MIN (MenuContext, menu->pagelen / 2);
495 if ((menu->top -= menu->pagelen) < 0)
497 if (menu->current >= menu->top + menu->pagelen)
498 menu->current = menu->top + menu->pagelen - 1;
499 menu->redraw = REDRAW_INDEX;
501 else if (menu->current && !menu->dialog)
504 menu->redraw = REDRAW_MOTION;
507 mutt_error _("You are on the first page.");
510 void menu_top_page (MUTTMENU *menu)
512 if (menu->current != menu->top)
514 menu->current = menu->top;
515 menu->redraw = REDRAW_MOTION;
519 void menu_bottom_page (MUTTMENU *menu)
523 menu->current = menu->top + menu->pagelen - 1;
524 if (menu->current > menu->max - 1)
525 menu->current = menu->max - 1;
526 menu->redraw = REDRAW_MOTION;
529 mutt_error _("No entries.");
532 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)
553 menu->redraw = REDRAW_MOTION;
556 mutt_error _("No entries.");
559 void menu_last_entry (MUTTMENU *menu)
563 menu->current = menu->max - 1;
564 menu->redraw = REDRAW_MOTION;
567 mutt_error _("No entries.");
570 void menu_half_up (MUTTMENU *menu)
574 if ((menu->top -= menu->pagelen / 2) < 0)
576 if (menu->current >= menu->top + menu->pagelen)
577 menu->current = menu->top + menu->pagelen - 1;
578 menu->redraw = REDRAW_INDEX;
580 else if (menu->current && !menu->dialog)
583 menu->redraw = REDRAW_MOTION;
586 mutt_error _("First entry is shown.");
589 void menu_half_down (MUTTMENU *menu)
593 if (menu->top + menu->pagelen < menu->max)
595 menu->top += menu->pagelen / 2;
596 if (menu->current < menu->top)
597 menu->current = menu->top;
598 menu->redraw = REDRAW_INDEX;
600 else if (menu->current != menu->max - 1 && !menu->dialog)
602 menu->current = menu->max - 1;
603 menu->redraw = REDRAW_INDEX;
606 mutt_error _("Last entry is shown.");
609 mutt_error _("No entries.");
612 void menu_current_top (MUTTMENU *menu)
616 menu->top = menu->current;
617 menu->redraw = REDRAW_INDEX;
620 mutt_error _("No entries.");
623 void menu_current_middle (MUTTMENU *menu)
627 menu->top = menu->current - menu->pagelen / 2;
630 menu->redraw = REDRAW_INDEX;
633 mutt_error _("No entries.");
636 void menu_current_bottom (MUTTMENU *menu)
640 menu->top = menu->current - menu->pagelen + 1;
643 menu->redraw = REDRAW_INDEX;
646 mutt_error _("No entries.");
649 void menu_next_entry (MUTTMENU *menu)
651 if (menu->current < menu->max - 1)
654 menu->redraw = REDRAW_MOTION;
657 mutt_error _("You are on the last entry.");
660 void menu_prev_entry (MUTTMENU *menu)
665 menu->redraw = REDRAW_MOTION;
668 mutt_error _("You are on the first entry.");
671 static int default_color (int i)
673 return ColorDefs[MT_COLOR_NORMAL];
676 static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
678 char buf[LONG_STRING];
680 menu_make_entry (buf, sizeof (buf), m, n);
681 return (regexec (re, buf, 0, NULL, 0));
684 MUTTMENU *mutt_new_menu (void)
686 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
691 p->redraw = REDRAW_FULL;
692 p->pagelen = PAGELEN;
693 p->color = default_color;
694 p->search = menu_search_generic;
698 void mutt_menuDestroy (MUTTMENU **p)
702 FREE (&(*p)->searchBuf);
706 for (i=0; i < (*p)->max; i++)
707 FREE (&(*p)->dialog[i]);
709 FREE (& (*p)->dialog);
715 #define M_SEARCH_UP 1
716 #define M_SEARCH_DOWN 2
718 static int menu_search (MUTTMENU *menu, int op)
723 char buf[SHORT_STRING];
725 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
727 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
728 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
729 _("Reverse search for: "),
730 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
732 mutt_str_replace (&menu->searchBuf, buf);
733 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
737 if (!menu->searchBuf)
739 mutt_error _("No search pattern.");
744 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
745 if (op == OP_SEARCH_OPPOSITE)
746 searchDir = -searchDir;
748 if ((r = REGCOMP (&re, menu->searchBuf, REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0)
750 regerror (r, &re, buf, sizeof (buf));
752 mutt_error ("%s", buf);
756 r = menu->current + searchDir;
757 while (r >= 0 && r < menu->max)
759 if (menu->search (menu, &re, r) == 0)
769 mutt_error _("Not found.");
773 static int menu_dialog_translate_op (int i)
781 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
783 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
784 return OP_BOTTOM_PAGE;
785 case OP_CURRENT_MIDDLE:
786 return OP_MIDDLE_PAGE;
792 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
805 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
807 *ip = OP_MAX + (p - menu->keys + 1);
812 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
817 int menu_redraw (MUTTMENU *menu)
819 /* See if all or part of the screen needs to be updated. */
820 if (menu->redraw & REDRAW_FULL)
822 menu_redraw_full (menu);
823 /* allow the caller to do any local configuration */
828 menu_check_recenter (menu);
830 if (menu->redraw & REDRAW_STATUS)
831 menu_redraw_status (menu);
832 if (menu->redraw & REDRAW_INDEX)
833 menu_redraw_index (menu);
834 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
835 menu_redraw_motion (menu);
836 else if (menu->redraw == REDRAW_CURRENT)
837 menu_redraw_current (menu);
840 menu_redraw_prompt (menu);
845 int mutt_menuLoop (MUTTMENU *menu)
851 if (option (OPTMENUCALLER))
853 unset_option (OPTMENUCALLER);
864 if (menu_redraw (menu) == OP_REDRAW)
867 menu->oldcurrent = menu->current;
870 /* move the cursor out of the way */
871 move (menu->current - menu->top + menu->offset,
872 (option (OPTARROWCURSOR) ? 2 : COLS-1));
876 /* try to catch dialog keys before ops */
877 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
880 i = km_dokey (menu->menu);
881 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
885 mvaddstr (LINES - 1, 0, "Tag-");
887 i = km_dokey (menu->menu);
889 CLEARLINE (LINES - 1);
891 else if (i == OP_TAG_PREFIX)
893 mutt_error _("No tagged entries.");
896 else /* None tagged, OP_TAG_PREFIX_COND */
902 if(tmp.op==OP_END_COND)break;
904 mutt_message _("Nothing to do.");
908 else if (menu->tagged && option (OPTAUTOTAG))
915 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
918 mutt_resize_screen ();
919 menu->redraw = REDRAW_FULL;
921 clearok(stdscr,TRUE);/*force complete redraw*/
931 /* Convert menubar movement to scrolling */
933 i = menu_dialog_translate_op (i);
938 menu_next_entry (menu);
941 menu_prev_entry (menu);
944 menu_half_down (menu);
950 menu_next_page (menu);
953 menu_prev_page (menu);
956 menu_next_line (menu);
959 menu_prev_line (menu);
962 menu_first_entry (menu);
965 menu_last_entry (menu);
968 menu_top_page (menu);
971 menu_middle_page (menu);
974 menu_bottom_page (menu);
977 menu_current_top (menu);
979 case OP_CURRENT_MIDDLE:
980 menu_current_middle (menu);
982 case OP_CURRENT_BOTTOM:
983 menu_current_bottom (menu);
986 case OP_SEARCH_REVERSE:
988 case OP_SEARCH_OPPOSITE:
989 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
991 menu->oldcurrent = menu->current;
992 if ((menu->current = menu_search (menu, i)) != -1)
993 menu->redraw = REDRAW_MOTION;
995 menu->current = menu->oldcurrent;
998 mutt_error _("Search is not implemented for this menu.");
1003 mutt_error _("Jumping is not implemented for dialogs.");
1008 case OP_ENTER_COMMAND:
1009 CurrentMenu = menu->menu;
1010 mutt_enter_command ();
1011 if (option (OPTFORCEREDRAWINDEX))
1013 menu->redraw = REDRAW_FULL;
1014 unset_option (OPTFORCEREDRAWINDEX);
1015 unset_option (OPTFORCEREDRAWPAGER);
1020 if (menu->tag && !menu->dialog)
1022 if (menu->tagprefix && !option (OPTAUTOTAG))
1024 for (i = 0; i < menu->max; i++)
1025 menu->tagged += menu->tag (menu, i, 0);
1026 menu->redraw = REDRAW_INDEX;
1030 int i = menu->tag (menu, menu->current, -1);
1032 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1035 menu->redraw = REDRAW_MOTION_RESYNCH;
1038 menu->redraw = REDRAW_CURRENT;
1041 mutt_error _("No entries.");
1044 mutt_error _("Tagging is not supported.");
1047 case OP_SHELL_ESCAPE:
1048 mutt_shell_escape ();
1049 MAYBE_REDRAW (menu->redraw);
1057 clearok (stdscr, TRUE);
1058 menu->redraw = REDRAW_FULL;
1062 mutt_help (menu->menu);
1063 menu->redraw = REDRAW_FULL;
1067 km_error_key (menu->menu);