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)
44 size_t n = mutt_strlen ((char *)s);
47 memset (&mbstate, 0, sizeof (mbstate));
53 SETCOLOR (MT_COLOR_TREE);
54 while (*s && *s < M_TREE_MAX)
59 if (option (OPTASCIICHARS))
61 else if (Charset_is_utf8)
62 addstr ("\342\224\224"); /* WACS_LLCORNER */
67 if (option (OPTASCIICHARS))
69 else if (Charset_is_utf8)
70 addstr ("\342\224\214"); /* WACS_ULCORNER */
75 if (option (OPTASCIICHARS))
77 else if (Charset_is_utf8)
78 addstr ("\342\224\234"); /* WACS_LTEE */
83 if (option (OPTASCIICHARS))
85 else if (Charset_is_utf8)
86 addstr ("\342\224\200"); /* WACS_HLINE */
91 if (option (OPTASCIICHARS))
93 else if (Charset_is_utf8)
94 addstr ("\342\224\202"); /* WACS_VLINE */
99 if (option (OPTASCIICHARS))
101 else if (Charset_is_utf8)
102 addstr ("\342\224\254"); /* WACS_TTEE */
107 if (option (OPTASCIICHARS))
109 else if (Charset_is_utf8)
110 addstr ("\342\224\264"); /* WACS_BTEE */
121 addch ('*'); /* fake thread indicator */
135 if (do_color) attrset(attr);
137 else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
139 addnstr ((char *)s, k);
147 static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i)
151 strncpy (s, menu->dialog[i], l);
152 menu->current = -1; /* hide menubar */
155 menu->make_entry (s, l, menu, i);
158 void menu_pad_string (char *s, size_t n)
160 int shift = option (OPTARROWCURSOR) ? 3 : 0;
162 char* tmpbuf = safe_malloc (n);
163 if (option(OPTMBOXPANE))
164 cols = COLS - shift - SidebarWidth;
167 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, strlen (s), 1);
169 snprintf(s,n,"%s",tmpbuf); /* overkill */
173 void menu_redraw_full (MUTTMENU *menu)
175 SETCOLOR (MT_COLOR_NORMAL);
176 /* clear() doesn't optimize screen redraws */
180 if (option (OPTHELP))
182 SETCOLOR (MT_COLOR_STATUS);
183 move (option (OPTSTATUSONTOP) ? LINES-2 : 0, 0);
184 mutt_paddstr (COLS, menu->help);
185 SETCOLOR (MT_COLOR_NORMAL);
187 menu->pagelen = LINES - 3;
191 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
192 menu->pagelen = LINES - 2;
197 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
200 void menu_redraw_status (MUTTMENU *menu)
204 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
205 SETCOLOR (MT_COLOR_STATUS);
206 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
207 mutt_paddstr (COLS, buf);
208 SETCOLOR (MT_COLOR_NORMAL);
209 menu->redraw &= ~REDRAW_STATUS;
212 void menu_redraw_index (MUTTMENU *menu)
218 for (i = menu->top; i < menu->top + menu->pagelen; i++)
222 menu_make_entry (buf, sizeof (buf), menu, i);
223 menu_pad_string (buf, sizeof (buf));
225 if (option (OPTARROWCURSOR))
227 attrset (menu->color (i));
228 CLEARLINE_WIN (i - menu->top + menu->offset);
230 if (i == menu->current)
232 attrset (menu->color (i));
233 ADDCOLOR (MT_COLOR_INDICATOR);
235 attrset (menu->color (i));
239 move (i - menu->top + menu->offset, SidebarWidth + 3);
241 print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
242 SETCOLOR (MT_COLOR_NORMAL);
246 attrset (menu->color (i));
248 if (i == menu->current)
250 ADDCOLOR (MT_COLOR_INDICATOR);
251 BKGDSET (MT_COLOR_INDICATOR);
254 CLEARLINE_WIN (i - menu->top + menu->offset);
255 print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current);
256 SETCOLOR (MT_COLOR_NORMAL);
257 BKGDSET (MT_COLOR_NORMAL);
261 CLEARLINE_WIN (i - menu->top + menu->offset);
266 void menu_redraw_motion (MUTTMENU *menu)
272 menu->redraw &= ~REDRAW_MOTION;
276 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
277 SETCOLOR (MT_COLOR_NORMAL);
278 BKGDSET (MT_COLOR_NORMAL);
280 if (option (OPTARROWCURSOR))
282 /* clear the pointer */
283 attrset (menu->color (menu->oldcurrent));
286 if (menu->redraw & REDRAW_MOTION_RESYNCH)
289 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
290 menu_pad_string (buf, sizeof (buf));
291 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
292 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
293 SETCOLOR (MT_COLOR_NORMAL);
296 /* now draw it in the new location */
297 move (menu->current + menu->offset - menu->top, SidebarWidth);
298 attrset (menu->color (menu->current));
299 ADDCOLOR (MT_COLOR_INDICATOR);
301 SETCOLOR (MT_COLOR_NORMAL);
305 /* erase the current indicator */
306 attrset (menu->color (menu->oldcurrent));
308 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
309 menu_pad_string (buf, sizeof (buf));
310 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
312 /* now draw the new one to reflect the change */
313 menu_make_entry (buf, sizeof (buf), menu, menu->current);
314 menu_pad_string (buf, sizeof (buf));
315 attrset (menu->color (menu->current));
316 ADDCOLOR (MT_COLOR_INDICATOR);
317 BKGDSET (MT_COLOR_INDICATOR);
318 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
319 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
320 SETCOLOR (MT_COLOR_NORMAL);
321 BKGDSET (MT_COLOR_NORMAL);
323 menu->redraw &= REDRAW_STATUS;
326 void menu_redraw_current (MUTTMENU *menu)
330 move (menu->current + menu->offset - menu->top, SidebarWidth);
331 menu_make_entry (buf, sizeof (buf), menu, menu->current);
332 menu_pad_string (buf, sizeof (buf));
334 if (option (OPTARROWCURSOR))
336 int attr = menu->color (menu->current);
339 attrset (menu->color (menu->current));
340 ADDCOLOR (MT_COLOR_INDICATOR);
344 menu_pad_string (buf, sizeof (buf));
345 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 1);
346 SETCOLOR (MT_COLOR_NORMAL);
350 attrset (menu->color (menu->current));
351 ADDCOLOR (MT_COLOR_INDICATOR);
352 BKGDSET (MT_COLOR_INDICATOR);
354 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
355 SETCOLOR (MT_COLOR_NORMAL);
356 BKGDSET (MT_COLOR_NORMAL);
358 menu->redraw &= REDRAW_STATUS;
361 void menu_redraw_prompt (MUTTMENU *menu)
365 if (option (OPTMSGERR))
368 unset_option (OPTMSGERR);
374 SETCOLOR (MT_COLOR_NORMAL);
375 mvaddstr (LINES - 1, 0, menu->prompt);
380 void menu_check_recenter (MUTTMENU *menu)
382 int c = MIN (MenuContext, menu->pagelen / 2);
383 int old_top = menu->top;
385 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) /* less entries than lines */
387 if (menu->top != 0) {
389 set_option (OPTNEEDREDRAW);
392 else if (menu->current >= menu->top + menu->pagelen - c) /* indicator below bottom threshold */
394 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
395 menu->top = menu->current - menu->pagelen + c + 1;
397 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
399 else if (menu->current < menu->top + c) /* indicator above top threshold */
401 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
402 menu->top = menu->current - c;
404 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
407 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
408 menu->top = MIN (menu->top, menu->max - menu->pagelen);
409 menu->top = MAX (menu->top, 0);
411 if (menu->top != old_top)
412 menu->redraw |= REDRAW_INDEX;
415 void menu_jump (MUTTMENU *menu)
418 char buf[SHORT_STRING];
422 mutt_ungetch (LastKey, 0);
424 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
427 if (n >= 0 && n < menu->max)
430 menu->redraw = REDRAW_MOTION;
433 mutt_error _("Invalid index number.");
437 mutt_error _("No entries.");
440 void menu_next_line (MUTTMENU *menu)
444 int c = MIN (MenuContext, menu->pagelen / 2);
446 if (menu->top + 1 < menu->max - c
447 && (option(OPTMENUMOVEOFF) || (menu->max > menu->pagelen && menu->top < menu->max - menu->pagelen)))
450 if (menu->current < menu->top + c && menu->current < menu->max - 1)
452 menu->redraw = REDRAW_INDEX;
455 mutt_error _("You cannot scroll down farther.");
458 mutt_error _("No entries.");
461 void menu_prev_line (MUTTMENU *menu)
465 int c = MIN (MenuContext, menu->pagelen / 2);
468 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
470 menu->redraw = REDRAW_INDEX;
473 mutt_error _("You cannot scroll up farther.");
476 void menu_next_page (MUTTMENU *menu)
480 if (menu->top + menu->pagelen < menu->max)
482 menu->top += menu->pagelen;
483 if (menu->current < menu->top)
484 menu->current = menu->top;
485 menu->redraw = REDRAW_INDEX;
487 else if (menu->current != menu->max - 1 && !menu->dialog)
489 menu->current = menu->max - 1;
490 menu->redraw = REDRAW_MOTION;
493 mutt_error _("You are on the last page.");
496 mutt_error _("No entries.");
499 void menu_prev_page (MUTTMENU *menu)
501 int c = MIN (MenuContext, menu->pagelen / 2);
505 if ((menu->top -= menu->pagelen) < 0)
507 if (menu->current >= menu->top + menu->pagelen)
508 menu->current = menu->top + menu->pagelen - 1;
509 menu->redraw = REDRAW_INDEX;
511 else if (menu->current && !menu->dialog)
514 menu->redraw = REDRAW_MOTION;
517 mutt_error _("You are on the first page.");
520 void menu_top_page (MUTTMENU *menu)
522 if (menu->current != menu->top)
524 menu->current = menu->top;
525 menu->redraw = REDRAW_MOTION;
529 void menu_bottom_page (MUTTMENU *menu)
533 menu->current = menu->top + menu->pagelen - 1;
534 if (menu->current > menu->max - 1)
535 menu->current = menu->max - 1;
536 menu->redraw = REDRAW_MOTION;
539 mutt_error _("No entries.");
542 void menu_middle_page (MUTTMENU *menu)
548 i = menu->top + menu->pagelen;
549 if (i > menu->max - 1)
551 menu->current = menu->top + (i - menu->top) / 2;
552 menu->redraw = REDRAW_MOTION;
555 mutt_error _("No entries.");
558 void menu_first_entry (MUTTMENU *menu)
563 menu->redraw = REDRAW_MOTION;
566 mutt_error _("No entries.");
569 void menu_last_entry (MUTTMENU *menu)
573 menu->current = menu->max - 1;
574 menu->redraw = REDRAW_MOTION;
577 mutt_error _("No entries.");
580 void menu_half_up (MUTTMENU *menu)
584 if ((menu->top -= menu->pagelen / 2) < 0)
586 if (menu->current >= menu->top + menu->pagelen)
587 menu->current = menu->top + menu->pagelen - 1;
588 menu->redraw = REDRAW_INDEX;
590 else if (menu->current && !menu->dialog)
593 menu->redraw = REDRAW_MOTION;
596 mutt_error _("First entry is shown.");
599 void menu_half_down (MUTTMENU *menu)
603 if (menu->top + menu->pagelen < menu->max)
605 menu->top += menu->pagelen / 2;
606 if (menu->current < menu->top)
607 menu->current = menu->top;
608 menu->redraw = REDRAW_INDEX;
610 else if (menu->current != menu->max - 1 && !menu->dialog)
612 menu->current = menu->max - 1;
613 menu->redraw = REDRAW_INDEX;
616 mutt_error _("Last entry is shown.");
619 mutt_error _("No entries.");
622 void menu_current_top (MUTTMENU *menu)
626 menu->top = menu->current;
627 menu->redraw = REDRAW_INDEX;
630 mutt_error _("No entries.");
633 void menu_current_middle (MUTTMENU *menu)
637 menu->top = menu->current - menu->pagelen / 2;
640 menu->redraw = REDRAW_INDEX;
643 mutt_error _("No entries.");
646 void menu_current_bottom (MUTTMENU *menu)
650 menu->top = menu->current - menu->pagelen + 1;
653 menu->redraw = REDRAW_INDEX;
656 mutt_error _("No entries.");
659 void menu_next_entry (MUTTMENU *menu)
661 if (menu->current < menu->max - 1)
664 menu->redraw = REDRAW_MOTION;
667 mutt_error _("You are on the last entry.");
670 void menu_prev_entry (MUTTMENU *menu)
675 menu->redraw = REDRAW_MOTION;
678 mutt_error _("You are on the first entry.");
681 static int default_color (int i)
683 return ColorDefs[MT_COLOR_NORMAL];
686 static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
688 char buf[LONG_STRING];
690 menu_make_entry (buf, sizeof (buf), m, n);
691 return (regexec (re, buf, 0, NULL, 0));
694 MUTTMENU *mutt_new_menu (void)
696 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
701 p->redraw = REDRAW_FULL;
702 p->pagelen = PAGELEN;
703 p->color = default_color;
704 p->search = menu_search_generic;
708 void mutt_menuDestroy (MUTTMENU **p)
712 FREE (&(*p)->searchBuf);
716 for (i=0; i < (*p)->max; i++)
717 FREE (&(*p)->dialog[i]);
719 FREE (& (*p)->dialog);
725 #define M_SEARCH_UP 1
726 #define M_SEARCH_DOWN 2
728 static int menu_search (MUTTMENU *menu, int op)
733 char buf[SHORT_STRING];
735 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
737 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
738 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
739 _("Reverse search for: "),
740 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
742 mutt_str_replace (&menu->searchBuf, buf);
743 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
747 if (!menu->searchBuf)
749 mutt_error _("No search pattern.");
754 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
755 if (op == OP_SEARCH_OPPOSITE)
756 searchDir = -searchDir;
758 if ((r = REGCOMP (&re, menu->searchBuf, REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0)
760 regerror (r, &re, buf, sizeof (buf));
762 mutt_error ("%s", buf);
766 r = menu->current + searchDir;
767 while (r >= 0 && r < menu->max)
769 if (menu->search (menu, &re, r) == 0)
779 mutt_error _("Not found.");
783 static int menu_dialog_translate_op (int i)
791 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
793 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
794 return OP_BOTTOM_PAGE;
795 case OP_CURRENT_MIDDLE:
796 return OP_MIDDLE_PAGE;
802 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
815 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
817 *ip = OP_MAX + (p - menu->keys + 1);
822 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
827 int menu_redraw (MUTTMENU *menu)
829 /* See if all or part of the screen needs to be updated. */
830 if (menu->redraw & REDRAW_FULL)
832 menu_redraw_full (menu);
833 /* allow the caller to do any local configuration */
838 menu_check_recenter (menu);
840 if (menu->redraw & REDRAW_STATUS)
841 menu_redraw_status (menu);
842 if (menu->redraw & REDRAW_INDEX)
843 menu_redraw_index (menu);
844 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
845 menu_redraw_motion (menu);
846 else if (menu->redraw == REDRAW_CURRENT)
847 menu_redraw_current (menu);
850 menu_redraw_prompt (menu);
855 int mutt_menuLoop (MUTTMENU *menu)
861 if (option (OPTMENUCALLER))
863 unset_option (OPTMENUCALLER);
874 if (menu_redraw (menu) == OP_REDRAW)
877 menu->oldcurrent = menu->current;
880 /* move the cursor out of the way */
881 move (menu->current - menu->top + menu->offset,
882 (option (OPTARROWCURSOR) ? 2 : COLS-1));
886 /* try to catch dialog keys before ops */
887 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
890 i = km_dokey (menu->menu);
891 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
895 mvaddstr (LINES - 1, 0, "Tag-");
897 i = km_dokey (menu->menu);
899 CLEARLINE (LINES - 1);
901 else if (i == OP_TAG_PREFIX)
903 mutt_error _("No tagged entries.");
906 else /* None tagged, OP_TAG_PREFIX_COND */
912 if(tmp.op==OP_END_COND)break;
914 mutt_message _("Nothing to do.");
918 else if (menu->tagged && option (OPTAUTOTAG))
925 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
928 mutt_resize_screen ();
929 menu->redraw = REDRAW_FULL;
931 clearok(stdscr,TRUE);/*force complete redraw*/
941 /* Convert menubar movement to scrolling */
943 i = menu_dialog_translate_op (i);
948 menu_next_entry (menu);
951 menu_prev_entry (menu);
954 menu_half_down (menu);
960 menu_next_page (menu);
963 menu_prev_page (menu);
966 menu_next_line (menu);
969 menu_prev_line (menu);
972 menu_first_entry (menu);
975 menu_last_entry (menu);
978 menu_top_page (menu);
981 menu_middle_page (menu);
984 menu_bottom_page (menu);
987 menu_current_top (menu);
989 case OP_CURRENT_MIDDLE:
990 menu_current_middle (menu);
992 case OP_CURRENT_BOTTOM:
993 menu_current_bottom (menu);
996 case OP_SEARCH_REVERSE:
998 case OP_SEARCH_OPPOSITE:
999 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
1001 menu->oldcurrent = menu->current;
1002 if ((menu->current = menu_search (menu, i)) != -1)
1003 menu->redraw = REDRAW_MOTION;
1005 menu->current = menu->oldcurrent;
1008 mutt_error _("Search is not implemented for this menu.");
1013 mutt_error _("Jumping is not implemented for dialogs.");
1018 case OP_ENTER_COMMAND:
1019 CurrentMenu = menu->menu;
1020 mutt_enter_command ();
1021 if (option (OPTFORCEREDRAWINDEX))
1023 menu->redraw = REDRAW_FULL;
1024 unset_option (OPTFORCEREDRAWINDEX);
1025 unset_option (OPTFORCEREDRAWPAGER);
1030 if (menu->tag && !menu->dialog)
1032 if (menu->tagprefix && !option (OPTAUTOTAG))
1034 for (i = 0; i < menu->max; i++)
1035 menu->tagged += menu->tag (menu, i, 0);
1036 menu->redraw = REDRAW_INDEX;
1040 int i = menu->tag (menu, menu->current, -1);
1042 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1045 menu->redraw = REDRAW_MOTION_RESYNCH;
1048 menu->redraw = REDRAW_CURRENT;
1051 mutt_error _("No entries.");
1054 mutt_error _("Tagging is not supported.");
1057 case OP_SHELL_ESCAPE:
1058 mutt_shell_escape ();
1059 MAYBE_REDRAW (menu->redraw);
1067 clearok (stdscr, TRUE);
1068 menu->redraw = REDRAW_FULL;
1072 mutt_help (menu->menu);
1073 menu->redraw = REDRAW_FULL;
1077 km_error_key (menu->menu);