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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "mutt_curses.h"
25 #include "mutt_menu.h"
35 extern int Charset_is_utf8; /* FIXME: bad modularisation */
37 extern size_t UngetCount;
39 static void print_enriched_string (int attr, unsigned char *s, int do_color)
43 size_t n = mutt_strlen ((char *)s);
46 memset (&mbstate, 0, sizeof (mbstate));
52 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);
136 else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
138 addnstr ((char *)s, k);
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 char *scratch = safe_strdup (s);
160 int shift = option (OPTARROWCURSOR) ? 3 : 0;
161 int cols = COLS - shift;
163 mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 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)
209 char buf[LONG_STRING];
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));
234 attrset (menu->color (i));
238 print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
239 SETCOLOR (MT_COLOR_NORMAL);
243 attrset (menu->color (i));
245 if (i == menu->current)
247 ADDCOLOR (MT_COLOR_INDICATOR);
248 BKGDSET (MT_COLOR_INDICATOR);
251 CLEARLINE (i - menu->top + menu->offset);
252 print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current);
253 SETCOLOR (MT_COLOR_NORMAL);
254 BKGDSET (MT_COLOR_NORMAL);
258 CLEARLINE (i - menu->top + menu->offset);
263 void menu_redraw_motion (MUTTMENU *menu)
265 char buf[LONG_STRING];
269 menu->redraw &= ~REDRAW_MOTION;
273 move (menu->oldcurrent + menu->offset - menu->top, 0);
274 SETCOLOR (MT_COLOR_NORMAL);
275 BKGDSET (MT_COLOR_NORMAL);
277 if (option (OPTARROWCURSOR))
279 /* clear the pointer */
280 attrset (menu->color (menu->oldcurrent));
283 if (menu->redraw & REDRAW_MOTION_RESYNCH)
286 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
287 menu_pad_string (buf, sizeof (buf));
288 move (menu->oldcurrent + menu->offset - menu->top, 3);
289 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
290 SETCOLOR (MT_COLOR_NORMAL);
293 /* now draw it in the new location */
294 move (menu->current + menu->offset - menu->top, 0);
295 attrset (menu->color (menu->current));
296 ADDCOLOR (MT_COLOR_INDICATOR);
298 SETCOLOR (MT_COLOR_NORMAL);
302 /* erase the current indicator */
303 attrset (menu->color (menu->oldcurrent));
305 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
306 menu_pad_string (buf, sizeof (buf));
307 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
309 /* now draw the new one to reflect the change */
310 menu_make_entry (buf, sizeof (buf), menu, menu->current);
311 menu_pad_string (buf, sizeof (buf));
312 attrset (menu->color (menu->current));
313 ADDCOLOR (MT_COLOR_INDICATOR);
314 BKGDSET (MT_COLOR_INDICATOR);
315 CLEARLINE (menu->current - menu->top + menu->offset);
316 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
317 SETCOLOR (MT_COLOR_NORMAL);
318 BKGDSET (MT_COLOR_NORMAL);
320 menu->redraw &= REDRAW_STATUS;
323 void menu_redraw_current (MUTTMENU *menu)
325 char buf[LONG_STRING];
327 move (menu->current + menu->offset - menu->top, 0);
328 menu_make_entry (buf, sizeof (buf), menu, menu->current);
329 menu_pad_string (buf, sizeof (buf));
331 if (option (OPTARROWCURSOR))
333 int attr = menu->color (menu->current);
336 attrset (menu->color (menu->current));
337 ADDCOLOR (MT_COLOR_INDICATOR);
341 menu_pad_string (buf, sizeof (buf));
342 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 1);
343 SETCOLOR (MT_COLOR_NORMAL);
347 attrset (menu->color (menu->current));
348 ADDCOLOR (MT_COLOR_INDICATOR);
349 BKGDSET (MT_COLOR_INDICATOR);
351 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
352 SETCOLOR (MT_COLOR_NORMAL);
353 BKGDSET (MT_COLOR_NORMAL);
355 menu->redraw &= REDRAW_STATUS;
358 void menu_redraw_prompt (MUTTMENU *menu)
362 if (option (OPTMSGERR))
365 unset_option (OPTMSGERR);
371 SETCOLOR (MT_COLOR_NORMAL);
372 mvaddstr (LINES - 1, 0, menu->prompt);
377 void menu_check_recenter (MUTTMENU *menu)
379 int c = MIN (MenuContext, menu->pagelen / 2);
380 int old_top = menu->top;
382 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) /* less entries than lines */
387 set_option (OPTNEEDREDRAW);
392 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext))
394 if (menu->current < menu->top + c)
395 menu->top = menu->current - c;
396 else if (menu->current >= menu->top + menu->pagelen - c)
397 menu->top = menu->current - menu->pagelen + c + 1;
401 if (menu->current < menu->top + c)
402 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
403 else if ((menu->current >= menu->top + menu->pagelen - c))
404 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
408 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
409 menu->top = MIN (menu->top, menu->max - menu->pagelen);
410 menu->top = MAX (menu->top, 0);
412 if (menu->top != old_top)
413 menu->redraw |= REDRAW_INDEX;
416 void menu_jump (MUTTMENU *menu)
419 char buf[SHORT_STRING];
423 mutt_ungetch (LastKey, 0);
425 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
428 if (n >= 0 && n < menu->max)
431 menu->redraw = REDRAW_MOTION;
434 mutt_error _("Invalid index number.");
438 mutt_error _("No entries.");
441 void menu_next_line (MUTTMENU *menu)
445 int c = MIN (MenuContext, menu->pagelen / 2);
447 if (menu->top + 1 < menu->max - c
448 && (option(OPTMENUMOVEOFF) || (menu->max > menu->pagelen && menu->top < menu->max - menu->pagelen)))
451 if (menu->current < menu->top + c && menu->current < menu->max - 1)
453 menu->redraw = REDRAW_INDEX;
456 mutt_error _("You cannot scroll down farther.");
459 mutt_error _("No entries.");
462 void menu_prev_line (MUTTMENU *menu)
466 int c = MIN (MenuContext, menu->pagelen / 2);
469 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
471 menu->redraw = REDRAW_INDEX;
474 mutt_error _("You cannot scroll up farther.");
478 * pageup: jumplen == -pagelen
479 * pagedown: jumplen == pagelen
480 * halfup: jumplen == -pagelen/2
481 * halfdown: jumplen == pagelen/2
483 #define DIRECTION ((neg * 2) + 1)
484 void menu_length_jump (MUTTMENU *menu, int jumplen)
486 int tmp, neg = (jumplen >= 0) ? 0 : -1;
487 int c = MIN (MenuContext, menu->pagelen / 2);
491 /* possible to scroll? */
492 if (DIRECTION * menu->top <
493 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/))))
495 menu->top += jumplen;
497 /* jumped too long? */
498 if ((neg || !option (OPTMENUMOVEOFF)) &&
499 DIRECTION * menu->top > tmp)
502 /* need to move the cursor? */
504 (tmp = (menu->current -
505 (menu->top + (neg ? (menu->pagelen - 1) - c : c))
507 menu->current -= tmp;
509 menu->redraw = REDRAW_INDEX;
511 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog)
513 menu->current += jumplen;
514 menu->redraw = REDRAW_MOTION;
517 mutt_error (neg ? _("You are on the first page.")
518 : _("You are on the last page."));
520 menu->current = MIN (menu->current, menu->max - 1);
521 menu->current = MAX (menu->current, 0);
524 mutt_error _("No entries.");
528 void menu_next_page (MUTTMENU *menu)
530 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
533 void menu_prev_page (MUTTMENU *menu)
535 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
538 void menu_half_down (MUTTMENU *menu)
540 menu_length_jump (menu, menu->pagelen / 2);
543 void menu_half_up (MUTTMENU *menu)
545 menu_length_jump (menu, 0 - menu->pagelen / 2);
548 void menu_top_page (MUTTMENU *menu)
550 if (menu->current != menu->top)
552 menu->current = menu->top;
553 menu->redraw = REDRAW_MOTION;
557 void menu_bottom_page (MUTTMENU *menu)
561 menu->current = menu->top + menu->pagelen - 1;
562 if (menu->current > menu->max - 1)
563 menu->current = menu->max - 1;
564 menu->redraw = REDRAW_MOTION;
567 mutt_error _("No entries.");
570 void menu_middle_page (MUTTMENU *menu)
576 i = menu->top + menu->pagelen;
577 if (i > menu->max - 1)
579 menu->current = menu->top + (i - menu->top) / 2;
580 menu->redraw = REDRAW_MOTION;
583 mutt_error _("No entries.");
586 void menu_first_entry (MUTTMENU *menu)
591 menu->redraw = REDRAW_MOTION;
594 mutt_error _("No entries.");
597 void menu_last_entry (MUTTMENU *menu)
601 menu->current = menu->max - 1;
602 menu->redraw = REDRAW_MOTION;
605 mutt_error _("No entries.");
608 void menu_current_top (MUTTMENU *menu)
612 menu->top = menu->current;
613 menu->redraw = REDRAW_INDEX;
616 mutt_error _("No entries.");
619 void menu_current_middle (MUTTMENU *menu)
623 menu->top = menu->current - menu->pagelen / 2;
626 menu->redraw = REDRAW_INDEX;
629 mutt_error _("No entries.");
632 void menu_current_bottom (MUTTMENU *menu)
636 menu->top = menu->current - menu->pagelen + 1;
639 menu->redraw = REDRAW_INDEX;
642 mutt_error _("No entries.");
645 void menu_next_entry (MUTTMENU *menu)
647 if (menu->current < menu->max - 1)
650 menu->redraw = REDRAW_MOTION;
653 mutt_error _("You are on the last entry.");
656 void menu_prev_entry (MUTTMENU *menu)
661 menu->redraw = REDRAW_MOTION;
664 mutt_error _("You are on the first entry.");
667 static int default_color (int i)
669 return ColorDefs[MT_COLOR_NORMAL];
672 static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
674 char buf[LONG_STRING];
676 menu_make_entry (buf, sizeof (buf), m, n);
677 return (regexec (re, buf, 0, NULL, 0));
680 MUTTMENU *mutt_new_menu (void)
682 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
687 p->redraw = REDRAW_FULL;
688 p->pagelen = PAGELEN;
689 p->color = default_color;
690 p->search = menu_search_generic;
694 void mutt_menuDestroy (MUTTMENU **p)
698 FREE (&(*p)->searchBuf);
702 for (i=0; i < (*p)->max; i++)
703 FREE (&(*p)->dialog[i]);
705 FREE (& (*p)->dialog);
708 FREE (p); /* __FREE_CHECKED__ */
711 #define M_SEARCH_UP 1
712 #define M_SEARCH_DOWN 2
714 static int menu_search (MUTTMENU *menu, int op)
719 char buf[SHORT_STRING];
721 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
723 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
724 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
725 _("Reverse search for: "),
726 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
728 mutt_str_replace (&menu->searchBuf, buf);
729 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
733 if (!menu->searchBuf)
735 mutt_error _("No search pattern.");
740 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
741 if (op == OP_SEARCH_OPPOSITE)
742 searchDir = -searchDir;
744 if ((r = REGCOMP (&re, menu->searchBuf, REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0)
746 regerror (r, &re, buf, sizeof (buf));
748 mutt_error ("%s", buf);
752 r = menu->current + searchDir;
753 while (r >= 0 && r < menu->max)
755 if (menu->search (menu, &re, r) == 0)
765 mutt_error _("Not found.");
769 static int menu_dialog_translate_op (int i)
777 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
779 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
780 return OP_BOTTOM_PAGE;
781 case OP_CURRENT_MIDDLE:
782 return OP_MIDDLE_PAGE;
788 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
801 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
803 *ip = OP_MAX + (p - menu->keys + 1);
808 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
813 int menu_redraw (MUTTMENU *menu)
815 /* See if all or part of the screen needs to be updated. */
816 if (menu->redraw & REDRAW_FULL)
818 menu_redraw_full (menu);
819 /* allow the caller to do any local configuration */
824 menu_check_recenter (menu);
826 if (menu->redraw & REDRAW_STATUS)
827 menu_redraw_status (menu);
828 if (menu->redraw & REDRAW_INDEX)
829 menu_redraw_index (menu);
830 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
831 menu_redraw_motion (menu);
832 else if (menu->redraw == REDRAW_CURRENT)
833 menu_redraw_current (menu);
836 menu_redraw_prompt (menu);
841 int mutt_menuLoop (MUTTMENU *menu)
847 if (option (OPTMENUCALLER))
849 unset_option (OPTMENUCALLER);
860 if (menu_redraw (menu) == OP_REDRAW)
863 menu->oldcurrent = menu->current;
866 /* move the cursor out of the way */
869 if (option (OPTARROWCURSOR))
870 move (menu->current - menu->top + menu->offset, 2);
871 else if (option (OPTBRAILLEFRIENDLY))
872 move (menu->current - menu->top + menu->offset, 0);
874 move (menu->current - menu->top + menu->offset, COLS - 1);
878 /* try to catch dialog keys before ops */
879 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
882 i = km_dokey (menu->menu);
883 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
887 mvaddstr (LINES - 1, 0, "Tag-");
889 i = km_dokey (menu->menu);
891 CLEARLINE (LINES - 1);
893 else if (i == OP_TAG_PREFIX)
895 mutt_error _("No tagged entries.");
898 else /* None tagged, OP_TAG_PREFIX_COND */
904 if(tmp.op==OP_END_COND)break;
906 mutt_message _("Nothing to do.");
910 else if (menu->tagged && option (OPTAUTOTAG))
917 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
920 mutt_resize_screen ();
921 menu->redraw = REDRAW_FULL;
923 clearok(stdscr,TRUE);/*force complete redraw*/
933 /* Convert menubar movement to scrolling */
935 i = menu_dialog_translate_op (i);
940 menu_next_entry (menu);
943 menu_prev_entry (menu);
946 menu_half_down (menu);
952 menu_next_page (menu);
955 menu_prev_page (menu);
958 menu_next_line (menu);
961 menu_prev_line (menu);
964 menu_first_entry (menu);
967 menu_last_entry (menu);
970 menu_top_page (menu);
973 menu_middle_page (menu);
976 menu_bottom_page (menu);
979 menu_current_top (menu);
981 case OP_CURRENT_MIDDLE:
982 menu_current_middle (menu);
984 case OP_CURRENT_BOTTOM:
985 menu_current_bottom (menu);
988 case OP_SEARCH_REVERSE:
990 case OP_SEARCH_OPPOSITE:
991 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
993 menu->oldcurrent = menu->current;
994 if ((menu->current = menu_search (menu, i)) != -1)
995 menu->redraw = REDRAW_MOTION;
997 menu->current = menu->oldcurrent;
1000 mutt_error _("Search is not implemented for this menu.");
1005 mutt_error _("Jumping is not implemented for dialogs.");
1010 case OP_ENTER_COMMAND:
1011 CurrentMenu = menu->menu;
1012 mutt_enter_command ();
1013 if (option (OPTFORCEREDRAWINDEX))
1015 menu->redraw = REDRAW_FULL;
1016 unset_option (OPTFORCEREDRAWINDEX);
1017 unset_option (OPTFORCEREDRAWPAGER);
1022 if (menu->tag && !menu->dialog)
1024 if (menu->tagprefix && !option (OPTAUTOTAG))
1026 for (i = 0; i < menu->max; i++)
1027 menu->tagged += menu->tag (menu, i, 0);
1028 menu->redraw = REDRAW_INDEX;
1032 int i = menu->tag (menu, menu->current, -1);
1034 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1037 menu->redraw = REDRAW_MOTION_RESYNCH;
1040 menu->redraw = REDRAW_CURRENT;
1043 mutt_error _("No entries.");
1046 mutt_error _("Tagging is not supported.");
1049 case OP_SHELL_ESCAPE:
1050 mutt_shell_escape ();
1051 MAYBE_REDRAW (menu->redraw);
1059 clearok (stdscr, TRUE);
1060 menu->redraw = REDRAW_FULL;
1064 mutt_help (menu->menu);
1065 menu->redraw = REDRAW_FULL;
1069 km_error_key (menu->menu);