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