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 waddch (stdscr, ACS_LLCORNER);
44 waddch (stdscr, ACS_ULCORNER);
47 waddch (stdscr, ACS_LTEE);
50 waddch (stdscr, ACS_HLINE);
53 waddch (stdscr, ACS_VLINE);
56 waddch (stdscr, ACS_TTEE);
59 waddch (stdscr, ACS_BTEE);
68 waddch (stdscr, '*'); /* fake thread indicator */
83 wattrset (stdscr, attr);
85 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) != (size_t)-1) {
86 waddnstr (stdscr, (char *) s, k);
94 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
97 m_strcpy(s, l, menu->dialog[i]);
98 menu->current = -1; /* hide menubar */
101 menu->make_entry (s, l, menu, i);
104 static void menu_pad_string (char *s, size_t n)
106 int cols = COLS - SW;
107 char *tmpbuf = p_new(char, n);
109 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
111 snprintf (s, n, "%s", tmpbuf); /* overkill */
115 void menu_redraw_full (MUTTMENU * menu)
117 SETCOLOR (MT_COLOR_NORMAL);
118 /* wclear(stdscr) doesn't optimize screen redraws */
119 wmove (stdscr, 0, 0);
122 if (option (OPTHELP)) {
123 SETCOLOR (MT_COLOR_STATUS);
124 wmove (stdscr, option (OPTSTATUSONTOP) ? LINES - 2 : 0, SW);
125 mutt_paddstr (COLS-SW, "");
126 SETCOLOR (MT_COLOR_NORMAL);
128 menu->pagelen = LINES - 3;
131 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
132 menu->pagelen = LINES - 2;
135 sidebar_draw_frames();
139 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
142 void menu_redraw_status (MUTTMENU * menu)
146 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
147 SETCOLOR (MT_COLOR_STATUS);
148 wmove (stdscr, option (OPTSTATUSONTOP) ? 0 : LINES - 2, SW);
149 mutt_paddstr (COLS-SW, buf);
150 SETCOLOR (MT_COLOR_NORMAL);
151 menu->redraw &= ~REDRAW_STATUS;
152 sidebar_draw_frames();
155 void menu_redraw_index (MUTTMENU * menu)
160 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
162 menu_make_entry (buf, sizeof (buf), menu, i);
163 menu_pad_string (buf, sizeof (buf));
165 wattrset (stdscr, menu->color (i));
167 if (i == menu->current) {
168 ADDCOLOR (MT_COLOR_INDICATOR);
169 BKGDSET (MT_COLOR_INDICATOR);
172 CLEARLINE_WIN (i - menu->top + menu->offset);
174 wmove (stdscr, i - menu->top + menu->offset, SW);
175 print_enriched_string (menu->color (i), (unsigned char *) buf,
177 SETCOLOR (MT_COLOR_NORMAL);
178 BKGDSET (MT_COLOR_NORMAL);
180 CLEARLINE_WIN (i - menu->top + menu->offset);
188 void menu_redraw_motion (MUTTMENU * menu)
193 menu->redraw &= ~REDRAW_MOTION;
197 wmove (stdscr, menu->oldcurrent + menu->offset - menu->top, SW);
198 SETCOLOR (MT_COLOR_NORMAL);
199 BKGDSET (MT_COLOR_NORMAL);
201 /* erase the current indicator */
202 wattrset (stdscr, menu->color (menu->oldcurrent));
204 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
205 menu_pad_string (buf, sizeof (buf));
206 print_enriched_string (menu->color (menu->oldcurrent),
207 (unsigned char *) buf, 1);
209 /* now draw the new one to reflect the change */
210 menu_make_entry (buf, sizeof (buf), menu, menu->current);
211 menu_pad_string (buf, sizeof (buf));
212 wattrset (stdscr, menu->color (menu->current));
213 ADDCOLOR (MT_COLOR_INDICATOR);
214 BKGDSET (MT_COLOR_INDICATOR);
215 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
216 wmove (stdscr, menu->current + menu->offset - menu->top, SW);
217 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
219 SETCOLOR (MT_COLOR_NORMAL);
220 BKGDSET (MT_COLOR_NORMAL);
222 menu->redraw &= REDRAW_STATUS;
225 void menu_redraw_current (MUTTMENU * menu)
229 wmove (stdscr, menu->current + menu->offset - menu->top, SW);
230 menu_make_entry (buf, sizeof (buf), menu, menu->current);
231 menu_pad_string (buf, sizeof (buf));
233 wattrset (stdscr, menu->color (menu->current));
234 ADDCOLOR (MT_COLOR_INDICATOR);
235 BKGDSET (MT_COLOR_INDICATOR);
237 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
239 SETCOLOR (MT_COLOR_NORMAL);
240 BKGDSET (MT_COLOR_NORMAL);
242 menu->redraw &= REDRAW_STATUS;
245 static void menu_redraw_prompt (MUTTMENU * menu)
248 if (option (OPTMSGERR)) {
250 unset_option (OPTMSGERR);
256 SETCOLOR (MT_COLOR_NORMAL);
257 mvwaddstr (stdscr, LINES - 1, 0, menu->prompt);
262 void menu_check_recenter (MUTTMENU * menu)
264 int c = MIN (MenuContext, menu->pagelen / 2);
265 int old_top = menu->top;
267 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
268 if (menu->top != 0) {
270 set_option (OPTNEEDREDRAW);
273 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
274 if (menu->current < menu->top + c)
275 menu->top = menu->current - c;
276 else if (menu->current >= menu->top + menu->pagelen - c)
277 menu->top = menu->current - menu->pagelen + c + 1;
279 if (menu->current < menu->top + c)
280 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
281 else if ((menu->current >= menu->top + menu->pagelen - c))
282 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
286 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
287 menu->top = MIN (menu->top, menu->max - menu->pagelen);
288 menu->top = MAX (menu->top, 0);
290 if (menu->top != old_top)
291 menu->redraw |= REDRAW_INDEX;
294 void menu_jump (MUTTMENU * menu)
300 mutt_ungetch (LastKey, 0);
302 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
304 if (n >= 0 && n < menu->max) {
306 menu->redraw = REDRAW_MOTION;
309 mutt_error _("Invalid index number.");
313 mutt_error _("No entries.");
316 void menu_next_line (MUTTMENU * menu)
319 int c = MIN (MenuContext, menu->pagelen / 2);
321 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
322 || (menu->max > menu->pagelen
324 menu->max - menu->pagelen))) {
326 if (menu->current < menu->top + c && menu->current < menu->max - 1)
328 menu->redraw = REDRAW_INDEX;
331 mutt_error _("You cannot scroll down farther.");
334 mutt_error _("No entries.");
337 void menu_prev_line (MUTTMENU * menu)
340 int c = MIN (MenuContext, menu->pagelen / 2);
343 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
345 menu->redraw = REDRAW_INDEX;
348 mutt_error _("You cannot scroll up farther.");
352 * pageup: jumplen == -pagelen
353 * pagedown: jumplen == pagelen
354 * halfup: jumplen == -pagelen/2
355 * halfdown: jumplen == pagelen/2
357 #define DIRECTION ((neg * 2) + 1)
358 static void menu_length_jump (MUTTMENU *menu, int jumplen) {
359 int tmp, neg = (jumplen >= 0) ? 0 : -1;
360 int c = MIN (MenuContext, menu->pagelen / 2);
363 /* possible to scroll? */
364 if (DIRECTION * menu->top <
365 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
366 menu->top += jumplen;
368 /* jumped too long? */
369 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
372 /* need to move the cursor? */
374 (tmp = (menu->current - (menu->top +
375 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
376 menu->current -= tmp;
378 menu->redraw = REDRAW_INDEX;
380 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
381 menu->current += jumplen;
382 menu->redraw = REDRAW_MOTION;
385 mutt_error (neg ? _("You are on the first page.")
386 : _("You are on the last page."));
388 menu->current = MIN (menu->current, menu->max - 1);
389 menu->current = MAX (menu->current, 0);
392 mutt_error _("No entries.");
396 void menu_next_page (MUTTMENU *menu) {
397 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
400 void menu_prev_page (MUTTMENU *menu) {
401 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
404 void menu_half_down (MUTTMENU *menu) {
405 menu_length_jump (menu, menu->pagelen / 2);
408 void menu_half_up (MUTTMENU *menu) {
409 menu_length_jump (menu, 0 - menu->pagelen / 2);
412 void menu_top_page (MUTTMENU *menu) {
413 if (menu->current != menu->top) {
414 menu->current = menu->top;
415 menu->redraw = REDRAW_MOTION;
419 void menu_bottom_page (MUTTMENU *menu) {
421 menu->current = menu->top + menu->pagelen - 1;
422 if (menu->current > menu->max - 1)
423 menu->current = menu->max - 1;
424 menu->redraw = REDRAW_MOTION;
427 mutt_error _("No entries.");
430 void menu_middle_page (MUTTMENU *menu) {
434 i = menu->top + menu->pagelen;
435 if (i > menu->max - 1)
437 menu->current = menu->top + (i - menu->top) / 2;
438 menu->redraw = REDRAW_MOTION;
441 mutt_error _("No entries.");
444 void menu_first_entry (MUTTMENU *menu) {
447 menu->redraw = REDRAW_MOTION;
450 mutt_error _("No entries.");
453 void menu_last_entry (MUTTMENU *menu) {
455 menu->current = menu->max - 1;
456 menu->redraw = REDRAW_MOTION;
459 mutt_error _("No entries.");
462 void menu_current_top (MUTTMENU * menu)
465 menu->top = menu->current;
466 menu->redraw = REDRAW_INDEX;
469 mutt_error _("No entries.");
472 void menu_current_middle (MUTTMENU * menu)
475 menu->top = menu->current - menu->pagelen / 2;
478 menu->redraw = REDRAW_INDEX;
481 mutt_error _("No entries.");
484 void menu_current_bottom (MUTTMENU * menu)
487 menu->top = menu->current - menu->pagelen + 1;
490 menu->redraw = REDRAW_INDEX;
493 mutt_error _("No entries.");
496 static void menu_next_entry (MUTTMENU * menu)
498 if (menu->current < menu->max - 1) {
500 menu->redraw = REDRAW_MOTION;
503 mutt_error _("You are on the last entry.");
506 static void menu_prev_entry (MUTTMENU * menu)
510 menu->redraw = REDRAW_MOTION;
513 mutt_error _("You are on the first entry.");
516 static int default_color (int i __attribute__ ((unused)))
518 return ColorDefs[MT_COLOR_NORMAL];
521 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
523 char buf[LONG_STRING];
525 menu_make_entry (buf, sizeof (buf), m, n);
526 return (regexec (re, buf, 0, NULL, 0));
529 MUTTMENU *mutt_new_menu (void)
531 MUTTMENU *p = p_new(MUTTMENU, 1);
536 p->redraw = REDRAW_FULL;
537 p->pagelen = PAGELEN;
538 p->color = default_color;
539 p->search = menu_search_generic;
543 void mutt_menuDestroy (MUTTMENU ** p)
547 p_delete(&(*p)->searchBuf);
550 for (i = 0; i < (*p)->max; i++)
551 p_delete(&(*p)->dialog[i]);
553 p_delete(&(*p)->dialog);
559 #define M_SEARCH_UP 1
560 #define M_SEARCH_DOWN 2
562 static int menu_search (MUTTMENU * menu, int op)
569 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
570 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
571 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
572 _("Reverse search for: "),
573 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
575 m_strreplace(&menu->searchBuf, buf);
576 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
579 if (!menu->searchBuf) {
580 mutt_error _("No search pattern.");
586 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
587 if (op == OP_SEARCH_OPPOSITE)
588 searchDir = -searchDir;
591 REGCOMP (&re, menu->searchBuf,
592 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
593 regerror (r, &re, buf, sizeof (buf));
595 mutt_error ("%s", buf);
599 r = menu->current + searchDir;
600 while (r >= 0 && r < menu->max) {
601 if (menu->search (menu, &re, r) == 0) {
610 mutt_error _("Not found.");
615 static int menu_dialog_translate_op (int i)
625 case OP_CURRENT_BOTTOM:
627 return OP_BOTTOM_PAGE;
628 case OP_CURRENT_MIDDLE:
629 return OP_MIDDLE_PAGE;
635 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
647 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
648 *ip = OP_MAX + (p - menu->keys + 1);
652 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
657 int menu_redraw (MUTTMENU * menu)
659 /* See if all or part of the screen needs to be updated. */
660 if (menu->redraw & REDRAW_FULL) {
661 menu_redraw_full (menu);
662 /* allow the caller to do any local configuration */
667 menu_check_recenter (menu);
669 if (menu->redraw & REDRAW_STATUS)
670 menu_redraw_status (menu);
671 if (menu->redraw & REDRAW_INDEX)
672 menu_redraw_index (menu);
673 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
674 menu_redraw_motion (menu);
675 else if (menu->redraw == REDRAW_CURRENT)
676 menu_redraw_current (menu);
679 menu_redraw_prompt (menu);
684 int mutt_menuLoop (MUTTMENU * menu)
689 if (option (OPTMENUCALLER)) {
690 unset_option (OPTMENUCALLER);
698 if (menu_redraw (menu) == OP_REDRAW)
701 menu->oldcurrent = menu->current;
703 if (option (OPTBRAILLEFRIENDLY))
704 wmove (stdscr, menu->current - menu->top + menu->offset, SW);
706 wmove (stdscr, menu->current - menu->top + menu->offset, COLS - 1);
711 /* try to catch dialog keys before ops */
712 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
715 i = km_dokey (menu->menu);
716 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
718 mvwaddstr (stdscr, LINES - 1, 0, "Tag-");
720 i = km_dokey (menu->menu);
722 CLEARLINE (LINES - 1);
724 else if (i == OP_TAG_PREFIX) {
725 mutt_error _("No tagged entries.");
729 else { /* None tagged, OP_TAG_PREFIX_COND */
733 while (UngetCount > 0) {
735 if (tmp.op == OP_END_COND)
738 mutt_message _("Nothing to do.");
743 else if (menu->tagged && option (OPTAUTOTAG))
751 mutt_resize_screen ();
752 menu->redraw = REDRAW_FULL;
754 clearok (stdscr, TRUE); /*force complete redraw */
763 /* Convert menubar movement to scrolling */
765 i = menu_dialog_translate_op (i);
769 menu_next_entry (menu);
772 menu_prev_entry (menu);
775 menu_half_down (menu);
781 menu_next_page (menu);
784 menu_prev_page (menu);
787 menu_next_line (menu);
790 menu_prev_line (menu);
793 menu_first_entry (menu);
796 menu_last_entry (menu);
799 menu_top_page (menu);
802 menu_middle_page (menu);
805 menu_bottom_page (menu);
808 menu_current_top (menu);
810 case OP_CURRENT_MIDDLE:
811 menu_current_middle (menu);
813 case OP_CURRENT_BOTTOM:
814 menu_current_bottom (menu);
817 case OP_SEARCH_REVERSE:
819 case OP_SEARCH_OPPOSITE:
820 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
821 menu->oldcurrent = menu->current;
822 if ((menu->current = menu_search (menu, i)) != -1)
823 menu->redraw = REDRAW_MOTION;
825 menu->current = menu->oldcurrent;
828 mutt_error _("Search is not implemented for this menu.");
833 mutt_error (_("Jumping is not implemented for dialogs."));
839 case OP_ENTER_COMMAND:
840 CurrentMenu = menu->menu;
841 mutt_enter_command ();
842 if (option (OPTFORCEREDRAWINDEX)) {
843 menu->redraw = REDRAW_FULL;
844 unset_option (OPTFORCEREDRAWINDEX);
845 unset_option (OPTFORCEREDRAWPAGER);
850 if (menu->tag && !menu->dialog) {
851 if (menu->tagprefix && !option (OPTAUTOTAG)) {
852 for (i = 0; i < menu->max; i++)
853 menu->tagged += menu->tag (menu, i, 0);
854 menu->redraw = REDRAW_INDEX;
856 else if (menu->max) {
857 int t = menu->tag (menu, menu->current, -1);
860 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
862 menu->redraw = REDRAW_MOTION_RESYNCH;
865 menu->redraw = REDRAW_CURRENT;
868 mutt_error _("No entries.");
871 mutt_error _("Tagging is not supported.");
874 case OP_SHELL_ESCAPE:
875 mutt_shell_escape ();
876 MAYBE_REDRAW (menu->redraw);
884 clearok (stdscr, TRUE);
885 menu->redraw = REDRAW_FULL;
889 mutt_help (menu->menu);
890 menu->redraw = REDRAW_FULL;
894 km_error_key (menu->menu);