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-ui/lib-ui.h>
18 #include <imap/imap.h>
19 #include <lib-ui/sidebar.h>
21 extern size_t UngetCount;
23 static void print_enriched_string (int attr, unsigned char *s, int do_color)
27 size_t n = m_strlen((char *) s);
32 if (*s < M_TREE_MAX) {
34 SETCOLOR(main_w, MT_COLOR_TREE);
35 while (*s && *s < M_TREE_MAX) {
38 waddch (main_w, ACS_LLCORNER);
41 waddch (main_w, ACS_ULCORNER);
44 waddch (main_w, ACS_LTEE);
47 waddch (main_w, ACS_HLINE);
50 waddch (main_w, ACS_VLINE);
53 waddch (main_w, ACS_TTEE);
56 waddch (main_w, ACS_BTEE);
65 waddch (main_w, '*'); /* fake thread indicator */
80 wattrset (main_w, attr);
82 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) != (size_t)-1) {
83 waddnstr (main_w, (char *) s, k);
91 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
94 m_strcpy(s, l, menu->dialog[i]);
95 menu->current = -1; /* hide menubar */
98 menu->make_entry (s, l, menu, i);
101 static void menu_pad_string (char *s, size_t n)
103 int cols = getmaxx(main_w);
104 char *tmpbuf = p_new(char, n);
106 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
108 snprintf (s, n, "%s", tmpbuf); /* overkill */
112 void menu_redraw_full (MUTTMENU * menu)
114 SETCOLOR(main_w, MT_COLOR_NORMAL);
115 /* wclear() doesn't optimize screen redraws */
116 wmove (main_w, 0, 0);
119 SETCOLOR(main_w, MT_COLOR_STATUS);
120 wmove (main_w, option (OPTSTATUSONTOP) ? LINES - 2 : 0, 0);
121 mutt_paddstr (main_w, getmaxx(main_w), "");
122 SETCOLOR(main_w, MT_COLOR_NORMAL);
124 menu->pagelen = LINES - 3;
127 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
130 void menu_redraw_status (MUTTMENU * menu)
134 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
135 SETCOLOR(main_w, MT_COLOR_STATUS);
136 wmove (main_w, option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
137 mutt_paddstr (main_w, getmaxx(main_w), buf);
138 SETCOLOR(main_w, MT_COLOR_NORMAL);
139 menu->redraw &= ~REDRAW_STATUS;
142 void menu_redraw_index (MUTTMENU * menu)
147 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
149 menu_make_entry (buf, sizeof (buf), menu, i);
150 menu_pad_string (buf, sizeof (buf));
152 wattrset (main_w, menu->color (i));
154 if (i == menu->current) {
155 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
156 BKGDSET(main_w, MT_COLOR_INDICATOR);
159 CLEARLINE(main_w, i - menu->top + menu->offset);
161 wmove (main_w, i - menu->top + menu->offset, 0);
162 print_enriched_string (menu->color (i), (unsigned char *) buf,
164 SETCOLOR(main_w, MT_COLOR_NORMAL);
165 BKGDSET(main_w, MT_COLOR_NORMAL);
167 CLEARLINE(main_w, i - menu->top + menu->offset);
175 void menu_redraw_motion (MUTTMENU * menu)
180 menu->redraw &= ~REDRAW_MOTION;
184 wmove (main_w, menu->oldcurrent + menu->offset - menu->top, 0);
185 SETCOLOR(main_w, MT_COLOR_NORMAL);
186 BKGDSET(main_w, MT_COLOR_NORMAL);
188 /* erase the current indicator */
189 wattrset (main_w, menu->color (menu->oldcurrent));
191 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
192 menu_pad_string (buf, sizeof (buf));
193 print_enriched_string (menu->color (menu->oldcurrent),
194 (unsigned char *) buf, 1);
196 /* now draw the new one to reflect the change */
197 menu_make_entry (buf, sizeof (buf), menu, menu->current);
198 menu_pad_string (buf, sizeof (buf));
199 wattrset (main_w, menu->color (menu->current));
200 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
201 BKGDSET(main_w, MT_COLOR_INDICATOR);
202 CLEARLINE(main_w, menu->current - menu->top + menu->offset);
203 wmove (main_w, menu->current + menu->offset - menu->top, 0);
204 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
206 SETCOLOR(main_w, MT_COLOR_NORMAL);
207 BKGDSET(main_w, MT_COLOR_NORMAL);
209 menu->redraw &= REDRAW_STATUS;
212 void menu_redraw_current (MUTTMENU * menu)
216 wmove (main_w, menu->current + menu->offset - menu->top, 0);
217 menu_make_entry (buf, sizeof (buf), menu, menu->current);
218 menu_pad_string (buf, sizeof (buf));
220 wattrset (main_w, menu->color (menu->current));
221 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
222 BKGDSET(main_w, MT_COLOR_INDICATOR);
224 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
226 SETCOLOR(main_w, MT_COLOR_NORMAL);
227 BKGDSET(main_w, MT_COLOR_NORMAL);
229 menu->redraw &= REDRAW_STATUS;
232 static void menu_redraw_prompt (MUTTMENU * menu)
235 if (option (OPTMSGERR)) {
237 unset_option (OPTMSGERR);
243 SETCOLOR(main_w, MT_COLOR_NORMAL);
244 mvwaddstr (main_w, LINES - 1, 0, menu->prompt);
249 void menu_check_recenter (MUTTMENU * menu)
251 int c = MIN (MenuContext, menu->pagelen / 2);
252 int old_top = menu->top;
254 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
255 if (menu->top != 0) {
257 set_option (OPTNEEDREDRAW);
260 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
261 if (menu->current < menu->top + c)
262 menu->top = menu->current - c;
263 else if (menu->current >= menu->top + menu->pagelen - c)
264 menu->top = menu->current - menu->pagelen + c + 1;
266 if (menu->current < menu->top + c)
267 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
268 else if ((menu->current >= menu->top + menu->pagelen - c))
269 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
273 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
274 menu->top = MIN (menu->top, menu->max - menu->pagelen);
275 menu->top = MAX (menu->top, 0);
277 if (menu->top != old_top)
278 menu->redraw |= REDRAW_INDEX;
281 void menu_jump (MUTTMENU * menu)
287 mutt_ungetch (LastKey, 0);
289 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
291 if (n >= 0 && n < menu->max) {
293 menu->redraw = REDRAW_MOTION;
296 mutt_error _("Invalid index number.");
300 mutt_error _("No entries.");
303 void menu_next_line (MUTTMENU * menu)
306 int c = MIN (MenuContext, menu->pagelen / 2);
308 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
309 || (menu->max > menu->pagelen
311 menu->max - menu->pagelen))) {
313 if (menu->current < menu->top + c && menu->current < menu->max - 1)
315 menu->redraw = REDRAW_INDEX;
318 mutt_error _("You cannot scroll down farther.");
321 mutt_error _("No entries.");
324 void menu_prev_line (MUTTMENU * menu)
327 int c = MIN (MenuContext, menu->pagelen / 2);
330 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
332 menu->redraw = REDRAW_INDEX;
335 mutt_error _("You cannot scroll up farther.");
339 * pageup: jumplen == -pagelen
340 * pagedown: jumplen == pagelen
341 * halfup: jumplen == -pagelen/2
342 * halfdown: jumplen == pagelen/2
344 #define DIRECTION ((neg * 2) + 1)
345 static void menu_length_jump (MUTTMENU *menu, int jumplen) {
346 int tmp, neg = (jumplen >= 0) ? 0 : -1;
347 int c = MIN (MenuContext, menu->pagelen / 2);
350 /* possible to scroll? */
351 if (DIRECTION * menu->top <
352 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
353 menu->top += jumplen;
355 /* jumped too long? */
356 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
359 /* need to move the cursor? */
361 (tmp = (menu->current - (menu->top +
362 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
363 menu->current -= tmp;
365 menu->redraw = REDRAW_INDEX;
367 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
368 menu->current += jumplen;
369 menu->redraw = REDRAW_MOTION;
372 mutt_error (neg ? _("You are on the first page.")
373 : _("You are on the last page."));
375 menu->current = MIN (menu->current, menu->max - 1);
376 menu->current = MAX (menu->current, 0);
379 mutt_error _("No entries.");
383 void menu_next_page (MUTTMENU *menu) {
384 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
387 void menu_prev_page (MUTTMENU *menu) {
388 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
391 void menu_half_down (MUTTMENU *menu) {
392 menu_length_jump (menu, menu->pagelen / 2);
395 void menu_half_up (MUTTMENU *menu) {
396 menu_length_jump (menu, 0 - menu->pagelen / 2);
399 void menu_top_page (MUTTMENU *menu) {
400 if (menu->current != menu->top) {
401 menu->current = menu->top;
402 menu->redraw = REDRAW_MOTION;
406 void menu_bottom_page (MUTTMENU *menu) {
408 menu->current = menu->top + menu->pagelen - 1;
409 if (menu->current > menu->max - 1)
410 menu->current = menu->max - 1;
411 menu->redraw = REDRAW_MOTION;
414 mutt_error _("No entries.");
417 void menu_middle_page (MUTTMENU *menu) {
421 i = menu->top + menu->pagelen;
422 if (i > menu->max - 1)
424 menu->current = menu->top + (i - menu->top) / 2;
425 menu->redraw = REDRAW_MOTION;
428 mutt_error _("No entries.");
431 void menu_first_entry (MUTTMENU *menu) {
434 menu->redraw = REDRAW_MOTION;
437 mutt_error _("No entries.");
440 void menu_last_entry (MUTTMENU *menu) {
442 menu->current = menu->max - 1;
443 menu->redraw = REDRAW_MOTION;
446 mutt_error _("No entries.");
449 void menu_current_top (MUTTMENU * menu)
452 menu->top = menu->current;
453 menu->redraw = REDRAW_INDEX;
456 mutt_error _("No entries.");
459 void menu_current_middle (MUTTMENU * menu)
462 menu->top = menu->current - menu->pagelen / 2;
465 menu->redraw = REDRAW_INDEX;
468 mutt_error _("No entries.");
471 void menu_current_bottom (MUTTMENU * menu)
474 menu->top = menu->current - menu->pagelen + 1;
477 menu->redraw = REDRAW_INDEX;
480 mutt_error _("No entries.");
483 static void menu_next_entry (MUTTMENU * menu)
485 if (menu->current < menu->max - 1) {
487 menu->redraw = REDRAW_MOTION;
490 mutt_error _("You are on the last entry.");
493 static void menu_prev_entry (MUTTMENU * menu)
497 menu->redraw = REDRAW_MOTION;
500 mutt_error _("You are on the first entry.");
503 static int default_color (int i __attribute__ ((unused)))
505 return ColorDefs[MT_COLOR_NORMAL];
508 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
510 char buf[LONG_STRING];
512 menu_make_entry (buf, sizeof (buf), m, n);
513 return (regexec (re, buf, 0, NULL, 0));
516 MUTTMENU *mutt_new_menu (void)
518 MUTTMENU *p = p_new(MUTTMENU, 1);
523 p->redraw = REDRAW_FULL;
524 p->pagelen = LINES - 3;
525 p->color = default_color;
526 p->search = menu_search_generic;
530 void mutt_menuDestroy (MUTTMENU ** p)
534 p_delete(&(*p)->searchBuf);
537 for (i = 0; i < (*p)->max; i++)
538 p_delete(&(*p)->dialog[i]);
540 p_delete(&(*p)->dialog);
546 #define M_SEARCH_UP 1
547 #define M_SEARCH_DOWN 2
549 static int menu_search (MUTTMENU * menu, int op)
556 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
557 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
558 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
559 _("Reverse search for: "),
560 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
562 m_strreplace(&menu->searchBuf, buf);
563 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
566 if (!menu->searchBuf) {
567 mutt_error _("No search pattern.");
573 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
574 if (op == OP_SEARCH_OPPOSITE)
575 searchDir = -searchDir;
578 REGCOMP (&re, menu->searchBuf,
579 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
580 regerror (r, &re, buf, sizeof (buf));
582 mutt_error ("%s", buf);
586 r = menu->current + searchDir;
587 while (r >= 0 && r < menu->max) {
588 if (menu->search (menu, &re, r) == 0) {
597 mutt_error _("Not found.");
602 static int menu_dialog_translate_op (int i)
612 case OP_CURRENT_BOTTOM:
614 return OP_BOTTOM_PAGE;
615 case OP_CURRENT_MIDDLE:
616 return OP_MIDDLE_PAGE;
622 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
634 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
635 *ip = OP_MAX + (p - menu->keys + 1);
639 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
644 int menu_redraw (MUTTMENU * menu)
646 /* See if all or part of the screen needs to be updated. */
647 if (menu->redraw & REDRAW_FULL) {
648 menu_redraw_full (menu);
649 /* allow the caller to do any local configuration */
654 menu_check_recenter (menu);
656 if (menu->redraw & REDRAW_STATUS)
657 menu_redraw_status (menu);
658 if (menu->redraw & REDRAW_INDEX)
659 menu_redraw_index (menu);
660 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
661 menu_redraw_motion (menu);
662 else if (menu->redraw == REDRAW_CURRENT)
663 menu_redraw_current (menu);
666 menu_redraw_prompt (menu);
671 int mutt_menuLoop (MUTTMENU * menu)
676 if (option (OPTMENUCALLER)) {
677 unset_option (OPTMENUCALLER);
685 if (menu_redraw (menu) == OP_REDRAW)
688 menu->oldcurrent = menu->current;
691 /* try to catch dialog keys before ops */
692 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
695 i = km_dokey (menu->menu);
696 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
698 mvwaddstr (main_w, LINES - 1, 0, "Tag-");
700 i = km_dokey (menu->menu);
702 CLEARLINE(main_w, LINES - 1);
704 else if (i == OP_TAG_PREFIX) {
705 mutt_error _("No tagged entries.");
709 else { /* None tagged, OP_TAG_PREFIX_COND */
713 while (UngetCount > 0) {
715 if (tmp.op == OP_END_COND)
718 mutt_message _("Nothing to do.");
723 else if (menu->tagged && option (OPTAUTOTAG))
732 menu->redraw = REDRAW_FULL;
741 /* Convert menubar movement to scrolling */
743 i = menu_dialog_translate_op (i);
747 menu_next_entry (menu);
750 menu_prev_entry (menu);
753 menu_half_down (menu);
759 menu_next_page (menu);
762 menu_prev_page (menu);
765 menu_next_line (menu);
768 menu_prev_line (menu);
771 menu_first_entry (menu);
774 menu_last_entry (menu);
777 menu_top_page (menu);
780 menu_middle_page (menu);
783 menu_bottom_page (menu);
786 menu_current_top (menu);
788 case OP_CURRENT_MIDDLE:
789 menu_current_middle (menu);
791 case OP_CURRENT_BOTTOM:
792 menu_current_bottom (menu);
795 case OP_SEARCH_REVERSE:
797 case OP_SEARCH_OPPOSITE:
798 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
799 menu->oldcurrent = menu->current;
800 if ((menu->current = menu_search (menu, i)) != -1)
801 menu->redraw = REDRAW_MOTION;
803 menu->current = menu->oldcurrent;
806 mutt_error _("Search is not implemented for this menu.");
811 mutt_error (_("Jumping is not implemented for dialogs."));
817 case OP_ENTER_COMMAND:
818 CurrentMenu = menu->menu;
819 mutt_enter_command ();
820 if (option (OPTFORCEREDRAWINDEX)) {
821 menu->redraw = REDRAW_FULL;
822 unset_option (OPTFORCEREDRAWINDEX);
823 unset_option (OPTFORCEREDRAWPAGER);
828 if (menu->tag && !menu->dialog) {
829 if (menu->tagprefix && !option (OPTAUTOTAG)) {
830 for (i = 0; i < menu->max; i++)
831 menu->tagged += menu->tag (menu, i, 0);
832 menu->redraw = REDRAW_INDEX;
834 else if (menu->max) {
835 int t = menu->tag (menu, menu->current, -1);
838 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
840 menu->redraw = REDRAW_MOTION_RESYNCH;
843 menu->redraw = REDRAW_CURRENT;
846 mutt_error _("No entries.");
849 mutt_error _("Tagging is not supported.");
852 case OP_SHELL_ESCAPE:
853 mutt_shell_escape ();
854 MAYBE_REDRAW (menu->redraw);
862 clearok (main_w, TRUE);
863 menu->redraw = REDRAW_FULL;
867 mutt_help (menu->menu);
868 menu->redraw = REDRAW_FULL;
872 km_error_key (menu->menu);