mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2002,2012 Michael R. Elkins <me@mutt.org>
3 *
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.
8 *
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.
13 *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#if HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "mutt.h"
24#include "mutt_curses.h"
25#include "mutt_menu.h"
26#include "mbyte.h"
27#ifdef USE_SIDEBAR
28#include "sidebar.h"
29#endif
30
31char* SearchBuffers[MENU_MAX];
32
33/* These are used to track the active menus, for redraw operations. */
34static size_t MenuStackCount = 0;
35static size_t MenuStackLen = 0;
36static MUTTMENU **MenuStack = NULL;
37
38static void print_enriched_string (int attr, unsigned char *s, int do_color)
39{
40 wchar_t wc;
41 size_t k;
42 size_t n = mutt_strlen ((char *)s);
43 mbstate_t mbstate;
44
45 memset (&mbstate, 0, sizeof (mbstate));
46 while (*s)
47 {
48 if (*s < MUTT_TREE_MAX)
49 {
50 if (do_color)
51 SETCOLOR (MT_COLOR_TREE);
52 while (*s && *s < MUTT_TREE_MAX)
53 {
54 switch (*s)
55 {
56 case MUTT_TREE_LLCORNER:
57 if (option (OPTASCIICHARS))
58 addch ('`');
59#ifdef WACS_LLCORNER
60 else
61 add_wch(WACS_LLCORNER);
62#else
63 else if (Charset_is_utf8)
64 addstr ("\342\224\224"); /* WACS_LLCORNER */
65 else
66 addch (ACS_LLCORNER);
67#endif
68 break;
69 case MUTT_TREE_ULCORNER:
70 if (option (OPTASCIICHARS))
71 addch (',');
72#ifdef WACS_ULCORNER
73 else
74 add_wch(WACS_ULCORNER);
75#else
76 else if (Charset_is_utf8)
77 addstr ("\342\224\214"); /* WACS_ULCORNER */
78 else
79 addch (ACS_ULCORNER);
80#endif
81 break;
82 case MUTT_TREE_LTEE:
83 if (option (OPTASCIICHARS))
84 addch ('|');
85#ifdef WACS_LTEE
86 else
87 add_wch(WACS_LTEE);
88#else
89 else if (Charset_is_utf8)
90 addstr ("\342\224\234"); /* WACS_LTEE */
91 else
92 addch (ACS_LTEE);
93#endif
94 break;
95 case MUTT_TREE_HLINE:
96 if (option (OPTASCIICHARS))
97 addch ('-');
98#ifdef WACS_HLINE
99 else
100 add_wch(WACS_HLINE);
101#else
102 else if (Charset_is_utf8)
103 addstr ("\342\224\200"); /* WACS_HLINE */
104 else
105 addch (ACS_HLINE);
106#endif
107 break;
108 case MUTT_TREE_VLINE:
109 if (option (OPTASCIICHARS))
110 addch ('|');
111#ifdef WACS_VLINE
112 else
113 add_wch(WACS_VLINE);
114#else
115 else if (Charset_is_utf8)
116 addstr ("\342\224\202"); /* WACS_VLINE */
117 else
118 addch (ACS_VLINE);
119#endif
120 break;
121 case MUTT_TREE_TTEE:
122 if (option (OPTASCIICHARS))
123 addch ('-');
124#ifdef WACS_TTEE
125 else
126 add_wch(WACS_TTEE);
127#else
128 else if (Charset_is_utf8)
129 addstr ("\342\224\254"); /* WACS_TTEE */
130 else
131 addch (ACS_TTEE);
132#endif
133 break;
134 case MUTT_TREE_BTEE:
135 if (option (OPTASCIICHARS))
136 addch ('-');
137#ifdef WACS_BTEE
138 else
139 add_wch(WACS_BTEE);
140#else
141 else if (Charset_is_utf8)
142 addstr ("\342\224\264"); /* WACS_BTEE */
143 else
144 addch (ACS_BTEE);
145#endif
146 break;
147 case MUTT_TREE_SPACE:
148 addch (' ');
149 break;
150 case MUTT_TREE_RARROW:
151 addch ('>');
152 break;
153 case MUTT_TREE_STAR:
154 addch ('*'); /* fake thread indicator */
155 break;
156 case MUTT_TREE_HIDDEN:
157 addch ('&');
158 break;
159 case MUTT_TREE_EQUALS:
160 addch ('=');
161 break;
162 case MUTT_TREE_MISSING:
163 addch ('?');
164 break;
165 }
166 s++, n--;
167 }
168 if (do_color) ATTRSET(attr);
169 }
170 else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
171 {
172 addnstr ((char *)s, k);
173 s += k, n-= k;
174 }
175 else
176 break;
177 }
178}
179
180static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i)
181{
182 if (menu->dialog)
183 {
184 strncpy (s, NONULL (menu->dialog[i]), l);
185 menu->current = -1; /* hide menubar */
186 }
187 else
188 menu->make_entry (s, l, menu, i);
189}
190
191static void menu_pad_string (MUTTMENU *menu, char *s, size_t n)
192{
193 char *scratch = safe_strdup (s);
194 int shift = option (OPTARROWCURSOR) ? 3 : 0;
195 int cols = menu->indexwin->cols - shift;
196
197 mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 1);
198 s[n - 1] = 0;
199 FREE (&scratch);
200}
201
202void menu_redraw_full (MUTTMENU *menu)
203{
204#if ! (defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM))
205 mutt_reflow_windows ();
206#endif
207 NORMAL_COLOR;
208 /* clear() doesn't optimize screen redraws */
209 move (0, 0);
210 clrtobot ();
211
212 if (option (OPTHELP))
213 {
214 SETCOLOR (MT_COLOR_STATUS);
215 mutt_window_move (menu->helpwin, 0, 0);
216 mutt_paddstr (menu->helpwin->cols, menu->help);
217 NORMAL_COLOR;
218 }
219 menu->offset = 0;
220 menu->pagelen = menu->indexwin->rows;
221
222 mutt_show_error ();
223
224 menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
225#ifdef USE_SIDEBAR
226 menu->redraw |= REDRAW_SIDEBAR;
227#endif
228}
229
230void menu_redraw_status (MUTTMENU *menu)
231{
232 char buf[STRING];
233
234 snprintf (buf, sizeof (buf), MUTT_MODEFMT, menu->title);
235 SETCOLOR (MT_COLOR_STATUS);
236 mutt_window_move (menu->statuswin, 0, 0);
237 mutt_paddstr (menu->statuswin->cols, buf);
238 NORMAL_COLOR;
239 menu->redraw &= ~REDRAW_STATUS;
240}
241
242#ifdef USE_SIDEBAR
243void menu_redraw_sidebar (MUTTMENU *menu)
244{
245 menu->redraw &= ~REDRAW_SIDEBAR;
246 mutt_sb_draw ();
247}
248#endif
249
250void menu_redraw_index (MUTTMENU *menu)
251{
252 char buf[LONG_STRING];
253 int i;
254 int do_color;
255 int attr;
256
257 for (i = menu->top; i < menu->top + menu->pagelen; i++)
258 {
259 if (i < menu->max)
260 {
261 attr = menu->color(i);
262
263 menu_make_entry (buf, sizeof (buf), menu, i);
264 menu_pad_string (menu, buf, sizeof (buf));
265
266 ATTRSET(attr);
267 mutt_window_move (menu->indexwin, i - menu->top + menu->offset, 0);
268 do_color = 1;
269
270 if (i == menu->current)
271 {
272 SETCOLOR(MT_COLOR_INDICATOR);
273 if (option(OPTARROWCURSOR))
274 {
275 addstr ("->");
276 ATTRSET(attr);
277 addch (' ');
278 }
279 else
280 do_color = 0;
281 }
282 else if (option(OPTARROWCURSOR))
283 addstr(" ");
284
285 print_enriched_string (attr, (unsigned char *) buf, do_color);
286 }
287 else
288 {
289 NORMAL_COLOR;
290 mutt_window_clearline (menu->indexwin, i - menu->top + menu->offset);
291 }
292 }
293 NORMAL_COLOR;
294 menu->redraw = 0;
295}
296
297void menu_redraw_motion (MUTTMENU *menu)
298{
299 char buf[LONG_STRING];
300 int old_color, cur_color;
301
302 if (menu->dialog)
303 {
304 menu->redraw &= ~REDRAW_MOTION;
305 return;
306 }
307
308 /* Note: menu->color() for the index can end up retrieving a message
309 * over imap (if matching against ~h for instance). This can
310 * generate status messages. So we want to call it *before* we
311 * position the cursor for drawing. */
312 old_color = menu->color (menu->oldcurrent);
313 mutt_window_move (menu->indexwin, menu->oldcurrent + menu->offset - menu->top, 0);
314 ATTRSET(old_color);
315
316 if (option (OPTARROWCURSOR))
317 {
318 /* clear the pointer */
319 addstr (" ");
320
321 if (menu->redraw & REDRAW_MOTION_RESYNCH)
322 {
323 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
324 menu_pad_string (menu, buf, sizeof (buf));
325 mutt_window_move (menu->indexwin, menu->oldcurrent + menu->offset - menu->top, 3);
326 print_enriched_string (old_color, (unsigned char *) buf, 1);
327 }
328
329 /* now draw it in the new location */
330 SETCOLOR(MT_COLOR_INDICATOR);
331 mutt_window_mvaddstr (menu->indexwin, menu->current + menu->offset - menu->top, 0, "->");
332 }
333 else
334 {
335 /* erase the current indicator */
336 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
337 menu_pad_string (menu, buf, sizeof (buf));
338 print_enriched_string (old_color, (unsigned char *) buf, 1);
339
340 /* now draw the new one to reflect the change */
341 cur_color = menu->color (menu->current);
342 menu_make_entry (buf, sizeof (buf), menu, menu->current);
343 menu_pad_string (menu, buf, sizeof (buf));
344 SETCOLOR(MT_COLOR_INDICATOR);
345 mutt_window_move (menu->indexwin, menu->current + menu->offset - menu->top, 0);
346 print_enriched_string (cur_color, (unsigned char *) buf, 0);
347 }
348 menu->redraw &= REDRAW_STATUS;
349 NORMAL_COLOR;
350}
351
352void menu_redraw_current (MUTTMENU *menu)
353{
354 char buf[LONG_STRING];
355 int attr = menu->color (menu->current);
356
357 mutt_window_move (menu->indexwin, menu->current + menu->offset - menu->top, 0);
358 menu_make_entry (buf, sizeof (buf), menu, menu->current);
359 menu_pad_string (menu, buf, sizeof (buf));
360
361 SETCOLOR(MT_COLOR_INDICATOR);
362 if (option (OPTARROWCURSOR))
363 {
364 addstr ("->");
365 ATTRSET(attr);
366 addch (' ');
367 menu_pad_string (menu, buf, sizeof (buf));
368 print_enriched_string (attr, (unsigned char *) buf, 1);
369 }
370 else
371 print_enriched_string (attr, (unsigned char *) buf, 0);
372 menu->redraw &= REDRAW_STATUS;
373 NORMAL_COLOR;
374}
375
376static void menu_redraw_prompt (MUTTMENU *menu)
377{
378 if (menu->dialog)
379 {
380 if (option (OPTMSGERR))
381 {
382 mutt_sleep (1);
383 unset_option (OPTMSGERR);
384 }
385
386 if (*Errorbuf)
387 mutt_clear_error ();
388
389 mutt_window_mvaddstr (menu->messagewin, 0, 0, menu->prompt);
390 mutt_window_clrtoeol (menu->messagewin);
391 }
392}
393
394void menu_check_recenter (MUTTMENU *menu)
395{
396 int c = MIN (MenuContext, menu->pagelen / 2);
397 int old_top = menu->top;
398
399 if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) /* less entries than lines */
400 {
401 if (menu->top != 0)
402 {
403 menu->top = 0;
404 menu->redraw |= REDRAW_INDEX;
405 }
406 }
407 else
408 {
409 if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext))
410 {
411 if (menu->current < menu->top + c)
412 menu->top = menu->current - c;
413 else if (menu->current >= menu->top + menu->pagelen - c)
414 menu->top = menu->current - menu->pagelen + c + 1;
415 }
416 else
417 {
418 if (menu->current < menu->top + c)
419 menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
420 else if ((menu->current >= menu->top + menu->pagelen - c))
421 menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
422 }
423 }
424
425 if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
426 menu->top = MIN (menu->top, menu->max - menu->pagelen);
427 menu->top = MAX (menu->top, 0);
428
429 if (menu->top != old_top)
430 menu->redraw |= REDRAW_INDEX;
431}
432
433void menu_jump (MUTTMENU *menu)
434{
435 int n;
436 char buf[SHORT_STRING];
437
438 if (menu->max)
439 {
440 mutt_unget_event (LastKey, 0);
441 buf[0] = 0;
442 if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
443 {
444 if (mutt_atoi (buf, &n) == 0 && n > 0 && n < menu->max + 1)
445 {
446 n--; /* msg numbers are 0-based */
447 menu->current = n;
448 menu->redraw = REDRAW_MOTION;
449 }
450 else
451 mutt_error _("Invalid index number.");
452 }
453 }
454 else
455 mutt_error _("No entries.");
456}
457
458void menu_next_line (MUTTMENU *menu)
459{
460 if (menu->max)
461 {
462 int c = MIN (MenuContext, menu->pagelen / 2);
463
464 if ((menu->top + 1 < menu->max - c) &&
465 (option(OPTMENUMOVEOFF) ||
466 (menu->max > menu->pagelen && menu->top < menu->max - menu->pagelen)))
467 {
468 menu->top++;
469 if (menu->current < menu->top + c && menu->current < menu->max - 1)
470 menu->current++;
471 menu->redraw = REDRAW_INDEX;
472 }
473 else
474 mutt_error _("You cannot scroll down farther.");
475 }
476 else
477 mutt_error _("No entries.");
478}
479
480void menu_prev_line (MUTTMENU *menu)
481{
482 if (menu->top > 0)
483 {
484 int c = MIN (MenuContext, menu->pagelen / 2);
485
486 menu->top--;
487 if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
488 menu->current--;
489 menu->redraw = REDRAW_INDEX;
490 }
491 else
492 mutt_error _("You cannot scroll up farther.");
493}
494
495/*
496 * pageup: jumplen == -pagelen
497 * pagedown: jumplen == pagelen
498 * halfup: jumplen == -pagelen/2
499 * halfdown: jumplen == pagelen/2
500 */
501#define DIRECTION ((neg * 2) + 1)
502static void menu_length_jump (MUTTMENU *menu, int jumplen)
503{
504 int tmp, neg = (jumplen >= 0) ? 0 : -1;
505 int c = MIN (MenuContext, menu->pagelen / 2);
506
507 if (menu->max)
508 {
509 /* possible to scroll? */
510 if (DIRECTION * menu->top <
511 (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/))))
512 {
513 menu->top += jumplen;
514
515 /* jumped too long? */
516 if ((neg || !option (OPTMENUMOVEOFF)) &&
517 DIRECTION * menu->top > tmp)
518 menu->top = tmp;
519
520 /* need to move the cursor? */
521 if ((DIRECTION *
522 (tmp = (menu->current -
523 (menu->top + (neg ? (menu->pagelen - 1) - c : c))
524 ))) < 0)
525 menu->current -= tmp;
526
527 menu->redraw = REDRAW_INDEX;
528 }
529 else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog)
530 {
531 menu->current += jumplen;
532 menu->redraw = REDRAW_MOTION;
533 }
534 else
535 mutt_error (neg ? _("You are on the first page.")
536 : _("You are on the last page."));
537
538 menu->current = MIN (menu->current, menu->max - 1);
539 menu->current = MAX (menu->current, 0);
540 }
541 else
542 mutt_error _("No entries.");
543}
544#undef DIRECTION
545
546void menu_next_page (MUTTMENU *menu)
547{
548 menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
549}
550
551void menu_prev_page (MUTTMENU *menu)
552{
553 menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
554}
555
556void menu_half_down (MUTTMENU *menu)
557{
558 menu_length_jump (menu, menu->pagelen / 2);
559}
560
561void menu_half_up (MUTTMENU *menu)
562{
563 menu_length_jump (menu, 0 - menu->pagelen / 2);
564}
565
566void menu_top_page (MUTTMENU *menu)
567{
568 if (menu->current != menu->top)
569 {
570 menu->current = menu->top;
571 menu->redraw = REDRAW_MOTION;
572 }
573}
574
575void menu_bottom_page (MUTTMENU *menu)
576{
577 if (menu->max)
578 {
579 menu->current = menu->top + menu->pagelen - 1;
580 if (menu->current > menu->max - 1)
581 menu->current = menu->max - 1;
582 menu->redraw = REDRAW_MOTION;
583 }
584 else
585 mutt_error _("No entries.");
586}
587
588void menu_middle_page (MUTTMENU *menu)
589{
590 int i;
591
592 if (menu->max)
593 {
594 i = menu->top + menu->pagelen;
595 if (i > menu->max - 1)
596 i = menu->max - 1;
597 menu->current = menu->top + (i - menu->top) / 2;
598 menu->redraw = REDRAW_MOTION;
599 }
600 else
601 mutt_error _("No entries.");
602}
603
604void menu_first_entry (MUTTMENU *menu)
605{
606 if (menu->max)
607 {
608 menu->current = 0;
609 menu->redraw = REDRAW_MOTION;
610 }
611 else
612 mutt_error _("No entries.");
613}
614
615void menu_last_entry (MUTTMENU *menu)
616{
617 if (menu->max)
618 {
619 menu->current = menu->max - 1;
620 menu->redraw = REDRAW_MOTION;
621 }
622 else
623 mutt_error _("No entries.");
624}
625
626void menu_current_top (MUTTMENU *menu)
627{
628 if (menu->max)
629 {
630 menu->top = menu->current;
631 menu->redraw = REDRAW_INDEX;
632 }
633 else
634 mutt_error _("No entries.");
635}
636
637void menu_current_middle (MUTTMENU *menu)
638{
639 if (menu->max)
640 {
641 menu->top = menu->current - menu->pagelen / 2;
642 if (menu->top < 0)
643 menu->top = 0;
644 menu->redraw = REDRAW_INDEX;
645 }
646 else
647 mutt_error _("No entries.");
648}
649
650void menu_current_bottom (MUTTMENU *menu)
651{
652 if (menu->max)
653 {
654 menu->top = menu->current - menu->pagelen + 1;
655 if (menu->top < 0)
656 menu->top = 0;
657 menu->redraw = REDRAW_INDEX;
658 }
659 else
660 mutt_error _("No entries.");
661}
662
663static void menu_next_entry (MUTTMENU *menu)
664{
665 if (menu->current < menu->max - 1)
666 {
667 menu->current++;
668 menu->redraw = REDRAW_MOTION;
669 }
670 else
671 mutt_error _("You are on the last entry.");
672}
673
674static void menu_prev_entry (MUTTMENU *menu)
675{
676 if (menu->current)
677 {
678 menu->current--;
679 menu->redraw = REDRAW_MOTION;
680 }
681 else
682 mutt_error _("You are on the first entry.");
683}
684
685static int default_color (int i)
686{
687 return ColorDefs[MT_COLOR_NORMAL];
688}
689
690static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
691{
692 char buf[LONG_STRING];
693
694 menu_make_entry (buf, sizeof (buf), m, n);
695 return (regexec (re, buf, 0, NULL, 0));
696}
697
698void mutt_menu_init (void)
699{
700 int i;
701
702 for (i = 0; i < MENU_MAX; i++)
703 SearchBuffers[i] = NULL;
704}
705
706MUTTMENU *mutt_new_menu (int menu)
707{
708 MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
709
710 if ((menu < 0) || (menu >= MENU_MAX))
711 menu = MENU_GENERIC;
712
713 p->menu = menu;
714 p->current = 0;
715 p->top = 0;
716 p->offset = 0;
717 p->redraw = REDRAW_FULL;
718 p->pagelen = MuttIndexWindow->rows;
719 p->indexwin = MuttIndexWindow;
720 p->statuswin = MuttStatusWindow;
721 p->helpwin = MuttHelpWindow;
722 p->messagewin = MuttMessageWindow;
723 p->color = default_color;
724 p->search = menu_search_generic;
725
726 return (p);
727}
728
729void mutt_menuDestroy (MUTTMENU **p)
730{
731 int i;
732
733 if ((*p)->dialog)
734 {
735 for (i=0; i < (*p)->max; i++)
736 FREE (&(*p)->dialog[i]);
737
738 FREE (& (*p)->dialog);
739 }
740
741 FREE (p); /* __FREE_CHECKED__ */
742}
743
744void mutt_menu_add_dialog_row (MUTTMENU *m, const char *row)
745{
746 if (m->dsize <= m->max)
747 {
748 m->dsize += 10;
749 safe_realloc (&m->dialog, m->dsize * sizeof (char *));
750 }
751 m->dialog[m->max++] = safe_strdup (row);
752}
753
754static MUTTMENU *get_current_menu (void)
755{
756 return MenuStackCount ? MenuStack[MenuStackCount - 1] : NULL;
757}
758
759void mutt_push_current_menu (MUTTMENU *menu)
760{
761 if (MenuStackCount >= MenuStackLen)
762 {
763 MenuStackLen += 5;
764 safe_realloc (&MenuStack, MenuStackLen * sizeof(MUTTMENU *));
765 }
766
767 MenuStack[MenuStackCount++] = menu;
768 CurrentMenu = menu->menu;
769}
770
771void mutt_pop_current_menu (MUTTMENU *menu)
772{
773 MUTTMENU *prev_menu;
774
775 if (!MenuStackCount ||
776 (MenuStack[MenuStackCount - 1] != menu))
777 {
778 dprint (1, (debugfile, "mutt_pop_current_menu() called with inactive menu\n"));
779 return;
780 }
781
782 MenuStackCount--;
783 prev_menu = get_current_menu ();
784 if (prev_menu)
785 {
786 CurrentMenu = prev_menu->menu;
787 prev_menu->redraw = REDRAW_FULL;
788 }
789 else
790 {
791 CurrentMenu = MENU_MAIN;
792 /* Clearing when Mutt exits would be an annoying change in
793 * behavior for those who have disabled alternative screens. The
794 * option is currently set by autocrypt initialization which mixes
795 * menus and prompts outside of the normal menu system state.
796 */
797 if (option (OPTMENUPOPCLEARSCREEN))
798 {
799 move (0, 0);
800 clrtobot ();
801 }
802 }
803}
804
805void mutt_set_current_menu_redraw (int redraw)
806{
807 MUTTMENU *current_menu;
808
809 current_menu = get_current_menu ();
810 if (current_menu)
811 current_menu->redraw |= redraw;
812}
813
814void mutt_set_current_menu_redraw_full (void)
815{
816 MUTTMENU *current_menu;
817
818 current_menu = get_current_menu ();
819 if (current_menu)
820 current_menu->redraw = REDRAW_FULL;
821}
822
823void mutt_set_menu_redraw (int menu_type, int redraw)
824{
825 if (CurrentMenu == menu_type)
826 mutt_set_current_menu_redraw (redraw);
827}
828
829void mutt_set_menu_redraw_full (int menu_type)
830{
831 if (CurrentMenu == menu_type)
832 mutt_set_current_menu_redraw_full ();
833}
834
835void mutt_current_menu_redraw ()
836{
837 MUTTMENU *current_menu;
838
839 current_menu = get_current_menu ();
840 if (current_menu)
841 {
842 if (menu_redraw (current_menu) == OP_REDRAW)
843 /* On a REDRAW_FULL with a non-customized redraw, menu_redraw()
844 * will return OP_REDRAW to give the calling menu-loop a chance to
845 * customize output.
846 */
847 menu_redraw (current_menu);
848 }
849}
850
851
852#define MUTT_SEARCH_UP 1
853#define MUTT_SEARCH_DOWN 2
854
855static int menu_search (MUTTMENU *menu, int op)
856{
857 int r, wrap = 0;
858 int searchDir;
859 regex_t re;
860 char buf[SHORT_STRING];
861 char* searchBuf = menu->menu >= 0 && menu->menu < MENU_MAX ?
862 SearchBuffers[menu->menu] : NULL;
863
864 if (!(searchBuf && *searchBuf) ||
865 (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE))
866 {
867 strfcpy (buf, searchBuf && *searchBuf ? searchBuf : "", sizeof (buf));
868 if (mutt_get_field ((op == OP_SEARCH || op == OP_SEARCH_NEXT)
869 ? _("Search for: ") : _("Reverse search for: "),
870 buf, sizeof (buf), MUTT_CLEAR) != 0 || !buf[0])
871 return (-1);
872 if (menu->menu >= 0 && menu->menu < MENU_MAX)
873 {
874 mutt_str_replace (&SearchBuffers[menu->menu], buf);
875 searchBuf = SearchBuffers[menu->menu];
876 }
877 menu->searchDir = (op == OP_SEARCH || op == OP_SEARCH_NEXT) ?
878 MUTT_SEARCH_DOWN : MUTT_SEARCH_UP;
879 }
880
881 searchDir = (menu->searchDir == MUTT_SEARCH_UP) ? -1 : 1;
882 if (op == OP_SEARCH_OPPOSITE)
883 searchDir = -searchDir;
884
885 if ((r = REGCOMP (&re, searchBuf, REG_NOSUB | mutt_which_case (searchBuf))) != 0)
886 {
887 regerror (r, &re, buf, sizeof (buf));
888 mutt_error ("%s", buf);
889 return (-1);
890 }
891
892 r = menu->current + searchDir;
893search_next:
894 if (wrap)
895 mutt_message (_("Search wrapped to top."));
896 while (r >= 0 && r < menu->max)
897 {
898 if (menu->search (menu, &re, r) == 0)
899 {
900 regfree (&re);
901 return r;
902 }
903
904 r += searchDir;
905 }
906
907 if (option (OPTWRAPSEARCH) && wrap++ == 0)
908 {
909 r = searchDir == 1 ? 0 : menu->max - 1;
910 goto search_next;
911 }
912 regfree (&re);
913 mutt_error _("Not found.");
914 return (-1);
915}
916
917static int menu_dialog_translate_op (int i)
918{
919 switch (i)
920 {
921 case OP_NEXT_ENTRY:
922 return OP_NEXT_LINE;
923 case OP_PREV_ENTRY:
924 return OP_PREV_LINE;
925 case OP_CURRENT_TOP: case OP_FIRST_ENTRY:
926 return OP_TOP_PAGE;
927 case OP_CURRENT_BOTTOM: case OP_LAST_ENTRY:
928 return OP_BOTTOM_PAGE;
929 case OP_CURRENT_MIDDLE:
930 return OP_MIDDLE_PAGE;
931 }
932
933 return i;
934}
935
936static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
937{
938 event_t ch;
939 char *p;
940
941 do
942 {
943 ch = mutt_getch();
944 } while (ch.ch == -2);
945
946 if (ch.ch < 0)
947 {
948 *ip = -1;
949 return 0;
950 }
951
952 if (ch.ch && (p = strchr (menu->keys, ch.ch)))
953 {
954 *ip = OP_MAX + (p - menu->keys + 1);
955 return 0;
956 }
957 else
958 {
959 mutt_unget_event (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
960 return -1;
961 }
962}
963
964int menu_redraw (MUTTMENU *menu)
965{
966 if (menu->custom_menu_redraw)
967 {
968 menu->custom_menu_redraw (menu);
969 return OP_NULL;
970 }
971
972 /* See if all or part of the screen needs to be updated. */
973 if (menu->redraw & REDRAW_FULL)
974 {
975 menu_redraw_full (menu);
976 /* allow the caller to do any local configuration */
977 return (OP_REDRAW);
978 }
979
980 if (!menu->dialog)
981 menu_check_recenter (menu);
982
983 if (menu->redraw & REDRAW_STATUS)
984 menu_redraw_status (menu);
985#ifdef USE_SIDEBAR
986 if (menu->redraw & REDRAW_SIDEBAR)
987 menu_redraw_sidebar (menu);
988#endif
989 if (menu->redraw & REDRAW_INDEX)
990 menu_redraw_index (menu);
991 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
992 menu_redraw_motion (menu);
993 else if (menu->redraw == REDRAW_CURRENT)
994 menu_redraw_current (menu);
995
996 if (menu->dialog)
997 menu_redraw_prompt (menu);
998
999 return OP_NULL;
1000}
1001
1002int mutt_menuLoop (MUTTMENU *menu)
1003{
1004 int i = OP_NULL;
1005
1006 FOREVER
1007 {
1008 if (option (OPTMENUCALLER))
1009 {
1010 unset_option (OPTMENUCALLER);
1011 return OP_NULL;
1012 }
1013
1014 /* Clear the tag prefix unless we just started it. Don't clear
1015 * the prefix on a timeout (i==-2), but do clear on an abort (i==-1)
1016 */
1017 if (menu->tagprefix &&
1018 i != OP_TAG_PREFIX && i != OP_TAG_PREFIX_COND && i != -2)
1019 menu->tagprefix = 0;
1020
1021 mutt_curs_set (0);
1022
1023 if (menu_redraw (menu) == OP_REDRAW)
1024 return OP_REDRAW;
1025
1026 /* give visual indication that the next command is a tag- command */
1027 if (menu->tagprefix)
1028 {
1029 mutt_window_mvaddstr (menu->messagewin, 0, 0, "tag-");
1030 mutt_window_clrtoeol (menu->messagewin);
1031 }
1032
1033 menu->oldcurrent = menu->current;
1034
1035
1036 /* move the cursor out of the way */
1037 if (option (OPTARROWCURSOR))
1038 mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset, 2);
1039 else if (option (OPTBRAILLEFRIENDLY))
1040 mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset, 0);
1041 else
1042 mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset,
1043 menu->indexwin->cols - 1);
1044
1045 mutt_refresh ();
1046
1047 /* try to catch dialog keys before ops */
1048 if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
1049 return i;
1050
1051 i = km_dokey (menu->menu);
1052 if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
1053 {
1054 if (menu->tagprefix)
1055 {
1056 menu->tagprefix = 0;
1057 mutt_window_clearline (menu->messagewin, 0);
1058 continue;
1059 }
1060
1061 if (menu->tagged)
1062 {
1063 menu->tagprefix = 1;
1064 continue;
1065 }
1066 else if (i == OP_TAG_PREFIX)
1067 {
1068 mutt_error _("No tagged entries.");
1069 i = -1;
1070 }
1071 else /* None tagged, OP_TAG_PREFIX_COND */
1072 {
1073 mutt_flush_macro_to_endcond ();
1074 mutt_message _("Nothing to do.");
1075 i = -1;
1076 }
1077 }
1078 else if (menu->tagged && option (OPTAUTOTAG))
1079 menu->tagprefix = 1;
1080
1081 mutt_curs_set (1);
1082
1083#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
1084 if (SigWinch)
1085 {
1086 SigWinch = 0;
1087 mutt_resize_screen ();
1088 clearok(stdscr,TRUE);/*force complete redraw*/
1089 }
1090#endif
1091
1092 if (i < 0)
1093 {
1094 if (menu->tagprefix)
1095 mutt_window_clearline (menu->messagewin, 0);
1096 continue;
1097 }
1098
1099 if (!menu->dialog)
1100 mutt_clear_error ();
1101
1102 /* Convert menubar movement to scrolling */
1103 if (menu->dialog)
1104 i = menu_dialog_translate_op (i);
1105
1106 switch (i)
1107 {
1108 case OP_NEXT_ENTRY:
1109 menu_next_entry (menu);
1110 break;
1111 case OP_PREV_ENTRY:
1112 menu_prev_entry (menu);
1113 break;
1114 case OP_HALF_DOWN:
1115 menu_half_down (menu);
1116 break;
1117 case OP_HALF_UP:
1118 menu_half_up (menu);
1119 break;
1120 case OP_NEXT_PAGE:
1121 menu_next_page (menu);
1122 break;
1123 case OP_PREV_PAGE:
1124 menu_prev_page (menu);
1125 break;
1126 case OP_NEXT_LINE:
1127 menu_next_line (menu);
1128 break;
1129 case OP_PREV_LINE:
1130 menu_prev_line (menu);
1131 break;
1132 case OP_FIRST_ENTRY:
1133 menu_first_entry (menu);
1134 break;
1135 case OP_LAST_ENTRY:
1136 menu_last_entry (menu);
1137 break;
1138 case OP_TOP_PAGE:
1139 menu_top_page (menu);
1140 break;
1141 case OP_MIDDLE_PAGE:
1142 menu_middle_page (menu);
1143 break;
1144 case OP_BOTTOM_PAGE:
1145 menu_bottom_page (menu);
1146 break;
1147 case OP_CURRENT_TOP:
1148 menu_current_top (menu);
1149 break;
1150 case OP_CURRENT_MIDDLE:
1151 menu_current_middle (menu);
1152 break;
1153 case OP_CURRENT_BOTTOM:
1154 menu_current_bottom (menu);
1155 break;
1156 case OP_SEARCH:
1157 case OP_SEARCH_REVERSE:
1158 case OP_SEARCH_NEXT:
1159 case OP_SEARCH_OPPOSITE:
1160 if (menu->search && !menu->dialog) /* Searching dialogs won't work */
1161 {
1162 menu->oldcurrent = menu->current;
1163 if ((menu->current = menu_search (menu, i)) != -1)
1164 menu->redraw = REDRAW_MOTION;
1165 else
1166 menu->current = menu->oldcurrent;
1167 }
1168 else
1169 mutt_error _("Search is not implemented for this menu.");
1170 break;
1171
1172 case OP_JUMP:
1173 if (menu->dialog)
1174 mutt_error _("Jumping is not implemented for dialogs.");
1175 else
1176 menu_jump (menu);
1177 break;
1178
1179 case OP_ENTER_COMMAND:
1180 mutt_enter_command ();
1181 break;
1182
1183 case OP_TAG:
1184 if (menu->tag && !menu->dialog)
1185 {
1186 if (menu->tagprefix && !option (OPTAUTOTAG))
1187 {
1188 for (i = 0; i < menu->max; i++)
1189 menu->tagged += menu->tag (menu, i, 0);
1190 menu->redraw |= REDRAW_INDEX;
1191 }
1192 else if (menu->max)
1193 {
1194 int i = menu->tag (menu, menu->current, -1);
1195 menu->tagged += i;
1196 if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1197 {
1198 menu->current++;
1199 menu->redraw |= REDRAW_MOTION_RESYNCH;
1200 }
1201 else
1202 menu->redraw |= REDRAW_CURRENT;
1203 }
1204 else
1205 mutt_error _("No entries.");
1206 }
1207 else
1208 mutt_error _("Tagging is not supported.");
1209 break;
1210
1211 case OP_SHELL_ESCAPE:
1212 mutt_shell_escape ();
1213 break;
1214
1215 case OP_WHAT_KEY:
1216 mutt_what_key ();
1217 break;
1218
1219 case OP_CHECK_STATS:
1220 mutt_check_stats ();
1221 break;
1222
1223 case OP_REDRAW:
1224 clearok (stdscr, TRUE);
1225 menu->redraw = REDRAW_FULL;
1226 break;
1227
1228 case OP_HELP:
1229 mutt_help (menu->menu);
1230 menu->redraw = REDRAW_FULL;
1231 break;
1232
1233 case OP_ERROR_HISTORY:
1234 mutt_error_history_display ();
1235 menu->redraw = REDRAW_FULL;
1236 break;
1237
1238 case OP_NULL:
1239 km_error_key (menu->menu);
1240 break;
1241
1242 case OP_END_COND:
1243 break;
1244
1245 default:
1246 return (i);
1247 }
1248 }
1249 /* not reached */
1250}