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