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"
31 extern int Charset_is_utf8; /* FIXME: bad modularisation */
33 extern size_t UngetCount;
35 static void print_enriched_string (int attr, unsigned char *s, int do_color)
39 size_t n = mutt_strlen ((char *)s);
43 memset (&mbstate, 0, sizeof (mbstate));
50 pair_content(PAIR_NUMBER(ColorDefs[MT_COLOR_TREE]), &f1, &b1);
51 pair_content(PAIR_NUMBER(attr), &f2, &b2);
53 SETCOLOR (MT_COLOR_TREE);
55 while (*s && *s < M_TREE_MAX)
60 if (option (OPTASCIICHARS))
62 else if (Charset_is_utf8)
63 addstr ("\342\224\224"); /* WACS_LLCORNER */
68 if (option (OPTASCIICHARS))
70 else if (Charset_is_utf8)
71 addstr ("\342\224\214"); /* WACS_ULCORNER */
76 if (option (OPTASCIICHARS))
78 else if (Charset_is_utf8)
79 addstr ("\342\224\234"); /* WACS_LTEE */
84 if (option (OPTASCIICHARS))
86 else if (Charset_is_utf8)
87 addstr ("\342\224\200"); /* WACS_HLINE */
92 if (option (OPTASCIICHARS))
94 else if (Charset_is_utf8)
95 addstr ("\342\224\202"); /* WACS_VLINE */
100 if (option (OPTASCIICHARS))
102 else if (Charset_is_utf8)
103 addstr ("\342\224\254"); /* WACS_TTEE */
108 if (option (OPTASCIICHARS))
110 else if (Charset_is_utf8)
111 addstr ("\342\224\264"); /* WACS_BTEE */
122 addch ('*'); /* fake thread indicator */
136 if (do_color) attrset(attr);
138 else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
140 addnstr ((char *)s, k);
148 static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i)
152 strncpy (s, menu->dialog[i], l);
153 menu->current = -1; /* hide menubar */
156 menu->make_entry (s, l, menu, i);
159 void menu_pad_string (char *s, size_t n)
161 int shift = option (OPTARROWCURSOR) ? 3 : 0;
162 int cols = COLS - shift;
164 mutt_format_string (s, n, cols, cols, 0, ' ', s, strlen (s), 1);
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)
212 for (i = menu->top; i < menu->top + menu->pagelen; i++)
216 menu_make_entry (buf, sizeof (buf), menu, i);
217 menu_pad_string (buf, sizeof (buf));
219 if (option (OPTARROWCURSOR))
221 attrset (menu->color (i));
222 CLEARLINE (i - menu->top + menu->offset);
224 if (i == menu->current)
226 attrset (menu->color (i));
227 ADDCOLOR (MT_COLOR_INDICATOR);
229 attrset (menu->color (i));
233 move (i - menu->top + menu->offset, 3);
235 print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
236 SETCOLOR (MT_COLOR_NORMAL);
240 attrset (menu->color (i));
242 if (i == menu->current)
244 ADDCOLOR (MT_COLOR_INDICATOR);
245 BKGDSET (MT_COLOR_INDICATOR);
248 CLEARLINE (i - menu->top + menu->offset);
249 print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current);
250 SETCOLOR (MT_COLOR_NORMAL);
251 BKGDSET (MT_COLOR_NORMAL);
255 CLEARLINE (i - menu->top + menu->offset);
260 void menu_redraw_motion (MUTTMENU *menu)
266 menu->redraw &= ~REDRAW_MOTION;
270 move (menu->oldcurrent + menu->offset - menu->top, 0);
271 SETCOLOR (MT_COLOR_NORMAL);
272 BKGDSET (MT_COLOR_NORMAL);
274 if (option (OPTARROWCURSOR))
276 /* clear the pointer */
277 attrset (menu->color (menu->oldcurrent));
280 if (menu->redraw & REDRAW_MOTION_RESYNCH)
283 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
284 menu_pad_string (buf, sizeof (buf));
285 move (menu->oldcurrent + menu->offset - menu->top, 3);
286 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
287 SETCOLOR (MT_COLOR_NORMAL);
290 /* now draw it in the new location */
291 move (menu->current + menu->offset - menu->top, 0);
292 attrset (menu->color (menu->current));
293 ADDCOLOR (MT_COLOR_INDICATOR);
295 SETCOLOR (MT_COLOR_NORMAL);
299 /* erase the current indicator */
300 attrset (menu->color (menu->oldcurrent));
302 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
303 menu_pad_string (buf, sizeof (buf));
304 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
306 /* now draw the new one to reflect the change */
307 menu_make_entry (buf, sizeof (buf), menu, menu->current);
308 menu_pad_string (buf, sizeof (buf));
309 attrset (menu->color (menu->current));
310 ADDCOLOR (MT_COLOR_INDICATOR);
311 BKGDSET (MT_COLOR_INDICATOR);
312 CLEARLINE (menu->current - menu->top + menu->offset);
313 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
314 SETCOLOR (MT_COLOR_NORMAL);
315 BKGDSET (MT_COLOR_NORMAL);
317 menu->redraw &= REDRAW_STATUS;
320 void menu_redraw_current (MUTTMENU *menu)
324 move (menu->current + menu->offset - menu->top, 0);
325 menu_make_entry (buf, sizeof (buf), menu, menu->current);
326 menu_pad_string (buf, sizeof (buf));
328 if (option (OPTARROWCURSOR))
330 int attr = menu->color (menu->current);
333 attrset (menu->color (menu->current));
334 ADDCOLOR (MT_COLOR_INDICATOR);
338 menu_pad_string (buf, sizeof (buf));
339 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 1);
340 SETCOLOR (MT_COLOR_NORMAL);
344 attrset (menu->color (menu->current));
345 ADDCOLOR (MT_COLOR_INDICATOR);
346 BKGDSET (MT_COLOR_INDICATOR);
348 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
349 SETCOLOR (MT_COLOR_NORMAL);
350 BKGDSET (MT_COLOR_NORMAL);
352 menu->redraw &= REDRAW_STATUS;
355 void menu_redraw_prompt (MUTTMENU *menu)
359 if (option (OPTMSGERR))
362 unset_option (OPTMSGERR);
368 SETCOLOR (MT_COLOR_NORMAL);
369 mvaddstr (LINES - 1, 0, menu->prompt);
374 void menu_check_recenter (MUTTMENU *menu)
376 if (menu->max <= menu->pagelen && menu->top != 0)
379 set_option (OPTNEEDREDRAW);
380 menu->redraw |= REDRAW_INDEX;
382 else if (menu->current >= menu->top + menu->pagelen)
384 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
385 menu->top = menu->current - menu->pagelen + 1;
387 menu->top += menu->pagelen * ((menu->current - menu->top) / menu->pagelen);
388 menu->redraw |= REDRAW_INDEX;
390 else if (menu->current < menu->top)
392 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
393 menu->top = menu->current;
396 menu->top -= menu->pagelen * ((menu->top + menu->pagelen - 1 - menu->current) / menu->pagelen);
400 menu->redraw |= REDRAW_INDEX;
404 void menu_jump (MUTTMENU *menu)
407 char buf[SHORT_STRING];
411 mutt_ungetch (LastKey, 0);
413 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
416 if (n >= 0 && n < menu->max)
419 menu->redraw = REDRAW_MOTION;
422 mutt_error _("Invalid index number.");
426 mutt_error _("No entries.");
429 void menu_next_line (MUTTMENU *menu)
433 if (menu->top + 1 < menu->max)
436 if (menu->current < menu->top)
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)
452 if (menu->current >= menu->top + menu->pagelen)
454 menu->redraw = REDRAW_INDEX;
457 mutt_error _("You cannot scroll up farther.");
460 void menu_next_page (MUTTMENU *menu)
464 if (menu->top + menu->pagelen < menu->max)
466 menu->top += menu->pagelen;
467 if (menu->current < menu->top)
468 menu->current = menu->top;
469 menu->redraw = REDRAW_INDEX;
471 else if (menu->current != menu->max - 1 && !menu->dialog)
473 menu->current = menu->max - 1;
474 menu->redraw = REDRAW_MOTION;
477 mutt_error _("You are on the last page.");
480 mutt_error _("No entries.");
483 void menu_prev_page (MUTTMENU *menu)
487 if ((menu->top -= menu->pagelen) < 0)
489 if (menu->current >= menu->top + menu->pagelen)
490 menu->current = menu->top + menu->pagelen - 1;
491 menu->redraw = REDRAW_INDEX;
493 else if (menu->current && !menu->dialog)
496 menu->redraw = REDRAW_MOTION;
499 mutt_error _("You are on the first page.");
502 void menu_top_page (MUTTMENU *menu)
504 if (menu->current != menu->top)
506 menu->current = menu->top;
507 menu->redraw = REDRAW_MOTION;
511 void menu_bottom_page (MUTTMENU *menu)
515 menu->current = menu->top + menu->pagelen - 1;
516 if (menu->current > menu->max - 1)
517 menu->current = menu->max - 1;
518 menu->redraw = REDRAW_MOTION;
521 mutt_error _("No entries.");
524 void menu_middle_page (MUTTMENU *menu)
530 i = menu->top + menu->pagelen;
531 if (i > menu->max - 1)
533 menu->current = menu->top + (i - menu->top) / 2;
534 menu->redraw = REDRAW_MOTION;
537 mutt_error _("No entries.");
540 void menu_first_entry (MUTTMENU *menu)
545 menu->redraw = REDRAW_MOTION;
548 mutt_error _("No entries.");
551 void menu_last_entry (MUTTMENU *menu)
555 menu->current = menu->max - 1;
556 menu->redraw = REDRAW_MOTION;
559 mutt_error _("No entries.");
562 void menu_half_up (MUTTMENU *menu)
566 if ((menu->top -= menu->pagelen / 2) < 0)
568 if (menu->current >= menu->top + menu->pagelen)
569 menu->current = menu->top + menu->pagelen - 1;
570 menu->redraw = REDRAW_INDEX;
572 else if (menu->current && !menu->dialog)
575 menu->redraw = REDRAW_MOTION;
578 mutt_error _("First entry is shown.");
581 void menu_half_down (MUTTMENU *menu)
585 if (menu->top + menu->pagelen < menu->max)
587 menu->top += menu->pagelen / 2;
588 if (menu->current < menu->top)
589 menu->current = menu->top;
590 menu->redraw = REDRAW_INDEX;
592 else if (menu->current != menu->max - 1 && !menu->dialog)
594 menu->current = menu->max - 1;
595 menu->redraw = REDRAW_INDEX;
598 mutt_error _("Last entry is shown.");
601 mutt_error _("No entries.");
604 void menu_current_top (MUTTMENU *menu)
608 menu->top = menu->current;
609 menu->redraw = REDRAW_INDEX;
612 mutt_error _("No entries.");
615 void menu_current_middle (MUTTMENU *menu)
619 menu->top = menu->current - menu->pagelen / 2;
622 menu->redraw = REDRAW_INDEX;
625 mutt_error _("No entries.");
628 void menu_current_bottom (MUTTMENU *menu)
632 menu->top = menu->current - menu->pagelen + 1;
635 menu->redraw = REDRAW_INDEX;
638 mutt_error _("No entries.");
641 void menu_next_entry (MUTTMENU *menu)
643 if (menu->current < menu->max - 1)
646 menu->redraw = REDRAW_MOTION;
649 mutt_error _("You are on the last entry.");
652 void menu_prev_entry (MUTTMENU *menu)
657 menu->redraw = REDRAW_MOTION;
660 mutt_error _("You are on the first entry.");
663 static int default_color (int i)
665 return ColorDefs[MT_COLOR_NORMAL];
668 static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
670 char buf[LONG_STRING];
672 menu_make_entry (buf, sizeof (buf), m, n);
673 return (regexec (re, buf, 0, NULL, 0));
676 MUTTMENU *mutt_new_menu (void)
678 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
683 p->redraw = REDRAW_FULL;
684 p->pagelen = PAGELEN;
685 p->color = default_color;
686 p->search = menu_search_generic;
690 void mutt_menuDestroy (MUTTMENU **p)
694 FREE (&(*p)->searchBuf);
698 for (i=0; i < (*p)->max; i++)
699 FREE (&(*p)->dialog[i]);
707 #define M_SEARCH_UP 1
708 #define M_SEARCH_DOWN 2
710 static int menu_search (MUTTMENU *menu, int op)
715 char buf[SHORT_STRING];
717 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
719 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
720 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
721 _("Reverse search for: "),
722 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
724 mutt_str_replace (&menu->searchBuf, buf);
725 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
729 if (!menu->searchBuf)
731 mutt_error _("No search pattern.");
736 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
737 if (op == OP_SEARCH_OPPOSITE)
738 searchDir = -searchDir;
740 if ((r = REGCOMP (&re, menu->searchBuf, REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0)
742 regerror (r, &re, buf, sizeof (buf));
744 mutt_error ("%s", buf);
748 r = menu->current + searchDir;
749 while (r >= 0 && r < menu->max)
751 if (menu->search (menu, &re, r) == 0)
761 mutt_error _("Not found.");
765 static int menu_dialog_translate_op (int i)
773 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
775 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
776 return OP_BOTTOM_PAGE;
777 case OP_CURRENT_MIDDLE:
778 return OP_MIDDLE_PAGE;
784 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
797 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
799 *ip = OP_MAX + (p - menu->keys + 1);
804 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
809 int menu_redraw (MUTTMENU *menu)
811 /* See if all or part of the screen needs to be updated. */
812 if (menu->redraw & REDRAW_FULL)
814 menu_redraw_full (menu);
815 /* allow the caller to do any local configuration */
820 menu_check_recenter (menu);
822 if (menu->redraw & REDRAW_STATUS)
823 menu_redraw_status (menu);
824 if (menu->redraw & REDRAW_INDEX)
825 menu_redraw_index (menu);
826 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
827 menu_redraw_motion (menu);
828 else if (menu->redraw == REDRAW_CURRENT)
829 menu_redraw_current (menu);
832 menu_redraw_prompt (menu);
837 int mutt_menuLoop (MUTTMENU *menu)
843 if (option (OPTMENUCALLER))
845 unset_option (OPTMENUCALLER);
856 if (menu_redraw (menu) == OP_REDRAW)
859 menu->oldcurrent = menu->current;
862 /* move the cursor out of the way */
863 move (menu->current - menu->top + menu->offset,
864 (option (OPTARROWCURSOR) ? 2 : COLS-1));
868 /* try to catch dialog keys before ops */
869 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
872 i = km_dokey (menu->menu);
873 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
877 mvaddstr (LINES - 1, 0, "Tag-");
879 i = km_dokey (menu->menu);
881 CLEARLINE (LINES - 1);
883 else if (i == OP_TAG_PREFIX)
885 mutt_error _("No tagged entries.");
888 else /* None tagged, OP_TAG_PREFIX_COND */
894 if(tmp.op==OP_END_COND)break;
896 mutt_message _("Nothing to do.");
900 else if (menu->tagged && option (OPTAUTOTAG))
907 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
910 mutt_resize_screen ();
911 menu->redraw = REDRAW_FULL;
913 clearok(stdscr,TRUE);/*force complete redraw*/
923 /* Convert menubar movement to scrolling */
925 i = menu_dialog_translate_op (i);
930 menu_next_entry (menu);
933 menu_prev_entry (menu);
936 menu_half_down (menu);
942 menu_next_page (menu);
945 menu_prev_page (menu);
948 menu_next_line (menu);
951 menu_prev_line (menu);
954 menu_first_entry (menu);
957 menu_last_entry (menu);
960 menu_top_page (menu);
963 menu_middle_page (menu);
966 menu_bottom_page (menu);
969 menu_current_top (menu);
971 case OP_CURRENT_MIDDLE:
972 menu_current_middle (menu);
974 case OP_CURRENT_BOTTOM:
975 menu_current_bottom (menu);
978 case OP_SEARCH_REVERSE:
980 case OP_SEARCH_OPPOSITE:
981 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
983 menu->oldcurrent = menu->current;
984 if ((menu->current = menu_search (menu, i)) != -1)
985 menu->redraw = REDRAW_MOTION;
987 menu->current = menu->oldcurrent;
990 mutt_error _("Search is not implemented for this menu.");
995 mutt_error _("Jumping is not implemented for dialogs.");
1000 case OP_ENTER_COMMAND:
1001 CurrentMenu = menu->menu;
1002 mutt_enter_command ();
1003 if (option (OPTFORCEREDRAWINDEX))
1005 menu->redraw = REDRAW_FULL;
1006 unset_option (OPTFORCEREDRAWINDEX);
1007 unset_option (OPTFORCEREDRAWPAGER);
1012 if (menu->tag && !menu->dialog)
1014 if (menu->tagprefix && !option (OPTAUTOTAG))
1016 for (i = 0; i < menu->max; i++)
1017 menu->tagged += menu->tag (menu, i, 0);
1018 menu->redraw = REDRAW_INDEX;
1022 int i = menu->tag (menu, menu->current, -1);
1024 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1027 menu->redraw = REDRAW_MOTION_RESYNCH;
1030 menu->redraw = REDRAW_CURRENT;
1033 mutt_error _("No entries.");
1036 mutt_error _("Tagging is not supported.");
1039 case OP_SHELL_ESCAPE:
1040 mutt_shell_escape ();
1041 MAYBE_REDRAW (menu->redraw);
1049 clearok (stdscr, TRUE);
1050 menu->redraw = REDRAW_FULL;
1054 mutt_help (menu->menu);
1055 menu->redraw = REDRAW_FULL;
1059 km_error_key (menu->menu);