]> git.llucax.com Git - software/mutt-debian.git/blob - menu.c
Imported Upstream version 1.5.18
[software/mutt-debian.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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
28 #ifdef USE_IMAP
29 #include "imap.h"
30 #endif
31
32 #include <string.h>
33 #include <stdlib.h>
34
35 extern int Charset_is_utf8; /* FIXME: bad modularisation */
36
37 extern size_t UngetCount;
38
39 static void print_enriched_string (int attr, unsigned char *s, int do_color)
40 {
41   wchar_t wc;
42   size_t k;
43   size_t n = mutt_strlen ((char *)s);
44   mbstate_t mbstate;
45
46   memset (&mbstate, 0, sizeof (mbstate));
47   while (*s)
48   {
49     if (*s < M_TREE_MAX)
50     {
51       if (do_color)
52         SETCOLOR (MT_COLOR_TREE);
53       while (*s && *s < M_TREE_MAX)
54       {
55         switch (*s)
56         {
57           case M_TREE_LLCORNER:
58             if (option (OPTASCIICHARS))
59               addch ('`');
60             else if (Charset_is_utf8)
61               addstr ("\342\224\224"); /* WACS_LLCORNER */
62             else
63               addch (ACS_LLCORNER);
64             break;
65           case M_TREE_ULCORNER:
66             if (option (OPTASCIICHARS))
67               addch (',');
68             else if (Charset_is_utf8)
69               addstr ("\342\224\214"); /* WACS_ULCORNER */
70             else
71               addch (ACS_ULCORNER);
72             break;
73           case M_TREE_LTEE:
74             if (option (OPTASCIICHARS))
75               addch ('|');
76             else if (Charset_is_utf8)
77               addstr ("\342\224\234"); /* WACS_LTEE */
78             else
79               addch (ACS_LTEE);
80             break;
81           case M_TREE_HLINE:
82             if (option (OPTASCIICHARS))
83               addch ('-');
84             else if (Charset_is_utf8)
85               addstr ("\342\224\200"); /* WACS_HLINE */
86             else
87               addch (ACS_HLINE);
88             break;
89           case M_TREE_VLINE:
90             if (option (OPTASCIICHARS))
91               addch ('|');
92             else if (Charset_is_utf8)
93               addstr ("\342\224\202"); /* WACS_VLINE */
94             else
95               addch (ACS_VLINE);
96             break;
97           case M_TREE_TTEE:
98             if (option (OPTASCIICHARS))
99               addch ('-');
100             else if (Charset_is_utf8)
101               addstr ("\342\224\254"); /* WACS_TTEE */
102             else
103               addch (ACS_TTEE);
104             break;
105           case M_TREE_BTEE:
106             if (option (OPTASCIICHARS))
107               addch ('-');
108             else if (Charset_is_utf8)
109               addstr ("\342\224\264"); /* WACS_BTEE */
110             else
111               addch (ACS_BTEE);
112             break;
113           case M_TREE_SPACE:
114             addch (' ');
115             break;
116           case M_TREE_RARROW:
117             addch ('>');
118             break;
119           case M_TREE_STAR:
120             addch ('*'); /* fake thread indicator */
121             break;
122           case M_TREE_HIDDEN:
123             addch ('&');
124             break;
125           case M_TREE_EQUALS:
126             addch ('=');
127             break;
128           case M_TREE_MISSING:
129             addch ('?');
130             break;
131         }
132         s++, n--;
133       }
134       if (do_color) attrset(attr);
135     }
136     else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
137     {
138       addnstr ((char *)s, k);
139       s += k, n-= k;
140     }
141     else
142       break;
143   }
144 }
145
146 static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i) 
147 {
148   if (menu->dialog) 
149   {
150     strncpy (s, menu->dialog[i], l);
151     menu->current = -1; /* hide menubar */
152   }
153   else
154     menu->make_entry (s, l, menu, i);
155 }
156
157 void menu_pad_string (char *s, size_t n)
158 {
159   char *scratch = safe_strdup (s);
160   int shift = option (OPTARROWCURSOR) ? 3 : 0;
161   int cols = COLS - shift;
162
163   mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 1);
164   s[n - 1] = 0;
165   FREE (&scratch);
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[LONG_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         {
234           attrset (menu->color (i));
235           addstr ("   ");
236         }
237
238         print_enriched_string (menu->color(i), (unsigned char *) buf, 1);
239         SETCOLOR (MT_COLOR_NORMAL);          
240       }
241       else
242       {
243         attrset (menu->color (i));
244             
245         if (i == menu->current)
246         {
247           ADDCOLOR (MT_COLOR_INDICATOR);
248           BKGDSET (MT_COLOR_INDICATOR);
249         }
250
251         CLEARLINE (i - menu->top + menu->offset);
252         print_enriched_string (menu->color(i), (unsigned char *) buf, i != menu->current);
253         SETCOLOR (MT_COLOR_NORMAL);
254         BKGDSET (MT_COLOR_NORMAL);
255       }
256     }
257     else
258       CLEARLINE (i - menu->top + menu->offset);
259   }
260   menu->redraw = 0;
261 }
262
263 void menu_redraw_motion (MUTTMENU *menu)
264 {
265   char buf[LONG_STRING];
266
267   if (menu->dialog) 
268   {
269     menu->redraw &= ~REDRAW_MOTION;
270     return;
271   }
272   
273   move (menu->oldcurrent + menu->offset - menu->top, 0);
274   SETCOLOR (MT_COLOR_NORMAL);
275   BKGDSET (MT_COLOR_NORMAL);
276
277   if (option (OPTARROWCURSOR))
278   {
279     /* clear the pointer */
280     attrset (menu->color (menu->oldcurrent));
281     addstr ("  ");
282
283     if (menu->redraw & REDRAW_MOTION_RESYNCH)
284     {
285       clrtoeol ();
286       menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
287       menu_pad_string (buf, sizeof (buf));
288       move (menu->oldcurrent + menu->offset - menu->top, 3);
289       print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
290       SETCOLOR (MT_COLOR_NORMAL);
291     }
292
293     /* now draw it in the new location */
294     move (menu->current + menu->offset - menu->top, 0);
295     attrset (menu->color (menu->current));
296     ADDCOLOR (MT_COLOR_INDICATOR);
297     addstr ("->");
298     SETCOLOR (MT_COLOR_NORMAL);
299   }
300   else
301   {
302     /* erase the current indicator */
303     attrset (menu->color (menu->oldcurrent));
304     clrtoeol ();
305     menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
306     menu_pad_string (buf, sizeof (buf));
307     print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
308
309     /* now draw the new one to reflect the change */
310     menu_make_entry (buf, sizeof (buf), menu, menu->current);
311     menu_pad_string (buf, sizeof (buf));
312     attrset (menu->color (menu->current));
313     ADDCOLOR (MT_COLOR_INDICATOR);
314     BKGDSET (MT_COLOR_INDICATOR);
315     CLEARLINE (menu->current - menu->top + menu->offset);
316     print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
317     SETCOLOR (MT_COLOR_NORMAL);
318     BKGDSET (MT_COLOR_NORMAL);
319   }
320   menu->redraw &= REDRAW_STATUS;
321 }
322
323 void menu_redraw_current (MUTTMENU *menu)
324 {
325   char buf[LONG_STRING];
326   
327   move (menu->current + menu->offset - menu->top, 0);
328   menu_make_entry (buf, sizeof (buf), menu, menu->current);
329   menu_pad_string (buf, sizeof (buf));
330
331   if (option (OPTARROWCURSOR))
332   {
333     int attr = menu->color (menu->current);
334     attrset (attr);
335     clrtoeol ();
336     attrset (menu->color (menu->current));
337     ADDCOLOR (MT_COLOR_INDICATOR);
338     addstr ("->");
339     attrset (attr);
340     addch (' ');
341     menu_pad_string (buf, sizeof (buf));
342     print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 1);
343     SETCOLOR (MT_COLOR_NORMAL);
344   }
345   else
346   {
347     attrset (menu->color (menu->current));
348     ADDCOLOR (MT_COLOR_INDICATOR);
349     BKGDSET (MT_COLOR_INDICATOR);
350     clrtoeol ();
351     print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
352     SETCOLOR (MT_COLOR_NORMAL);
353     BKGDSET (MT_COLOR_NORMAL);
354   }
355   menu->redraw &= REDRAW_STATUS;
356 }
357
358 void menu_redraw_prompt (MUTTMENU *menu)
359 {
360   if (menu->dialog) 
361   {
362     if (option (OPTMSGERR)) 
363     {
364       mutt_sleep (1);
365       unset_option (OPTMSGERR);
366     }
367
368     if (*Errorbuf)
369       mutt_clear_error ();
370
371     SETCOLOR (MT_COLOR_NORMAL);
372     mvaddstr (LINES - 1, 0, menu->prompt);
373     clrtoeol ();
374   }
375 }
376
377 void menu_check_recenter (MUTTMENU *menu)
378 {
379   int c = MIN (MenuContext, menu->pagelen / 2);
380   int old_top = menu->top;
381
382   if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) /* less entries than lines */
383   {
384     if (menu->top != 0) 
385     {
386       menu->top = 0;
387       set_option (OPTNEEDREDRAW);
388     }
389   }
390   else 
391   {
392     if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext))
393     {
394       if (menu->current < menu->top + c)
395         menu->top = menu->current - c;
396       else if (menu->current >= menu->top + menu->pagelen - c)
397         menu->top = menu->current - menu->pagelen + c + 1;
398     }
399     else
400     {
401       if (menu->current < menu->top + c)
402         menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
403       else if ((menu->current >= menu->top + menu->pagelen - c))
404         menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;     
405     }
406   }
407
408   if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
409     menu->top = MIN (menu->top, menu->max - menu->pagelen);
410   menu->top = MAX (menu->top, 0);
411
412   if (menu->top != old_top)
413     menu->redraw |= REDRAW_INDEX;
414 }
415
416 void menu_jump (MUTTMENU *menu)
417 {
418   int n;
419   char buf[SHORT_STRING];
420
421   if (menu->max)
422   {
423     mutt_ungetch (LastKey, 0);
424     buf[0] = 0;
425     if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
426     {
427       n = atoi (buf) - 1;
428       if (n >= 0 && n < menu->max)
429       {
430         menu->current = n;
431         menu->redraw = REDRAW_MOTION;
432       }
433       else
434         mutt_error _("Invalid index number.");
435     }
436   }
437   else
438     mutt_error _("No entries.");
439 }
440
441 void menu_next_line (MUTTMENU *menu)
442 {
443   if (menu->max)
444   {
445     int c = MIN (MenuContext, menu->pagelen / 2);
446
447     if (menu->top + 1 < menu->max - c
448       && (option(OPTMENUMOVEOFF) || (menu->max > menu->pagelen && menu->top < menu->max - menu->pagelen)))
449     {
450       menu->top++;
451       if (menu->current < menu->top + c && menu->current < menu->max - 1)
452         menu->current++;
453       menu->redraw = REDRAW_INDEX;
454     }
455     else
456       mutt_error _("You cannot scroll down farther.");
457   }
458   else
459     mutt_error _("No entries.");
460 }
461
462 void menu_prev_line (MUTTMENU *menu)
463 {
464   if (menu->top > 0)
465   {
466     int c = MIN (MenuContext, menu->pagelen / 2);
467
468     menu->top--;
469     if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
470       menu->current--;
471     menu->redraw = REDRAW_INDEX;
472   }
473   else
474     mutt_error _("You cannot scroll up farther.");
475 }
476
477 /* 
478  * pageup:   jumplen == -pagelen
479  * pagedown: jumplen == pagelen
480  * halfup:   jumplen == -pagelen/2
481  * halfdown: jumplen == pagelen/2
482  */
483 #define DIRECTION ((neg * 2) + 1)
484 void menu_length_jump (MUTTMENU *menu, int jumplen)
485 {
486   int tmp, neg = (jumplen >= 0) ? 0 : -1;
487   int c = MIN (MenuContext, menu->pagelen / 2);
488
489   if (menu->max)
490   {
491     /* possible to scroll? */
492     if (DIRECTION * menu->top <
493         (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/))))
494     {
495       menu->top += jumplen;
496
497       /* jumped too long? */
498       if ((neg || !option (OPTMENUMOVEOFF)) &&
499           DIRECTION * menu->top > tmp)
500         menu->top = tmp;
501
502       /* need to move the cursor? */
503       if ((DIRECTION *
504            (tmp = (menu->current -
505                    (menu->top + (neg ? (menu->pagelen - 1) - c : c))
506           ))) < 0)
507         menu->current -= tmp;
508
509       menu->redraw = REDRAW_INDEX;
510     }
511     else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog)
512     {
513       menu->current += jumplen;
514       menu->redraw = REDRAW_MOTION;
515     }
516     else
517       mutt_error (neg ? _("You are on the first page.")
518                       : _("You are on the last page."));
519
520     menu->current = MIN (menu->current, menu->max - 1);
521     menu->current = MAX (menu->current, 0);
522   }
523   else
524     mutt_error _("No entries.");
525 }
526 #undef DIRECTION
527
528 void menu_next_page (MUTTMENU *menu)
529 {
530   menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
531 }
532
533 void menu_prev_page (MUTTMENU *menu)
534 {
535   menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
536 }
537
538 void menu_half_down (MUTTMENU *menu)
539 {
540   menu_length_jump (menu, menu->pagelen / 2);
541 }
542
543 void menu_half_up (MUTTMENU *menu)
544 {
545   menu_length_jump (menu, 0 - menu->pagelen / 2);
546 }
547
548 void menu_top_page (MUTTMENU *menu)
549 {
550   if (menu->current != menu->top)
551   {
552     menu->current = menu->top;
553     menu->redraw = REDRAW_MOTION;
554   }
555 }
556
557 void menu_bottom_page (MUTTMENU *menu)
558 {
559   if (menu->max)
560   {
561     menu->current = menu->top + menu->pagelen - 1;
562     if (menu->current > menu->max - 1)
563       menu->current = menu->max - 1;
564     menu->redraw = REDRAW_MOTION;
565   }
566   else
567     mutt_error _("No entries.");
568 }
569
570 void menu_middle_page (MUTTMENU *menu)
571 {
572   int i;
573
574   if (menu->max)
575   {
576     i = menu->top + menu->pagelen;
577     if (i > menu->max - 1)
578       i = menu->max - 1;
579     menu->current = menu->top + (i - menu->top) / 2;
580     menu->redraw = REDRAW_MOTION;
581   }
582   else
583     mutt_error _("No entries.");
584 }
585
586 void menu_first_entry (MUTTMENU *menu)
587 {
588   if (menu->max)
589   {
590     menu->current = 0;
591     menu->redraw = REDRAW_MOTION;
592   }
593   else
594     mutt_error _("No entries.");
595 }
596
597 void menu_last_entry (MUTTMENU *menu)
598 {
599   if (menu->max)
600   {
601     menu->current = menu->max - 1;
602     menu->redraw = REDRAW_MOTION;
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);             /* __FREE_CHECKED__ */
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     
868     
869     if (option (OPTARROWCURSOR))
870       move (menu->current - menu->top + menu->offset, 2);
871     else if (option (OPTBRAILLEFRIENDLY))
872       move (menu->current - menu->top + menu->offset, 0);
873     else
874       move (menu->current - menu->top + menu->offset, COLS - 1);
875
876     mutt_refresh ();
877     
878     /* try to catch dialog keys before ops */
879     if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
880       return i;
881                     
882     i = km_dokey (menu->menu);
883     if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
884     {
885       if (menu->tagged)
886       {
887         mvaddstr (LINES - 1, 0, "Tag-");
888         clrtoeol ();
889         i = km_dokey (menu->menu);
890         menu->tagprefix = 1;
891         CLEARLINE (LINES - 1);
892       }
893       else if (i == OP_TAG_PREFIX)
894       {
895         mutt_error _("No tagged entries.");
896         i = -1;
897       }
898       else /* None tagged, OP_TAG_PREFIX_COND */
899       {
900         event_t tmp;
901         while(UngetCount>0)
902         {
903           tmp=mutt_getch();
904           if(tmp.op==OP_END_COND)break;
905         }
906         mutt_message _("Nothing to do.");
907         i = -1;
908       }
909     }
910     else if (menu->tagged && option (OPTAUTOTAG))
911       menu->tagprefix = 1;
912     else
913       menu->tagprefix = 0;
914
915     mutt_curs_set (1);
916
917 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
918     if (SigWinch)
919     {
920       mutt_resize_screen ();
921       menu->redraw = REDRAW_FULL;
922       SigWinch = 0;
923       clearok(stdscr,TRUE);/*force complete redraw*/
924     }
925 #endif
926
927     if (i == -1)
928       continue;
929
930     if (!menu->dialog)
931       mutt_clear_error ();
932
933     /* Convert menubar movement to scrolling */
934     if (menu->dialog) 
935       i = menu_dialog_translate_op (i);
936
937     switch (i)
938     {
939       case OP_NEXT_ENTRY:
940         menu_next_entry (menu);
941         break;
942       case OP_PREV_ENTRY:
943         menu_prev_entry (menu);
944         break;
945       case OP_HALF_DOWN:
946         menu_half_down (menu);
947         break;
948       case OP_HALF_UP:
949         menu_half_up (menu);
950         break;
951       case OP_NEXT_PAGE:
952         menu_next_page (menu);
953         break;
954       case OP_PREV_PAGE:
955         menu_prev_page (menu);
956         break;
957       case OP_NEXT_LINE:
958         menu_next_line (menu);
959         break;
960       case OP_PREV_LINE:
961         menu_prev_line (menu);
962         break;
963       case OP_FIRST_ENTRY:
964         menu_first_entry (menu);
965         break;
966       case OP_LAST_ENTRY:
967         menu_last_entry (menu);
968         break;
969       case OP_TOP_PAGE:
970         menu_top_page (menu);
971         break;
972       case OP_MIDDLE_PAGE:
973         menu_middle_page (menu);
974         break;
975       case OP_BOTTOM_PAGE:
976         menu_bottom_page (menu);
977         break;
978       case OP_CURRENT_TOP:
979         menu_current_top (menu);
980         break;
981       case OP_CURRENT_MIDDLE:
982         menu_current_middle (menu);
983         break;
984       case OP_CURRENT_BOTTOM:
985         menu_current_bottom (menu);
986         break;
987       case OP_SEARCH:
988       case OP_SEARCH_REVERSE:
989       case OP_SEARCH_NEXT:
990       case OP_SEARCH_OPPOSITE:
991         if (menu->search && !menu->dialog) /* Searching dialogs won't work */
992         {
993           menu->oldcurrent = menu->current;
994           if ((menu->current = menu_search (menu, i)) != -1)
995             menu->redraw = REDRAW_MOTION;
996           else
997             menu->current = menu->oldcurrent;
998         }
999         else
1000           mutt_error _("Search is not implemented for this menu.");
1001         break;
1002
1003       case OP_JUMP:
1004         if (menu->dialog)
1005           mutt_error _("Jumping is not implemented for dialogs.");
1006         else
1007           menu_jump (menu);
1008         break;
1009
1010       case OP_ENTER_COMMAND:
1011         CurrentMenu = menu->menu;
1012         mutt_enter_command ();
1013         if (option (OPTFORCEREDRAWINDEX))
1014         {
1015           menu->redraw = REDRAW_FULL;
1016           unset_option (OPTFORCEREDRAWINDEX);
1017           unset_option (OPTFORCEREDRAWPAGER);
1018         }
1019         break;
1020
1021       case OP_TAG:
1022         if (menu->tag && !menu->dialog)
1023         {
1024           if (menu->tagprefix && !option (OPTAUTOTAG))
1025           {
1026             for (i = 0; i < menu->max; i++)
1027               menu->tagged += menu->tag (menu, i, 0);
1028             menu->redraw = REDRAW_INDEX;
1029           }
1030           else if (menu->max)
1031           {
1032             int i = menu->tag (menu, menu->current, -1);
1033             menu->tagged += i;
1034             if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1035             {
1036               menu->current++;
1037               menu->redraw = REDRAW_MOTION_RESYNCH;
1038             }
1039             else
1040               menu->redraw = REDRAW_CURRENT;
1041           }
1042           else
1043             mutt_error _("No entries.");
1044         }
1045         else
1046           mutt_error _("Tagging is not supported.");
1047         break;
1048
1049       case OP_SHELL_ESCAPE:
1050         mutt_shell_escape ();
1051         MAYBE_REDRAW (menu->redraw);
1052         break;
1053
1054       case OP_WHAT_KEY:
1055         mutt_what_key ();
1056         break;
1057
1058       case OP_REDRAW:
1059         clearok (stdscr, TRUE);
1060         menu->redraw = REDRAW_FULL;
1061         break;
1062
1063       case OP_HELP:
1064         mutt_help (menu->menu);
1065         menu->redraw = REDRAW_FULL;
1066         break;
1067
1068       case OP_NULL:
1069         km_error_key (menu->menu);
1070         break;
1071
1072       case OP_END_COND:
1073         break;
1074
1075       default:
1076         return (i);
1077     }
1078   }
1079   /* not reached */
1080 }