2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
15 #include "mutt_curses.h"
16 #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);
42 memset (&mbstate, 0, sizeof (mbstate));
44 if (*s < M_TREE_MAX) {
46 SETCOLOR (MT_COLOR_TREE);
47 while (*s && *s < M_TREE_MAX) {
50 if (option (OPTASCIICHARS))
52 else if (Charset_is_utf8)
53 addstr ("\342\224\224"); /* WACS_LLCORNER */
58 if (option (OPTASCIICHARS))
60 else if (Charset_is_utf8)
61 addstr ("\342\224\214"); /* WACS_ULCORNER */
66 if (option (OPTASCIICHARS))
68 else if (Charset_is_utf8)
69 addstr ("\342\224\234"); /* WACS_LTEE */
74 if (option (OPTASCIICHARS))
76 else if (Charset_is_utf8)
77 addstr ("\342\224\200"); /* WACS_HLINE */
82 if (option (OPTASCIICHARS))
84 else if (Charset_is_utf8)
85 addstr ("\342\224\202"); /* WACS_VLINE */
90 if (option (OPTASCIICHARS))
92 else if (Charset_is_utf8)
93 addstr ("\342\224\254"); /* WACS_TTEE */
98 if (option (OPTASCIICHARS))
100 else if (Charset_is_utf8)
101 addstr ("\342\224\264"); /* WACS_BTEE */
112 addch ('*'); /* fake thread indicator */
129 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
130 addnstr ((char *) s, k);
138 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
141 strncpy (s, menu->dialog[i], l);
142 menu->current = -1; /* hide menubar */
145 menu->make_entry (s, l, menu, i);
148 void menu_pad_string (char *s, size_t n)
150 int shift = option (OPTARROWCURSOR) ? 3 : 0;
152 char *tmpbuf = safe_malloc (n);
154 if (option (OPTMBOXPANE))
155 cols = COLS - shift - SidebarWidth;
158 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, mutt_strlen (s), 1);
160 snprintf (s, n, "%s", tmpbuf); /* overkill */
164 void menu_redraw_full (MUTTMENU * menu)
166 SETCOLOR (MT_COLOR_NORMAL);
167 /* clear() doesn't optimize screen redraws */
171 if (option (OPTHELP)) {
172 SETCOLOR (MT_COLOR_STATUS);
173 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, 0);
174 mutt_paddstr (COLS, menu->help);
175 SETCOLOR (MT_COLOR_NORMAL);
177 menu->pagelen = LINES - 3;
180 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
181 menu->pagelen = LINES - 2;
186 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
189 void menu_redraw_status (MUTTMENU * menu)
193 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
194 SETCOLOR (MT_COLOR_STATUS);
195 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
196 mutt_paddstr (COLS, buf);
197 SETCOLOR (MT_COLOR_NORMAL);
198 menu->redraw &= ~REDRAW_STATUS;
201 void menu_redraw_index (MUTTMENU * menu)
207 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)) {
213 attrset (menu->color (i));
214 CLEARLINE_WIN (i - menu->top + menu->offset);
216 if (i == menu->current) {
217 attrset (menu->color (i));
218 ADDCOLOR (MT_COLOR_INDICATOR);
220 attrset (menu->color (i));
224 attrset (menu->color (i));
225 move (i - menu->top + menu->offset, SidebarWidth);
229 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
230 SETCOLOR (MT_COLOR_NORMAL);
233 attrset (menu->color (i));
235 if (i == menu->current) {
236 ADDCOLOR (MT_COLOR_INDICATOR);
237 BKGDSET (MT_COLOR_INDICATOR);
240 CLEARLINE_WIN (i - menu->top + menu->offset);
241 print_enriched_string (menu->color (i), (unsigned char *) buf,
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)
258 menu->redraw &= ~REDRAW_MOTION;
262 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
263 SETCOLOR (MT_COLOR_NORMAL);
264 BKGDSET (MT_COLOR_NORMAL);
266 if (option (OPTARROWCURSOR)) {
267 /* clear the pointer */
268 attrset (menu->color (menu->oldcurrent));
271 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
273 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
274 menu_pad_string (buf, sizeof (buf));
275 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
276 print_enriched_string (menu->color (menu->oldcurrent),
277 (unsigned char *) buf, 1);
278 SETCOLOR (MT_COLOR_NORMAL);
281 /* now draw it in the new location */
282 move (menu->current + menu->offset - menu->top, SidebarWidth);
283 attrset (menu->color (menu->current));
284 ADDCOLOR (MT_COLOR_INDICATOR);
286 SETCOLOR (MT_COLOR_NORMAL);
289 /* erase the current indicator */
290 attrset (menu->color (menu->oldcurrent));
292 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
293 menu_pad_string (buf, sizeof (buf));
294 print_enriched_string (menu->color (menu->oldcurrent),
295 (unsigned char *) buf, 1);
297 /* now draw the new one to reflect the change */
298 menu_make_entry (buf, sizeof (buf), menu, menu->current);
299 menu_pad_string (buf, sizeof (buf));
300 attrset (menu->color (menu->current));
301 ADDCOLOR (MT_COLOR_INDICATOR);
302 BKGDSET (MT_COLOR_INDICATOR);
303 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
304 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
306 SETCOLOR (MT_COLOR_NORMAL);
307 BKGDSET (MT_COLOR_NORMAL);
309 menu->redraw &= REDRAW_STATUS;
312 void menu_redraw_current (MUTTMENU * menu)
316 move (menu->current + menu->offset - menu->top, SidebarWidth);
317 menu_make_entry (buf, sizeof (buf), menu, menu->current);
318 menu_pad_string (buf, sizeof (buf));
320 if (option (OPTARROWCURSOR)) {
321 int attr = menu->color (menu->current);
325 attrset (menu->color (menu->current));
326 ADDCOLOR (MT_COLOR_INDICATOR);
330 menu_pad_string (buf, sizeof (buf));
331 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
333 SETCOLOR (MT_COLOR_NORMAL);
336 attrset (menu->color (menu->current));
337 ADDCOLOR (MT_COLOR_INDICATOR);
338 BKGDSET (MT_COLOR_INDICATOR);
340 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
342 SETCOLOR (MT_COLOR_NORMAL);
343 BKGDSET (MT_COLOR_NORMAL);
345 menu->redraw &= REDRAW_STATUS;
348 void menu_redraw_prompt (MUTTMENU * menu)
351 if (option (OPTMSGERR)) {
353 unset_option (OPTMSGERR);
359 SETCOLOR (MT_COLOR_NORMAL);
360 mvaddstr (LINES - 1, 0, menu->prompt);
365 void menu_check_recenter (MUTTMENU * menu)
367 int c = MIN (MenuContext, menu->pagelen / 2);
368 int old_top = menu->top;
370 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
371 if (menu->top != 0) {
373 set_option (OPTNEEDREDRAW);
376 else if (menu->current >= menu->top + menu->pagelen - c) { /* indicator below bottom threshold */
377 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
378 menu->top = menu->current - menu->pagelen + c + 1;
382 c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
384 else if (menu->current < menu->top + c) { /* indicator above top threshold */
385 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
386 menu->top = menu->current - c;
390 c) * ((menu->top + menu->pagelen - 1 -
391 menu->current) / (menu->pagelen - c)) - c;
394 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
395 menu->top = MIN (menu->top, menu->max - menu->pagelen);
396 menu->top = MAX (menu->top, 0);
398 if (menu->top != old_top)
399 menu->redraw |= REDRAW_INDEX;
402 void menu_jump (MUTTMENU * menu)
405 char buf[SHORT_STRING];
408 mutt_ungetch (LastKey, 0);
410 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
412 if (n >= 0 && n < menu->max) {
414 menu->redraw = REDRAW_MOTION;
417 mutt_error _("Invalid index number.");
421 mutt_error _("No entries.");
424 void menu_next_line (MUTTMENU * menu)
427 int c = MIN (MenuContext, menu->pagelen / 2);
429 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
430 || (menu->max > menu->pagelen
432 menu->max - menu->pagelen))) {
434 if (menu->current < menu->top + c && menu->current < menu->max - 1)
436 menu->redraw = REDRAW_INDEX;
439 mutt_error _("You cannot scroll down farther.");
442 mutt_error _("No entries.");
445 void menu_prev_line (MUTTMENU * menu)
448 int c = MIN (MenuContext, menu->pagelen / 2);
451 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
453 menu->redraw = REDRAW_INDEX;
456 mutt_error _("You cannot scroll up farther.");
459 void menu_next_page (MUTTMENU * menu)
462 if (menu->top + menu->pagelen < menu->max) {
463 menu->top += menu->pagelen;
464 if (menu->current < menu->top)
465 menu->current = menu->top;
466 menu->redraw = REDRAW_INDEX;
468 else if (menu->current != menu->max - 1 && !menu->dialog) {
469 menu->current = menu->max - 1;
470 menu->redraw = REDRAW_MOTION;
473 mutt_error _("You are on the last page.");
476 mutt_error _("No entries.");
479 void menu_prev_page (MUTTMENU * menu)
481 int c = MIN (MenuContext, menu->pagelen / 2);
484 if ((menu->top -= menu->pagelen) < 0)
486 if (menu->current >= menu->top + menu->pagelen)
487 menu->current = menu->top + menu->pagelen - 1;
488 menu->redraw = REDRAW_INDEX;
490 else if (menu->current && !menu->dialog) {
492 menu->redraw = REDRAW_MOTION;
495 mutt_error _("You are on the first page.");
498 void menu_top_page (MUTTMENU * menu)
500 if (menu->current != menu->top) {
501 menu->current = menu->top;
502 menu->redraw = REDRAW_MOTION;
506 void menu_bottom_page (MUTTMENU * menu)
509 menu->current = menu->top + menu->pagelen - 1;
510 if (menu->current > menu->max - 1)
511 menu->current = menu->max - 1;
512 menu->redraw = REDRAW_MOTION;
515 mutt_error _("No entries.");
518 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)
537 menu->redraw = REDRAW_MOTION;
540 mutt_error _("No entries.");
543 void menu_last_entry (MUTTMENU * menu)
546 menu->current = menu->max - 1;
547 menu->redraw = REDRAW_MOTION;
550 mutt_error _("No entries.");
553 void menu_half_up (MUTTMENU * menu)
556 if ((menu->top -= menu->pagelen / 2) < 0)
558 if (menu->current >= menu->top + menu->pagelen)
559 menu->current = menu->top + menu->pagelen - 1;
560 menu->redraw = REDRAW_INDEX;
562 else if (menu->current && !menu->dialog) {
564 menu->redraw = REDRAW_MOTION;
567 mutt_error _("First entry is shown.");
570 void menu_half_down (MUTTMENU * menu)
573 if (menu->top + menu->pagelen < menu->max) {
574 menu->top += menu->pagelen / 2;
575 if (menu->current < menu->top)
576 menu->current = menu->top;
577 menu->redraw = REDRAW_INDEX;
579 else if (menu->current != menu->max - 1 && !menu->dialog) {
580 menu->current = menu->max - 1;
581 menu->redraw = REDRAW_INDEX;
584 mutt_error _("Last entry is shown.");
587 mutt_error _("No entries.");
590 void menu_current_top (MUTTMENU * menu)
593 menu->top = menu->current;
594 menu->redraw = REDRAW_INDEX;
597 mutt_error _("No entries.");
600 void menu_current_middle (MUTTMENU * menu)
603 menu->top = menu->current - menu->pagelen / 2;
606 menu->redraw = REDRAW_INDEX;
609 mutt_error _("No entries.");
612 void menu_current_bottom (MUTTMENU * menu)
615 menu->top = menu->current - menu->pagelen + 1;
618 menu->redraw = REDRAW_INDEX;
621 mutt_error _("No entries.");
624 void menu_next_entry (MUTTMENU * menu)
626 if (menu->current < menu->max - 1) {
628 menu->redraw = REDRAW_MOTION;
631 mutt_error _("You are on the last entry.");
634 void menu_prev_entry (MUTTMENU * menu)
638 menu->redraw = REDRAW_MOTION;
641 mutt_error _("You are on the first entry.");
644 static int default_color (int i)
646 return ColorDefs[MT_COLOR_NORMAL];
649 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
651 char buf[LONG_STRING];
653 menu_make_entry (buf, sizeof (buf), m, n);
654 return (regexec (re, buf, 0, NULL, 0));
657 MUTTMENU *mutt_new_menu (void)
659 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
664 p->redraw = REDRAW_FULL;
665 p->pagelen = PAGELEN;
666 p->color = default_color;
667 p->search = menu_search_generic;
671 void mutt_menuDestroy (MUTTMENU ** p)
675 FREE (&(*p)->searchBuf);
678 for (i = 0; i < (*p)->max; i++)
679 FREE (&(*p)->dialog[i]);
681 FREE (&(*p)->dialog);
687 #define M_SEARCH_UP 1
688 #define M_SEARCH_DOWN 2
690 static int menu_search (MUTTMENU * menu, int op)
695 char buf[SHORT_STRING];
697 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
698 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
699 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
700 _("Reverse search for: "),
701 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
703 str_replace (&menu->searchBuf, buf);
704 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
707 if (!menu->searchBuf) {
708 mutt_error _("No search pattern.");
714 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
715 if (op == OP_SEARCH_OPPOSITE)
716 searchDir = -searchDir;
719 REGCOMP (&re, menu->searchBuf,
720 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
721 regerror (r, &re, buf, sizeof (buf));
723 mutt_error ("%s", buf);
727 r = menu->current + searchDir;
728 while (r >= 0 && r < menu->max) {
729 if (menu->search (menu, &re, r) == 0) {
738 mutt_error _("Not found.");
743 static int menu_dialog_translate_op (int i)
753 case OP_CURRENT_BOTTOM:
755 return OP_BOTTOM_PAGE;
756 case OP_CURRENT_MIDDLE:
757 return OP_MIDDLE_PAGE;
763 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
775 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
776 *ip = OP_MAX + (p - menu->keys + 1);
780 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
785 int menu_redraw (MUTTMENU * menu)
787 /* See if all or part of the screen needs to be updated. */
788 if (menu->redraw & REDRAW_FULL) {
789 menu_redraw_full (menu);
790 /* allow the caller to do any local configuration */
795 menu_check_recenter (menu);
797 if (menu->redraw & REDRAW_STATUS)
798 menu_redraw_status (menu);
799 if (menu->redraw & REDRAW_INDEX)
800 menu_redraw_index (menu);
801 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
802 menu_redraw_motion (menu);
803 else if (menu->redraw == REDRAW_CURRENT)
804 menu_redraw_current (menu);
807 menu_redraw_prompt (menu);
812 int mutt_menuLoop (MUTTMENU * menu)
817 if (option (OPTMENUCALLER)) {
818 unset_option (OPTMENUCALLER);
829 if (menu_redraw (menu) == OP_REDRAW)
832 menu->oldcurrent = menu->current;
834 if (option (OPTARROWCURSOR))
835 move (menu->current - menu->top + menu->offset, 2);
836 else if (option (OPTBRAILLEFRIENDLY))
837 move (menu->current - menu->top + menu->offset, 0);
839 move (menu->current - menu->top + menu->offset, COLS - 1);
844 /* try to catch dialog keys before ops */
845 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
848 i = km_dokey (menu->menu);
849 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
851 mvaddstr (LINES - 1, 0, "Tag-");
853 i = km_dokey (menu->menu);
855 CLEARLINE (LINES - 1);
857 else if (i == OP_TAG_PREFIX) {
858 mutt_error _("No tagged entries.");
862 else { /* None tagged, OP_TAG_PREFIX_COND */
866 while (UngetCount > 0) {
868 if (tmp.op == OP_END_COND)
871 mutt_message _("Nothing to do.");
876 else if (menu->tagged && option (OPTAUTOTAG))
883 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
885 mutt_resize_screen ();
886 menu->redraw = REDRAW_FULL;
888 clearok (stdscr, TRUE); /*force complete redraw */
898 /* Convert menubar movement to scrolling */
900 i = menu_dialog_translate_op (i);
904 menu_next_entry (menu);
907 menu_prev_entry (menu);
910 menu_half_down (menu);
916 menu_next_page (menu);
919 menu_prev_page (menu);
922 menu_next_line (menu);
925 menu_prev_line (menu);
928 menu_first_entry (menu);
931 menu_last_entry (menu);
934 menu_top_page (menu);
937 menu_middle_page (menu);
940 menu_bottom_page (menu);
943 menu_current_top (menu);
945 case OP_CURRENT_MIDDLE:
946 menu_current_middle (menu);
948 case OP_CURRENT_BOTTOM:
949 menu_current_bottom (menu);
952 case OP_SEARCH_REVERSE:
954 case OP_SEARCH_OPPOSITE:
955 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
956 menu->oldcurrent = menu->current;
957 if ((menu->current = menu_search (menu, i)) != -1)
958 menu->redraw = REDRAW_MOTION;
960 menu->current = menu->oldcurrent;
963 mutt_error _("Search is not implemented for this menu.");
968 mutt_error (_("Jumping is not implemented for dialogs."));
974 case OP_ENTER_COMMAND:
975 CurrentMenu = menu->menu;
976 mutt_enter_command ();
977 if (option (OPTFORCEREDRAWINDEX)) {
978 menu->redraw = REDRAW_FULL;
979 unset_option (OPTFORCEREDRAWINDEX);
980 unset_option (OPTFORCEREDRAWPAGER);
985 if (menu->tag && !menu->dialog) {
986 if (menu->tagprefix && !option (OPTAUTOTAG)) {
987 for (i = 0; i < menu->max; i++)
988 menu->tagged += menu->tag (menu, i, 0);
989 menu->redraw = REDRAW_INDEX;
991 else if (menu->max) {
992 int i = menu->tag (menu, menu->current, -1);
995 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) {
997 menu->redraw = REDRAW_MOTION_RESYNCH;
1000 menu->redraw = REDRAW_CURRENT;
1003 mutt_error _("No entries.");
1006 mutt_error _("Tagging is not supported.");
1009 case OP_SHELL_ESCAPE:
1010 mutt_shell_escape ();
1011 MAYBE_REDRAW (menu->redraw);
1019 clearok (stdscr, TRUE);
1020 menu->redraw = REDRAW_FULL;
1024 mutt_help (menu->menu);
1025 menu->redraw = REDRAW_FULL;
1029 km_error_key (menu->menu);