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