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.
14 #include <lib-lib/mem.h>
15 #include <lib-lib/str.h>
16 #include <lib-lib/macros.h>
20 #include "mutt_curses.h"
21 #include "mutt_menu.h"
25 #include <imap/imap.h>
30 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
32 extern size_t UngetCount;
34 static void print_enriched_string (int attr, unsigned char *s, int do_color)
38 size_t n = m_strlen((char *) s);
43 if (*s < M_TREE_MAX) {
45 SETCOLOR (MT_COLOR_TREE);
46 while (*s && *s < M_TREE_MAX) {
49 if (option (OPTASCIICHARS))
51 else if (Charset_is_utf8)
52 addstr ("\342\224\224"); /* WACS_LLCORNER */
57 if (option (OPTASCIICHARS))
59 else if (Charset_is_utf8)
60 addstr ("\342\224\214"); /* WACS_ULCORNER */
65 if (option (OPTASCIICHARS))
67 else if (Charset_is_utf8)
68 addstr ("\342\224\234"); /* WACS_LTEE */
73 if (option (OPTASCIICHARS))
75 else if (Charset_is_utf8)
76 addstr ("\342\224\200"); /* WACS_HLINE */
81 if (option (OPTASCIICHARS))
83 else if (Charset_is_utf8)
84 addstr ("\342\224\202"); /* WACS_VLINE */
89 if (option (OPTASCIICHARS))
91 else if (Charset_is_utf8)
92 addstr ("\342\224\254"); /* WACS_TTEE */
97 if (option (OPTASCIICHARS))
99 else if (Charset_is_utf8)
100 addstr ("\342\224\264"); /* WACS_BTEE */
111 addch ('*'); /* fake thread indicator */
128 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
129 addnstr ((char *) s, k);
137 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
140 m_strcpy(s, l, menu->dialog[i]);
141 menu->current = -1; /* hide menubar */
144 menu->make_entry (s, l, menu, i);
147 void menu_pad_string (char *s, size_t n)
149 int shift = option (OPTARROWCURSOR) ? 3 : 0;
151 char *tmpbuf = p_new(char, n);
153 if (option (OPTMBOXPANE))
154 cols = COLS - shift - SidebarWidth;
157 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
159 snprintf (s, n, "%s", tmpbuf); /* overkill */
163 void menu_redraw_full (MUTTMENU * menu)
165 SETCOLOR (MT_COLOR_NORMAL);
166 /* clear() doesn't optimize screen redraws */
170 if (option (OPTHELP)) {
171 SETCOLOR (MT_COLOR_STATUS);
172 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, SW);
173 mutt_paddstr (COLS-SW, menu->help);
174 SETCOLOR (MT_COLOR_NORMAL);
176 menu->pagelen = LINES - 3;
179 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
180 menu->pagelen = LINES - 2;
183 sidebar_draw_frames();
187 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
190 void menu_redraw_status (MUTTMENU * menu)
194 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
195 SETCOLOR (MT_COLOR_STATUS);
196 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, SW);
197 mutt_paddstr (COLS-SW, buf);
198 SETCOLOR (MT_COLOR_NORMAL);
199 menu->redraw &= ~REDRAW_STATUS;
200 sidebar_draw_frames();
203 void menu_redraw_index (MUTTMENU * menu)
208 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
210 menu_make_entry (buf, sizeof (buf), menu, i);
211 menu_pad_string (buf, sizeof (buf));
213 if (option (OPTARROWCURSOR)) {
214 attrset (menu->color (i));
215 CLEARLINE_WIN (i - menu->top + menu->offset);
217 if (i == menu->current) {
218 attrset (menu->color (i));
219 ADDCOLOR (MT_COLOR_INDICATOR);
220 BKGDSET (MT_COLOR_INDICATOR);
222 attrset (menu->color (i));
226 attrset (menu->color (i));
227 move (i - menu->top + menu->offset, SW);
231 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
232 SETCOLOR (MT_COLOR_NORMAL);
233 BKGDSET (MT_COLOR_NORMAL);
236 attrset (menu->color (i));
238 if (i == menu->current) {
239 ADDCOLOR (MT_COLOR_INDICATOR);
240 BKGDSET (MT_COLOR_INDICATOR);
243 CLEARLINE_WIN (i - menu->top + menu->offset);
245 move (i - menu->top + menu->offset, SW);
246 print_enriched_string (menu->color (i), (unsigned char *) buf,
248 SETCOLOR (MT_COLOR_NORMAL);
249 BKGDSET (MT_COLOR_NORMAL);
253 CLEARLINE_WIN (i - menu->top + menu->offset);
256 /* sidebar_draw_frames(); */
261 void menu_redraw_motion (MUTTMENU * menu)
266 menu->redraw &= ~REDRAW_MOTION;
270 move (menu->oldcurrent + menu->offset - menu->top, SW);
271 SETCOLOR (MT_COLOR_NORMAL);
272 BKGDSET (MT_COLOR_NORMAL);
274 if (option (OPTARROWCURSOR)) {
275 /* clear the pointer */
276 attrset (menu->color (menu->oldcurrent));
279 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
281 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
282 menu_pad_string (buf, sizeof (buf));
283 move (menu->oldcurrent + menu->offset - menu->top, SW + 3);
284 print_enriched_string (menu->color (menu->oldcurrent),
285 (unsigned char *) buf, 1);
286 SETCOLOR (MT_COLOR_NORMAL);
289 /* now draw it in the new location */
290 move (menu->current + menu->offset - menu->top, SW);
291 attrset (menu->color (menu->current));
292 ADDCOLOR (MT_COLOR_INDICATOR);
294 SETCOLOR (MT_COLOR_NORMAL);
297 /* erase the current indicator */
298 attrset (menu->color (menu->oldcurrent));
300 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
301 menu_pad_string (buf, sizeof (buf));
302 print_enriched_string (menu->color (menu->oldcurrent),
303 (unsigned char *) buf, 1);
305 /* now draw the new one to reflect the change */
306 menu_make_entry (buf, sizeof (buf), menu, menu->current);
307 menu_pad_string (buf, sizeof (buf));
308 attrset (menu->color (menu->current));
309 ADDCOLOR (MT_COLOR_INDICATOR);
310 BKGDSET (MT_COLOR_INDICATOR);
311 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
312 move (menu->current + menu->offset - menu->top, SW);
313 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
315 SETCOLOR (MT_COLOR_NORMAL);
316 BKGDSET (MT_COLOR_NORMAL);
318 menu->redraw &= REDRAW_STATUS;
321 void menu_redraw_current (MUTTMENU * menu)
325 move (menu->current + menu->offset - menu->top, SW);
326 menu_make_entry (buf, sizeof (buf), menu, menu->current);
327 menu_pad_string (buf, sizeof (buf));
329 if (option (OPTARROWCURSOR)) {
330 int attr = menu->color (menu->current);
334 attrset (menu->color (menu->current));
335 ADDCOLOR (MT_COLOR_INDICATOR);
339 menu_pad_string (buf, sizeof (buf));
340 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
342 SETCOLOR (MT_COLOR_NORMAL);
345 attrset (menu->color (menu->current));
346 ADDCOLOR (MT_COLOR_INDICATOR);
347 BKGDSET (MT_COLOR_INDICATOR);
349 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
351 SETCOLOR (MT_COLOR_NORMAL);
352 BKGDSET (MT_COLOR_NORMAL);
354 menu->redraw &= REDRAW_STATUS;
357 void menu_redraw_prompt (MUTTMENU * menu)
360 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 int c = MIN (MenuContext, menu->pagelen / 2);
377 int old_top = menu->top;
379 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
380 if (menu->top != 0) {
382 set_option (OPTNEEDREDRAW);
385 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
386 if (menu->current < menu->top + c)
387 menu->top = menu->current - c;
388 else if (menu->current >= menu->top + menu->pagelen - c)
389 menu->top = menu->current - menu->pagelen + c + 1;
391 if (menu->current < menu->top + c)
392 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
393 else if ((menu->current >= menu->top + menu->pagelen - c))
394 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
398 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
399 menu->top = MIN (menu->top, menu->max - menu->pagelen);
400 menu->top = MAX (menu->top, 0);
402 if (menu->top != old_top)
403 menu->redraw |= REDRAW_INDEX;
406 void menu_jump (MUTTMENU * menu)
409 char buf[SHORT_STRING];
412 mutt_ungetch (LastKey, 0);
414 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
416 if (n >= 0 && n < menu->max) {
418 menu->redraw = REDRAW_MOTION;
421 mutt_error _("Invalid index number.");
425 mutt_error _("No entries.");
428 void menu_next_line (MUTTMENU * menu)
431 int c = MIN (MenuContext, menu->pagelen / 2);
433 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
434 || (menu->max > menu->pagelen
436 menu->max - menu->pagelen))) {
438 if (menu->current < menu->top + c && menu->current < menu->max - 1)
440 menu->redraw = REDRAW_INDEX;
443 mutt_error _("You cannot scroll down farther.");
446 mutt_error _("No entries.");
449 void menu_prev_line (MUTTMENU * menu)
452 int c = MIN (MenuContext, menu->pagelen / 2);
455 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
457 menu->redraw = REDRAW_INDEX;
460 mutt_error _("You cannot scroll up farther.");
464 * pageup: jumplen == -pagelen
465 * pagedown: jumplen == pagelen
466 * halfup: jumplen == -pagelen/2
467 * halfdown: jumplen == pagelen/2
469 #define DIRECTION ((neg * 2) + 1)
470 void menu_length_jump (MUTTMENU *menu, int jumplen) {
471 int tmp, neg = (jumplen >= 0) ? 0 : -1;
472 int c = MIN (MenuContext, menu->pagelen / 2);
475 /* possible to scroll? */
476 if (DIRECTION * menu->top <
477 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
478 menu->top += jumplen;
480 /* jumped too long? */
481 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
484 /* need to move the cursor? */
486 (tmp = (menu->current - (menu->top +
487 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
488 menu->current -= tmp;
490 menu->redraw = REDRAW_INDEX;
492 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
493 menu->current += jumplen;
494 menu->redraw = REDRAW_MOTION;
497 mutt_error (neg ? _("You are on the first page.")
498 : _("You are on the last page."));
500 menu->current = MIN (menu->current, menu->max - 1);
501 menu->current = MAX (menu->current, 0);
504 mutt_error _("No entries.");
508 void menu_next_page (MUTTMENU *menu) {
509 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
512 void menu_prev_page (MUTTMENU *menu) {
513 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
516 void menu_half_down (MUTTMENU *menu) {
517 menu_length_jump (menu, menu->pagelen / 2);
520 void menu_half_up (MUTTMENU *menu) {
521 menu_length_jump (menu, 0 - menu->pagelen / 2);
524 void menu_top_page (MUTTMENU *menu) {
525 if (menu->current != menu->top) {
526 menu->current = menu->top;
527 menu->redraw = REDRAW_MOTION;
531 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) {
546 i = menu->top + menu->pagelen;
547 if (i > menu->max - 1)
549 menu->current = menu->top + (i - menu->top) / 2;
550 menu->redraw = REDRAW_MOTION;
553 mutt_error _("No entries.");
556 void menu_first_entry (MUTTMENU *menu) {
559 menu->redraw = REDRAW_MOTION;
562 mutt_error _("No entries.");
565 void menu_last_entry (MUTTMENU *menu) {
567 menu->current = menu->max - 1;
568 menu->redraw = REDRAW_MOTION;
571 mutt_error _("No entries.");
574 void menu_current_top (MUTTMENU * menu)
577 menu->top = menu->current;
578 menu->redraw = REDRAW_INDEX;
581 mutt_error _("No entries.");
584 void menu_current_middle (MUTTMENU * menu)
587 menu->top = menu->current - menu->pagelen / 2;
590 menu->redraw = REDRAW_INDEX;
593 mutt_error _("No entries.");
596 void menu_current_bottom (MUTTMENU * menu)
599 menu->top = menu->current - menu->pagelen + 1;
602 menu->redraw = REDRAW_INDEX;
605 mutt_error _("No entries.");
608 void menu_next_entry (MUTTMENU * menu)
610 if (menu->current < menu->max - 1) {
612 menu->redraw = REDRAW_MOTION;
615 mutt_error _("You are on the last entry.");
618 void menu_prev_entry (MUTTMENU * menu)
622 menu->redraw = REDRAW_MOTION;
625 mutt_error _("You are on the first entry.");
628 static int default_color (int i __attribute__ ((unused)))
630 return ColorDefs[MT_COLOR_NORMAL];
633 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
635 char buf[LONG_STRING];
637 menu_make_entry (buf, sizeof (buf), m, n);
638 return (regexec (re, buf, 0, NULL, 0));
641 MUTTMENU *mutt_new_menu (void)
643 MUTTMENU *p = p_new(MUTTMENU, 1);
648 p->redraw = REDRAW_FULL;
649 p->pagelen = PAGELEN;
650 p->color = default_color;
651 p->search = menu_search_generic;
655 void mutt_menuDestroy (MUTTMENU ** p)
659 p_delete(&(*p)->searchBuf);
662 for (i = 0; i < (*p)->max; i++)
663 p_delete(&(*p)->dialog[i]);
665 p_delete(&(*p)->dialog);
671 #define M_SEARCH_UP 1
672 #define M_SEARCH_DOWN 2
674 static int menu_search (MUTTMENU * menu, int op)
679 char buf[SHORT_STRING];
681 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
682 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
683 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
684 _("Reverse search for: "),
685 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
687 m_strreplace(&menu->searchBuf, buf);
688 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
691 if (!menu->searchBuf) {
692 mutt_error _("No search pattern.");
698 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
699 if (op == OP_SEARCH_OPPOSITE)
700 searchDir = -searchDir;
703 REGCOMP (&re, menu->searchBuf,
704 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
705 regerror (r, &re, buf, sizeof (buf));
707 mutt_error ("%s", buf);
711 r = menu->current + searchDir;
712 while (r >= 0 && r < menu->max) {
713 if (menu->search (menu, &re, r) == 0) {
722 mutt_error _("Not found.");
727 static int menu_dialog_translate_op (int i)
737 case OP_CURRENT_BOTTOM:
739 return OP_BOTTOM_PAGE;
740 case OP_CURRENT_MIDDLE:
741 return OP_MIDDLE_PAGE;
747 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
759 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
760 *ip = OP_MAX + (p - menu->keys + 1);
764 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
769 int menu_redraw (MUTTMENU * menu)
771 /* See if all or part of the screen needs to be updated. */
772 if (menu->redraw & REDRAW_FULL) {
773 menu_redraw_full (menu);
774 /* allow the caller to do any local configuration */
779 menu_check_recenter (menu);
781 if (menu->redraw & REDRAW_STATUS)
782 menu_redraw_status (menu);
783 if (menu->redraw & REDRAW_INDEX)
784 menu_redraw_index (menu);
785 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
786 menu_redraw_motion (menu);
787 else if (menu->redraw == REDRAW_CURRENT)
788 menu_redraw_current (menu);
791 menu_redraw_prompt (menu);
796 int mutt_menuLoop (MUTTMENU * menu)
801 if (option (OPTMENUCALLER)) {
802 unset_option (OPTMENUCALLER);
810 if (menu_redraw (menu) == OP_REDRAW)
813 menu->oldcurrent = menu->current;
815 if (option (OPTARROWCURSOR))
816 move (menu->current - menu->top + menu->offset, SW + 2);
817 else if (option (OPTBRAILLEFRIENDLY))
818 move (menu->current - menu->top + menu->offset, SW);
820 move (menu->current - menu->top + menu->offset, COLS - 1);
825 /* try to catch dialog keys before ops */
826 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
829 i = km_dokey (menu->menu);
830 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
832 mvaddstr (LINES - 1, 0, "Tag-");
834 i = km_dokey (menu->menu);
836 CLEARLINE (LINES - 1);
838 else if (i == OP_TAG_PREFIX) {
839 mutt_error _("No tagged entries.");
843 else { /* None tagged, OP_TAG_PREFIX_COND */
847 while (UngetCount > 0) {
849 if (tmp.op == OP_END_COND)
852 mutt_message _("Nothing to do.");
857 else if (menu->tagged && option (OPTAUTOTAG))
864 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
866 mutt_resize_screen ();
867 menu->redraw = REDRAW_FULL;
869 clearok (stdscr, TRUE); /*force complete redraw */
879 /* Convert menubar movement to scrolling */
881 i = menu_dialog_translate_op (i);
885 menu_next_entry (menu);
888 menu_prev_entry (menu);
891 menu_half_down (menu);
897 menu_next_page (menu);
900 menu_prev_page (menu);
903 menu_next_line (menu);
906 menu_prev_line (menu);
909 menu_first_entry (menu);
912 menu_last_entry (menu);
915 menu_top_page (menu);
918 menu_middle_page (menu);
921 menu_bottom_page (menu);
924 menu_current_top (menu);
926 case OP_CURRENT_MIDDLE:
927 menu_current_middle (menu);
929 case OP_CURRENT_BOTTOM:
930 menu_current_bottom (menu);
933 case OP_SEARCH_REVERSE:
935 case OP_SEARCH_OPPOSITE:
936 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
937 menu->oldcurrent = menu->current;
938 if ((menu->current = menu_search (menu, i)) != -1)
939 menu->redraw = REDRAW_MOTION;
941 menu->current = menu->oldcurrent;
944 mutt_error _("Search is not implemented for this menu.");
949 mutt_error (_("Jumping is not implemented for dialogs."));
955 case OP_ENTER_COMMAND:
956 CurrentMenu = menu->menu;
957 mutt_enter_command ();
958 if (option (OPTFORCEREDRAWINDEX)) {
959 menu->redraw = REDRAW_FULL;
960 unset_option (OPTFORCEREDRAWINDEX);
961 unset_option (OPTFORCEREDRAWPAGER);
966 if (menu->tag && !menu->dialog) {
967 if (menu->tagprefix && !option (OPTAUTOTAG)) {
968 for (i = 0; i < menu->max; i++)
969 menu->tagged += menu->tag (menu, i, 0);
970 menu->redraw = REDRAW_INDEX;
972 else if (menu->max) {
973 int t = menu->tag (menu, menu->current, -1);
976 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
978 menu->redraw = REDRAW_MOTION_RESYNCH;
981 menu->redraw = REDRAW_CURRENT;
984 mutt_error _("No entries.");
987 mutt_error _("Tagging is not supported.");
990 case OP_SHELL_ESCAPE:
991 mutt_shell_escape ();
992 MAYBE_REDRAW (menu->redraw);
999 case OP_REBUILD_CACHE:
1000 mx_rebuild_cache ();
1004 clearok (stdscr, TRUE);
1005 menu->redraw = REDRAW_FULL;
1009 mutt_help (menu->menu);
1010 menu->redraw = REDRAW_FULL;
1014 km_error_key (menu->menu);