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 #define SW (option(OPTMBOXPANE)?SidebarWidth:0)
23 extern size_t UngetCount;
25 static void print_enriched_string (int attr, unsigned char *s, int do_color)
29 size_t n = m_strlen((char *) s);
34 if (*s < M_TREE_MAX) {
36 SETCOLOR (MT_COLOR_TREE);
37 while (*s && *s < M_TREE_MAX) {
40 waddch (stdscr, ACS_LLCORNER);
43 waddch (stdscr, ACS_ULCORNER);
46 waddch (stdscr, ACS_LTEE);
49 waddch (stdscr, ACS_HLINE);
52 waddch (stdscr, ACS_VLINE);
55 waddch (stdscr, ACS_TTEE);
58 waddch (stdscr, ACS_BTEE);
67 waddch (stdscr, '*'); /* fake thread indicator */
82 wattrset (stdscr, attr);
84 else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) != (size_t)-1) {
85 waddnstr (stdscr, (char *) s, k);
93 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
96 m_strcpy(s, l, menu->dialog[i]);
97 menu->current = -1; /* hide menubar */
100 menu->make_entry (s, l, menu, i);
103 static void menu_pad_string (char *s, size_t n)
105 int cols = COLS - SW;
106 char *tmpbuf = p_new(char, n);
108 mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
110 snprintf (s, n, "%s", tmpbuf); /* overkill */
114 void menu_redraw_full (MUTTMENU * menu)
116 SETCOLOR (MT_COLOR_NORMAL);
117 /* wclear(stdscr) doesn't optimize screen redraws */
118 wmove (stdscr, 0, 0);
121 if (option (OPTHELP)) {
122 SETCOLOR (MT_COLOR_STATUS);
123 wmove (stdscr, option (OPTSTATUSONTOP) ? LINES - 2 : 0, SW);
124 mutt_paddstr (COLS-SW, "");
125 SETCOLOR (MT_COLOR_NORMAL);
127 menu->pagelen = LINES - 3;
130 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
131 menu->pagelen = LINES - 2;
134 sidebar_draw_frames();
138 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
141 void menu_redraw_status (MUTTMENU * menu)
145 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
146 SETCOLOR (MT_COLOR_STATUS);
147 wmove (stdscr, option (OPTSTATUSONTOP) ? 0 : LINES - 2, SW);
148 mutt_paddstr (COLS-SW, buf);
149 SETCOLOR (MT_COLOR_NORMAL);
150 menu->redraw &= ~REDRAW_STATUS;
151 sidebar_draw_frames();
154 void menu_redraw_index (MUTTMENU * menu)
159 for (i = menu->top; i < menu->top + menu->pagelen; i++) {
161 menu_make_entry (buf, sizeof (buf), menu, i);
162 menu_pad_string (buf, sizeof (buf));
164 wattrset (stdscr, menu->color (i));
166 if (i == menu->current) {
167 ADDCOLOR (MT_COLOR_INDICATOR);
168 BKGDSET (MT_COLOR_INDICATOR);
171 CLEARLINE_WIN (i - menu->top + menu->offset);
173 wmove (stdscr, i - menu->top + menu->offset, SW);
174 print_enriched_string (menu->color (i), (unsigned char *) buf,
176 SETCOLOR (MT_COLOR_NORMAL);
177 BKGDSET (MT_COLOR_NORMAL);
179 CLEARLINE_WIN (i - menu->top + menu->offset);
187 void menu_redraw_motion (MUTTMENU * menu)
192 menu->redraw &= ~REDRAW_MOTION;
196 wmove (stdscr, menu->oldcurrent + menu->offset - menu->top, SW);
197 SETCOLOR (MT_COLOR_NORMAL);
198 BKGDSET (MT_COLOR_NORMAL);
200 /* erase the current indicator */
201 wattrset (stdscr, menu->color (menu->oldcurrent));
203 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
204 menu_pad_string (buf, sizeof (buf));
205 print_enriched_string (menu->color (menu->oldcurrent),
206 (unsigned char *) buf, 1);
208 /* now draw the new one to reflect the change */
209 menu_make_entry (buf, sizeof (buf), menu, menu->current);
210 menu_pad_string (buf, sizeof (buf));
211 wattrset (stdscr, menu->color (menu->current));
212 ADDCOLOR (MT_COLOR_INDICATOR);
213 BKGDSET (MT_COLOR_INDICATOR);
214 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
215 wmove (stdscr, menu->current + menu->offset - menu->top, SW);
216 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
218 SETCOLOR (MT_COLOR_NORMAL);
219 BKGDSET (MT_COLOR_NORMAL);
221 menu->redraw &= REDRAW_STATUS;
224 void menu_redraw_current (MUTTMENU * menu)
228 wmove (stdscr, menu->current + menu->offset - menu->top, SW);
229 menu_make_entry (buf, sizeof (buf), menu, menu->current);
230 menu_pad_string (buf, sizeof (buf));
232 wattrset (stdscr, menu->color (menu->current));
233 ADDCOLOR (MT_COLOR_INDICATOR);
234 BKGDSET (MT_COLOR_INDICATOR);
236 print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
238 SETCOLOR (MT_COLOR_NORMAL);
239 BKGDSET (MT_COLOR_NORMAL);
241 menu->redraw &= REDRAW_STATUS;
244 static void menu_redraw_prompt (MUTTMENU * menu)
247 if (option (OPTMSGERR)) {
249 unset_option (OPTMSGERR);
255 SETCOLOR (MT_COLOR_NORMAL);
256 mvwaddstr (stdscr, LINES - 1, 0, menu->prompt);
261 void menu_check_recenter (MUTTMENU * menu)
263 int c = MIN (MenuContext, menu->pagelen / 2);
264 int old_top = menu->top;
266 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
267 if (menu->top != 0) {
269 set_option (OPTNEEDREDRAW);
272 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
273 if (menu->current < menu->top + c)
274 menu->top = menu->current - c;
275 else if (menu->current >= menu->top + menu->pagelen - c)
276 menu->top = menu->current - menu->pagelen + c + 1;
278 if (menu->current < menu->top + c)
279 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
280 else if ((menu->current >= menu->top + menu->pagelen - c))
281 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
285 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
286 menu->top = MIN (menu->top, menu->max - menu->pagelen);
287 menu->top = MAX (menu->top, 0);
289 if (menu->top != old_top)
290 menu->redraw |= REDRAW_INDEX;
293 void menu_jump (MUTTMENU * menu)
299 mutt_ungetch (LastKey, 0);
301 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
303 if (n >= 0 && n < menu->max) {
305 menu->redraw = REDRAW_MOTION;
308 mutt_error _("Invalid index number.");
312 mutt_error _("No entries.");
315 void menu_next_line (MUTTMENU * menu)
318 int c = MIN (MenuContext, menu->pagelen / 2);
320 if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
321 || (menu->max > menu->pagelen
323 menu->max - menu->pagelen))) {
325 if (menu->current < menu->top + c && menu->current < menu->max - 1)
327 menu->redraw = REDRAW_INDEX;
330 mutt_error _("You cannot scroll down farther.");
333 mutt_error _("No entries.");
336 void menu_prev_line (MUTTMENU * menu)
339 int c = MIN (MenuContext, menu->pagelen / 2);
342 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
344 menu->redraw = REDRAW_INDEX;
347 mutt_error _("You cannot scroll up farther.");
351 * pageup: jumplen == -pagelen
352 * pagedown: jumplen == pagelen
353 * halfup: jumplen == -pagelen/2
354 * halfdown: jumplen == pagelen/2
356 #define DIRECTION ((neg * 2) + 1)
357 static void menu_length_jump (MUTTMENU *menu, int jumplen) {
358 int tmp, neg = (jumplen >= 0) ? 0 : -1;
359 int c = MIN (MenuContext, menu->pagelen / 2);
362 /* possible to scroll? */
363 if (DIRECTION * menu->top <
364 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
365 menu->top += jumplen;
367 /* jumped too long? */
368 if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
371 /* need to move the cursor? */
373 (tmp = (menu->current - (menu->top +
374 (neg ? (menu->pagelen - 1) - c : c))))) < 0)
375 menu->current -= tmp;
377 menu->redraw = REDRAW_INDEX;
379 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
380 menu->current += jumplen;
381 menu->redraw = REDRAW_MOTION;
384 mutt_error (neg ? _("You are on the first page.")
385 : _("You are on the last page."));
387 menu->current = MIN (menu->current, menu->max - 1);
388 menu->current = MAX (menu->current, 0);
391 mutt_error _("No entries.");
395 void menu_next_page (MUTTMENU *menu) {
396 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
399 void menu_prev_page (MUTTMENU *menu) {
400 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
403 void menu_half_down (MUTTMENU *menu) {
404 menu_length_jump (menu, menu->pagelen / 2);
407 void menu_half_up (MUTTMENU *menu) {
408 menu_length_jump (menu, 0 - menu->pagelen / 2);
411 void menu_top_page (MUTTMENU *menu) {
412 if (menu->current != menu->top) {
413 menu->current = menu->top;
414 menu->redraw = REDRAW_MOTION;
418 void menu_bottom_page (MUTTMENU *menu) {
420 menu->current = menu->top + menu->pagelen - 1;
421 if (menu->current > menu->max - 1)
422 menu->current = menu->max - 1;
423 menu->redraw = REDRAW_MOTION;
426 mutt_error _("No entries.");
429 void menu_middle_page (MUTTMENU *menu) {
433 i = menu->top + menu->pagelen;
434 if (i > menu->max - 1)
436 menu->current = menu->top + (i - menu->top) / 2;
437 menu->redraw = REDRAW_MOTION;
440 mutt_error _("No entries.");
443 void menu_first_entry (MUTTMENU *menu) {
446 menu->redraw = REDRAW_MOTION;
449 mutt_error _("No entries.");
452 void menu_last_entry (MUTTMENU *menu) {
454 menu->current = menu->max - 1;
455 menu->redraw = REDRAW_MOTION;
458 mutt_error _("No entries.");
461 void menu_current_top (MUTTMENU * menu)
464 menu->top = menu->current;
465 menu->redraw = REDRAW_INDEX;
468 mutt_error _("No entries.");
471 void menu_current_middle (MUTTMENU * menu)
474 menu->top = menu->current - menu->pagelen / 2;
477 menu->redraw = REDRAW_INDEX;
480 mutt_error _("No entries.");
483 void menu_current_bottom (MUTTMENU * menu)
486 menu->top = menu->current - menu->pagelen + 1;
489 menu->redraw = REDRAW_INDEX;
492 mutt_error _("No entries.");
495 static void menu_next_entry (MUTTMENU * menu)
497 if (menu->current < menu->max - 1) {
499 menu->redraw = REDRAW_MOTION;
502 mutt_error _("You are on the last entry.");
505 static void menu_prev_entry (MUTTMENU * menu)
509 menu->redraw = REDRAW_MOTION;
512 mutt_error _("You are on the first entry.");
515 static int default_color (int i __attribute__ ((unused)))
517 return ColorDefs[MT_COLOR_NORMAL];
520 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
522 char buf[LONG_STRING];
524 menu_make_entry (buf, sizeof (buf), m, n);
525 return (regexec (re, buf, 0, NULL, 0));
528 MUTTMENU *mutt_new_menu (void)
530 MUTTMENU *p = p_new(MUTTMENU, 1);
535 p->redraw = REDRAW_FULL;
536 p->pagelen = PAGELEN;
537 p->color = default_color;
538 p->search = menu_search_generic;
542 void mutt_menuDestroy (MUTTMENU ** p)
546 p_delete(&(*p)->searchBuf);
549 for (i = 0; i < (*p)->max; i++)
550 p_delete(&(*p)->dialog[i]);
552 p_delete(&(*p)->dialog);
558 #define M_SEARCH_UP 1
559 #define M_SEARCH_DOWN 2
561 static int menu_search (MUTTMENU * menu, int op)
568 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
569 m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
570 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
571 _("Reverse search for: "),
572 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
574 m_strreplace(&menu->searchBuf, buf);
575 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
578 if (!menu->searchBuf) {
579 mutt_error _("No search pattern.");
585 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
586 if (op == OP_SEARCH_OPPOSITE)
587 searchDir = -searchDir;
590 REGCOMP (&re, menu->searchBuf,
591 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
592 regerror (r, &re, buf, sizeof (buf));
594 mutt_error ("%s", buf);
598 r = menu->current + searchDir;
599 while (r >= 0 && r < menu->max) {
600 if (menu->search (menu, &re, r) == 0) {
609 mutt_error _("Not found.");
614 static int menu_dialog_translate_op (int i)
624 case OP_CURRENT_BOTTOM:
626 return OP_BOTTOM_PAGE;
627 case OP_CURRENT_MIDDLE:
628 return OP_MIDDLE_PAGE;
634 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
646 if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
647 *ip = OP_MAX + (p - menu->keys + 1);
651 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
656 int menu_redraw (MUTTMENU * menu)
658 /* See if all or part of the screen needs to be updated. */
659 if (menu->redraw & REDRAW_FULL) {
660 menu_redraw_full (menu);
661 /* allow the caller to do any local configuration */
666 menu_check_recenter (menu);
668 if (menu->redraw & REDRAW_STATUS)
669 menu_redraw_status (menu);
670 if (menu->redraw & REDRAW_INDEX)
671 menu_redraw_index (menu);
672 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
673 menu_redraw_motion (menu);
674 else if (menu->redraw == REDRAW_CURRENT)
675 menu_redraw_current (menu);
678 menu_redraw_prompt (menu);
683 int mutt_menuLoop (MUTTMENU * menu)
688 if (option (OPTMENUCALLER)) {
689 unset_option (OPTMENUCALLER);
697 if (menu_redraw (menu) == OP_REDRAW)
700 menu->oldcurrent = menu->current;
702 if (option (OPTBRAILLEFRIENDLY))
703 wmove (stdscr, menu->current - menu->top + menu->offset, SW);
705 wmove (stdscr, menu->current - menu->top + menu->offset, COLS - 1);
710 /* try to catch dialog keys before ops */
711 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
714 i = km_dokey (menu->menu);
715 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
717 mvwaddstr (stdscr, LINES - 1, 0, "Tag-");
719 i = km_dokey (menu->menu);
721 CLEARLINE (LINES - 1);
723 else if (i == OP_TAG_PREFIX) {
724 mutt_error _("No tagged entries.");
728 else { /* None tagged, OP_TAG_PREFIX_COND */
732 while (UngetCount > 0) {
734 if (tmp.op == OP_END_COND)
737 mutt_message _("Nothing to do.");
742 else if (menu->tagged && option (OPTAUTOTAG))
750 mutt_resize_screen ();
751 menu->redraw = REDRAW_FULL;
753 clearok (stdscr, TRUE); /*force complete redraw */
762 /* Convert menubar movement to scrolling */
764 i = menu_dialog_translate_op (i);
768 menu_next_entry (menu);
771 menu_prev_entry (menu);
774 menu_half_down (menu);
780 menu_next_page (menu);
783 menu_prev_page (menu);
786 menu_next_line (menu);
789 menu_prev_line (menu);
792 menu_first_entry (menu);
795 menu_last_entry (menu);
798 menu_top_page (menu);
801 menu_middle_page (menu);
804 menu_bottom_page (menu);
807 menu_current_top (menu);
809 case OP_CURRENT_MIDDLE:
810 menu_current_middle (menu);
812 case OP_CURRENT_BOTTOM:
813 menu_current_bottom (menu);
816 case OP_SEARCH_REVERSE:
818 case OP_SEARCH_OPPOSITE:
819 if (menu->search && !menu->dialog) { /* Searching dialogs won't work */
820 menu->oldcurrent = menu->current;
821 if ((menu->current = menu_search (menu, i)) != -1)
822 menu->redraw = REDRAW_MOTION;
824 menu->current = menu->oldcurrent;
827 mutt_error _("Search is not implemented for this menu.");
832 mutt_error (_("Jumping is not implemented for dialogs."));
838 case OP_ENTER_COMMAND:
839 CurrentMenu = menu->menu;
840 mutt_enter_command ();
841 if (option (OPTFORCEREDRAWINDEX)) {
842 menu->redraw = REDRAW_FULL;
843 unset_option (OPTFORCEREDRAWINDEX);
844 unset_option (OPTFORCEREDRAWPAGER);
849 if (menu->tag && !menu->dialog) {
850 if (menu->tagprefix && !option (OPTAUTOTAG)) {
851 for (i = 0; i < menu->max; i++)
852 menu->tagged += menu->tag (menu, i, 0);
853 menu->redraw = REDRAW_INDEX;
855 else if (menu->max) {
856 int t = menu->tag (menu, menu->current, -1);
859 if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
861 menu->redraw = REDRAW_MOTION_RESYNCH;
864 menu->redraw = REDRAW_CURRENT;
867 mutt_error _("No entries.");
870 mutt_error _("Tagging is not supported.");
873 case OP_SHELL_ESCAPE:
874 mutt_shell_escape ();
875 MAYBE_REDRAW (menu->redraw);
883 clearok (stdscr, TRUE);
884 menu->redraw = REDRAW_FULL;
888 mutt_help (menu->menu);
889 menu->redraw = REDRAW_FULL;
893 km_error_key (menu->menu);