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>
20 extern size_t UngetCount;
22 static void print_enriched_string (int attr, unsigned char *s, int do_color)
26 size_t n = m_strlen((char *) s);
31 if (*s < M_TREE_MAX) {
33 SETCOLOR(main_w, MT_COLOR_TREE);
34 while (*s && *s < M_TREE_MAX) {
37 waddch (main_w, ACS_LLCORNER);
40 waddch (main_w, ACS_ULCORNER);
43 waddch (main_w, ACS_LTEE);
46 waddch (main_w, ACS_HLINE);
49 waddch (main_w, ACS_VLINE);
52 waddch (main_w, ACS_TTEE);
55 waddch (main_w, ACS_BTEE);
64 waddch (main_w, '*'); /* fake thread indicator */
79 wattrset (main_w, attr);
81 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) != (size_t)-1) {
82 waddnstr (main_w, (char *) s, k);
90 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
93 m_strcpy(s, l, menu->dialog[i]);
94 menu->current = -1; /* hide menubar */
97 menu->make_entry (s, l, menu, i);
100 static void menu_pad_string (char *s, size_t n)
102 int cols = getmaxx(main_w);
103 char *tmpbuf = p_new(char, n);
105 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
107 snprintf (s, n, "%s", tmpbuf); /* overkill */
111 void menu_redraw_full (MUTTMENU * menu)
113 SETCOLOR(main_w, MT_COLOR_NORMAL);
115 SETCOLOR(main_w, MT_COLOR_SIDEBAR);
116 mvwhline(main_w, LINES - 2, 0, ACS_HLINE, getmaxx(main_w));
117 SETCOLOR(main_w, MT_COLOR_NORMAL);
119 menu->pagelen = LINES - 3;
122 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
125 void menu_redraw_status (MUTTMENU * menu)
129 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
130 SETCOLOR(main_w, MT_COLOR_STATUS);
131 wmove (main_w, 0, 0);
132 mutt_paddstr (main_w, getmaxx(main_w), buf);
133 SETCOLOR(main_w, MT_COLOR_NORMAL);
134 menu->redraw &= ~REDRAW_STATUS;
137 void menu_redraw_index (MUTTMENU * menu)
142 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
144 menu_make_entry (buf, sizeof (buf), menu, i);
145 menu_pad_string (buf, sizeof (buf));
147 wattrset (main_w, menu->color (i));
149 if (i == menu->current) {
150 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
151 BKGDSET(main_w, MT_COLOR_INDICATOR);
154 CLEARLINE(main_w, i - menu->top + menu->offset);
156 wmove (main_w, i - menu->top + menu->offset, 0);
157 print_enriched_string (menu->color (i), (unsigned char *) buf,
159 SETCOLOR(main_w, MT_COLOR_NORMAL);
160 BKGDSET(main_w, MT_COLOR_NORMAL);
162 CLEARLINE(main_w, i - menu->top + menu->offset);
170 void menu_redraw_motion (MUTTMENU * menu)
175 menu->redraw &= ~REDRAW_MOTION;
179 SETCOLOR(main_w, MT_COLOR_NORMAL);
180 BKGDSET(main_w, MT_COLOR_NORMAL);
182 /* erase the current indicator */
183 wattrset(main_w, menu->color(menu->oldcurrent));
184 wmove (main_w, menu->oldcurrent + menu->offset - menu->top, 0);
186 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
187 menu_pad_string (buf, sizeof (buf));
188 print_enriched_string (menu->color (menu->oldcurrent),
189 (unsigned char *) buf, 1);
191 /* now draw the new one to reflect the change */
192 menu_make_entry (buf, sizeof (buf), menu, menu->current);
193 menu_pad_string (buf, sizeof (buf));
194 wattrset (main_w, menu->color (menu->current));
195 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
196 BKGDSET(main_w, MT_COLOR_INDICATOR);
197 CLEARLINE(main_w, menu->current - menu->top + menu->offset);
198 wmove (main_w, menu->current + menu->offset - menu->top, 0);
199 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
201 SETCOLOR(main_w, MT_COLOR_NORMAL);
202 BKGDSET(main_w, MT_COLOR_NORMAL);
204 menu->redraw &= REDRAW_STATUS;
207 void menu_redraw_current (MUTTMENU * menu)
211 menu_make_entry (buf, sizeof (buf), menu, menu->current);
212 menu_pad_string (buf, sizeof (buf));
214 wattrset (main_w, menu->color (menu->current));
215 ADDCOLOR(main_w, MT_COLOR_INDICATOR);
216 BKGDSET(main_w, MT_COLOR_INDICATOR);
217 wmove (main_w, menu->current + menu->offset - menu->top, 0);
219 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
221 SETCOLOR(main_w, MT_COLOR_NORMAL);
222 BKGDSET(main_w, MT_COLOR_NORMAL);
224 menu->redraw &= REDRAW_STATUS;
227 static void menu_redraw_prompt (MUTTMENU * menu)
230 if (option (OPTMSGERR)) {
232 unset_option (OPTMSGERR);
238 SETCOLOR(stdscr, MT_COLOR_NORMAL);
239 mvwaddstr(stdscr, LINES - 1, 0, menu->prompt);
244 void menu_check_recenter (MUTTMENU * menu)
246 int c = MIN (MenuContext, menu->pagelen / 2);
247 int old_top = menu->top;
249 if ((menu->pagelen <= 0) || (c < MenuContext)) {
250 if (menu->current < menu->top + c)
251 menu->top = menu->current - c;
252 else if (menu->current >= menu->top + menu->pagelen - c)
253 menu->top = menu->current - menu->pagelen + c + 1;
255 if (menu->current < menu->top + c)
256 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
257 else if ((menu->current >= menu->top + menu->pagelen - c))
258 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
261 menu->top = MAX (menu->top, 0);
263 if (menu->top != old_top)
264 menu->redraw |= REDRAW_INDEX;
267 void menu_jump (MUTTMENU * menu)
273 mutt_ungetch (LastKey, 0);
275 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
277 if (n >= 0 && n < menu->max) {
279 menu->redraw = REDRAW_MOTION;
282 mutt_error _("Invalid index number.");
286 mutt_error _("No entries.");
289 void menu_next_line (MUTTMENU * menu)
292 int c = MIN (MenuContext, menu->pagelen / 2);
294 if (menu->top + 1 < menu->max - c) {
296 if (menu->current < menu->top + c && menu->current < menu->max - 1)
298 menu->redraw = REDRAW_INDEX;
300 mutt_error _("You cannot scroll down farther.");
303 mutt_error _("No entries.");
307 void menu_prev_line (MUTTMENU * menu)
310 int c = MIN (MenuContext, menu->pagelen / 2);
313 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
315 menu->redraw = REDRAW_INDEX;
318 mutt_error _("You cannot scroll up farther.");
322 * pageup: jumplen == -pagelen
323 * pagedown: jumplen == pagelen
324 * halfup: jumplen == -pagelen/2
325 * halfdown: jumplen == pagelen/2
327 #define DIRECTION ((neg * 2) + 1)
328 static void menu_length_jump (MUTTMENU *menu, int jumplen) {
329 int tmp, neg = (jumplen >= 0) ? 0 : -1;
330 int c = MIN (MenuContext, menu->pagelen / 2);
333 /* possible to scroll? */
334 if (DIRECTION * menu->top <
335 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
336 menu->top += jumplen;
338 /* jumped too long? */
339 if (neg && DIRECTION * menu->top > tmp)
342 /* need to move the cursor? */
344 (tmp = (menu->current - (menu->top +
345 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
346 menu->current -= tmp;
348 menu->redraw = REDRAW_INDEX;
350 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
351 menu->current += jumplen;
352 menu->redraw = REDRAW_MOTION;
355 mutt_error (neg ? _("You are on the first page.")
356 : _("You are on the last page."));
358 menu->current = MIN (menu->current, menu->max - 1);
359 menu->current = MAX (menu->current, 0);
362 mutt_error _("No entries.");
366 void menu_next_page (MUTTMENU *menu) {
367 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
370 void menu_prev_page (MUTTMENU *menu) {
371 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
374 void menu_half_down (MUTTMENU *menu) {
375 menu_length_jump (menu, menu->pagelen / 2);
378 void menu_half_up (MUTTMENU *menu) {
379 menu_length_jump (menu, 0 - menu->pagelen / 2);
382 void menu_top_page (MUTTMENU *menu) {
383 if (menu->current != menu->top) {
384 menu->current = menu->top;
385 menu->redraw = REDRAW_MOTION;
389 void menu_bottom_page (MUTTMENU *menu) {
391 menu->current = menu->top + menu->pagelen - 1;
392 if (menu->current > menu->max - 1)
393 menu->current = menu->max - 1;
394 menu->redraw = REDRAW_MOTION;
397 mutt_error _("No entries.");
400 void menu_middle_page (MUTTMENU *menu) {
404 i = menu->top + menu->pagelen;
405 if (i > menu->max - 1)
407 menu->current = menu->top + (i - menu->top) / 2;
408 menu->redraw = REDRAW_MOTION;
411 mutt_error _("No entries.");
414 void menu_first_entry (MUTTMENU *menu) {
417 menu->redraw = REDRAW_MOTION;
420 mutt_error _("No entries.");
423 void menu_last_entry (MUTTMENU *menu) {
425 menu->current = menu->max - 1;
426 menu->redraw = REDRAW_MOTION;
429 mutt_error _("No entries.");
432 void menu_current_top (MUTTMENU * menu)
435 menu->top = menu->current;
436 menu->redraw = REDRAW_INDEX;
439 mutt_error _("No entries.");
442 void menu_current_middle (MUTTMENU * menu)
445 menu->top = menu->current - menu->pagelen / 2;
448 menu->redraw = REDRAW_INDEX;
451 mutt_error _("No entries.");
454 void menu_current_bottom (MUTTMENU * menu)
457 menu->top = menu->current - menu->pagelen + 1;
460 menu->redraw = REDRAW_INDEX;
463 mutt_error _("No entries.");
466 static void menu_next_entry (MUTTMENU * menu)
468 if (menu->current < menu->max - 1) {
470 menu->redraw = REDRAW_MOTION;
473 mutt_error _("You are on the last entry.");
476 static void menu_prev_entry (MUTTMENU * menu)
480 menu->redraw = REDRAW_MOTION;
483 mutt_error _("You are on the first entry.");
486 static int default_color (int i __attribute__ ((unused)))
488 return ColorDefs[MT_COLOR_NORMAL];
491 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
493 char buf[LONG_STRING];
495 menu_make_entry (buf, sizeof (buf), m, n);
496 return (regexec (re, buf, 0, NULL, 0));
499 MUTTMENU *mutt_new_menu (void)
501 MUTTMENU *p = p_new(MUTTMENU, 1);
506 p->redraw = REDRAW_FULL;
507 p->pagelen = LINES - 3;
508 p->color = default_color;
509 p->search = menu_search_generic;
513 void mutt_menuDestroy (MUTTMENU ** p)
517 p_delete(&(*p)->searchBuf);
520 for (i = 0; i < (*p)->max; i++)
521 p_delete(&(*p)->dialog[i]);
523 p_delete(&(*p)->dialog);
529 #define M_SEARCH_UP 1
530 #define M_SEARCH_DOWN 2
532 static int menu_search (MUTTMENU * menu, int op)
539 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
540 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
541 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
542 _("Reverse search for: "),
543 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
545 m_strreplace(&menu->searchBuf, buf);
546 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
549 if (!menu->searchBuf) {
550 mutt_error _("No search pattern.");
556 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
557 if (op == OP_SEARCH_OPPOSITE)
558 searchDir = -searchDir;
561 REGCOMP (&re, menu->searchBuf,
562 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
563 regerror (r, &re, buf, sizeof (buf));
565 mutt_error ("%s", buf);
569 r = menu->current + searchDir;
570 while (r >= 0 && r < menu->max) {
571 if (menu->search (menu, &re, r) == 0) {
580 mutt_error _("Not found.");
585 static int menu_dialog_translate_op (int i)
595 case OP_CURRENT_BOTTOM:
597 return OP_BOTTOM_PAGE;
598 case OP_CURRENT_MIDDLE:
599 return OP_MIDDLE_PAGE;
605 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
617 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
618 *ip = OP_MAX + (p - menu->keys + 1);
622 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
627 int menu_redraw (MUTTMENU * menu)
629 /* See if all or part of the screen needs to be updated. */
630 if (menu->redraw & REDRAW_FULL) {
631 menu_redraw_full (menu);
632 /* allow the caller to do any local configuration */
637 menu_check_recenter (menu);
639 if (menu->redraw & REDRAW_STATUS)
640 menu_redraw_status (menu);
641 if (menu->redraw & REDRAW_INDEX)
642 menu_redraw_index (menu);
643 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
644 menu_redraw_motion (menu);
645 else if (menu->redraw == REDRAW_CURRENT)
646 menu_redraw_current (menu);
649 menu_redraw_prompt (menu);
654 int mutt_menuLoop (MUTTMENU * menu)
659 if (option (OPTMENUCALLER)) {
660 unset_option (OPTMENUCALLER);
668 if (menu_redraw (menu) == OP_REDRAW)
671 menu->oldcurrent = menu->current;
674 /* try to catch dialog keys before ops */
675 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
678 i = km_dokey (menu->menu);
679 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
681 mvwaddstr(stdscr, LINES - 1, 0, "Tag-");
683 i = km_dokey (menu->menu);
685 CLEARLINE(stdscr, LINES - 1);
687 else if (i == OP_TAG_PREFIX) {
688 mutt_error _("No tagged entries.");
692 else { /* None tagged, OP_TAG_PREFIX_COND */
696 while (UngetCount > 0) {
698 if (tmp.op == OP_END_COND)
701 mutt_message _("Nothing to do.");
706 else if (menu->tagged && option (OPTAUTOTAG))
715 menu->redraw = REDRAW_FULL;
724 /* Convert menubar movement to scrolling */
726 i = menu_dialog_translate_op (i);
730 menu_next_entry (menu);
733 menu_prev_entry (menu);
736 menu_half_down (menu);
742 menu_next_page (menu);
745 menu_prev_page (menu);
748 menu_next_line (menu);
751 menu_prev_line (menu);
754 menu_first_entry (menu);
757 menu_last_entry (menu);
760 menu_top_page (menu);
763 menu_middle_page (menu);
766 menu_bottom_page (menu);
769 menu_current_top (menu);
771 case OP_CURRENT_MIDDLE:
772 menu_current_middle (menu);
774 case OP_CURRENT_BOTTOM:
775 menu_current_bottom (menu);
778 case OP_SEARCH_REVERSE:
780 case OP_SEARCH_OPPOSITE:
781 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
782 menu->oldcurrent = menu->current;
783 if ((menu->current = menu_search (menu, i)) != -1)
784 menu->redraw = REDRAW_MOTION;
786 menu->current = menu->oldcurrent;
789 mutt_error _("Search is not implemented for this menu.");
794 mutt_error (_("Jumping is not implemented for dialogs."));
800 case OP_ENTER_COMMAND:
801 CurrentMenu = menu->menu;
802 mutt_enter_command ();
803 if (option (OPTFORCEREDRAWINDEX)) {
804 menu->redraw = REDRAW_FULL;
805 unset_option (OPTFORCEREDRAWINDEX);
806 unset_option (OPTFORCEREDRAWPAGER);
811 if (menu->tag && !menu->dialog) {
812 if (menu->tagprefix && !option (OPTAUTOTAG)) {
813 for (i = 0; i < menu->max; i++)
814 menu->tagged += menu->tag (menu, i, 0);
815 menu->redraw = REDRAW_INDEX;
817 else if (menu->max) {
818 int t = menu->tag (menu, menu->current, -1);
821 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
823 menu->redraw = REDRAW_MOTION_RESYNCH;
826 menu->redraw = REDRAW_CURRENT;
829 mutt_error _("No entries.");
832 mutt_error _("Tagging is not supported.");
835 case OP_SHELL_ESCAPE:
836 mutt_shell_escape ();
837 MAYBE_REDRAW (menu->redraw);
841 clearok (main_w, TRUE);
842 menu->redraw = REDRAW_FULL;
846 mutt_help (menu->menu);
847 menu->redraw = REDRAW_FULL;
851 km_error_key (menu->menu);