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