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