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