4e9904708bd77bb5536637783f3f7666d6c1206f
[apps/madmutt.git] / lib-ui / 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 <string.h>
15 #include <stdlib.h>
16 #include <wchar.h>
17
18 #include <lib-lib/mem.h>
19 #include <lib-lib/str.h>
20 #include <lib-lib/macros.h>
21
22 #include "curses.h"
23 #include "enter.h"
24 #include "menu.h"
25
26 #include "mutt.h"
27 #include "charset.h"
28
29 #include <imap/imap.h>
30 #include <lib-ui/sidebar.h>
31
32 #define SW              (option(OPTMBOXPANE)?SidebarWidth:0)
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 = m_strlen((char *) s);
41   mbstate_t mbstate;
42
43   p_clear(&mbstate, 1);
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     m_strcpy(s, l, menu->dialog[i]);
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 = p_new(char, 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, m_strlen(s), 1);
160   tmpbuf[n - 1] = 0;
161   snprintf (s, n, "%s", tmpbuf);        /* overkill */
162   p_delete(&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, SW);
175     mutt_paddstr (COLS-SW, 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   sidebar_draw_frames();
186
187   mutt_show_error ();
188
189   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
190 }
191
192 void menu_redraw_status (MUTTMENU * menu)
193 {
194   char buf[STRING];
195
196   snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
197   SETCOLOR (MT_COLOR_STATUS);
198   move (option (OPTSTATUSONTOP) ? 0 : LINES - 2, SW);
199   mutt_paddstr (COLS-SW, buf);
200   SETCOLOR (MT_COLOR_NORMAL);
201   menu->redraw &= ~REDRAW_STATUS;
202   sidebar_draw_frames();
203 }
204
205 void menu_redraw_index (MUTTMENU * menu)
206 {
207   char buf[STRING];
208   int i;
209
210   for (i = menu->top; i < menu->top + menu->pagelen; i++) {
211     if (i < menu->max) {
212       menu_make_entry (buf, sizeof (buf), menu, i);
213       menu_pad_string (buf, sizeof (buf));
214
215       if (option (OPTARROWCURSOR)) {
216         attrset (menu->color (i));
217         CLEARLINE_WIN (i - menu->top + menu->offset);
218
219         if (i == menu->current) {
220           attrset (menu->color (i));
221           ADDCOLOR (MT_COLOR_INDICATOR);
222           BKGDSET (MT_COLOR_INDICATOR);
223           addstr ("->");
224           attrset (menu->color (i));
225           addch (' ');
226         }
227         else {
228           attrset (menu->color (i));
229           move (i - menu->top + menu->offset, SW);
230           addstr ("   ");
231         }
232
233         print_enriched_string (menu->color (i), (unsigned char *) buf, 1);
234         SETCOLOR (MT_COLOR_NORMAL);
235         BKGDSET (MT_COLOR_NORMAL);
236       }
237       else {
238         attrset (menu->color (i));
239
240         if (i == menu->current) {
241           ADDCOLOR (MT_COLOR_INDICATOR);
242           BKGDSET (MT_COLOR_INDICATOR);
243         }
244
245         CLEARLINE_WIN (i - menu->top + menu->offset);
246
247         move (i - menu->top + menu->offset, SW);
248         print_enriched_string (menu->color (i), (unsigned char *) buf,
249                                i != menu->current);
250         SETCOLOR (MT_COLOR_NORMAL);
251         BKGDSET (MT_COLOR_NORMAL);
252       }
253     }
254     else
255       CLEARLINE_WIN (i - menu->top + menu->offset);
256   }
257   sidebar_draw (1);
258 /*  sidebar_draw_frames(); */
259
260   menu->redraw = 0;
261 }
262
263 void menu_redraw_motion (MUTTMENU * menu)
264 {
265   char buf[STRING];
266
267   if (menu->dialog) {
268     menu->redraw &= ~REDRAW_MOTION;
269     return;
270   }
271
272   move (menu->oldcurrent + menu->offset - menu->top, SW);
273   SETCOLOR (MT_COLOR_NORMAL);
274   BKGDSET (MT_COLOR_NORMAL);
275
276   if (option (OPTARROWCURSOR)) {
277     /* clear the pointer */
278     attrset (menu->color (menu->oldcurrent));
279     addstr ("  ");
280
281     if (menu->redraw & REDRAW_MOTION_RESYNCH) {
282       clrtoeol ();
283       menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
284       menu_pad_string (buf, sizeof (buf));
285       move (menu->oldcurrent + menu->offset - menu->top, SW + 3);
286       print_enriched_string (menu->color (menu->oldcurrent),
287                              (unsigned char *) buf, 1);
288       SETCOLOR (MT_COLOR_NORMAL);
289     }
290
291     /* now draw it in the new location */
292     move (menu->current + menu->offset - menu->top, SW);
293     attrset (menu->color (menu->current));
294     ADDCOLOR (MT_COLOR_INDICATOR);
295     addstr ("->");
296     SETCOLOR (MT_COLOR_NORMAL);
297   }
298   else {
299     /* erase the current indicator */
300     attrset (menu->color (menu->oldcurrent));
301     clrtoeol ();
302     menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
303     menu_pad_string (buf, sizeof (buf));
304     print_enriched_string (menu->color (menu->oldcurrent),
305                            (unsigned char *) buf, 1);
306
307     /* now draw the new one to reflect the change */
308     menu_make_entry (buf, sizeof (buf), menu, menu->current);
309     menu_pad_string (buf, sizeof (buf));
310     attrset (menu->color (menu->current));
311     ADDCOLOR (MT_COLOR_INDICATOR);
312     BKGDSET (MT_COLOR_INDICATOR);
313     CLEARLINE_WIN (menu->current - menu->top + menu->offset);
314     move (menu->current + menu->offset - menu->top, SW);
315     print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
316                            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[STRING];
326
327   move (menu->current + menu->offset - menu->top, SW);
328   menu_make_entry (buf, sizeof (buf), menu, menu->current);
329   menu_pad_string (buf, sizeof (buf));
330
331   if (option (OPTARROWCURSOR)) {
332     int attr = menu->color (menu->current);
333
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,
343                            1);
344     SETCOLOR (MT_COLOR_NORMAL);
345   }
346   else {
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,
352                            0);
353     SETCOLOR (MT_COLOR_NORMAL);
354     BKGDSET (MT_COLOR_NORMAL);
355   }
356   menu->redraw &= REDRAW_STATUS;
357 }
358
359 void menu_redraw_prompt (MUTTMENU * menu)
360 {
361   if (menu->dialog) {
362     if (option (OPTMSGERR)) {
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   int c = MIN (MenuContext, menu->pagelen / 2);
379   int old_top = menu->top;
380
381   if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) { /* less entries than lines */
382     if (menu->top != 0) {
383       menu->top = 0;
384       set_option (OPTNEEDREDRAW);
385     }
386   } else {
387     if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext)) {
388       if (menu->current < menu->top + c)
389         menu->top = menu->current - c;
390       else if (menu->current >= menu->top + menu->pagelen - c)
391         menu->top = menu->current - menu->pagelen + c + 1;
392     } else {
393       if (menu->current < menu->top + c)
394         menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
395       else if ((menu->current >= menu->top + menu->pagelen - c))
396         menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
397     }
398   }
399
400   if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
401     menu->top = MIN (menu->top, menu->max - menu->pagelen);
402   menu->top = MAX (menu->top, 0);
403
404   if (menu->top != old_top)
405     menu->redraw |= REDRAW_INDEX;
406 }
407
408 void menu_jump (MUTTMENU * menu)
409 {
410   int n;
411   char buf[SHORT_STRING];
412
413   if (menu->max) {
414     mutt_ungetch (LastKey, 0);
415     buf[0] = 0;
416     if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
417       n = atoi (buf) - 1;
418       if (n >= 0 && n < menu->max) {
419         menu->current = n;
420         menu->redraw = REDRAW_MOTION;
421       }
422       else
423         mutt_error _("Invalid index number.");
424     }
425   }
426   else
427     mutt_error _("No entries.");
428 }
429
430 void menu_next_line (MUTTMENU * menu)
431 {
432   if (menu->max) {
433     int c = MIN (MenuContext, menu->pagelen / 2);
434
435     if (menu->top + 1 < menu->max - c && (option (OPTMENUMOVEOFF)
436                                           || (menu->max > menu->pagelen
437                                               && menu->top <
438                                               menu->max - menu->pagelen))) {
439       menu->top++;
440       if (menu->current < menu->top + c && menu->current < menu->max - 1)
441         menu->current++;
442       menu->redraw = REDRAW_INDEX;
443     }
444     else
445       mutt_error _("You cannot scroll down farther.");
446   }
447   else
448     mutt_error _("No entries.");
449 }
450
451 void menu_prev_line (MUTTMENU * menu)
452 {
453   if (menu->top > 0) {
454     int c = MIN (MenuContext, menu->pagelen / 2);
455
456     menu->top--;
457     if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
458       menu->current--;
459     menu->redraw = REDRAW_INDEX;
460   }
461   else
462     mutt_error _("You cannot scroll up farther.");
463 }
464
465 /* 
466  * pageup:   jumplen == -pagelen
467  * pagedown: jumplen == pagelen
468  * halfup:   jumplen == -pagelen/2
469  * halfdown: jumplen == pagelen/2
470  */
471 #define DIRECTION ((neg * 2) + 1)
472 void menu_length_jump (MUTTMENU *menu, int jumplen) {
473   int tmp, neg = (jumplen >= 0) ? 0 : -1;
474   int c = MIN (MenuContext, menu->pagelen / 2);
475
476   if (menu->max) {
477     /* possible to scroll? */
478     if (DIRECTION * menu->top < 
479         (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
480       menu->top += jumplen;
481
482       /* jumped too long? */
483       if ((neg || !option (OPTMENUMOVEOFF)) && DIRECTION * menu->top > tmp)
484         menu->top = tmp;
485
486       /* need to move the cursor? */
487       if ((DIRECTION * 
488            (tmp = (menu->current - (menu->top + 
489                                     (neg ? (menu->pagelen - 1) - c : c))))) < 0)
490         menu->current -= tmp;
491
492       menu->redraw = REDRAW_INDEX;
493     }
494     else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
495       menu->current += jumplen;
496       menu->redraw = REDRAW_MOTION;
497     }
498     else
499       mutt_error (neg ? _("You are on the first page.")
500                   : _("You are on the last page."));
501
502     menu->current = MIN (menu->current, menu->max - 1);
503     menu->current = MAX (menu->current, 0);
504   }
505   else
506     mutt_error _("No entries.");
507 }
508 #undef DIRECTION
509
510 void menu_next_page (MUTTMENU *menu) {
511   menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
512 }
513
514 void menu_prev_page (MUTTMENU *menu) {
515   menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
516 }
517
518 void menu_half_down (MUTTMENU *menu) {
519   menu_length_jump (menu, menu->pagelen / 2);
520 }
521
522 void menu_half_up (MUTTMENU *menu) {
523   menu_length_jump (menu, 0 - menu->pagelen / 2);
524 }
525
526 void menu_top_page (MUTTMENU *menu) {
527   if (menu->current != menu->top) {
528     menu->current = menu->top;
529     menu->redraw = REDRAW_MOTION;
530   }
531 }
532
533 void menu_bottom_page (MUTTMENU *menu) {
534   if (menu->max) {
535     menu->current = menu->top + menu->pagelen - 1;
536     if (menu->current > menu->max - 1)
537       menu->current = menu->max - 1;
538     menu->redraw = REDRAW_MOTION;
539   }
540   else
541     mutt_error _("No entries.");
542 }
543
544 void menu_middle_page (MUTTMENU *menu) {
545   int i;
546
547   if (menu->max) {
548     i = menu->top + menu->pagelen;
549     if (i > menu->max - 1)
550       i = menu->max - 1;
551     menu->current = menu->top + (i - menu->top) / 2;
552     menu->redraw = REDRAW_MOTION;
553   }
554   else
555     mutt_error _("No entries.");
556 }
557
558 void menu_first_entry (MUTTMENU *menu) {
559   if (menu->max) {
560     menu->current = 0;
561     menu->redraw = REDRAW_MOTION;
562   }
563   else
564     mutt_error _("No entries.");
565 }
566
567 void menu_last_entry (MUTTMENU *menu) {
568   if (menu->max) {
569     menu->current = menu->max - 1;
570     menu->redraw = REDRAW_MOTION;
571   }
572   else
573     mutt_error _("No entries.");
574 }
575
576 void menu_current_top (MUTTMENU * menu)
577 {
578   if (menu->max) {
579     menu->top = menu->current;
580     menu->redraw = REDRAW_INDEX;
581   }
582   else
583     mutt_error _("No entries.");
584 }
585
586 void menu_current_middle (MUTTMENU * menu)
587 {
588   if (menu->max) {
589     menu->top = menu->current - menu->pagelen / 2;
590     if (menu->top < 0)
591       menu->top = 0;
592     menu->redraw = REDRAW_INDEX;
593   }
594   else
595     mutt_error _("No entries.");
596 }
597
598 void menu_current_bottom (MUTTMENU * menu)
599 {
600   if (menu->max) {
601     menu->top = menu->current - menu->pagelen + 1;
602     if (menu->top < 0)
603       menu->top = 0;
604     menu->redraw = REDRAW_INDEX;
605   }
606   else
607     mutt_error _("No entries.");
608 }
609
610 void menu_next_entry (MUTTMENU * menu)
611 {
612   if (menu->current < menu->max - 1) {
613     menu->current++;
614     menu->redraw = REDRAW_MOTION;
615   }
616   else
617     mutt_error _("You are on the last entry.");
618 }
619
620 void menu_prev_entry (MUTTMENU * menu)
621 {
622   if (menu->current) {
623     menu->current--;
624     menu->redraw = REDRAW_MOTION;
625   }
626   else
627     mutt_error _("You are on the first entry.");
628 }
629
630 static int default_color (int i __attribute__ ((unused)))
631 {
632   return ColorDefs[MT_COLOR_NORMAL];
633 }
634
635 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
636 {
637   char buf[LONG_STRING];
638
639   menu_make_entry (buf, sizeof (buf), m, n);
640   return (regexec (re, buf, 0, NULL, 0));
641 }
642
643 MUTTMENU *mutt_new_menu (void)
644 {
645   MUTTMENU *p = p_new(MUTTMENU, 1);
646
647   p->current = 0;
648   p->top = 0;
649   p->offset = 1;
650   p->redraw = REDRAW_FULL;
651   p->pagelen = PAGELEN;
652   p->color = default_color;
653   p->search = menu_search_generic;
654   return (p);
655 }
656
657 void mutt_menuDestroy (MUTTMENU ** p)
658 {
659   int i;
660
661   p_delete(&(*p)->searchBuf);
662
663   if ((*p)->dialog) {
664     for (i = 0; i < (*p)->max; i++)
665       p_delete(&(*p)->dialog[i]);
666
667     p_delete(&(*p)->dialog);
668   }
669
670   p_delete(p);
671 }
672
673 #define M_SEARCH_UP   1
674 #define M_SEARCH_DOWN 2
675
676 static int menu_search (MUTTMENU * menu, int op)
677 {
678   int r;
679   int searchDir;
680   regex_t re;
681   char buf[SHORT_STRING];
682
683   if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
684     m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
685     if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
686                         _("Reverse search for: "),
687                         buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
688       return (-1);
689     m_strreplace(&menu->searchBuf, buf);
690     menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
691   }
692   else {
693     if (!menu->searchBuf) {
694       mutt_error _("No search pattern.");
695
696       return (-1);
697     }
698   }
699
700   searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
701   if (op == OP_SEARCH_OPPOSITE)
702     searchDir = -searchDir;
703
704   if ((r =
705        REGCOMP (&re, menu->searchBuf,
706                 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
707     regerror (r, &re, buf, sizeof (buf));
708     regfree (&re);
709     mutt_error ("%s", buf);
710     return (-1);
711   }
712
713   r = menu->current + searchDir;
714   while (r >= 0 && r < menu->max) {
715     if (menu->search (menu, &re, r) == 0) {
716       regfree (&re);
717       return r;
718     }
719
720     r += searchDir;
721   }
722
723   regfree (&re);
724   mutt_error _("Not found.");
725
726   return (-1);
727 }
728
729 static int menu_dialog_translate_op (int i)
730 {
731   switch (i) {
732   case OP_NEXT_ENTRY:
733     return OP_NEXT_LINE;
734   case OP_PREV_ENTRY:
735     return OP_PREV_LINE;
736   case OP_CURRENT_TOP:
737   case OP_FIRST_ENTRY:
738     return OP_TOP_PAGE;
739   case OP_CURRENT_BOTTOM:
740   case OP_LAST_ENTRY:
741     return OP_BOTTOM_PAGE;
742   case OP_CURRENT_MIDDLE:
743     return OP_MIDDLE_PAGE;
744   }
745
746   return i;
747 }
748
749 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
750 {
751   event_t ch;
752   char *p;
753
754   ch = mutt_getch ();
755
756   if (ch.ch == -1) {
757     *ip = -1;
758     return 0;
759   }
760
761   if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
762     *ip = OP_MAX + (p - menu->keys + 1);
763     return 0;
764   }
765   else {
766     mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
767     return -1;
768   }
769 }
770
771 int menu_redraw (MUTTMENU * menu)
772 {
773   /* See if all or part of the screen needs to be updated.  */
774   if (menu->redraw & REDRAW_FULL) {
775     menu_redraw_full (menu);
776     /* allow the caller to do any local configuration */
777     return (OP_REDRAW);
778   }
779
780   if (!menu->dialog)
781     menu_check_recenter (menu);
782
783   if (menu->redraw & REDRAW_STATUS)
784     menu_redraw_status (menu);
785   if (menu->redraw & REDRAW_INDEX)
786     menu_redraw_index (menu);
787   else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
788     menu_redraw_motion (menu);
789   else if (menu->redraw == REDRAW_CURRENT)
790     menu_redraw_current (menu);
791
792   if (menu->dialog)
793     menu_redraw_prompt (menu);
794
795   return OP_NULL;
796 }
797
798 int mutt_menuLoop (MUTTMENU * menu)
799 {
800   int i = OP_NULL;
801
802   for (;;) {
803     if (option (OPTMENUCALLER)) {
804       unset_option (OPTMENUCALLER);
805       return OP_NULL;
806     }
807
808
809     mutt_curs_set (0);
810     imap_keepalive ();
811
812     if (menu_redraw (menu) == OP_REDRAW)
813       return OP_REDRAW;
814
815     menu->oldcurrent = menu->current;
816
817     if (option (OPTARROWCURSOR))
818       move (menu->current - menu->top + menu->offset, SW + 2);
819     else if (option (OPTBRAILLEFRIENDLY))
820       move (menu->current - menu->top + menu->offset, SW);
821     else
822       move (menu->current - menu->top + menu->offset, COLS - 1);
823
824     mutt_refresh ();
825
826
827     /* try to catch dialog keys before ops */
828     if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
829       return i;
830
831     i = km_dokey (menu->menu);
832     if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
833       if (menu->tagged) {
834         mvaddstr (LINES - 1, 0, "Tag-");
835         clrtoeol ();
836         i = km_dokey (menu->menu);
837         menu->tagprefix = 1;
838         CLEARLINE (LINES - 1);
839       }
840       else if (i == OP_TAG_PREFIX) {
841         mutt_error _("No tagged entries.");
842
843         i = -1;
844       }
845       else {                    /* None tagged, OP_TAG_PREFIX_COND */
846
847         event_t tmp;
848
849         while (UngetCount > 0) {
850           tmp = mutt_getch ();
851           if (tmp.op == OP_END_COND)
852             break;
853         }
854         mutt_message _("Nothing to do.");
855
856         i = -1;
857       }
858     }
859     else if (menu->tagged && option (OPTAUTOTAG))
860       menu->tagprefix = 1;
861     else
862       menu->tagprefix = 0;
863
864     mutt_curs_set (1);
865
866 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
867     if (SigWinch) {
868       mutt_resize_screen ();
869       menu->redraw = REDRAW_FULL;
870       SigWinch = 0;
871       clearok (stdscr, TRUE);   /*force complete redraw */
872     }
873 #endif
874
875     if (i == -1)
876       continue;
877
878     if (!menu->dialog)
879       mutt_clear_error ();
880
881     /* Convert menubar movement to scrolling */
882     if (menu->dialog)
883       i = menu_dialog_translate_op (i);
884
885     switch (i) {
886     case OP_NEXT_ENTRY:
887       menu_next_entry (menu);
888       break;
889     case OP_PREV_ENTRY:
890       menu_prev_entry (menu);
891       break;
892     case OP_HALF_DOWN:
893       menu_half_down (menu);
894       break;
895     case OP_HALF_UP:
896       menu_half_up (menu);
897       break;
898     case OP_NEXT_PAGE:
899       menu_next_page (menu);
900       break;
901     case OP_PREV_PAGE:
902       menu_prev_page (menu);
903       break;
904     case OP_NEXT_LINE:
905       menu_next_line (menu);
906       break;
907     case OP_PREV_LINE:
908       menu_prev_line (menu);
909       break;
910     case OP_FIRST_ENTRY:
911       menu_first_entry (menu);
912       break;
913     case OP_LAST_ENTRY:
914       menu_last_entry (menu);
915       break;
916     case OP_TOP_PAGE:
917       menu_top_page (menu);
918       break;
919     case OP_MIDDLE_PAGE:
920       menu_middle_page (menu);
921       break;
922     case OP_BOTTOM_PAGE:
923       menu_bottom_page (menu);
924       break;
925     case OP_CURRENT_TOP:
926       menu_current_top (menu);
927       break;
928     case OP_CURRENT_MIDDLE:
929       menu_current_middle (menu);
930       break;
931     case OP_CURRENT_BOTTOM:
932       menu_current_bottom (menu);
933       break;
934     case OP_SEARCH:
935     case OP_SEARCH_REVERSE:
936     case OP_SEARCH_NEXT:
937     case OP_SEARCH_OPPOSITE:
938       if (menu->search && !menu->dialog) {      /* Searching dialogs won't work */
939         menu->oldcurrent = menu->current;
940         if ((menu->current = menu_search (menu, i)) != -1)
941           menu->redraw = REDRAW_MOTION;
942         else
943           menu->current = menu->oldcurrent;
944       }
945       else
946         mutt_error _("Search is not implemented for this menu.");
947       break;
948
949     case OP_JUMP:
950       if (menu->dialog)
951         mutt_error (_("Jumping is not implemented for dialogs."));
952
953       else
954         menu_jump (menu);
955       break;
956
957     case OP_ENTER_COMMAND:
958       CurrentMenu = menu->menu;
959       mutt_enter_command ();
960       if (option (OPTFORCEREDRAWINDEX)) {
961         menu->redraw = REDRAW_FULL;
962         unset_option (OPTFORCEREDRAWINDEX);
963         unset_option (OPTFORCEREDRAWPAGER);
964       }
965       break;
966
967     case OP_TAG:
968       if (menu->tag && !menu->dialog) {
969         if (menu->tagprefix && !option (OPTAUTOTAG)) {
970           for (i = 0; i < menu->max; i++)
971             menu->tagged += menu->tag (menu, i, 0);
972           menu->redraw = REDRAW_INDEX;
973         }
974         else if (menu->max) {
975           int t = menu->tag (menu, menu->current, -1);
976
977           menu->tagged += t;
978           if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
979             menu->current++;
980             menu->redraw = REDRAW_MOTION_RESYNCH;
981           }
982           else
983             menu->redraw = REDRAW_CURRENT;
984         }
985         else
986           mutt_error _("No entries.");
987       }
988       else
989         mutt_error _("Tagging is not supported.");
990       break;
991
992     case OP_SHELL_ESCAPE:
993       mutt_shell_escape ();
994       MAYBE_REDRAW (menu->redraw);
995       break;
996
997     case OP_WHAT_KEY:
998       mutt_what_key ();
999       break;
1000
1001     case OP_REBUILD_CACHE:
1002       mx_rebuild_cache ();
1003       break;
1004
1005     case OP_REDRAW:
1006       clearok (stdscr, TRUE);
1007       menu->redraw = REDRAW_FULL;
1008       break;
1009
1010     case OP_HELP:
1011       mutt_help (menu->menu);
1012       menu->redraw = REDRAW_FULL;
1013       break;
1014
1015     case OP_NULL:
1016       km_error_key (menu->menu);
1017       break;
1018
1019     case OP_END_COND:
1020       break;
1021
1022     default:
1023       return (i);
1024     }
1025   }
1026   /* not reached */
1027 }