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