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