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