2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
20 #include "mutt_curses.h"
21 #include "mutt_menu.h"
32 extern int Charset_is_utf8; /* FIXME: bad modularisation */
34 extern size_t UngetCount;
36 static void print_enriched_string (int attr, unsigned char *s, int do_color)
40 size_t n = mutt_strlen ((char *)s);
44 memset (&mbstate, 0, sizeof (mbstate));
51 pair_content(PAIR_NUMBER(ColorDefs[MT_COLOR_TREE]), &f1, &b1);
52 pair_content(PAIR_NUMBER(attr), &f2, &b2);
54 SETCOLOR (MT_COLOR_TREE);
56 while (*s && *s < M_TREE_MAX)
61 if (option (OPTASCIICHARS))
63 else if (Charset_is_utf8)
64 addstr ("\342\224\224"); /* WACS_LLCORNER */
69 if (option (OPTASCIICHARS))
71 else if (Charset_is_utf8)
72 addstr ("\342\224\214"); /* WACS_ULCORNER */
77 if (option (OPTASCIICHARS))
79 else if (Charset_is_utf8)
80 addstr ("\342\224\234"); /* WACS_LTEE */
85 if (option (OPTASCIICHARS))
87 else if (Charset_is_utf8)
88 addstr ("\342\224\200"); /* WACS_HLINE */
93 if (option (OPTASCIICHARS))
95 else if (Charset_is_utf8)
96 addstr ("\342\224\202"); /* WACS_VLINE */
101 if (option (OPTASCIICHARS))
103 else if (Charset_is_utf8)
104 addstr ("\342\224\254"); /* WACS_TTEE */
109 if (option (OPTASCIICHARS))
111 else if (Charset_is_utf8)
112 addstr ("\342\224\264"); /* WACS_BTEE */
123 addch ('*'); /* fake thread indicator */
137 if (do_color) attrset(attr);
139 else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
141 addnstr ((char *)s, k);
149 static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i)
153 strncpy (s, menu->dialog[i], l);
154 menu->current = -1; /* hide menubar */
157 menu->make_entry (s, l, menu, i);
160 void menu_pad_string (char *s, size_t n)
162 int shift = option (OPTARROWCURSOR) ? 3 : 0;
163 int cols = COLS - shift - SidebarWidth;
165 mutt_format_string (s, n, cols, cols, 0, ' ', s, strlen (s), 1);
169 void menu_redraw_full (MUTTMENU *menu)
171 SETCOLOR (MT_COLOR_NORMAL);
172 /* clear() doesn't optimize screen redraws */
176 if (option (OPTHELP))
178 SETCOLOR (MT_COLOR_STATUS);
179 move (option (OPTSTATUSONTOP) ? LINES-2 : 0, 0);
180 mutt_paddstr (COLS, menu->help);
181 SETCOLOR (MT_COLOR_NORMAL);
183 menu->pagelen = LINES - 3;
187 menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
188 menu->pagelen = LINES - 2;
193 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
196 void menu_redraw_status (MUTTMENU *menu)
200 snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
201 SETCOLOR (MT_COLOR_STATUS);
202 move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
203 mutt_paddstr (COLS, buf);
204 SETCOLOR (MT_COLOR_NORMAL);
205 menu->redraw &= ~REDRAW_STATUS;
208 void menu_redraw_index (MUTTMENU *menu)
214 for (i = menu->top; i < menu->top + menu->pagelen; i++)
218 menu_make_entry (buf, sizeof (buf), menu, i);
219 menu_pad_string (buf, sizeof (buf));
221 if (option (OPTARROWCURSOR))
223 attrset (menu->color (i));
224 CLEARLINE_WIN (i - menu->top + menu->offset);
226 if (i == menu->current)
228 attrset (menu->color (i));
229 ADDCOLOR (MT_COLOR_INDICATOR);
231 attrset (menu->color (i));
235 move (i - menu->top + menu->offset, SidebarWidth + 3);
237 print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
238 SETCOLOR (MT_COLOR_NORMAL);
242 attrset (menu->color (i));
244 if (i == menu->current)
246 ADDCOLOR (MT_COLOR_INDICATOR);
247 BKGDSET (MT_COLOR_INDICATOR);
250 CLEARLINE_WIN (i - menu->top + menu->offset);
251 print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current);
252 SETCOLOR (MT_COLOR_NORMAL);
253 BKGDSET (MT_COLOR_NORMAL);
257 CLEARLINE_WIN (i - menu->top + menu->offset);
262 void menu_redraw_motion (MUTTMENU *menu)
268 menu->redraw &= ~REDRAW_MOTION;
272 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
273 SETCOLOR (MT_COLOR_NORMAL);
274 BKGDSET (MT_COLOR_NORMAL);
276 if (option (OPTARROWCURSOR))
278 /* clear the pointer */
279 attrset (menu->color (menu->oldcurrent));
282 if (menu->redraw & REDRAW_MOTION_RESYNCH)
285 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
286 menu_pad_string (buf, sizeof (buf));
287 move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
288 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
289 SETCOLOR (MT_COLOR_NORMAL);
292 /* now draw it in the new location */
293 move (menu->current + menu->offset - menu->top, SidebarWidth);
294 attrset (menu->color (menu->current));
295 ADDCOLOR (MT_COLOR_INDICATOR);
297 SETCOLOR (MT_COLOR_NORMAL);
301 /* erase the current indicator */
302 attrset (menu->color (menu->oldcurrent));
304 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
305 menu_pad_string (buf, sizeof (buf));
306 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
308 /* now draw the new one to reflect the change */
309 menu_make_entry (buf, sizeof (buf), menu, menu->current);
310 menu_pad_string (buf, sizeof (buf));
311 attrset (menu->color (menu->current));
312 ADDCOLOR (MT_COLOR_INDICATOR);
313 BKGDSET (MT_COLOR_INDICATOR);
314 CLEARLINE_WIN (menu->current - menu->top + menu->offset);
315 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
316 SETCOLOR (MT_COLOR_NORMAL);
317 BKGDSET (MT_COLOR_NORMAL);
319 menu->redraw &= REDRAW_STATUS;
322 void menu_redraw_current (MUTTMENU *menu)
326 move (menu->current + menu->offset - menu->top, SidebarWidth);
327 menu_make_entry (buf, sizeof (buf), menu, menu->current);
328 menu_pad_string (buf, sizeof (buf));
330 if (option (OPTARROWCURSOR))
332 int attr = menu->color (menu->current);
335 attrset (menu->color (menu->current));
336 ADDCOLOR (MT_COLOR_INDICATOR);
340 menu_pad_string (buf, sizeof (buf));
341 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 1);
342 SETCOLOR (MT_COLOR_NORMAL);
346 attrset (menu->color (menu->current));
347 ADDCOLOR (MT_COLOR_INDICATOR);
348 BKGDSET (MT_COLOR_INDICATOR);
350 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
351 SETCOLOR (MT_COLOR_NORMAL);
352 BKGDSET (MT_COLOR_NORMAL);
354 menu->redraw &= REDRAW_STATUS;
357 void menu_redraw_prompt (MUTTMENU *menu)
361 if (option (OPTMSGERR))
364 unset_option (OPTMSGERR);
370 SETCOLOR (MT_COLOR_NORMAL);
371 mvaddstr (LINES - 1, 0, menu->prompt);
376 void menu_check_recenter (MUTTMENU *menu)
378 if (menu->max <= menu->pagelen && menu->top != 0)
381 set_option (OPTNEEDREDRAW);
382 menu->redraw |= REDRAW_INDEX;
384 else if (menu->current >= menu->top + menu->pagelen)
386 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
387 menu->top = menu->current - menu->pagelen + 1;
389 menu->top += menu->pagelen * ((menu->current - menu->top) / menu->pagelen);
390 menu->redraw |= REDRAW_INDEX;
392 else if (menu->current < menu->top)
394 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
395 menu->top = menu->current;
398 menu->top -= menu->pagelen * ((menu->top + menu->pagelen - 1 - menu->current) / menu->pagelen);
402 menu->redraw |= REDRAW_INDEX;
406 void menu_jump (MUTTMENU *menu)
409 char buf[SHORT_STRING];
413 mutt_ungetch (LastKey, 0);
415 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
418 if (n >= 0 && n < menu->max)
421 menu->redraw = REDRAW_MOTION;
424 mutt_error _("Invalid index number.");
428 mutt_error _("No entries.");
431 void menu_next_line (MUTTMENU *menu)
435 if (menu->top + 1 < menu->max)
438 if (menu->current < menu->top)
440 menu->redraw = REDRAW_INDEX;
443 mutt_error _("You cannot scroll down farther.");
446 mutt_error _("No entries.");
449 void menu_prev_line (MUTTMENU *menu)
454 if (menu->current >= menu->top + menu->pagelen)
456 menu->redraw = REDRAW_INDEX;
459 mutt_error _("You cannot scroll up farther.");
462 void menu_next_page (MUTTMENU *menu)
466 if (menu->top + menu->pagelen < menu->max)
468 menu->top += menu->pagelen;
469 if (menu->current < menu->top)
470 menu->current = menu->top;
471 menu->redraw = REDRAW_INDEX;
473 else if (menu->current != menu->max - 1 && !menu->dialog)
475 menu->current = menu->max - 1;
476 menu->redraw = REDRAW_MOTION;
479 mutt_error _("You are on the last page.");
482 mutt_error _("No entries.");
485 void menu_prev_page (MUTTMENU *menu)
489 if ((menu->top -= menu->pagelen) < 0)
491 if (menu->current >= menu->top + menu->pagelen)
492 menu->current = menu->top + menu->pagelen - 1;
493 menu->redraw = REDRAW_INDEX;
495 else if (menu->current && !menu->dialog)
498 menu->redraw = REDRAW_MOTION;
501 mutt_error _("You are on the first page.");
504 void menu_top_page (MUTTMENU *menu)
506 if (menu->current != menu->top)
508 menu->current = menu->top;
509 menu->redraw = REDRAW_MOTION;
513 void menu_bottom_page (MUTTMENU *menu)
517 menu->current = menu->top + menu->pagelen - 1;
518 if (menu->current > menu->max - 1)
519 menu->current = menu->max - 1;
520 menu->redraw = REDRAW_MOTION;
523 mutt_error _("No entries.");
526 void menu_middle_page (MUTTMENU *menu)
532 i = menu->top + menu->pagelen;
533 if (i > menu->max - 1)
535 menu->current = menu->top + (i - menu->top) / 2;
536 menu->redraw = REDRAW_MOTION;
539 mutt_error _("No entries.");
542 void menu_first_entry (MUTTMENU *menu)
547 menu->redraw = REDRAW_MOTION;
550 mutt_error _("No entries.");
553 void menu_last_entry (MUTTMENU *menu)
557 menu->current = menu->max - 1;
558 menu->redraw = REDRAW_MOTION;
561 mutt_error _("No entries.");
564 void menu_half_up (MUTTMENU *menu)
568 if ((menu->top -= menu->pagelen / 2) < 0)
570 if (menu->current >= menu->top + menu->pagelen)
571 menu->current = menu->top + menu->pagelen - 1;
572 menu->redraw = REDRAW_INDEX;
574 else if (menu->current && !menu->dialog)
577 menu->redraw = REDRAW_MOTION;
580 mutt_error _("First entry is shown.");
583 void menu_half_down (MUTTMENU *menu)
587 if (menu->top + menu->pagelen < menu->max)
589 menu->top += menu->pagelen / 2;
590 if (menu->current < menu->top)
591 menu->current = menu->top;
592 menu->redraw = REDRAW_INDEX;
594 else if (menu->current != menu->max - 1 && !menu->dialog)
596 menu->current = menu->max - 1;
597 menu->redraw = REDRAW_INDEX;
600 mutt_error _("Last entry is shown.");
603 mutt_error _("No entries.");
606 void menu_current_top (MUTTMENU *menu)
610 menu->top = menu->current;
611 menu->redraw = REDRAW_INDEX;
614 mutt_error _("No entries.");
617 void menu_current_middle (MUTTMENU *menu)
621 menu->top = menu->current - menu->pagelen / 2;
624 menu->redraw = REDRAW_INDEX;
627 mutt_error _("No entries.");
630 void menu_current_bottom (MUTTMENU *menu)
634 menu->top = menu->current - menu->pagelen + 1;
637 menu->redraw = REDRAW_INDEX;
640 mutt_error _("No entries.");
643 void menu_next_entry (MUTTMENU *menu)
645 if (menu->current < menu->max - 1)
648 menu->redraw = REDRAW_MOTION;
651 mutt_error _("You are on the last entry.");
654 void menu_prev_entry (MUTTMENU *menu)
659 menu->redraw = REDRAW_MOTION;
662 mutt_error _("You are on the first entry.");
665 static int default_color (int i)
667 return ColorDefs[MT_COLOR_NORMAL];
670 static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
672 char buf[LONG_STRING];
674 menu_make_entry (buf, sizeof (buf), m, n);
675 return (regexec (re, buf, 0, NULL, 0));
678 MUTTMENU *mutt_new_menu (void)
680 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
685 p->redraw = REDRAW_FULL;
686 p->pagelen = PAGELEN;
687 p->color = default_color;
688 p->search = menu_search_generic;
692 void mutt_menuDestroy (MUTTMENU **p)
696 FREE (&(*p)->searchBuf);
700 for (i=0; i < (*p)->max; i++)
701 FREE (&(*p)->dialog[i]);
709 #define M_SEARCH_UP 1
710 #define M_SEARCH_DOWN 2
712 static int menu_search (MUTTMENU *menu, int op)
717 char buf[SHORT_STRING];
719 if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE)
721 strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
722 if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
723 _("Reverse search for: "),
724 buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
726 mutt_str_replace (&menu->searchBuf, buf);
727 menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
731 if (!menu->searchBuf)
733 mutt_error _("No search pattern.");
738 searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
739 if (op == OP_SEARCH_OPPOSITE)
740 searchDir = -searchDir;
742 if ((r = REGCOMP (&re, menu->searchBuf, REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0)
744 regerror (r, &re, buf, sizeof (buf));
746 mutt_error ("%s", buf);
750 r = menu->current + searchDir;
751 while (r >= 0 && r < menu->max)
753 if (menu->search (menu, &re, r) == 0)
763 mutt_error _("Not found.");
767 static int menu_dialog_translate_op (int i)
775 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
777 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
778 return OP_BOTTOM_PAGE;
779 case OP_CURRENT_MIDDLE:
780 return OP_MIDDLE_PAGE;
786 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
799 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
801 *ip = OP_MAX + (p - menu->keys + 1);
806 mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
811 int menu_redraw (MUTTMENU *menu)
813 /* See if all or part of the screen needs to be updated. */
814 if (menu->redraw & REDRAW_FULL)
816 menu_redraw_full (menu);
817 /* allow the caller to do any local configuration */
822 menu_check_recenter (menu);
824 if (menu->redraw & REDRAW_STATUS)
825 menu_redraw_status (menu);
826 if (menu->redraw & REDRAW_INDEX)
827 menu_redraw_index (menu);
828 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
829 menu_redraw_motion (menu);
830 else if (menu->redraw == REDRAW_CURRENT)
831 menu_redraw_current (menu);
834 menu_redraw_prompt (menu);
839 int mutt_menuLoop (MUTTMENU *menu)
845 if (option (OPTMENUCALLER))
847 unset_option (OPTMENUCALLER);
858 if (menu_redraw (menu) == OP_REDRAW)
861 menu->oldcurrent = menu->current;
864 /* move the cursor out of the way */
865 move (menu->current - menu->top + menu->offset,
866 (option (OPTARROWCURSOR) ? 2 : COLS-1));
870 /* try to catch dialog keys before ops */
871 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
874 i = km_dokey (menu->menu);
875 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
879 mvaddstr (LINES - 1, 0, "Tag-");
881 i = km_dokey (menu->menu);
883 CLEARLINE (LINES - 1);
885 else if (i == OP_TAG_PREFIX)
887 mutt_error _("No tagged entries.");
890 else /* None tagged, OP_TAG_PREFIX_COND */
896 if(tmp.op==OP_END_COND)break;
898 mutt_message _("Nothing to do.");
902 else if (menu->tagged && option (OPTAUTOTAG))
909 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
912 mutt_resize_screen ();
913 menu->redraw = REDRAW_FULL;
915 clearok(stdscr,TRUE);/*force complete redraw*/
925 /* Convert menubar movement to scrolling */
927 i = menu_dialog_translate_op (i);
932 menu_next_entry (menu);
935 menu_prev_entry (menu);
938 menu_half_down (menu);
944 menu_next_page (menu);
947 menu_prev_page (menu);
950 menu_next_line (menu);
953 menu_prev_line (menu);
956 menu_first_entry (menu);
959 menu_last_entry (menu);
962 menu_top_page (menu);
965 menu_middle_page (menu);
968 menu_bottom_page (menu);
971 menu_current_top (menu);
973 case OP_CURRENT_MIDDLE:
974 menu_current_middle (menu);
976 case OP_CURRENT_BOTTOM:
977 menu_current_bottom (menu);
980 case OP_SEARCH_REVERSE:
982 case OP_SEARCH_OPPOSITE:
983 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
985 menu->oldcurrent = menu->current;
986 if ((menu->current = menu_search (menu, i)) != -1)
987 menu->redraw = REDRAW_MOTION;
989 menu->current = menu->oldcurrent;
992 mutt_error _("Search is not implemented for this menu.");
997 mutt_error _("Jumping is not implemented for dialogs.");
1002 case OP_ENTER_COMMAND:
1003 CurrentMenu = menu->menu;
1004 mutt_enter_command ();
1005 if (option (OPTFORCEREDRAWINDEX))
1007 menu->redraw = REDRAW_FULL;
1008 unset_option (OPTFORCEREDRAWINDEX);
1009 unset_option (OPTFORCEREDRAWPAGER);
1014 if (menu->tag && !menu->dialog)
1016 if (menu->tagprefix && !option (OPTAUTOTAG))
1018 for (i = 0; i < menu->max; i++)
1019 menu->tagged += menu->tag (menu, i, 0);
1020 menu->redraw = REDRAW_INDEX;
1024 int i = menu->tag (menu, menu->current, -1);
1026 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1029 menu->redraw = REDRAW_MOTION_RESYNCH;
1032 menu->redraw = REDRAW_CURRENT;
1035 mutt_error _("No entries.");
1038 mutt_error _("Tagging is not supported.");
1041 case OP_SHELL_ESCAPE:
1042 mutt_shell_escape ();
1043 MAYBE_REDRAW (menu->redraw);
1051 clearok (stdscr, TRUE);
1052 menu->redraw = REDRAW_FULL;
1056 mutt_help (menu->menu);
1057 menu->redraw = REDRAW_FULL;
1061 km_error_key (menu->menu);