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>
26 #include <imap/imap.h>
33 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
35 extern size_t UngetCount;
37 static void print_enriched_string (int attr, unsigned char *s, int do_color)
41 size_t n = m_strlen((char *) s);
46 if (*s < M_TREE_MAX) {
48 SETCOLOR (MT_COLOR_TREE);
49 while (*s && *s < M_TREE_MAX) {
52 if (option (OPTASCIICHARS))
54 else if (Charset_is_utf8)
55 addstr ("\342\224\224"); /* WACS_LLCORNER */
60 if (option (OPTASCIICHARS))
62 else if (Charset_is_utf8)
63 addstr ("\342\224\214"); /* WACS_ULCORNER */
68 if (option (OPTASCIICHARS))
70 else if (Charset_is_utf8)
71 addstr ("\342\224\234"); /* WACS_LTEE */
76 if (option (OPTASCIICHARS))
78 else if (Charset_is_utf8)
79 addstr ("\342\224\200"); /* WACS_HLINE */
84 if (option (OPTASCIICHARS))
86 else if (Charset_is_utf8)
87 addstr ("\342\224\202"); /* WACS_VLINE */
92 if (option (OPTASCIICHARS))
94 else if (Charset_is_utf8)
95 addstr ("\342\224\254"); /* WACS_TTEE */
100 if (option (OPTASCIICHARS))
102 else if (Charset_is_utf8)
103 addstr ("\342\224\264"); /* WACS_BTEE */
114 addch ('*'); /* fake thread indicator */
131 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
132 addnstr ((char *) s, k);
140 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
143 m_strcpy(s, l, menu->dialog[i]);
144 menu->current = -1; /* hide menubar */
147 menu->make_entry (s, l, menu, i);
150 void menu_pad_string (char *s, size_t n)
152 int shift = option (OPTARROWCURSOR) ? 3 : 0;
154 char *tmpbuf = p_new(char, n);
156 if (option (OPTMBOXPANE))
157 cols = COLS - shift - SidebarWidth;
160 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
162 snprintf (s, n, "%s", tmpbuf); /* overkill */
166 void menu_redraw_full (MUTTMENU * menu)
168 SETCOLOR (MT_COLOR_NORMAL);
169 /* clear() doesn't optimize screen redraws */
173 if (option (OPTHELP)) {
174 SETCOLOR (MT_COLOR_STATUS);
175 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, SW);
176 mutt_paddstr (COLS-SW, menu->help);
177 SETCOLOR (MT_COLOR_NORMAL);
179 menu->pagelen = LINES - 3;
182 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
183 menu->pagelen = LINES - 2;
186 sidebar_draw_frames();
190 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
193 void menu_redraw_status (MUTTMENU * menu)
197 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
198 SETCOLOR (MT_COLOR_STATUS);
199 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, SW);
200 mutt_paddstr (COLS-SW, buf);
201 SETCOLOR (MT_COLOR_NORMAL);
202 menu->redraw &= ~REDRAW_STATUS;
203 sidebar_draw_frames();
206 void menu_redraw_index (MUTTMENU * menu)
211 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
213 menu_make_entry (buf, sizeof (buf), menu, i);
214 menu_pad_string (buf, sizeof (buf));
216 if (option (OPTARROWCURSOR)) {
217 attrset (menu->color (i));
218 CLEARLINE_WIN (i - menu->top + menu->offset);
220 if (i == menu->current) {
221 attrset (menu->color (i));
222 ADDCOLOR (MT_COLOR_INDICATOR);
223 BKGDSET (MT_COLOR_INDICATOR);
225 attrset (menu->color (i));
229 attrset (menu->color (i));
230 move (i - menu->top + menu->offset, SW);
234 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
235 SETCOLOR (MT_COLOR_NORMAL);
236 BKGDSET (MT_COLOR_NORMAL);
239 attrset (menu->color (i));
241 if (i == menu->current) {
242 ADDCOLOR (MT_COLOR_INDICATOR);
243 BKGDSET (MT_COLOR_INDICATOR);
246 CLEARLINE_WIN (i - menu->top + menu->offset);
248 move (i - menu->top + menu->offset, SW);
249 print_enriched_string (menu->color (i), (unsigned char *) buf,
251 SETCOLOR (MT_COLOR_NORMAL);
252 BKGDSET (MT_COLOR_NORMAL);
256 CLEARLINE_WIN (i - menu->top + menu->offset);
259 /* sidebar_draw_frames(); */
264 void menu_redraw_motion (MUTTMENU * menu)
269 menu->redraw &= ~REDRAW_MOTION;
273 move (menu->oldcurrent + menu->offset - menu->top, SW);
274 SETCOLOR (MT_COLOR_NORMAL);
275 BKGDSET (MT_COLOR_NORMAL);
277 if (option (OPTARROWCURSOR)) {
278 /* clear the pointer */
279 attrset (menu->color (menu->oldcurrent));
282 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
284 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
285 menu_pad_string (buf, sizeof (buf));
286 move (menu->oldcurrent + menu->offset - menu->top, SW + 3);
287 print_enriched_string (menu->color (menu->oldcurrent),
288 (unsigned char *) buf, 1);
289 SETCOLOR (MT_COLOR_NORMAL);
292 /* now draw it in the new location */
293 move (menu->current + menu->offset - menu->top, SW);
294 attrset (menu->color (menu->current));
295 ADDCOLOR (MT_COLOR_INDICATOR);
297 SETCOLOR (MT_COLOR_NORMAL);
300 /* erase the current indicator */
301 attrset (menu->color (menu->oldcurrent));
303 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
304 menu_pad_string (buf, sizeof (buf));
305 print_enriched_string (menu->color (menu->oldcurrent),
306 (unsigned char *) buf, 1);
308 /* now draw the new one to reflect the change */
309 menu_make_entry (buf, sizeof (buf), menu, menu->current);
310 menu_pad_string (buf, sizeof (buf));
311 attrset (menu->color (menu->current));
312 ADDCOLOR (MT_COLOR_INDICATOR);
313 BKGDSET (MT_COLOR_INDICATOR);
314 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
315 move (menu->current + menu->offset - menu->top, SW);
316 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
318 SETCOLOR (MT_COLOR_NORMAL);
319 BKGDSET (MT_COLOR_NORMAL);
321 menu->redraw &= REDRAW_STATUS;
324 void menu_redraw_current (MUTTMENU * menu)
328 move (menu->current + menu->offset - menu->top, SW);
329 menu_make_entry (buf, sizeof (buf), menu, menu->current);
330 menu_pad_string (buf, sizeof (buf));
332 if (option (OPTARROWCURSOR)) {
333 int attr = menu->color (menu->current);
337 attrset (menu->color (menu->current));
338 ADDCOLOR (MT_COLOR_INDICATOR);
342 menu_pad_string (buf, sizeof (buf));
343 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
345 SETCOLOR (MT_COLOR_NORMAL);
348 attrset (menu->color (menu->current));
349 ADDCOLOR (MT_COLOR_INDICATOR);
350 BKGDSET (MT_COLOR_INDICATOR);
352 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
354 SETCOLOR (MT_COLOR_NORMAL);
355 BKGDSET (MT_COLOR_NORMAL);
357 menu->redraw &= REDRAW_STATUS;
360 void menu_redraw_prompt (MUTTMENU * menu)
363 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 */
383 if (menu->top != 0) {
385 set_option (OPTNEEDREDRAW);
388 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
389 if (menu->current < menu->top + c)
390 menu->top = menu->current - c;
391 else if (menu->current >= menu->top + menu->pagelen - c)
392 menu->top = menu->current - menu->pagelen + c + 1;
394 if (menu->current < menu->top + c)
395 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
396 else if ((menu->current >= menu->top + menu->pagelen - c))
397 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
401 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
402 menu->top = MIN (menu->top, menu->max - menu->pagelen);
403 menu->top = MAX (menu->top, 0);
405 if (menu->top != old_top)
406 menu->redraw |= REDRAW_INDEX;
409 void menu_jump (MUTTMENU * menu)
412 char buf[SHORT_STRING];
415 mutt_ungetch (LastKey, 0);
417 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
419 if (n >= 0 && n < menu->max) {
421 menu->redraw = REDRAW_MOTION;
424 mutt_error _("Invalid index number.");
428 mutt_error _("No entries.");
431 void menu_next_line (MUTTMENU * menu)
434 int c = MIN (MenuContext, menu->pagelen / 2);
436 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
437 || (menu->max > menu->pagelen
439 menu->max - menu->pagelen))) {
441 if (menu->current < menu->top + c && menu->current < menu->max - 1)
443 menu->redraw = REDRAW_INDEX;
446 mutt_error _("You cannot scroll down farther.");
449 mutt_error _("No entries.");
452 void menu_prev_line (MUTTMENU * menu)
455 int c = MIN (MenuContext, menu->pagelen / 2);
458 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
460 menu->redraw = REDRAW_INDEX;
463 mutt_error _("You cannot scroll up farther.");
467 * pageup: jumplen == -pagelen
468 * pagedown: jumplen == pagelen
469 * halfup: jumplen == -pagelen/2
470 * halfdown: jumplen == pagelen/2
472 #define DIRECTION ((neg * 2) + 1)
473 void menu_length_jump (MUTTMENU *menu, int jumplen) {
474 int tmp, neg = (jumplen >= 0) ? 0 : -1;
475 int c = MIN (MenuContext, menu->pagelen / 2);
478 /* possible to scroll? */
479 if (DIRECTION * menu->top <
480 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
481 menu->top += jumplen;
483 /* jumped too long? */
484 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
487 /* need to move the cursor? */
489 (tmp = (menu->current - (menu->top +
490 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
491 menu->current -= tmp;
493 menu->redraw = REDRAW_INDEX;
495 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
496 menu->current += jumplen;
497 menu->redraw = REDRAW_MOTION;
500 mutt_error (neg ? _("You are on the first page.")
501 : _("You are on the last page."));
503 menu->current = MIN (menu->current, menu->max - 1);
504 menu->current = MAX (menu->current, 0);
507 mutt_error _("No entries.");
511 void menu_next_page (MUTTMENU *menu) {
512 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
515 void menu_prev_page (MUTTMENU *menu) {
516 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
519 void menu_half_down (MUTTMENU *menu) {
520 menu_length_jump (menu, menu->pagelen / 2);
523 void menu_half_up (MUTTMENU *menu) {
524 menu_length_jump (menu, 0 - menu->pagelen / 2);
527 void menu_top_page (MUTTMENU *menu) {
528 if (menu->current != menu->top) {
529 menu->current = menu->top;
530 menu->redraw = REDRAW_MOTION;
534 void menu_bottom_page (MUTTMENU *menu) {
536 menu->current = menu->top + menu->pagelen - 1;
537 if (menu->current > menu->max - 1)
538 menu->current = menu->max - 1;
539 menu->redraw = REDRAW_MOTION;
542 mutt_error _("No entries.");
545 void menu_middle_page (MUTTMENU *menu) {
549 i = menu->top + menu->pagelen;
550 if (i > menu->max - 1)
552 menu->current = menu->top + (i - menu->top) / 2;
553 menu->redraw = REDRAW_MOTION;
556 mutt_error _("No entries.");
559 void menu_first_entry (MUTTMENU *menu) {
562 menu->redraw = REDRAW_MOTION;
565 mutt_error _("No entries.");
568 void menu_last_entry (MUTTMENU *menu) {
570 menu->current = menu->max - 1;
571 menu->redraw = REDRAW_MOTION;
574 mutt_error _("No entries.");
577 void menu_current_top (MUTTMENU * menu)
580 menu->top = menu->current;
581 menu->redraw = REDRAW_INDEX;
584 mutt_error _("No entries.");
587 void menu_current_middle (MUTTMENU * menu)
590 menu->top = menu->current - menu->pagelen / 2;
593 menu->redraw = REDRAW_INDEX;
596 mutt_error _("No entries.");
599 void menu_current_bottom (MUTTMENU * menu)
602 menu->top = menu->current - menu->pagelen + 1;
605 menu->redraw = REDRAW_INDEX;
608 mutt_error _("No entries.");
611 void menu_next_entry (MUTTMENU * menu)
613 if (menu->current < menu->max - 1) {
615 menu->redraw = REDRAW_MOTION;
618 mutt_error _("You are on the last entry.");
621 void menu_prev_entry (MUTTMENU * menu)
625 menu->redraw = REDRAW_MOTION;
628 mutt_error _("You are on the first entry.");
631 static int default_color (int i __attribute__ ((unused)))
633 return ColorDefs[MT_COLOR_NORMAL];
636 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
638 char buf[LONG_STRING];
640 menu_make_entry (buf, sizeof (buf), m, n);
641 return (regexec (re, buf, 0, NULL, 0));
644 MUTTMENU *mutt_new_menu (void)
646 MUTTMENU *p = p_new(MUTTMENU, 1);
651 p->redraw = REDRAW_FULL;
652 p->pagelen = PAGELEN;
653 p->color = default_color;
654 p->search = menu_search_generic;
658 void mutt_menuDestroy (MUTTMENU ** p)
662 p_delete(&(*p)->searchBuf);
665 for (i = 0; i < (*p)->max; i++)
666 p_delete(&(*p)->dialog[i]);
668 p_delete(&(*p)->dialog);
674 #define M_SEARCH_UP 1
675 #define M_SEARCH_DOWN 2
677 static int menu_search (MUTTMENU * menu, int op)
682 char buf[SHORT_STRING];
684 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
685 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
686 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
687 _("Reverse search for: "),
688 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
690 m_strreplace(&menu->searchBuf, buf);
691 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
694 if (!menu->searchBuf) {
695 mutt_error _("No search pattern.");
701 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
702 if (op == OP_SEARCH_OPPOSITE)
703 searchDir = -searchDir;
706 REGCOMP (&re, menu->searchBuf,
707 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
708 regerror (r, &re, buf, sizeof (buf));
710 mutt_error ("%s", buf);
714 r = menu->current + searchDir;
715 while (r >= 0 && r < menu->max) {
716 if (menu->search (menu, &re, r) == 0) {
725 mutt_error _("Not found.");
730 static int menu_dialog_translate_op (int i)
740 case OP_CURRENT_BOTTOM:
742 return OP_BOTTOM_PAGE;
743 case OP_CURRENT_MIDDLE:
744 return OP_MIDDLE_PAGE;
750 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
762 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
763 *ip = OP_MAX + (p - menu->keys + 1);
767 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
772 int menu_redraw (MUTTMENU * menu)
774 /* See if all or part of the screen needs to be updated. */
775 if (menu->redraw & REDRAW_FULL) {
776 menu_redraw_full (menu);
777 /* allow the caller to do any local configuration */
782 menu_check_recenter (menu);
784 if (menu->redraw & REDRAW_STATUS)
785 menu_redraw_status (menu);
786 if (menu->redraw & REDRAW_INDEX)
787 menu_redraw_index (menu);
788 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
789 menu_redraw_motion (menu);
790 else if (menu->redraw == REDRAW_CURRENT)
791 menu_redraw_current (menu);
794 menu_redraw_prompt (menu);
799 int mutt_menuLoop (MUTTMENU * menu)
804 if (option (OPTMENUCALLER)) {
805 unset_option (OPTMENUCALLER);
813 if (menu_redraw (menu) == OP_REDRAW)
816 menu->oldcurrent = menu->current;
818 if (option (OPTARROWCURSOR))
819 move (menu->current - menu->top + menu->offset, SW + 2);
820 else if (option (OPTBRAILLEFRIENDLY))
821 move (menu->current - menu->top + menu->offset, SW);
823 move (menu->current - menu->top + menu->offset, COLS - 1);
828 /* try to catch dialog keys before ops */
829 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
832 i = km_dokey (menu->menu);
833 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
835 mvaddstr (LINES - 1, 0, "Tag-");
837 i = km_dokey (menu->menu);
839 CLEARLINE (LINES - 1);
841 else if (i == OP_TAG_PREFIX) {
842 mutt_error _("No tagged entries.");
846 else { /* None tagged, OP_TAG_PREFIX_COND */
850 while (UngetCount > 0) {
852 if (tmp.op == OP_END_COND)
855 mutt_message _("Nothing to do.");
860 else if (menu->tagged && option (OPTAUTOTAG))
867 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
869 mutt_resize_screen ();
870 menu->redraw = REDRAW_FULL;
872 clearok (stdscr, TRUE); /*force complete redraw */
882 /* Convert menubar movement to scrolling */
884 i = menu_dialog_translate_op (i);
888 menu_next_entry (menu);
891 menu_prev_entry (menu);
894 menu_half_down (menu);
900 menu_next_page (menu);
903 menu_prev_page (menu);
906 menu_next_line (menu);
909 menu_prev_line (menu);
912 menu_first_entry (menu);
915 menu_last_entry (menu);
918 menu_top_page (menu);
921 menu_middle_page (menu);
924 menu_bottom_page (menu);
927 menu_current_top (menu);
929 case OP_CURRENT_MIDDLE:
930 menu_current_middle (menu);
932 case OP_CURRENT_BOTTOM:
933 menu_current_bottom (menu);
936 case OP_SEARCH_REVERSE:
938 case OP_SEARCH_OPPOSITE:
939 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
940 menu->oldcurrent = menu->current;
941 if ((menu->current = menu_search (menu, i)) != -1)
942 menu->redraw = REDRAW_MOTION;
944 menu->current = menu->oldcurrent;
947 mutt_error _("Search is not implemented for this menu.");
952 mutt_error (_("Jumping is not implemented for dialogs."));
958 case OP_ENTER_COMMAND:
959 CurrentMenu = menu->menu;
960 mutt_enter_command ();
961 if (option (OPTFORCEREDRAWINDEX)) {
962 menu->redraw = REDRAW_FULL;
963 unset_option (OPTFORCEREDRAWINDEX);
964 unset_option (OPTFORCEREDRAWPAGER);
969 if (menu->tag && !menu->dialog) {
970 if (menu->tagprefix && !option (OPTAUTOTAG)) {
971 for (i = 0; i < menu->max; i++)
972 menu->tagged += menu->tag (menu, i, 0);
973 menu->redraw = REDRAW_INDEX;
975 else if (menu->max) {
976 int t = menu->tag (menu, menu->current, -1);
979 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
981 menu->redraw = REDRAW_MOTION_RESYNCH;
984 menu->redraw = REDRAW_CURRENT;
987 mutt_error _("No entries.");
990 mutt_error _("Tagging is not supported.");
993 case OP_SHELL_ESCAPE:
994 mutt_shell_escape ();
995 MAYBE_REDRAW (menu->redraw);
1002 case OP_REBUILD_CACHE:
1003 mx_rebuild_cache ();
1007 clearok (stdscr, TRUE);
1008 menu->redraw = REDRAW_FULL;
1012 mutt_help (menu->menu);
1013 menu->redraw = REDRAW_FULL;
1017 km_error_key (menu->menu);