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>
17 #include <imap/imap.h>
19 extern size_t UngetCount;
21 static void print_enriched_string (int attr, unsigned char *s, int do_color)
25 size_t n = m_strlen((char *) s);
30 if (*s < M_TREE_MAX) {
32 SETCOLOR(main_w, MT_COLOR_TREE);
33 while (*s && *s < M_TREE_MAX) {
36 waddch (main_w, ACS_LLCORNER);
39 waddch (main_w, ACS_ULCORNER);
42 waddch (main_w, ACS_LTEE);
45 waddch (main_w, ACS_HLINE);
48 waddch (main_w, ACS_VLINE);
51 waddch (main_w, ACS_TTEE);
54 waddch (main_w, ACS_BTEE);
63 waddch (main_w, '*'); /* fake thread indicator */
78 wattrset (main_w, attr);
80 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) != (size_t)-1) {
81 waddnstr (main_w, (char *) s, k);
89 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
92 m_strcpy(s, l, menu->dialog[i]);
93 menu->current = -1; /* hide menubar */
96 menu->make_entry (s, l, menu, i);
99 static void menu_pad_string (char *s, size_t n)
101 int cols = getmaxx(main_w);
102 char *tmpbuf = p_new(char, n);
104 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
106 snprintf (s, n, "%s", tmpbuf); /* overkill */
110 void menu_redraw_full (MUTTMENU * menu)
112 SETCOLOR(main_w, MT_COLOR_NORMAL);
114 SETCOLOR(main_w, MT_COLOR_SIDEBAR);
115 mvwhline(main_w, LINES - 2, 0, ACS_HLINE, getmaxx(main_w));
116 SETCOLOR(main_w, MT_COLOR_NORMAL);
118 menu->pagelen = LINES - 3;
121 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
124 void menu_redraw_status (MUTTMENU * menu)
128 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
129 SETCOLOR(main_w, MT_COLOR_STATUS);
130 wmove (main_w, 0, 0);
131 mutt_paddstr (main_w, getmaxx(main_w), buf);
132 SETCOLOR(main_w, MT_COLOR_NORMAL);
133 menu->redraw &= ~REDRAW_STATUS;
136 void menu_redraw_index (MUTTMENU * menu)
141 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
143 menu_make_entry (buf, sizeof (buf), menu, i);
144 menu_pad_string (buf, sizeof (buf));
146 wattrset (main_w, menu->color (i));
148 if (i == menu->current) {
149 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
150 BKGDSET(main_w, MT_COLOR_INDICATOR);
153 CLEARLINE(main_w, i - menu->top + menu->offset);
155 wmove (main_w, i - menu->top + menu->offset, 0);
156 print_enriched_string (menu->color (i), (unsigned char *) buf,
158 SETCOLOR(main_w, MT_COLOR_NORMAL);
159 BKGDSET(main_w, MT_COLOR_NORMAL);
161 CLEARLINE(main_w, i - menu->top + menu->offset);
169 void menu_redraw_motion (MUTTMENU * menu)
174 menu->redraw &= ~REDRAW_MOTION;
178 SETCOLOR(main_w, MT_COLOR_NORMAL);
179 BKGDSET(main_w, MT_COLOR_NORMAL);
181 /* erase the current indicator */
182 wattrset(main_w, menu->color(menu->oldcurrent));
183 wmove (main_w, menu->oldcurrent + menu->offset - menu->top, 0);
185 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
186 menu_pad_string (buf, sizeof (buf));
187 print_enriched_string (menu->color (menu->oldcurrent),
188 (unsigned char *) buf, 1);
190 /* now draw the new one to reflect the change */
191 menu_make_entry (buf, sizeof (buf), menu, menu->current);
192 menu_pad_string (buf, sizeof (buf));
193 wattrset (main_w, menu->color (menu->current));
194 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
195 BKGDSET(main_w, MT_COLOR_INDICATOR);
196 CLEARLINE(main_w, menu->current - menu->top + menu->offset);
197 wmove (main_w, menu->current + menu->offset - menu->top, 0);
198 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
200 SETCOLOR(main_w, MT_COLOR_NORMAL);
201 BKGDSET(main_w, MT_COLOR_NORMAL);
203 menu->redraw &= REDRAW_STATUS;
206 void menu_redraw_current (MUTTMENU * menu)
210 menu_make_entry (buf, sizeof (buf), menu, menu->current);
211 menu_pad_string (buf, sizeof (buf));
213 wattrset (main_w, menu->color (menu->current));
214 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
215 BKGDSET(main_w, MT_COLOR_INDICATOR);
216 wmove (main_w, menu->current + menu->offset - menu->top, 0);
218 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
220 SETCOLOR(main_w, MT_COLOR_NORMAL);
221 BKGDSET(main_w, MT_COLOR_NORMAL);
223 menu->redraw &= REDRAW_STATUS;
226 static void menu_redraw_prompt (MUTTMENU * menu)
229 if (option (OPTMSGERR)) {
231 unset_option (OPTMSGERR);
237 SETCOLOR(stdscr, MT_COLOR_NORMAL);
238 mvwaddstr(stdscr, LINES - 1, 0, menu->prompt);
243 void menu_check_recenter (MUTTMENU * menu)
245 int c = MIN (MenuContext, menu->pagelen / 2);
246 int old_top = menu->top;
248 if ((menu->pagelen <= 0) || (c < MenuContext)) {
249 if (menu->current < menu->top + c)
250 menu->top = menu->current - c;
251 else if (menu->current >= menu->top + menu->pagelen - c)
252 menu->top = menu->current - menu->pagelen + c + 1;
254 if (menu->current < menu->top + c)
255 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
256 else if ((menu->current >= menu->top + menu->pagelen - c))
257 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
260 menu->top = MAX (menu->top, 0);
262 if (menu->top != old_top)
263 menu->redraw |= REDRAW_INDEX;
266 void menu_jump (MUTTMENU * menu)
272 mutt_ungetch (LastKey, 0);
274 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
276 if (n >= 0 && n < menu->max) {
278 menu->redraw = REDRAW_MOTION;
281 mutt_error _("Invalid index number.");
285 mutt_error _("No entries.");
288 void menu_next_line (MUTTMENU * menu)
291 int c = MIN (MenuContext, menu->pagelen / 2);
293 if (menu->top + 1 < menu->max - c) {
295 if (menu->current < menu->top + c && menu->current < menu->max - 1)
297 menu->redraw = REDRAW_INDEX;
299 mutt_error _("You cannot scroll down farther.");
302 mutt_error _("No entries.");
306 void menu_prev_line (MUTTMENU * menu)
309 int c = MIN (MenuContext, menu->pagelen / 2);
312 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
314 menu->redraw = REDRAW_INDEX;
317 mutt_error _("You cannot scroll up farther.");
321 * pageup: jumplen == -pagelen
322 * pagedown: jumplen == pagelen
323 * halfup: jumplen == -pagelen/2
324 * halfdown: jumplen == pagelen/2
326 #define DIRECTION ((neg * 2) + 1)
327 static void menu_length_jump (MUTTMENU *menu, int jumplen) {
328 int tmp, neg = (jumplen >= 0) ? 0 : -1;
329 int c = MIN (MenuContext, menu->pagelen / 2);
332 /* possible to scroll? */
333 if (DIRECTION * menu->top <
334 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
335 menu->top += jumplen;
337 /* jumped too long? */
338 if (neg && DIRECTION * menu->top > tmp)
341 /* need to move the cursor? */
343 (tmp = (menu->current - (menu->top +
344 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
345 menu->current -= tmp;
347 menu->redraw = REDRAW_INDEX;
349 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
350 menu->current += jumplen;
351 menu->redraw = REDRAW_MOTION;
354 mutt_error (neg ? _("You are on the first page.")
355 : _("You are on the last page."));
357 menu->current = MIN (menu->current, menu->max - 1);
358 menu->current = MAX (menu->current, 0);
361 mutt_error _("No entries.");
365 void menu_next_page (MUTTMENU *menu) {
366 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
369 void menu_prev_page (MUTTMENU *menu) {
370 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
373 void menu_half_down (MUTTMENU *menu) {
374 menu_length_jump (menu, menu->pagelen / 2);
377 void menu_half_up (MUTTMENU *menu) {
378 menu_length_jump (menu, 0 - menu->pagelen / 2);
381 void menu_top_page (MUTTMENU *menu) {
382 if (menu->current != menu->top) {
383 menu->current = menu->top;
384 menu->redraw = REDRAW_MOTION;
388 void menu_bottom_page (MUTTMENU *menu) {
390 menu->current = menu->top + menu->pagelen - 1;
391 if (menu->current > menu->max - 1)
392 menu->current = menu->max - 1;
393 menu->redraw = REDRAW_MOTION;
396 mutt_error _("No entries.");
399 void menu_middle_page (MUTTMENU *menu) {
403 i = menu->top + menu->pagelen;
404 if (i > menu->max - 1)
406 menu->current = menu->top + (i - menu->top) / 2;
407 menu->redraw = REDRAW_MOTION;
410 mutt_error _("No entries.");
413 void menu_first_entry (MUTTMENU *menu) {
416 menu->redraw = REDRAW_MOTION;
419 mutt_error _("No entries.");
422 void menu_last_entry (MUTTMENU *menu) {
424 menu->current = menu->max - 1;
425 menu->redraw = REDRAW_MOTION;
428 mutt_error _("No entries.");
431 void menu_current_top (MUTTMENU * menu)
434 menu->top = menu->current;
435 menu->redraw = REDRAW_INDEX;
438 mutt_error _("No entries.");
441 void menu_current_middle (MUTTMENU * menu)
444 menu->top = menu->current - menu->pagelen / 2;
447 menu->redraw = REDRAW_INDEX;
450 mutt_error _("No entries.");
453 void menu_current_bottom (MUTTMENU * menu)
456 menu->top = menu->current - menu->pagelen + 1;
459 menu->redraw = REDRAW_INDEX;
462 mutt_error _("No entries.");
465 static void menu_next_entry (MUTTMENU * menu)
467 if (menu->current < menu->max - 1) {
469 menu->redraw = REDRAW_MOTION;
472 mutt_error _("You are on the last entry.");
475 static void menu_prev_entry (MUTTMENU * menu)
479 menu->redraw = REDRAW_MOTION;
482 mutt_error _("You are on the first entry.");
485 static int default_color (int i __attribute__ ((unused)))
487 return ColorDefs[MT_COLOR_NORMAL];
490 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
492 char buf[LONG_STRING];
494 menu_make_entry (buf, sizeof (buf), m, n);
495 return (regexec (re, buf, 0, NULL, 0));
498 MUTTMENU *mutt_new_menu (void)
500 MUTTMENU *p = p_new(MUTTMENU, 1);
505 p->redraw = REDRAW_FULL;
506 p->pagelen = LINES - 3;
507 p->color = default_color;
508 p->search = menu_search_generic;
512 void mutt_menuDestroy (MUTTMENU ** p)
516 p_delete(&(*p)->searchBuf);
519 for (i = 0; i < (*p)->max; i++)
520 p_delete(&(*p)->dialog[i]);
522 p_delete(&(*p)->dialog);
528 #define M_SEARCH_UP 1
529 #define M_SEARCH_DOWN 2
531 static int menu_search (MUTTMENU * menu, int op)
538 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
539 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
540 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
541 _("Reverse search for: "),
542 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
544 m_strreplace(&menu->searchBuf, buf);
545 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
548 if (!menu->searchBuf) {
549 mutt_error _("No search pattern.");
555 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
556 if (op == OP_SEARCH_OPPOSITE)
557 searchDir = -searchDir;
560 REGCOMP (&re, menu->searchBuf,
561 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
562 regerror (r, &re, buf, sizeof (buf));
564 mutt_error ("%s", buf);
568 r = menu->current + searchDir;
569 while (r >= 0 && r < menu->max) {
570 if (menu->search (menu, &re, r) == 0) {
579 mutt_error _("Not found.");
584 static int menu_dialog_translate_op (int i)
594 case OP_CURRENT_BOTTOM:
596 return OP_BOTTOM_PAGE;
597 case OP_CURRENT_MIDDLE:
598 return OP_MIDDLE_PAGE;
604 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
616 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
617 *ip = OP_MAX + (p - menu->keys + 1);
621 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
626 int menu_redraw (MUTTMENU * menu)
628 /* See if all or part of the screen needs to be updated. */
629 if (menu->redraw & REDRAW_FULL) {
630 menu_redraw_full (menu);
631 /* allow the caller to do any local configuration */
636 menu_check_recenter (menu);
638 if (menu->redraw & REDRAW_STATUS)
639 menu_redraw_status (menu);
640 if (menu->redraw & REDRAW_INDEX)
641 menu_redraw_index (menu);
642 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
643 menu_redraw_motion (menu);
644 else if (menu->redraw == REDRAW_CURRENT)
645 menu_redraw_current (menu);
648 menu_redraw_prompt (menu);
653 int mutt_menuLoop (MUTTMENU * menu)
658 if (option (OPTMENUCALLER)) {
659 unset_option (OPTMENUCALLER);
667 if (menu_redraw (menu) == OP_REDRAW)
670 menu->oldcurrent = menu->current;
673 /* try to catch dialog keys before ops */
674 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
677 i = km_dokey (menu->menu);
678 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
680 mvwaddstr(stdscr, LINES - 1, 0, "Tag-");
682 i = km_dokey (menu->menu);
684 CLEARLINE(stdscr, LINES - 1);
686 else if (i == OP_TAG_PREFIX) {
687 mutt_error _("No tagged entries.");
691 else { /* None tagged, OP_TAG_PREFIX_COND */
695 while (UngetCount > 0) {
697 if (tmp.op == OP_END_COND)
700 mutt_message _("Nothing to do.");
705 else if (menu->tagged && option (OPTAUTOTAG))
714 menu->redraw = REDRAW_FULL;
723 /* Convert menubar movement to scrolling */
725 i = menu_dialog_translate_op (i);
729 menu_next_entry (menu);
732 menu_prev_entry (menu);
735 menu_half_down (menu);
741 menu_next_page (menu);
744 menu_prev_page (menu);
747 menu_next_line (menu);
750 menu_prev_line (menu);
753 menu_first_entry (menu);
756 menu_last_entry (menu);
759 menu_top_page (menu);
762 menu_middle_page (menu);
765 menu_bottom_page (menu);
768 menu_current_top (menu);
770 case OP_CURRENT_MIDDLE:
771 menu_current_middle (menu);
773 case OP_CURRENT_BOTTOM:
774 menu_current_bottom (menu);
777 case OP_SEARCH_REVERSE:
779 case OP_SEARCH_OPPOSITE:
780 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
781 menu->oldcurrent = menu->current;
782 if ((menu->current = menu_search (menu, i)) != -1)
783 menu->redraw = REDRAW_MOTION;
785 menu->current = menu->oldcurrent;
788 mutt_error _("Search is not implemented for this menu.");
793 mutt_error (_("Jumping is not implemented for dialogs."));
799 case OP_ENTER_COMMAND:
800 CurrentMenu = menu->menu;
801 mutt_enter_command ();
802 if (option (OPTFORCEREDRAWINDEX)) {
803 menu->redraw = REDRAW_FULL;
804 unset_option (OPTFORCEREDRAWINDEX);
805 unset_option (OPTFORCEREDRAWPAGER);
810 if (menu->tag && !menu->dialog) {
811 if (menu->tagprefix && !option (OPTAUTOTAG)) {
812 for (i = 0; i < menu->max; i++)
813 menu->tagged += menu->tag (menu, i, 0);
814 menu->redraw = REDRAW_INDEX;
816 else if (menu->max) {
817 int t = menu->tag (menu, menu->current, -1);
820 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
822 menu->redraw = REDRAW_MOTION_RESYNCH;
825 menu->redraw = REDRAW_CURRENT;
828 mutt_error _("No entries.");
831 mutt_error _("Tagging is not supported.");
834 case OP_SHELL_ESCAPE:
835 mutt_shell_escape ();
836 MAYBE_REDRAW (menu->redraw);
840 clearok (main_w, TRUE);
841 menu->redraw = REDRAW_FULL;
845 mutt_help (menu->menu);
846 menu->redraw = REDRAW_FULL;
850 km_error_key (menu->menu);