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   }
377   else if (menu->current >= menu->top + menu->pagelen - c) {    /* indicator below bottom threshold */
378     if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
379       menu->top = menu->current - menu->pagelen + c + 1;
380     else
381       menu->top +=
382         (menu->pagelen -
383          c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
384   }
385   else if (menu->current < menu->top + c) {     /* indicator above top threshold */
386     if (option (OPTMENUSCROLL) || (menu->pagelen <= 0))
387       menu->top = menu->current - c;
388     else
389       menu->top -=
390         (menu->pagelen -
391          c) * ((menu->top + menu->pagelen - 1 -
392                 menu->current) / (menu->pagelen - c)) - c;
393   }
394
395   if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
396     menu->top = MIN (menu->top, menu->max - menu->pagelen);
397   menu->top = MAX (menu->top, 0);
398
399   if (menu->top != old_top)
400     menu->redraw |= REDRAW_INDEX;
401 }
402
403 void menu_jump (MUTTMENU * menu)
404 {
405   int n;
406   char buf[SHORT_STRING];
407
408   if (menu->max) {
409     mutt_ungetch (LastKey, 0);
410     buf[0] = 0;
411     if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
412       n = atoi (buf) - 1;
413       if (n >= 0 && n < menu->max) {
414         menu->current = n;
415         menu->redraw = REDRAW_MOTION;
416       }
417       else
418         mutt_error _("Invalid index number.");
419     }
420   }
421   else
422     mutt_error _("No entries.");
423 }
424
425 void menu_next_line (MUTTMENU * menu)
426 {
427   if (menu->max) {
428     int c = MIN (MenuContext, menu->pagelen / 2);
429
430     if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
431                                           || (menu->max > menu->pagelen
432                                               && menu->top <
433                                               menu->max - menu->pagelen))) {
434       menu->top++;
435       if (menu->current < menu->top + c && menu->current < menu->max - 1)
436         menu->current++;
437       menu->redraw = REDRAW_INDEX;
438     }
439     else
440       mutt_error _("You cannot scroll down farther.");
441   }
442   else
443     mutt_error _("No entries.");
444 }
445
446 void menu_prev_line (MUTTMENU * menu)
447 {
448   if (menu->top > 0) {
449     int c = MIN (MenuContext, menu->pagelen / 2);
450
451     menu->top--;
452     if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
453       menu->current--;
454     menu->redraw = REDRAW_INDEX;
455   }
456   else
457     mutt_error _("You cannot scroll up farther.");
458 }
459
460 /* 
461  * pageup:   jumplen == -pagelen
462  * pagedown: jumplen == pagelen
463  * halfup:   jumplen == -pagelen/2
464  * halfdown: jumplen == pagelen/2
465  */
466 #define DIRECTION ((neg * 2) + 1)
467 void menu_length_jump (MUTTMENU *menu, int jumplen) {
468   int tmp, neg = (jumplen >= 0) ? 0 : -1;
469   int c = MIN (MenuContext, menu->pagelen / 2);
470
471   if (menu->max) {
472     /* possible to scroll? */
473     if (DIRECTION * menu->top < 
474         (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
475       menu->top += jumplen;
476
477       /* jumped too long? */
478       if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
479         menu->top = tmp;
480
481       /* need to move the cursor? */
482       if ((DIRECTION * 
483            (tmp = (menu->current - (menu->top + 
484                                     (neg ? (menu->pagelen - 1) - c : c))))) < 0)
485         menu->current -= tmp;
486
487       menu->redraw = REDRAW_INDEX;
488     }
489     else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
490       menu->current += jumplen;
491       menu->redraw = REDRAW_MOTION;
492     }
493     else
494       mutt_error (neg ? _("You are on the first page.")
495                   : _("You are on the last page."));
496
497     menu->current = MIN (menu->current, menu->max - 1);
498     menu->current = MAX (menu->current, 0);
499   }
500   else
501     mutt_error _("No entries.");
502 }
503 #undef DIRECTION
504
505 void menu_next_page (MUTTMENU *menu) {
506   menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
507 }
508
509 void menu_prev_page (MUTTMENU *menu) {
510   menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
511 }
512
513 void menu_half_down (MUTTMENU *menu) {
514   menu_length_jump (menu, menu->pagelen / 2);
515 }
516
517 void menu_half_up (MUTTMENU *menu) {
518   menu_length_jump (menu, 0 - menu->pagelen / 2);
519 }
520
521 void menu_top_page (MUTTMENU *menu) {
522   if (menu->current != menu->top) {
523     menu->current = menu->top;
524     menu->redraw = REDRAW_MOTION;
525   }
526 }
527
528 void menu_bottom_page (MUTTMENU *menu) {
529   if (menu->max) {
530     menu->current = menu->top + menu->pagelen - 1;
531     if (menu->current > menu->max - 1)
532       menu->current = menu->max - 1;
533     menu->redraw = REDRAW_MOTION;
534   }
535   else
536     mutt_error _("No entries.");
537 }
538
539 void menu_middle_page (MUTTMENU *menu) {
540   int i;
541
542   if (menu->max) {
543     i = menu->top + menu->pagelen;
544     if (i > menu->max - 1)
545       i = menu->max - 1;
546     menu->current = menu->top + (i - menu->top) / 2;
547     menu->redraw = REDRAW_MOTION;
548   }
549   else
550     mutt_error _("No entries.");
551 }
552
553 void menu_first_entry (MUTTMENU *menu) {
554   if (menu->max) {
555     menu->current = 0;
556     menu->redraw = REDRAW_MOTION;
557   }
558   else
559     mutt_error _("No entries.");
560 }
561
562 void menu_last_entry (MUTTMENU *menu) {
563   if (menu->max) {
564     menu->current = menu->max - 1;
565     menu->redraw = REDRAW_MOTION;
566   }
567   else
568     mutt_error _("No entries.");
569 }
570
571 void menu_current_top (MUTTMENU * menu)
572 {
573   if (menu->max) {
574     menu->top = menu->current;
575     menu->redraw = REDRAW_INDEX;
576   }
577   else
578     mutt_error _("No entries.");
579 }
580
581 void menu_current_middle (MUTTMENU * menu)
582 {
583   if (menu->max) {
584     menu->top = menu->current - menu->pagelen / 2;
585     if (menu->top < 0)
586       menu->top = 0;
587     menu->redraw = REDRAW_INDEX;
588   }
589   else
590     mutt_error _("No entries.");
591 }
592
593 void menu_current_bottom (MUTTMENU * menu)
594 {
595   if (menu->max) {
596     menu->top = menu->current - menu->pagelen + 1;
597     if (menu->top < 0)
598       menu->top = 0;
599     menu->redraw = REDRAW_INDEX;
600   }
601   else
602     mutt_error _("No entries.");
603 }
604
605 void menu_next_entry (MUTTMENU * menu)
606 {
607   if (menu->current < menu->max - 1) {
608     menu->current++;
609     menu->redraw = REDRAW_MOTION;
610   }
611   else
612     mutt_error _("You are on the last entry.");
613 }
614
615 void menu_prev_entry (MUTTMENU * menu)
616 {
617   if (menu->current) {
618     menu->current--;
619     menu->redraw = REDRAW_MOTION;
620   }
621   else
622     mutt_error _("You are on the first entry.");
623 }
624
625 static int default_color (int i)
626 {
627   return ColorDefs[MT_COLOR_NORMAL];
628 }
629
630 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
631 {
632   char buf[LONG_STRING];
633
634   menu_make_entry (buf, sizeof (buf), m, n);
635   return (regexec (re, buf, 0, NULL, 0));
636 }
637
638 MUTTMENU *mutt_new_menu (void)
639 {
640   MUTTMENU *p = (MUTTMENU *) mem_calloc (1, sizeof (MUTTMENU));
641
642   p->current = 0;
643   p->top = 0;
644   p->offset = 1;
645   p->redraw = REDRAW_FULL;
646   p->pagelen = PAGELEN;
647   p->color = default_color;
648   p->search = menu_search_generic;
649   return (p);
650 }
651
652 void mutt_menuDestroy (MUTTMENU ** p)
653 {
654   int i;
655
656   mem_free (&(*p)->searchBuf);
657
658   if ((*p)->dialog) {
659     for (i = 0; i < (*p)->max; i++)
660       mem_free (&(*p)->dialog[i]);
661
662     mem_free (&(*p)->dialog);
663   }
664
665   mem_free (p);
666 }
667
668 #define M_SEARCH_UP   1
669 #define M_SEARCH_DOWN 2
670
671 static int menu_search (MUTTMENU * menu, int op)
672 {
673   int r;
674   int searchDir;
675   regex_t re;
676   char buf[SHORT_STRING];
677
678   if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
679     strfcpy (buf, menu->searchBuf ? menu->searchBuf : "", sizeof (buf));
680     if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
681                         _("Reverse search for: "),
682                         buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
683       return (-1);
684     str_replace (&menu->searchBuf, buf);
685     menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
686   }
687   else {
688     if (!menu->searchBuf) {
689       mutt_error _("No search pattern.");
690
691       return (-1);
692     }
693   }
694
695   searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
696   if (op == OP_SEARCH_OPPOSITE)
697     searchDir = -searchDir;
698
699   if ((r =
700        REGCOMP (&re, menu->searchBuf,
701                 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
702     regerror (r, &re, buf, sizeof (buf));
703     regfree (&re);
704     mutt_error ("%s", buf);
705     return (-1);
706   }
707
708   r = menu->current + searchDir;
709   while (r >= 0 && r < menu->max) {
710     if (menu->search (menu, &re, r) == 0) {
711       regfree (&re);
712       return r;
713     }
714
715     r += searchDir;
716   }
717
718   regfree (&re);
719   mutt_error _("Not found.");
720
721   return (-1);
722 }
723
724 static int menu_dialog_translate_op (int i)
725 {
726   switch (i) {
727   case OP_NEXT_ENTRY:
728     return OP_NEXT_LINE;
729   case OP_PREV_ENTRY:
730     return OP_PREV_LINE;
731   case OP_CURRENT_TOP:
732   case OP_FIRST_ENTRY:
733     return OP_TOP_PAGE;
734   case OP_CURRENT_BOTTOM:
735   case OP_LAST_ENTRY:
736     return OP_BOTTOM_PAGE;
737   case OP_CURRENT_MIDDLE:
738     return OP_MIDDLE_PAGE;
739   }
740
741   return i;
742 }
743
744 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
745 {
746   event_t ch;
747   char *p;
748
749   ch = mutt_getch ();
750
751   if (ch.ch == -1) {
752     *ip = -1;
753     return 0;
754   }
755
756   if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
757     *ip = OP_MAX + (p - menu->keys + 1);
758     return 0;
759   }
760   else {
761     mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
762     return -1;
763   }
764 }
765
766 int menu_redraw (MUTTMENU * menu)
767 {
768   /* See if all or part of the screen needs to be updated.  */
769   if (menu->redraw & REDRAW_FULL) {
770     menu_redraw_full (menu);
771     /* allow the caller to do any local configuration */
772     return (OP_REDRAW);
773   }
774
775   if (!menu->dialog)
776     menu_check_recenter (menu);
777
778   if (menu->redraw & REDRAW_STATUS)
779     menu_redraw_status (menu);
780   if (menu->redraw & REDRAW_INDEX)
781     menu_redraw_index (menu);
782   else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
783     menu_redraw_motion (menu);
784   else if (menu->redraw == REDRAW_CURRENT)
785     menu_redraw_current (menu);
786
787   if (menu->dialog)
788     menu_redraw_prompt (menu);
789
790   return OP_NULL;
791 }
792
793 int mutt_menuLoop (MUTTMENU * menu)
794 {
795   int i = OP_NULL;
796
797   FOREVER {
798     if (option (OPTMENUCALLER)) {
799       unset_option (OPTMENUCALLER);
800       return OP_NULL;
801     }
802
803
804     mutt_curs_set (0);
805
806 #ifdef USE_IMAP
807     imap_keepalive ();
808 #endif
809
810     if (menu_redraw (menu) == OP_REDRAW)
811       return OP_REDRAW;
812
813     menu->oldcurrent = menu->current;
814
815     if (option (OPTARROWCURSOR))
816       move (menu->current - menu->top + menu->offset, 2);
817     else if (option (OPTBRAILLEFRIENDLY))
818       move (menu->current - menu->top + menu->offset, 0);
819     else
820       move (menu->current - menu->top + menu->offset, COLS - 1);
821
822     mutt_refresh ();
823
824
825     /* try to catch dialog keys before ops */
826     if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
827       return i;
828
829     i = km_dokey (menu->menu);
830     if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
831       if (menu->tagged) {
832         mvaddstr (LINES - 1, 0, "Tag-");
833         clrtoeol ();
834         i = km_dokey (menu->menu);
835         menu->tagprefix = 1;
836         CLEARLINE (LINES - 1);
837       }
838       else if (i == OP_TAG_PREFIX) {
839         mutt_error _("No tagged entries.");
840
841         i = -1;
842       }
843       else {                    /* None tagged, OP_TAG_PREFIX_COND */
844
845         event_t tmp;
846
847         while (UngetCount > 0) {
848           tmp = mutt_getch ();
849           if (tmp.op == OP_END_COND)
850             break;
851         }
852         mutt_message _("Nothing to do.");
853
854         i = -1;
855       }
856     }
857     else if (menu->tagged && option (OPTAUTOTAG))
858       menu->tagprefix = 1;
859     else
860       menu->tagprefix = 0;
861
862     mutt_curs_set (1);
863
864 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
865     if (SigWinch) {
866       mutt_resize_screen ();
867       menu->redraw = REDRAW_FULL;
868       SigWinch = 0;
869       clearok (stdscr, TRUE);   /*force complete redraw */
870     }
871 #endif
872
873     if (i == -1)
874       continue;
875
876     if (!menu->dialog)
877       mutt_clear_error ();
878
879     /* Convert menubar movement to scrolling */
880     if (menu->dialog)
881       i = menu_dialog_translate_op (i);
882
883     switch (i) {
884     case OP_NEXT_ENTRY:
885       menu_next_entry (menu);
886       break;
887     case OP_PREV_ENTRY:
888       menu_prev_entry (menu);
889       break;
890     case OP_HALF_DOWN:
891       menu_half_down (menu);
892       break;
893     case OP_HALF_UP:
894       menu_half_up (menu);
895       break;
896     case OP_NEXT_PAGE:
897       menu_next_page (menu);
898       break;
899     case OP_PREV_PAGE:
900       menu_prev_page (menu);
901       break;
902     case OP_NEXT_LINE:
903       menu_next_line (menu);
904       break;
905     case OP_PREV_LINE:
906       menu_prev_line (menu);
907       break;
908     case OP_FIRST_ENTRY:
909       menu_first_entry (menu);
910       break;
911     case OP_LAST_ENTRY:
912       menu_last_entry (menu);
913       break;
914     case OP_TOP_PAGE:
915       menu_top_page (menu);
916       break;
917     case OP_MIDDLE_PAGE:
918       menu_middle_page (menu);
919       break;
920     case OP_BOTTOM_PAGE:
921       menu_bottom_page (menu);
922       break;
923     case OP_CURRENT_TOP:
924       menu_current_top (menu);
925       break;
926     case OP_CURRENT_MIDDLE:
927       menu_current_middle (menu);
928       break;
929     case OP_CURRENT_BOTTOM:
930       menu_current_bottom (menu);
931       break;
932     case OP_SEARCH:
933     case OP_SEARCH_REVERSE:
934     case OP_SEARCH_NEXT:
935     case OP_SEARCH_OPPOSITE:
936       if (menu->search && !menu->dialog) {      /* Searching dialogs won't work */
937         menu->oldcurrent = menu->current;
938         if ((menu->current = menu_search (menu, i)) != -1)
939           menu->redraw = REDRAW_MOTION;
940         else
941           menu->current = menu->oldcurrent;
942       }
943       else
944         mutt_error _("Search is not implemented for this menu.");
945       break;
946
947     case OP_JUMP:
948       if (menu->dialog)
949         mutt_error (_("Jumping is not implemented for dialogs."));
950
951       else
952         menu_jump (menu);
953       break;
954
955     case OP_ENTER_COMMAND:
956       CurrentMenu = menu->menu;
957       mutt_enter_command ();
958       if (option (OPTFORCEREDRAWINDEX)) {
959         menu->redraw = REDRAW_FULL;
960         unset_option (OPTFORCEREDRAWINDEX);
961         unset_option (OPTFORCEREDRAWPAGER);
962       }
963       break;
964
965     case OP_TAG:
966       if (menu->tag && !menu->dialog) {
967         if (menu->tagprefix && !option (OPTAUTOTAG)) {
968           for (i = 0; i < menu->max; i++)
969             menu->tagged += menu->tag (menu, i, 0);
970           menu->redraw = REDRAW_INDEX;
971         }
972         else if (menu->max) {
973           int i = menu->tag (menu, menu->current, -1);
974
975           menu->tagged += i;
976           if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) {
977             menu->current++;
978             menu->redraw = REDRAW_MOTION_RESYNCH;
979           }
980           else
981             menu->redraw = REDRAW_CURRENT;
982         }
983         else
984           mutt_error _("No entries.");
985       }
986       else
987         mutt_error _("Tagging is not supported.");
988       break;
989
990     case OP_SHELL_ESCAPE:
991       mutt_shell_escape ();
992       MAYBE_REDRAW (menu->redraw);
993       break;
994
995     case OP_WHAT_KEY:
996       mutt_what_key ();
997       break;
998
999     case OP_REDRAW:
1000       clearok (stdscr, TRUE);
1001       menu->redraw = REDRAW_FULL;
1002       break;
1003
1004     case OP_HELP:
1005       mutt_help (menu->menu);
1006       menu->redraw = REDRAW_FULL;
1007       break;
1008
1009     case OP_NULL:
1010       km_error_key (menu->menu);
1011       break;
1012
1013     case OP_END_COND:
1014       break;
1015
1016     default:
1017       return (i);
1018     }
1019   }
1020   /* not reached */
1021 }