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