Rocco Rutte:
[apps/madmutt.git] / menu.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
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.
8  */
9
10 #if HAVE_CONFIG_H
11 # include "config.h"
12 #endif
13
14 #include "mutt.h"
15 #include "enter.h"
16 #include "mutt_curses.h"
17 #include "mutt_menu.h"
18 #include "mbyte.h"
19 #include "sidebar.h"
20
21 #ifdef USE_IMAP
22 #include "imap.h"
23 #endif
24
25 #include "lib/mem.h"
26 #include "lib/intl.h"
27 #include "lib/str.h"
28
29 #include <string.h>
30 #include <stdlib.h>
31
32 extern int Charset_is_utf8;     /* FIXME: bad modularisation */
33
34 extern size_t UngetCount;
35
36 static void print_enriched_string (int attr, unsigned char *s, int do_color)
37 {
38   wchar_t wc;
39   size_t k;
40   size_t n = str_len ((char *) s);
41   mbstate_t mbstate;
42
43   memset (&mbstate, 0, sizeof (mbstate));
44   while (*s) {
45     if (*s < M_TREE_MAX) {
46       if (do_color)
47         SETCOLOR (MT_COLOR_TREE);
48       while (*s && *s < M_TREE_MAX) {
49         switch (*s) {
50         case M_TREE_LLCORNER:
51           if (option (OPTASCIICHARS))
52             addch ('`');
53           else if (Charset_is_utf8)
54             addstr ("\342\224\224");    /* WACS_LLCORNER */
55           else
56             addch (ACS_LLCORNER);
57           break;
58         case M_TREE_ULCORNER:
59           if (option (OPTASCIICHARS))
60             addch (',');
61           else if (Charset_is_utf8)
62             addstr ("\342\224\214");    /* WACS_ULCORNER */
63           else
64             addch (ACS_ULCORNER);
65           break;
66         case M_TREE_LTEE:
67           if (option (OPTASCIICHARS))
68             addch ('|');
69           else if (Charset_is_utf8)
70             addstr ("\342\224\234");    /* WACS_LTEE */
71           else
72             addch (ACS_LTEE);
73           break;
74         case M_TREE_HLINE:
75           if (option (OPTASCIICHARS))
76             addch ('-');
77           else if (Charset_is_utf8)
78             addstr ("\342\224\200");    /* WACS_HLINE */
79           else
80             addch (ACS_HLINE);
81           break;
82         case M_TREE_VLINE:
83           if (option (OPTASCIICHARS))
84             addch ('|');
85           else if (Charset_is_utf8)
86             addstr ("\342\224\202");    /* WACS_VLINE */
87           else
88             addch (ACS_VLINE);
89           break;
90         case M_TREE_TTEE:
91           if (option (OPTASCIICHARS))
92             addch ('-');
93           else if (Charset_is_utf8)
94             addstr ("\342\224\254");    /* WACS_TTEE */
95           else
96             addch (ACS_TTEE);
97           break;
98         case M_TREE_BTEE:
99           if (option (OPTASCIICHARS))
100             addch ('-');
101           else if (Charset_is_utf8)
102             addstr ("\342\224\264");    /* WACS_BTEE */
103           else
104             addch (ACS_BTEE);
105           break;
106         case M_TREE_SPACE:
107           addch (' ');
108           break;
109         case M_TREE_RARROW:
110           addch ('>');
111           break;
112         case M_TREE_STAR:
113           addch ('*');          /* fake thread indicator */
114           break;
115         case M_TREE_HIDDEN:
116           addch ('&');
117           break;
118         case M_TREE_EQUALS:
119           addch ('=');
120           break;
121         case M_TREE_MISSING:
122           addch ('?');
123           break;
124         }
125         s++, n--;
126       }
127       if (do_color)
128         attrset (attr);
129     }
130     else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) > 0) {
131       addnstr ((char *) s, k);
132       s += k, n -= k;
133     }
134     else
135       break;
136   }
137 }
138
139 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
140 {
141   if (menu->dialog) {
142     strncpy (s, menu->dialog[i], l);
143     menu->current = -1;         /* hide menubar */
144   }
145   else
146     menu->make_entry (s, l, menu, i);
147 }
148
149 void menu_pad_string (char *s, size_t n)
150 {
151   int shift = option (OPTARROWCURSOR) ? 3 : 0;
152   int cols;
153   char *tmpbuf = mem_malloc (n);
154
155   if (option (OPTMBOXPANE))
156     cols = COLS - shift - SidebarWidth;
157   else
158     cols = COLS - shift;
159   mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, str_len (s), 1);
160   tmpbuf[n - 1] = 0;
161   snprintf (s, n, "%s", tmpbuf);        /* overkill */
162   mem_free (&tmpbuf);
163 }
164
165 void menu_redraw_full (MUTTMENU * menu)
166 {
167   SETCOLOR (MT_COLOR_NORMAL);
168   /* clear() doesn't optimize screen redraws */
169   move (0, 0);
170   clrtobot ();
171
172   if (option (OPTHELP)) {
173     SETCOLOR (MT_COLOR_STATUS);
174     move (option (OPTSTATUSONTOP) ? LINES - 2 : 0, 0);
175     mutt_paddstr (COLS, menu->help);
176     SETCOLOR (MT_COLOR_NORMAL);
177     menu->offset = 1;
178     menu->pagelen = LINES - 3;
179   }
180   else {
181     menu->offset = option (OPTSTATUSONTOP) ? 1 : 0;
182     menu->pagelen = LINES - 2;
183   }
184
185   mutt_show_error ();
186
187   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
188 }
189
190 void menu_redraw_status (MUTTMENU * menu)
191 {
192   char buf[STRING];
193
194   snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
195   SETCOLOR (MT_COLOR_STATUS);
196   move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, 0);
197   mutt_paddstr (COLS, buf);
198   SETCOLOR (MT_COLOR_NORMAL);
199   menu->redraw &= ~REDRAW_STATUS;
200 }
201
202 void menu_redraw_index (MUTTMENU * menu)
203 {
204   char buf[STRING];
205   int i;
206
207   sidebar_draw (1);
208   for (i = menu->top; i < menu->top + menu->pagelen; i++) {
209     if (i < menu->max) {
210       menu_make_entry (buf, sizeof (buf), menu, i);
211       menu_pad_string (buf, sizeof (buf));
212
213       if (option (OPTARROWCURSOR)) {
214         attrset (menu->color (i));
215         CLEARLINE_WIN (i - menu->top + menu->offset);
216
217         if (i == menu->current) {
218           attrset (menu->color (i));
219           ADDCOLOR (MT_COLOR_INDICATOR);
220           addstr ("->");
221           attrset (menu->color (i));
222           addch (' ');
223         }
224         else {
225           attrset (menu->color (i));
226           move (i - menu->top + menu->offset, SidebarWidth);
227           addstr ("   ");
228         }
229
230         print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
231         SETCOLOR (MT_COLOR_NORMAL);
232       }
233       else {
234         attrset (menu->color (i));
235
236         if (i == menu->current) {
237           ADDCOLOR (MT_COLOR_INDICATOR);
238           BKGDSET (MT_COLOR_INDICATOR);
239         }
240
241         CLEARLINE_WIN (i - menu->top + menu->offset);
242         print_enriched_string (menu->color (i), (unsigned char *) buf,
243                                i != menu->current);
244         SETCOLOR (MT_COLOR_NORMAL);
245         BKGDSET (MT_COLOR_NORMAL);
246       }
247     }
248     else
249       CLEARLINE_WIN (i - menu->top + menu->offset);
250   }
251   menu->redraw = 0;
252 }
253
254 void menu_redraw_motion (MUTTMENU * menu)
255 {
256   char buf[STRING];
257
258   if (menu->dialog) {
259     menu->redraw &= ~REDRAW_MOTION;
260     return;
261   }
262
263   move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
264   SETCOLOR (MT_COLOR_NORMAL);
265   BKGDSET (MT_COLOR_NORMAL);
266
267   if (option (OPTARROWCURSOR)) {
268     /* clear the pointer */
269     attrset (menu->color (menu->oldcurrent));
270     addstr ("  ");
271
272     if (menu->redraw & REDRAW_MOTION_RESYNCH) {
273       clrtoeol ();
274       menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
275       menu_pad_string (buf, sizeof (buf));
276       move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
277       print_enriched_string (menu->color (menu->oldcurrent),
278                              (unsigned char *) buf, 1);
279       SETCOLOR (MT_COLOR_NORMAL);
280     }
281
282     /* now draw it in the new location */
283     move (menu->current + menu->offset - menu->top, SidebarWidth);
284     attrset (menu->color (menu->current));
285     ADDCOLOR (MT_COLOR_INDICATOR);
286     addstr ("->");
287     SETCOLOR (MT_COLOR_NORMAL);
288   }
289   else {
290     /* erase the current indicator */
291     attrset (menu->color (menu->oldcurrent));
292     clrtoeol ();
293     menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
294     menu_pad_string (buf, sizeof (buf));
295     print_enriched_string (menu->color (menu->oldcurrent),
296                            (unsigned char *) buf, 1);
297
298     /* now draw the new one to reflect the change */
299     menu_make_entry (buf, sizeof (buf), menu, menu->current);
300     menu_pad_string (buf, sizeof (buf));
301     attrset (menu->color (menu->current));
302     ADDCOLOR (MT_COLOR_INDICATOR);
303     BKGDSET (MT_COLOR_INDICATOR);
304     CLEARLINE_WIN (menu->current - menu->top + menu->offset);
305     print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
306                            0);
307     SETCOLOR (MT_COLOR_NORMAL);
308     BKGDSET (MT_COLOR_NORMAL);
309   }
310   menu->redraw &= REDRAW_STATUS;
311 }
312
313 void menu_redraw_current (MUTTMENU * menu)
314 {
315   char buf[STRING];
316
317   move (menu->current + menu->offset - menu->top, SidebarWidth);
318   menu_make_entry (buf, sizeof (buf), menu, menu->current);
319   menu_pad_string (buf, sizeof (buf));
320
321   if (option (OPTARROWCURSOR)) {
322     int attr = menu->color (menu->current);
323
324     attrset (attr);
325     clrtoeol ();
326     attrset (menu->color (menu->current));
327     ADDCOLOR (MT_COLOR_INDICATOR);
328     addstr ("->");
329     attrset (attr);
330     addch (' ');
331     menu_pad_string (buf, sizeof (buf));
332     print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
333                            1);
334     SETCOLOR (MT_COLOR_NORMAL);
335   }
336   else {
337     attrset (menu->color (menu->current));
338     ADDCOLOR (MT_COLOR_INDICATOR);
339     BKGDSET (MT_COLOR_INDICATOR);
340     clrtoeol ();
341     print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
342                            0);
343     SETCOLOR (MT_COLOR_NORMAL);
344     BKGDSET (MT_COLOR_NORMAL);
345   }
346   menu->redraw &= REDRAW_STATUS;
347 }
348
349 void menu_redraw_prompt (MUTTMENU * menu)
350 {
351   if (menu->dialog) {
352     if (option (OPTMSGERR)) {
353       mutt_sleep (1);
354       unset_option (OPTMSGERR);
355     }
356
357     if (*Errorbuf)
358       mutt_clear_error ();
359
360     SETCOLOR (MT_COLOR_NORMAL);
361     mvaddstr (LINES - 1, 0, menu->prompt);
362     clrtoeol ();
363   }
364 }
365
366 void menu_check_recenter (MUTTMENU * menu)
367 {
368   int c = MIN (MenuContext, menu->pagelen / 2);
369   int old_top = menu->top;
370
371   if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
372     if (menu->top != 0) {
373       menu->top = 0;
374       set_option (OPTNEEDREDRAW);
375     }
376   } else {
377     if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
378       if (menu->current < menu->top + c)
379         menu->top = menu->current - c;
380       else if (menu->current >= menu->top + menu->pagelen - c)
381         menu->top = menu->current - menu->pagelen + c + 1;
382     } else {
383       if (menu->current < menu->top + c)
384         menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
385       else if ((menu->current >= menu->top + menu->pagelen - c))
386         menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
387     }
388   }
389
390   if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
391     menu->top = MIN (menu->top, menu->max - menu->pagelen);
392   menu->top = MAX (menu->top, 0);
393
394   if (menu->top != old_top)
395     menu->redraw |= REDRAW_INDEX;
396 }
397
398 void menu_jump (MUTTMENU * menu)
399 {
400   int n;
401   char buf[SHORT_STRING];
402
403   if (menu->max) {
404     mutt_ungetch (LastKey, 0);
405     buf[0] = 0;
406     if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
407       n = atoi (buf) - 1;
408       if (n >= 0 && n < menu->max) {
409         menu->current = n;
410         menu->redraw = REDRAW_MOTION;
411       }
412       else
413         mutt_error _("Invalid index number.");
414     }
415   }
416   else
417     mutt_error _("No entries.");
418 }
419
420 void menu_next_line (MUTTMENU * menu)
421 {
422   if (menu->max) {
423     int c = MIN (MenuContext, menu->pagelen / 2);
424
425     if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
426                                           || (menu->max > menu->pagelen
427                                               && menu->top <
428                                               menu->max - menu->pagelen))) {
429       menu->top++;
430       if (menu->current < menu->top + c && menu->current < menu->max - 1)
431         menu->current++;
432       menu->redraw = REDRAW_INDEX;
433     }
434     else
435       mutt_error _("You cannot scroll down farther.");
436   }
437   else
438     mutt_error _("No entries.");
439 }
440
441 void menu_prev_line (MUTTMENU * menu)
442 {
443   if (menu->top > 0) {
444     int c = MIN (MenuContext, menu->pagelen / 2);
445
446     menu->top--;
447     if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
448       menu->current--;
449     menu->redraw = REDRAW_INDEX;
450   }
451   else
452     mutt_error _("You cannot scroll up farther.");
453 }
454
455 /* 
456  * pageup:   jumplen == -pagelen
457  * pagedown: jumplen == pagelen
458  * halfup:   jumplen == -pagelen/2
459  * halfdown: jumplen == pagelen/2
460  */
461 #define DIRECTION ((neg * 2) + 1)
462 void menu_length_jump (MUTTMENU *menu, int jumplen) {
463   int tmp, neg = (jumplen >= 0) ? 0 : -1;
464   int c = MIN (MenuContext, menu->pagelen / 2);
465
466   if (menu->max) {
467     /* possible to scroll? */
468     if (DIRECTION * menu->top < 
469         (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
470       menu->top += jumplen;
471
472       /* jumped too long? */
473       if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
474         menu->top = tmp;
475
476       /* need to move the cursor? */
477       if ((DIRECTION * 
478            (tmp = (menu->current - (menu->top + 
479                                     (neg ? (menu->pagelen - 1) - c : c))))) < 0)
480         menu->current -= tmp;
481
482       menu->redraw = REDRAW_INDEX;
483     }
484     else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
485       menu->current += jumplen;
486       menu->redraw = REDRAW_MOTION;
487     }
488     else
489       mutt_error (neg ? _("You are on the first page.")
490                   : _("You are on the last page."));
491
492     menu->current = MIN (menu->current, menu->max - 1);
493     menu->current = MAX (menu->current, 0);
494   }
495   else
496     mutt_error _("No entries.");
497 }
498 #undef DIRECTION
499
500 void menu_next_page (MUTTMENU *menu) {
501   menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
502 }
503
504 void menu_prev_page (MUTTMENU *menu) {
505   menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
506 }
507
508 void menu_half_down (MUTTMENU *menu) {
509   menu_length_jump (menu, menu->pagelen / 2);
510 }
511
512 void menu_half_up (MUTTMENU *menu) {
513   menu_length_jump (menu, 0 - menu->pagelen / 2);
514 }
515
516 void menu_top_page (MUTTMENU *menu) {
517   if (menu->current != menu->top) {
518     menu->current = menu->top;
519     menu->redraw = REDRAW_MOTION;
520   }
521 }
522
523 void menu_bottom_page (MUTTMENU *menu) {
524   if (menu->max) {
525     menu->current = menu->top + menu->pagelen - 1;
526     if (menu->current > menu->max - 1)
527       menu->current = menu->max - 1;
528     menu->redraw = REDRAW_MOTION;
529   }
530   else
531     mutt_error _("No entries.");
532 }
533
534 void menu_middle_page (MUTTMENU *menu) {
535   int i;
536
537   if (menu->max) {
538     i = menu->top + menu->pagelen;
539     if (i > menu->max - 1)
540       i = menu->max - 1;
541     menu->current = menu->top + (i - menu->top) / 2;
542     menu->redraw = REDRAW_MOTION;
543   }
544   else
545     mutt_error _("No entries.");
546 }
547
548 void menu_first_entry (MUTTMENU *menu) {
549   if (menu->max) {
550     menu->current = 0;
551     menu->redraw = REDRAW_MOTION;
552   }
553   else
554     mutt_error _("No entries.");
555 }
556
557 void menu_last_entry (MUTTMENU *menu) {
558   if (menu->max) {
559     menu->current = menu->max - 1;
560     menu->redraw = REDRAW_MOTION;
561   }
562   else
563     mutt_error _("No entries.");
564 }
565
566 void menu_current_top (MUTTMENU * menu)
567 {
568   if (menu->max) {
569     menu->top = menu->current;
570     menu->redraw = REDRAW_INDEX;
571   }
572   else
573     mutt_error _("No entries.");
574 }
575
576 void menu_current_middle (MUTTMENU * menu)
577 {
578   if (menu->max) {
579     menu->top = menu->current - menu->pagelen / 2;
580     if (menu->top < 0)
581       menu->top = 0;
582     menu->redraw = REDRAW_INDEX;
583   }
584   else
585     mutt_error _("No entries.");
586 }
587
588 void menu_current_bottom (MUTTMENU * menu)
589 {
590   if (menu->max) {
591     menu->top = menu->current - menu->pagelen + 1;
592     if (menu->top < 0)
593       menu->top = 0;
594     menu->redraw = REDRAW_INDEX;
595   }
596   else
597     mutt_error _("No entries.");
598 }
599
600 void menu_next_entry (MUTTMENU * menu)
601 {
602   if (menu->current < menu->max - 1) {
603     menu->current++;
604     menu->redraw = REDRAW_MOTION;
605   }
606   else
607     mutt_error _("You are on the last entry.");
608 }
609
610 void menu_prev_entry (MUTTMENU * menu)
611 {
612   if (menu->current) {
613     menu->current--;
614     menu->redraw = REDRAW_MOTION;
615   }
616   else
617     mutt_error _("You are on the first entry.");
618 }
619
620 static int default_color (int i)
621 {
622   return ColorDefs[MT_COLOR_NORMAL];
623 }
624
625 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
626 {
627   char buf[LONG_STRING];
628
629   menu_make_entry (buf, sizeof (buf), m, n);
630   return (regexec (re, buf, 0, NULL, 0));
631 }
632
633 MUTTMENU *mutt_new_menu (void)
634 {
635   MUTTMENU *p = (MUTTMENU *) mem_calloc (1, sizeof (MUTTMENU));
636
637   p->current = 0;
638   p->top = 0;
639   p->offset = 1;
640   p->redraw = REDRAW_FULL;
641   p->pagelen = PAGELEN;
642   p->color = default_color;
643   p->search = menu_search_generic;
644   return (p);
645 }
646
647 void mutt_menuDestroy (MUTTMENU ** p)
648 {
649   int i;
650
651   mem_free (&(*p)->searchBuf);
652
653   if ((*p)->dialog) {
654     for (i = 0; i < (*p)->max; i++)
655       mem_free (&(*p)->dialog[i]);
656
657     mem_free (&(*p)->dialog);
658   }
659
660   mem_free (p);
661 }
662
663 #define M_SEARCH_UP   1
664 #define M_SEARCH_DOWN 2
665
666 static int menu_search (MUTTMENU * menu, int op)
667 {
668   int r;
669   int searchDir;
670   regex_t re;
671   char buf[SHORT_STRING];
672
673   if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
674     strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
675     if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
676                         _("Reverse search for: "),
677                         buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
678       return (-1);
679     str_replace (&menu->searchBuf, buf);
680     menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
681   }
682   else {
683     if (!menu->searchBuf) {
684       mutt_error _("No search pattern.");
685
686       return (-1);
687     }
688   }
689
690   searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
691   if (op == OP_SEARCH_OPPOSITE)
692     searchDir = -searchDir;
693
694   if ((r =
695        REGCOMP (&re, menu->searchBuf,
696                 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
697     regerror (r, &re, buf, sizeof (buf));
698     regfree (&re);
699     mutt_error ("%s", buf);
700     return (-1);
701   }
702
703   r = menu->current + searchDir;
704   while (r >= 0 && r < menu->max) {
705     if (menu->search (menu, &re, r) == 0) {
706       regfree (&re);
707       return r;
708     }
709
710     r += searchDir;
711   }
712
713   regfree (&re);
714   mutt_error _("Not found.");
715
716   return (-1);
717 }
718
719 static int menu_dialog_translate_op (int i)
720 {
721   switch (i) {
722   case OP_NEXT_ENTRY:
723     return OP_NEXT_LINE;
724   case OP_PREV_ENTRY:
725     return OP_PREV_LINE;
726   case OP_CURRENT_TOP:
727   case OP_FIRST_ENTRY:
728     return OP_TOP_PAGE;
729   case OP_CURRENT_BOTTOM:
730   case OP_LAST_ENTRY:
731     return OP_BOTTOM_PAGE;
732   case OP_CURRENT_MIDDLE:
733     return OP_MIDDLE_PAGE;
734   }
735
736   return i;
737 }
738
739 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
740 {
741   event_t ch;
742   char *p;
743
744   ch = mutt_getch ();
745
746   if (ch.ch == -1) {
747     *ip = -1;
748     return 0;
749   }
750
751   if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
752     *ip = OP_MAX + (p - menu->keys + 1);
753     return 0;
754   }
755   else {
756     mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
757     return -1;
758   }
759 }
760
761 int menu_redraw (MUTTMENU * menu)
762 {
763   /* See if all or part of the screen needs to be updated.  */
764   if (menu->redraw & REDRAW_FULL) {
765     menu_redraw_full (menu);
766     /* allow the caller to do any local configuration */
767     return (OP_REDRAW);
768   }
769
770   if (!menu->dialog)
771     menu_check_recenter (menu);
772
773   if (menu->redraw & REDRAW_STATUS)
774     menu_redraw_status (menu);
775   if (menu->redraw & REDRAW_INDEX)
776     menu_redraw_index (menu);
777   else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
778     menu_redraw_motion (menu);
779   else if (menu->redraw == REDRAW_CURRENT)
780     menu_redraw_current (menu);
781
782   if (menu->dialog)
783     menu_redraw_prompt (menu);
784
785   return OP_NULL;
786 }
787
788 int mutt_menuLoop (MUTTMENU * menu)
789 {
790   int i = OP_NULL;
791
792   FOREVER {
793     if (option (OPTMENUCALLER)) {
794       unset_option (OPTMENUCALLER);
795       return OP_NULL;
796     }
797
798
799     mutt_curs_set (0);
800
801 #ifdef USE_IMAP
802     imap_keepalive ();
803 #endif
804
805     if (menu_redraw (menu) == OP_REDRAW)
806       return OP_REDRAW;
807
808     menu->oldcurrent = menu->current;
809
810     if (option (OPTARROWCURSOR))
811       move (menu->current - menu->top + menu->offset, 2);
812     else if (option (OPTBRAILLEFRIENDLY))
813       move (menu->current - menu->top + menu->offset, 0);
814     else
815       move (menu->current - menu->top + menu->offset, COLS - 1);
816
817     mutt_refresh ();
818
819
820     /* try to catch dialog keys before ops */
821     if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
822       return i;
823
824     i = km_dokey (menu->menu);
825     if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
826       if (menu->tagged) {
827         mvaddstr (LINES - 1, 0, "Tag-");
828         clrtoeol ();
829         i = km_dokey (menu->menu);
830         menu->tagprefix = 1;
831         CLEARLINE (LINES - 1);
832       }
833       else if (i == OP_TAG_PREFIX) {
834         mutt_error _("No tagged entries.");
835
836         i = -1;
837       }
838       else {                    /* None tagged, OP_TAG_PREFIX_COND */
839
840         event_t tmp;
841
842         while (UngetCount > 0) {
843           tmp = mutt_getch ();
844           if (tmp.op == OP_END_COND)
845             break;
846         }
847         mutt_message _("Nothing to do.");
848
849         i = -1;
850       }
851     }
852     else if (menu->tagged && option (OPTAUTOTAG))
853       menu->tagprefix = 1;
854     else
855       menu->tagprefix = 0;
856
857     mutt_curs_set (1);
858
859 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
860     if (SigWinch) {
861       mutt_resize_screen ();
862       menu->redraw = REDRAW_FULL;
863       SigWinch = 0;
864       clearok (stdscr, TRUE);   /*force complete redraw */
865     }
866 #endif
867
868     if (i == -1)
869       continue;
870
871     if (!menu->dialog)
872       mutt_clear_error ();
873
874     /* Convert menubar movement to scrolling */
875     if (menu->dialog)
876       i = menu_dialog_translate_op (i);
877
878     switch (i) {
879     case OP_NEXT_ENTRY:
880       menu_next_entry (menu);
881       break;
882     case OP_PREV_ENTRY:
883       menu_prev_entry (menu);
884       break;
885     case OP_HALF_DOWN:
886       menu_half_down (menu);
887       break;
888     case OP_HALF_UP:
889       menu_half_up (menu);
890       break;
891     case OP_NEXT_PAGE:
892       menu_next_page (menu);
893       break;
894     case OP_PREV_PAGE:
895       menu_prev_page (menu);
896       break;
897     case OP_NEXT_LINE:
898       menu_next_line (menu);
899       break;
900     case OP_PREV_LINE:
901       menu_prev_line (menu);
902       break;
903     case OP_FIRST_ENTRY:
904       menu_first_entry (menu);
905       break;
906     case OP_LAST_ENTRY:
907       menu_last_entry (menu);
908       break;
909     case OP_TOP_PAGE:
910       menu_top_page (menu);
911       break;
912     case OP_MIDDLE_PAGE:
913       menu_middle_page (menu);
914       break;
915     case OP_BOTTOM_PAGE:
916       menu_bottom_page (menu);
917       break;
918     case OP_CURRENT_TOP:
919       menu_current_top (menu);
920       break;
921     case OP_CURRENT_MIDDLE:
922       menu_current_middle (menu);
923       break;
924     case OP_CURRENT_BOTTOM:
925       menu_current_bottom (menu);
926       break;
927     case OP_SEARCH:
928     case OP_SEARCH_REVERSE:
929     case OP_SEARCH_NEXT:
930     case OP_SEARCH_OPPOSITE:
931       if (menu->search && !menu->dialog) {      /* Searching dialogs won't work */
932         menu->oldcurrent = menu->current;
933         if ((menu->current = menu_search (menu, i)) != -1)
934           menu->redraw = REDRAW_MOTION;
935         else
936           menu->current = menu->oldcurrent;
937       }
938       else
939         mutt_error _("Search is not implemented for this menu.");
940       break;
941
942     case OP_JUMP:
943       if (menu->dialog)
944         mutt_error (_("Jumping is not implemented for dialogs."));
945
946       else
947         menu_jump (menu);
948       break;
949
950     case OP_ENTER_COMMAND:
951       CurrentMenu = menu->menu;
952       mutt_enter_command ();
953       if (option (OPTFORCEREDRAWINDEX)) {
954         menu->redraw = REDRAW_FULL;
955         unset_option (OPTFORCEREDRAWINDEX);
956         unset_option (OPTFORCEREDRAWPAGER);
957       }
958       break;
959
960     case OP_TAG:
961       if (menu->tag && !menu->dialog) {
962         if (menu->tagprefix && !option (OPTAUTOTAG)) {
963           for (i = 0; i < menu->max; i++)
964             menu->tagged += menu->tag (menu, i, 0);
965           menu->redraw = REDRAW_INDEX;
966         }
967         else if (menu->max) {
968           int i = menu->tag (menu, menu->current, -1);
969
970           menu->tagged += i;
971           if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) {
972             menu->current++;
973             menu->redraw = REDRAW_MOTION_RESYNCH;
974           }
975           else
976             menu->redraw = REDRAW_CURRENT;
977         }
978         else
979           mutt_error _("No entries.");
980       }
981       else
982         mutt_error _("Tagging is not supported.");
983       break;
984
985     case OP_SHELL_ESCAPE:
986       mutt_shell_escape ();
987       MAYBE_REDRAW (menu->redraw);
988       break;
989
990     case OP_WHAT_KEY:
991       mutt_what_key ();
992       break;
993
994     case OP_REBUILD_CACHE:
995       mx_rebuild_cache ();
996       break;
997
998     case OP_REDRAW:
999       clearok (stdscr, TRUE);
1000       menu->redraw = REDRAW_FULL;
1001       break;
1002
1003     case OP_HELP:
1004       mutt_help (menu->menu);
1005       menu->redraw = REDRAW_FULL;
1006       break;
1007
1008     case OP_NULL:
1009       km_error_key (menu->menu);
1010       break;
1011
1012     case OP_END_COND:
1013       break;
1014
1015     default:
1016       return (i);
1017     }
1018   }
1019   /* not reached */
1020 }