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.
18 #include <lib-lib/mem.h>
19 #include <lib-lib/str.h>
20 #include <lib-lib/macros.h>
29 #include <imap/imap.h>
30 #include <lib-ui/sidebar.h>
32 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
34 extern size_t UngetCount;
36 static void print_enriched_string (int attr, unsigned char *s, int do_color)
40 size_t n = m_strlen((char *) s);
45 if (*s < M_TREE_MAX) {
47 SETCOLOR (MT_COLOR_TREE);
48 while (*s && *s < M_TREE_MAX) {
51 if (option (OPTASCIICHARS))
53 else if (Charset_is_utf8)
54 addstr ("\342\224\224"); /* WACS_LLCORNER */
59 if (option (OPTASCIICHARS))
61 else if (Charset_is_utf8)
62 addstr ("\342\224\214"); /* WACS_ULCORNER */
67 if (option (OPTASCIICHARS))
69 else if (Charset_is_utf8)
70 addstr ("\342\224\234"); /* WACS_LTEE */
75 if (option (OPTASCIICHARS))
77 else if (Charset_is_utf8)
78 addstr ("\342\224\200"); /* WACS_HLINE */
83 if (option (OPTASCIICHARS))
85 else if (Charset_is_utf8)
86 addstr ("\342\224\202"); /* WACS_VLINE */
91 if (option (OPTASCIICHARS))
93 else if (Charset_is_utf8)
94 addstr ("\342\224\254"); /* WACS_TTEE */
99 if (option (OPTASCIICHARS))
101 else if (Charset_is_utf8)
102 addstr ("\342\224\264"); /* WACS_BTEE */
113 addch ('*'); /* fake thread indicator */
130 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
131 addnstr ((char *) s, k);
139 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
142 m_strcpy(s, l, menu->dialog[i]);
143 menu->current = -1; /* hide menubar */
146 menu->make_entry (s, l, menu, i);
149 void menu_pad_string (char *s, size_t n)
151 int shift = option (OPTARROWCURSOR) ? 3 : 0;
153 char *tmpbuf = p_new(char, n);
155 if (option (OPTMBOXPANE))
156 cols = COLS - shift - SidebarWidth;
159 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
161 snprintf (s, n, "%s", tmpbuf); /* overkill */
165 void menu_redraw_full (MUTTMENU * menu)
167 SETCOLOR (MT_COLOR_NORMAL);
168 /* clear() doesn't optimize screen redraws */
172 if (option (OPTHELP)) {
173 SETCOLOR (MT_COLOR_STATUS);
174 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, SW);
175 mutt_paddstr (COLS-SW, menu->help);
176 SETCOLOR (MT_COLOR_NORMAL);
178 menu->pagelen = LINES - 3;
181 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
182 menu->pagelen = LINES - 2;
185 sidebar_draw_frames();
189 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
192 void menu_redraw_status (MUTTMENU * menu)
196 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
197 SETCOLOR (MT_COLOR_STATUS);
198 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, SW);
199 mutt_paddstr (COLS-SW, buf);
200 SETCOLOR (MT_COLOR_NORMAL);
201 menu->redraw &= ~REDRAW_STATUS;
202 sidebar_draw_frames();
205 void menu_redraw_index (MUTTMENU * menu)
210 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
212 menu_make_entry (buf, sizeof (buf), menu, i);
213 menu_pad_string (buf, sizeof (buf));
215 if (option (OPTARROWCURSOR)) {
216 attrset (menu->color (i));
217 CLEARLINE_WIN (i - menu->top + menu->offset);
219 if (i == menu->current) {
220 attrset (menu->color (i));
221 ADDCOLOR (MT_COLOR_INDICATOR);
222 BKGDSET (MT_COLOR_INDICATOR);
224 attrset (menu->color (i));
228 attrset (menu->color (i));
229 move (i - menu->top + menu->offset, SW);
233 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
234 SETCOLOR (MT_COLOR_NORMAL);
235 BKGDSET (MT_COLOR_NORMAL);
238 attrset (menu->color (i));
240 if (i == menu->current) {
241 ADDCOLOR (MT_COLOR_INDICATOR);
242 BKGDSET (MT_COLOR_INDICATOR);
245 CLEARLINE_WIN (i - menu->top + menu->offset);
247 move (i - menu->top + menu->offset, SW);
248 print_enriched_string (menu->color (i), (unsigned char *) buf,
250 SETCOLOR (MT_COLOR_NORMAL);
251 BKGDSET (MT_COLOR_NORMAL);
255 CLEARLINE_WIN (i - menu->top + menu->offset);
258 /* sidebar_draw_frames(); */
263 void menu_redraw_motion (MUTTMENU * menu)
268 menu->redraw &= ~REDRAW_MOTION;
272 move (menu->oldcurrent + menu->offset - menu->top, SW);
273 SETCOLOR (MT_COLOR_NORMAL);
274 BKGDSET (MT_COLOR_NORMAL);
276 if (option (OPTARROWCURSOR)) {
277 /* clear the pointer */
278 attrset (menu->color (menu->oldcurrent));
281 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, SW + 3);
286 print_enriched_string (menu->color (menu->oldcurrent),
287 (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, SW);
293 attrset (menu->color (menu->current));
294 ADDCOLOR (MT_COLOR_INDICATOR);
296 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),
305 (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 move (menu->current + menu->offset - menu->top, SW);
315 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
317 SETCOLOR (MT_COLOR_NORMAL);
318 BKGDSET (MT_COLOR_NORMAL);
320 menu->redraw &= REDRAW_STATUS;
323 void menu_redraw_current (MUTTMENU * menu)
327 move (menu->current + menu->offset - menu->top, SW);
328 menu_make_entry (buf, sizeof (buf), menu, menu->current);
329 menu_pad_string (buf, sizeof (buf));
331 if (option (OPTARROWCURSOR)) {
332 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,
344 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,
353 SETCOLOR (MT_COLOR_NORMAL);
354 BKGDSET (MT_COLOR_NORMAL);
356 menu->redraw &= REDRAW_STATUS;
359 void menu_redraw_prompt (MUTTMENU * menu)
362 if (option (OPTMSGERR)) {
364 unset_option (OPTMSGERR);
370 SETCOLOR (MT_COLOR_NORMAL);
371 mvaddstr (LINES - 1, 0, menu->prompt);
376 void menu_check_recenter (MUTTMENU * menu)
378 int c = MIN (MenuContext, menu->pagelen / 2);
379 int old_top = menu->top;
381 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
382 if (menu->top != 0) {
384 set_option (OPTNEEDREDRAW);
387 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
388 if (menu->current < menu->top + c)
389 menu->top = menu->current - c;
390 else if (menu->current >= menu->top + menu->pagelen - c)
391 menu->top = menu->current - menu->pagelen + c + 1;
393 if (menu->current < menu->top + c)
394 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
395 else if ((menu->current >= menu->top + menu->pagelen - c))
396 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
400 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
401 menu->top = MIN (menu->top, menu->max - menu->pagelen);
402 menu->top = MAX (menu->top, 0);
404 if (menu->top != old_top)
405 menu->redraw |= REDRAW_INDEX;
408 void menu_jump (MUTTMENU * menu)
411 char buf[SHORT_STRING];
414 mutt_ungetch (LastKey, 0);
416 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
418 if (n >= 0 && n < menu->max) {
420 menu->redraw = REDRAW_MOTION;
423 mutt_error _("Invalid index number.");
427 mutt_error _("No entries.");
430 void menu_next_line (MUTTMENU * menu)
433 int c = MIN (MenuContext, menu->pagelen / 2);
435 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
436 || (menu->max > menu->pagelen
438 menu->max - menu->pagelen))) {
440 if (menu->current < menu->top + c && menu->current < menu->max - 1)
442 menu->redraw = REDRAW_INDEX;
445 mutt_error _("You cannot scroll down farther.");
448 mutt_error _("No entries.");
451 void menu_prev_line (MUTTMENU * menu)
454 int c = MIN (MenuContext, menu->pagelen / 2);
457 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
459 menu->redraw = REDRAW_INDEX;
462 mutt_error _("You cannot scroll up farther.");
466 * pageup: jumplen == -pagelen
467 * pagedown: jumplen == pagelen
468 * halfup: jumplen == -pagelen/2
469 * halfdown: jumplen == pagelen/2
471 #define DIRECTION ((neg * 2) + 1)
472 void menu_length_jump (MUTTMENU *menu, int jumplen) {
473 int tmp, neg = (jumplen >= 0) ? 0 : -1;
474 int c = MIN (MenuContext, menu->pagelen / 2);
477 /* possible to scroll? */
478 if (DIRECTION * menu->top <
479 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
480 menu->top += jumplen;
482 /* jumped too long? */
483 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
486 /* need to move the cursor? */
488 (tmp = (menu->current - (menu->top +
489 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
490 menu->current -= tmp;
492 menu->redraw = REDRAW_INDEX;
494 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
495 menu->current += jumplen;
496 menu->redraw = REDRAW_MOTION;
499 mutt_error (neg ? _("You are on the first page.")
500 : _("You are on the last page."));
502 menu->current = MIN (menu->current, menu->max - 1);
503 menu->current = MAX (menu->current, 0);
506 mutt_error _("No entries.");
510 void menu_next_page (MUTTMENU *menu) {
511 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
514 void menu_prev_page (MUTTMENU *menu) {
515 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
518 void menu_half_down (MUTTMENU *menu) {
519 menu_length_jump (menu, menu->pagelen / 2);
522 void menu_half_up (MUTTMENU *menu) {
523 menu_length_jump (menu, 0 - menu->pagelen / 2);
526 void menu_top_page (MUTTMENU *menu) {
527 if (menu->current != menu->top) {
528 menu->current = menu->top;
529 menu->redraw = REDRAW_MOTION;
533 void menu_bottom_page (MUTTMENU *menu) {
535 menu->current = menu->top + menu->pagelen - 1;
536 if (menu->current > menu->max - 1)
537 menu->current = menu->max - 1;
538 menu->redraw = REDRAW_MOTION;
541 mutt_error _("No entries.");
544 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) {
561 menu->redraw = REDRAW_MOTION;
564 mutt_error _("No entries.");
567 void menu_last_entry (MUTTMENU *menu) {
569 menu->current = menu->max - 1;
570 menu->redraw = REDRAW_MOTION;
573 mutt_error _("No entries.");
576 void menu_current_top (MUTTMENU * menu)
579 menu->top = menu->current;
580 menu->redraw = REDRAW_INDEX;
583 mutt_error _("No entries.");
586 void menu_current_middle (MUTTMENU * menu)
589 menu->top = menu->current - menu->pagelen / 2;
592 menu->redraw = REDRAW_INDEX;
595 mutt_error _("No entries.");
598 void menu_current_bottom (MUTTMENU * menu)
601 menu->top = menu->current - menu->pagelen + 1;
604 menu->redraw = REDRAW_INDEX;
607 mutt_error _("No entries.");
610 void menu_next_entry (MUTTMENU * menu)
612 if (menu->current < menu->max - 1) {
614 menu->redraw = REDRAW_MOTION;
617 mutt_error _("You are on the last entry.");
620 void menu_prev_entry (MUTTMENU * menu)
624 menu->redraw = REDRAW_MOTION;
627 mutt_error _("You are on the first entry.");
630 static int default_color (int i __attribute__ ((unused)))
632 return ColorDefs[MT_COLOR_NORMAL];
635 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
637 char buf[LONG_STRING];
639 menu_make_entry (buf, sizeof (buf), m, n);
640 return (regexec (re, buf, 0, NULL, 0));
643 MUTTMENU *mutt_new_menu (void)
645 MUTTMENU *p = p_new(MUTTMENU, 1);
650 p->redraw = REDRAW_FULL;
651 p->pagelen = PAGELEN;
652 p->color = default_color;
653 p->search = menu_search_generic;
657 void mutt_menuDestroy (MUTTMENU ** p)
661 p_delete(&(*p)->searchBuf);
664 for (i = 0; i < (*p)->max; i++)
665 p_delete(&(*p)->dialog[i]);
667 p_delete(&(*p)->dialog);
673 #define M_SEARCH_UP 1
674 #define M_SEARCH_DOWN 2
676 static int menu_search (MUTTMENU * menu, int op)
681 char buf[SHORT_STRING];
683 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
684 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
685 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
686 _("Reverse search for: "),
687 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
689 m_strreplace(&menu->searchBuf, buf);
690 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
693 if (!menu->searchBuf) {
694 mutt_error _("No search pattern.");
700 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
701 if (op == OP_SEARCH_OPPOSITE)
702 searchDir = -searchDir;
705 REGCOMP (&re, menu->searchBuf,
706 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
707 regerror (r, &re, buf, sizeof (buf));
709 mutt_error ("%s", buf);
713 r = menu->current + searchDir;
714 while (r >= 0 && r < menu->max) {
715 if (menu->search (menu, &re, r) == 0) {
724 mutt_error _("Not found.");
729 static int menu_dialog_translate_op (int i)
739 case OP_CURRENT_BOTTOM:
741 return OP_BOTTOM_PAGE;
742 case OP_CURRENT_MIDDLE:
743 return OP_MIDDLE_PAGE;
749 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
761 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
762 *ip = OP_MAX + (p - menu->keys + 1);
766 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
771 int menu_redraw (MUTTMENU * menu)
773 /* See if all or part of the screen needs to be updated. */
774 if (menu->redraw & REDRAW_FULL) {
775 menu_redraw_full (menu);
776 /* allow the caller to do any local configuration */
781 menu_check_recenter (menu);
783 if (menu->redraw & REDRAW_STATUS)
784 menu_redraw_status (menu);
785 if (menu->redraw & REDRAW_INDEX)
786 menu_redraw_index (menu);
787 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
788 menu_redraw_motion (menu);
789 else if (menu->redraw == REDRAW_CURRENT)
790 menu_redraw_current (menu);
793 menu_redraw_prompt (menu);
798 int mutt_menuLoop (MUTTMENU * menu)
803 if (option (OPTMENUCALLER)) {
804 unset_option (OPTMENUCALLER);
812 if (menu_redraw (menu) == OP_REDRAW)
815 menu->oldcurrent = menu->current;
817 if (option (OPTARROWCURSOR))
818 move (menu->current - menu->top + menu->offset, SW + 2);
819 else if (option (OPTBRAILLEFRIENDLY))
820 move (menu->current - menu->top + menu->offset, SW);
822 move (menu->current - menu->top + menu->offset, COLS - 1);
827 /* try to catch dialog keys before ops */
828 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
831 i = km_dokey (menu->menu);
832 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
834 mvaddstr (LINES - 1, 0, "Tag-");
836 i = km_dokey (menu->menu);
838 CLEARLINE (LINES - 1);
840 else if (i == OP_TAG_PREFIX) {
841 mutt_error _("No tagged entries.");
845 else { /* None tagged, OP_TAG_PREFIX_COND */
849 while (UngetCount > 0) {
851 if (tmp.op == OP_END_COND)
854 mutt_message _("Nothing to do.");
859 else if (menu->tagged && option (OPTAUTOTAG))
866 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
868 mutt_resize_screen ();
869 menu->redraw = REDRAW_FULL;
871 clearok (stdscr, TRUE); /*force complete redraw */
881 /* Convert menubar movement to scrolling */
883 i = menu_dialog_translate_op (i);
887 menu_next_entry (menu);
890 menu_prev_entry (menu);
893 menu_half_down (menu);
899 menu_next_page (menu);
902 menu_prev_page (menu);
905 menu_next_line (menu);
908 menu_prev_line (menu);
911 menu_first_entry (menu);
914 menu_last_entry (menu);
917 menu_top_page (menu);
920 menu_middle_page (menu);
923 menu_bottom_page (menu);
926 menu_current_top (menu);
928 case OP_CURRENT_MIDDLE:
929 menu_current_middle (menu);
931 case OP_CURRENT_BOTTOM:
932 menu_current_bottom (menu);
935 case OP_SEARCH_REVERSE:
937 case OP_SEARCH_OPPOSITE:
938 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
939 menu->oldcurrent = menu->current;
940 if ((menu->current = menu_search (menu, i)) != -1)
941 menu->redraw = REDRAW_MOTION;
943 menu->current = menu->oldcurrent;
946 mutt_error _("Search is not implemented for this menu.");
951 mutt_error (_("Jumping is not implemented for dialogs."));
957 case OP_ENTER_COMMAND:
958 CurrentMenu = menu->menu;
959 mutt_enter_command ();
960 if (option (OPTFORCEREDRAWINDEX)) {
961 menu->redraw = REDRAW_FULL;
962 unset_option (OPTFORCEREDRAWINDEX);
963 unset_option (OPTFORCEREDRAWPAGER);
968 if (menu->tag && !menu->dialog) {
969 if (menu->tagprefix && !option (OPTAUTOTAG)) {
970 for (i = 0; i < menu->max; i++)
971 menu->tagged += menu->tag (menu, i, 0);
972 menu->redraw = REDRAW_INDEX;
974 else if (menu->max) {
975 int t = menu->tag (menu, menu->current, -1);
978 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
980 menu->redraw = REDRAW_MOTION_RESYNCH;
983 menu->redraw = REDRAW_CURRENT;
986 mutt_error _("No entries.");
989 mutt_error _("Tagging is not supported.");
992 case OP_SHELL_ESCAPE:
993 mutt_shell_escape ();
994 MAYBE_REDRAW (menu->redraw);
1001 case OP_REBUILD_CACHE:
1002 mx_rebuild_cache ();
1006 clearok (stdscr, TRUE);
1007 menu->redraw = REDRAW_FULL;
1011 mutt_help (menu->menu);
1012 menu->redraw = REDRAW_FULL;
1016 km_error_key (menu->menu);