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.
10 #include <lib-lib/lib-lib.h>
19 #include <imap/imap.h>
20 #include <lib-ui/sidebar.h>
22 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
24 extern size_t UngetCount;
26 static void print_enriched_string (int attr, unsigned char *s, int do_color)
30 size_t n = m_strlen((char *) s);
35 if (*s < M_TREE_MAX) {
37 SETCOLOR (MT_COLOR_TREE);
38 while (*s && *s < M_TREE_MAX) {
41 if (option (OPTASCIICHARS))
43 else if (Charset_is_utf8)
44 addstr ("\342\224\224"); /* WACS_LLCORNER */
49 if (option (OPTASCIICHARS))
51 else if (Charset_is_utf8)
52 addstr ("\342\224\214"); /* WACS_ULCORNER */
57 if (option (OPTASCIICHARS))
59 else if (Charset_is_utf8)
60 addstr ("\342\224\234"); /* WACS_LTEE */
65 if (option (OPTASCIICHARS))
67 else if (Charset_is_utf8)
68 addstr ("\342\224\200"); /* WACS_HLINE */
73 if (option (OPTASCIICHARS))
75 else if (Charset_is_utf8)
76 addstr ("\342\224\202"); /* WACS_VLINE */
81 if (option (OPTASCIICHARS))
83 else if (Charset_is_utf8)
84 addstr ("\342\224\254"); /* WACS_TTEE */
89 if (option (OPTASCIICHARS))
91 else if (Charset_is_utf8)
92 addstr ("\342\224\264"); /* WACS_BTEE */
103 addch ('*'); /* fake thread indicator */
120 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
121 addnstr ((char *) s, k);
129 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
132 m_strcpy(s, l, menu->dialog[i]);
133 menu->current = -1; /* hide menubar */
136 menu->make_entry (s, l, menu, i);
139 static void menu_pad_string (char *s, size_t n)
141 int shift = option (OPTARROWCURSOR) ? 3 : 0;
143 char *tmpbuf = p_new(char, n);
145 if (option (OPTMBOXPANE))
146 cols = COLS - shift - SidebarWidth;
149 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
151 snprintf (s, n, "%s", tmpbuf); /* overkill */
155 void menu_redraw_full (MUTTMENU * menu)
157 SETCOLOR (MT_COLOR_NORMAL);
158 /* clear() doesn't optimize screen redraws */
162 if (option (OPTHELP)) {
163 SETCOLOR (MT_COLOR_STATUS);
164 move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, SW);
165 mutt_paddstr (COLS-SW, menu->help);
166 SETCOLOR (MT_COLOR_NORMAL);
168 menu->pagelen = LINES - 3;
171 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
172 menu->pagelen = LINES - 2;
175 sidebar_draw_frames();
179 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
182 void menu_redraw_status (MUTTMENU * menu)
186 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
187 SETCOLOR (MT_COLOR_STATUS);
188 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, SW);
189 mutt_paddstr (COLS-SW, buf);
190 SETCOLOR (MT_COLOR_NORMAL);
191 menu->redraw &= ~REDRAW_STATUS;
192 sidebar_draw_frames();
195 void menu_redraw_index (MUTTMENU * menu)
200 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
202 menu_make_entry (buf, sizeof (buf), menu, i);
203 menu_pad_string (buf, sizeof (buf));
205 if (option (OPTARROWCURSOR)) {
206 attrset (menu->color (i));
207 CLEARLINE_WIN (i - menu->top + menu->offset);
209 if (i == menu->current) {
210 attrset (menu->color (i));
211 ADDCOLOR (MT_COLOR_INDICATOR);
212 BKGDSET (MT_COLOR_INDICATOR);
214 attrset (menu->color (i));
218 attrset (menu->color (i));
219 move (i - menu->top + menu->offset, SW);
223 print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
224 SETCOLOR (MT_COLOR_NORMAL);
225 BKGDSET (MT_COLOR_NORMAL);
228 attrset (menu->color (i));
230 if (i == menu->current) {
231 ADDCOLOR (MT_COLOR_INDICATOR);
232 BKGDSET (MT_COLOR_INDICATOR);
235 CLEARLINE_WIN (i - menu->top + menu->offset);
237 move (i - menu->top + menu->offset, SW);
238 print_enriched_string (menu->color (i), (unsigned char *) buf,
240 SETCOLOR (MT_COLOR_NORMAL);
241 BKGDSET (MT_COLOR_NORMAL);
245 CLEARLINE_WIN (i - menu->top + menu->offset);
252 void menu_redraw_motion (MUTTMENU * menu)
257 menu->redraw &= ~REDRAW_MOTION;
261 move (menu->oldcurrent + menu->offset - menu->top, SW);
262 SETCOLOR (MT_COLOR_NORMAL);
263 BKGDSET (MT_COLOR_NORMAL);
265 if (option (OPTARROWCURSOR)) {
266 /* clear the pointer */
267 attrset (menu->color (menu->oldcurrent));
270 if (menu->redraw & REDRAW_MOTION_RESYNCH) {
272 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
273 menu_pad_string (buf, sizeof (buf));
274 move (menu->oldcurrent + menu->offset - menu->top, SW + 3);
275 print_enriched_string (menu->color (menu->oldcurrent),
276 (unsigned char *) buf, 1);
277 SETCOLOR (MT_COLOR_NORMAL);
280 /* now draw it in the new location */
281 move (menu->current + menu->offset - menu->top, SW);
282 attrset (menu->color (menu->current));
283 ADDCOLOR (MT_COLOR_INDICATOR);
285 SETCOLOR (MT_COLOR_NORMAL);
288 /* erase the current indicator */
289 attrset (menu->color (menu->oldcurrent));
291 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
292 menu_pad_string (buf, sizeof (buf));
293 print_enriched_string (menu->color (menu->oldcurrent),
294 (unsigned char *) buf, 1);
296 /* now draw the new one to reflect the change */
297 menu_make_entry (buf, sizeof (buf), menu, menu->current);
298 menu_pad_string (buf, sizeof (buf));
299 attrset (menu->color (menu->current));
300 ADDCOLOR (MT_COLOR_INDICATOR);
301 BKGDSET (MT_COLOR_INDICATOR);
302 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
303 move (menu->current + menu->offset - menu->top, SW);
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, SW);
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 static 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 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
377 if (menu->current < menu->top + c)
378 menu->top = menu->current - c;
379 else if (menu->current >= menu->top + menu->pagelen - c)
380 menu->top = menu->current - menu->pagelen + c + 1;
382 if (menu->current < menu->top + c)
383 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
384 else if ((menu->current >= menu->top + menu->pagelen - c))
385 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
389 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
390 menu->top = MIN (menu->top, menu->max - menu->pagelen);
391 menu->top = MAX (menu->top, 0);
393 if (menu->top != old_top)
394 menu->redraw |= REDRAW_INDEX;
397 void menu_jump (MUTTMENU * menu)
400 char buf[SHORT_STRING];
403 mutt_ungetch (LastKey, 0);
405 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
407 if (n >= 0 && n < menu->max) {
409 menu->redraw = REDRAW_MOTION;
412 mutt_error _("Invalid index number.");
416 mutt_error _("No entries.");
419 void menu_next_line (MUTTMENU * menu)
422 int c = MIN (MenuContext, menu->pagelen / 2);
424 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
425 || (menu->max > menu->pagelen
427 menu->max - menu->pagelen))) {
429 if (menu->current < menu->top + c && menu->current < menu->max - 1)
431 menu->redraw = REDRAW_INDEX;
434 mutt_error _("You cannot scroll down farther.");
437 mutt_error _("No entries.");
440 void menu_prev_line (MUTTMENU * menu)
443 int c = MIN (MenuContext, menu->pagelen / 2);
446 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
448 menu->redraw = REDRAW_INDEX;
451 mutt_error _("You cannot scroll up farther.");
455 * pageup: jumplen == -pagelen
456 * pagedown: jumplen == pagelen
457 * halfup: jumplen == -pagelen/2
458 * halfdown: jumplen == pagelen/2
460 #define DIRECTION ((neg * 2) + 1)
461 static void menu_length_jump (MUTTMENU *menu, int jumplen) {
462 int tmp, neg = (jumplen >= 0) ? 0 : -1;
463 int c = MIN (MenuContext, menu->pagelen / 2);
466 /* possible to scroll? */
467 if (DIRECTION * menu->top <
468 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
469 menu->top += jumplen;
471 /* jumped too long? */
472 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
475 /* need to move the cursor? */
477 (tmp = (menu->current - (menu->top +
478 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
479 menu->current -= tmp;
481 menu->redraw = REDRAW_INDEX;
483 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
484 menu->current += jumplen;
485 menu->redraw = REDRAW_MOTION;
488 mutt_error (neg ? _("You are on the first page.")
489 : _("You are on the last page."));
491 menu->current = MIN (menu->current, menu->max - 1);
492 menu->current = MAX (menu->current, 0);
495 mutt_error _("No entries.");
499 void menu_next_page (MUTTMENU *menu) {
500 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
503 void menu_prev_page (MUTTMENU *menu) {
504 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
507 void menu_half_down (MUTTMENU *menu) {
508 menu_length_jump (menu, menu->pagelen / 2);
511 void menu_half_up (MUTTMENU *menu) {
512 menu_length_jump (menu, 0 - menu->pagelen / 2);
515 void menu_top_page (MUTTMENU *menu) {
516 if (menu->current != menu->top) {
517 menu->current = menu->top;
518 menu->redraw = REDRAW_MOTION;
522 void menu_bottom_page (MUTTMENU *menu) {
524 menu->current = menu->top + menu->pagelen - 1;
525 if (menu->current > menu->max - 1)
526 menu->current = menu->max - 1;
527 menu->redraw = REDRAW_MOTION;
530 mutt_error _("No entries.");
533 void menu_middle_page (MUTTMENU *menu) {
537 i = menu->top + menu->pagelen;
538 if (i > menu->max - 1)
540 menu->current = menu->top + (i - menu->top) / 2;
541 menu->redraw = REDRAW_MOTION;
544 mutt_error _("No entries.");
547 void menu_first_entry (MUTTMENU *menu) {
550 menu->redraw = REDRAW_MOTION;
553 mutt_error _("No entries.");
556 void menu_last_entry (MUTTMENU *menu) {
558 menu->current = menu->max - 1;
559 menu->redraw = REDRAW_MOTION;
562 mutt_error _("No entries.");
565 void menu_current_top (MUTTMENU * menu)
568 menu->top = menu->current;
569 menu->redraw = REDRAW_INDEX;
572 mutt_error _("No entries.");
575 void menu_current_middle (MUTTMENU * menu)
578 menu->top = menu->current - menu->pagelen / 2;
581 menu->redraw = REDRAW_INDEX;
584 mutt_error _("No entries.");
587 void menu_current_bottom (MUTTMENU * menu)
590 menu->top = menu->current - menu->pagelen + 1;
593 menu->redraw = REDRAW_INDEX;
596 mutt_error _("No entries.");
599 static void menu_next_entry (MUTTMENU * menu)
601 if (menu->current < menu->max - 1) {
603 menu->redraw = REDRAW_MOTION;
606 mutt_error _("You are on the last entry.");
609 static void menu_prev_entry (MUTTMENU * menu)
613 menu->redraw = REDRAW_MOTION;
616 mutt_error _("You are on the first entry.");
619 static int default_color (int i __attribute__ ((unused)))
621 return ColorDefs[MT_COLOR_NORMAL];
624 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
626 char buf[LONG_STRING];
628 menu_make_entry (buf, sizeof (buf), m, n);
629 return (regexec (re, buf, 0, NULL, 0));
632 MUTTMENU *mutt_new_menu (void)
634 MUTTMENU *p = p_new(MUTTMENU, 1);
639 p->redraw = REDRAW_FULL;
640 p->pagelen = PAGELEN;
641 p->color = default_color;
642 p->search = menu_search_generic;
646 void mutt_menuDestroy (MUTTMENU ** p)
650 p_delete(&(*p)->searchBuf);
653 for (i = 0; i < (*p)->max; i++)
654 p_delete(&(*p)->dialog[i]);
656 p_delete(&(*p)->dialog);
662 #define M_SEARCH_UP 1
663 #define M_SEARCH_DOWN 2
665 static int menu_search (MUTTMENU * menu, int op)
670 char buf[SHORT_STRING];
672 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
673 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
674 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
675 _("Reverse search for: "),
676 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
678 m_strreplace(&menu->searchBuf, buf);
679 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
682 if (!menu->searchBuf) {
683 mutt_error _("No search pattern.");
689 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
690 if (op == OP_SEARCH_OPPOSITE)
691 searchDir = -searchDir;
694 REGCOMP (&re, menu->searchBuf,
695 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
696 regerror (r, &re, buf, sizeof (buf));
698 mutt_error ("%s", buf);
702 r = menu->current + searchDir;
703 while (r >= 0 && r < menu->max) {
704 if (menu->search (menu, &re, r) == 0) {
713 mutt_error _("Not found.");
718 static int menu_dialog_translate_op (int i)
728 case OP_CURRENT_BOTTOM:
730 return OP_BOTTOM_PAGE;
731 case OP_CURRENT_MIDDLE:
732 return OP_MIDDLE_PAGE;
738 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
750 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
751 *ip = OP_MAX + (p - menu->keys + 1);
755 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
760 int menu_redraw (MUTTMENU * menu)
762 /* See if all or part of the screen needs to be updated. */
763 if (menu->redraw & REDRAW_FULL) {
764 menu_redraw_full (menu);
765 /* allow the caller to do any local configuration */
770 menu_check_recenter (menu);
772 if (menu->redraw & REDRAW_STATUS)
773 menu_redraw_status (menu);
774 if (menu->redraw & REDRAW_INDEX)
775 menu_redraw_index (menu);
776 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
777 menu_redraw_motion (menu);
778 else if (menu->redraw == REDRAW_CURRENT)
779 menu_redraw_current (menu);
782 menu_redraw_prompt (menu);
787 int mutt_menuLoop (MUTTMENU * menu)
792 if (option (OPTMENUCALLER)) {
793 unset_option (OPTMENUCALLER);
801 if (menu_redraw (menu) == OP_REDRAW)
804 menu->oldcurrent = menu->current;
806 if (option (OPTARROWCURSOR))
807 move (menu->current - menu->top + menu->offset, SW + 2);
808 else if (option (OPTBRAILLEFRIENDLY))
809 move (menu->current - menu->top + menu->offset, SW);
811 move (menu->current - menu->top + menu->offset, COLS - 1);
816 /* try to catch dialog keys before ops */
817 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
820 i = km_dokey (menu->menu);
821 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
823 mvaddstr (LINES - 1, 0, "Tag-");
825 i = km_dokey (menu->menu);
827 CLEARLINE (LINES - 1);
829 else if (i == OP_TAG_PREFIX) {
830 mutt_error _("No tagged entries.");
834 else { /* None tagged, OP_TAG_PREFIX_COND */
838 while (UngetCount > 0) {
840 if (tmp.op == OP_END_COND)
843 mutt_message _("Nothing to do.");
848 else if (menu->tagged && option (OPTAUTOTAG))
855 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
857 mutt_resize_screen ();
858 menu->redraw = REDRAW_FULL;
860 clearok (stdscr, TRUE); /*force complete redraw */
870 /* Convert menubar movement to scrolling */
872 i = menu_dialog_translate_op (i);
876 menu_next_entry (menu);
879 menu_prev_entry (menu);
882 menu_half_down (menu);
888 menu_next_page (menu);
891 menu_prev_page (menu);
894 menu_next_line (menu);
897 menu_prev_line (menu);
900 menu_first_entry (menu);
903 menu_last_entry (menu);
906 menu_top_page (menu);
909 menu_middle_page (menu);
912 menu_bottom_page (menu);
915 menu_current_top (menu);
917 case OP_CURRENT_MIDDLE:
918 menu_current_middle (menu);
920 case OP_CURRENT_BOTTOM:
921 menu_current_bottom (menu);
924 case OP_SEARCH_REVERSE:
926 case OP_SEARCH_OPPOSITE:
927 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
928 menu->oldcurrent = menu->current;
929 if ((menu->current = menu_search (menu, i)) != -1)
930 menu->redraw = REDRAW_MOTION;
932 menu->current = menu->oldcurrent;
935 mutt_error _("Search is not implemented for this menu.");
940 mutt_error (_("Jumping is not implemented for dialogs."));
946 case OP_ENTER_COMMAND:
947 CurrentMenu = menu->menu;
948 mutt_enter_command ();
949 if (option (OPTFORCEREDRAWINDEX)) {
950 menu->redraw = REDRAW_FULL;
951 unset_option (OPTFORCEREDRAWINDEX);
952 unset_option (OPTFORCEREDRAWPAGER);
957 if (menu->tag && !menu->dialog) {
958 if (menu->tagprefix && !option (OPTAUTOTAG)) {
959 for (i = 0; i < menu->max; i++)
960 menu->tagged += menu->tag (menu, i, 0);
961 menu->redraw = REDRAW_INDEX;
963 else if (menu->max) {
964 int t = menu->tag (menu, menu->current, -1);
967 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
969 menu->redraw = REDRAW_MOTION_RESYNCH;
972 menu->redraw = REDRAW_CURRENT;
975 mutt_error _("No entries.");
978 mutt_error _("Tagging is not supported.");
981 case OP_SHELL_ESCAPE:
982 mutt_shell_escape ();
983 MAYBE_REDRAW (menu->redraw);
990 case OP_REBUILD_CACHE:
995 clearok (stdscr, TRUE);
996 menu->redraw = REDRAW_FULL;
1000 mutt_help (menu->menu);
1001 menu->redraw = REDRAW_FULL;
1005 km_error_key (menu->menu);