928834cadd0c42ee94085eadeced0417f89b2ace
[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 #include <lib-ui/lib-ui.h>
11
12 #include "enter.h"
13 #include "menu.h"
14
15 #include "mutt.h"
16 #include "charset.h"
17
18 #include <imap/imap.h>
19 #include <lib-ui/sidebar.h>
20
21 extern size_t UngetCount;
22
23 static void print_enriched_string (int attr, unsigned char *s, int do_color)
24 {
25   wchar_t wc;
26   size_t k;
27   size_t n = m_strlen((char *) s);
28   mbstate_t mbstate;
29
30   p_clear(&mbstate, 1);
31   while (*s) {
32     if (*s < M_TREE_MAX) {
33       if (do_color)
34         SETCOLOR(main_w, MT_COLOR_TREE);
35       while (*s && *s < M_TREE_MAX) {
36         switch (*s) {
37         case M_TREE_LLCORNER:
38           waddch (main_w, ACS_LLCORNER);
39           break;
40         case M_TREE_ULCORNER:
41           waddch (main_w, ACS_ULCORNER);
42           break;
43         case M_TREE_LTEE:
44           waddch (main_w, ACS_LTEE);
45           break;
46         case M_TREE_HLINE:
47           waddch (main_w, ACS_HLINE);
48           break;
49         case M_TREE_VLINE:
50           waddch (main_w, ACS_VLINE);
51           break;
52         case M_TREE_TTEE:
53           waddch (main_w, ACS_TTEE);
54           break;
55         case M_TREE_BTEE:
56           waddch (main_w, ACS_BTEE);
57           break;
58         case M_TREE_SPACE:
59           waddch (main_w, ' ');
60           break;
61         case M_TREE_RARROW:
62           waddch (main_w, '>');
63           break;
64         case M_TREE_STAR:
65           waddch (main_w, '*');          /* fake thread indicator */
66           break;
67         case M_TREE_HIDDEN:
68           waddch (main_w, '&');
69           break;
70         case M_TREE_EQUALS:
71           waddch (main_w, '=');
72           break;
73         case M_TREE_MISSING:
74           waddch (main_w, '?');
75           break;
76         }
77         s++, n--;
78       }
79       if (do_color)
80         wattrset (main_w, attr);
81     }
82     else if ((k = mbrtowc (&wc, (char *) s, n, &mbstate)) != (size_t)-1) {
83       waddnstr (main_w, (char *) s, k);
84       s += k, n -= k;
85     }
86     else
87       break;
88   }
89 }
90
91 static void menu_make_entry (char *s, int l, MUTTMENU * menu, int i)
92 {
93   if (menu->dialog) {
94     m_strcpy(s, l, menu->dialog[i]);
95     menu->current = -1;         /* hide menubar */
96   }
97   else
98     menu->make_entry (s, l, menu, i);
99 }
100
101 static void menu_pad_string (char *s, size_t n)
102 {
103   int cols = getmaxx(main_w);
104   char *tmpbuf = p_new(char, n);
105
106   mutt_format_string (tmpbuf, n, cols, cols, 0, ' ', s, m_strlen(s), 1);
107   tmpbuf[n - 1] = 0;
108   snprintf (s, n, "%s", tmpbuf);        /* overkill */
109   p_delete(&tmpbuf);
110 }
111
112 void menu_redraw_full (MUTTMENU * menu)
113 {
114   SETCOLOR(main_w, MT_COLOR_NORMAL);
115   /* wclear() doesn't optimize screen redraws */
116   wmove (main_w, 0, 0);
117   wclrtobot (main_w);
118
119   SETCOLOR(main_w, MT_COLOR_STATUS);
120   wmove (main_w, LINES - 2, 0);
121   mutt_paddstr (main_w, getmaxx(main_w), "");
122   SETCOLOR(main_w, MT_COLOR_NORMAL);
123   menu->offset = 1;
124   menu->pagelen = LINES - 3;
125   mutt_show_error ();
126
127   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
128 }
129
130 void menu_redraw_status (MUTTMENU * menu)
131 {
132   char buf[STRING];
133
134   snprintf (buf, sizeof (buf), M_MODEFMT, menu->title);
135   SETCOLOR(main_w, MT_COLOR_STATUS);
136   wmove (main_w, 0, 0);
137   mutt_paddstr (main_w, getmaxx(main_w), buf);
138   SETCOLOR(main_w, MT_COLOR_NORMAL);
139   menu->redraw &= ~REDRAW_STATUS;
140 }
141
142 void menu_redraw_index (MUTTMENU * menu)
143 {
144   char buf[STRING];
145   int i;
146
147   for (i = menu->top; i < menu->top + menu->pagelen; i++) {
148     if (i < menu->max) {
149       menu_make_entry (buf, sizeof (buf), menu, i);
150       menu_pad_string (buf, sizeof (buf));
151
152       wattrset (main_w, menu->color (i));
153
154       if (i == menu->current) {
155         ADDCOLOR(main_w, MT_COLOR_INDICATOR);
156         BKGDSET(main_w, MT_COLOR_INDICATOR);
157       }
158
159       CLEARLINE(main_w, i - menu->top + menu->offset);
160
161       wmove (main_w, i - menu->top + menu->offset, 0);
162       print_enriched_string (menu->color (i), (unsigned char *) buf,
163                              i != menu->current);
164       SETCOLOR(main_w, MT_COLOR_NORMAL);
165       BKGDSET(main_w, MT_COLOR_NORMAL);
166     } else {
167       CLEARLINE(main_w, i - menu->top + menu->offset);
168     }
169   }
170   sidebar_draw ();
171
172   menu->redraw = 0;
173 }
174
175 void menu_redraw_motion (MUTTMENU * menu)
176 {
177   char buf[STRING];
178
179   if (menu->dialog) {
180     menu->redraw &= ~REDRAW_MOTION;
181     return;
182   }
183
184   wmove (main_w, menu->oldcurrent + menu->offset - menu->top, 0);
185   SETCOLOR(main_w, MT_COLOR_NORMAL);
186   BKGDSET(main_w, MT_COLOR_NORMAL);
187
188   /* erase the current indicator */
189   wattrset (main_w, menu->color (menu->oldcurrent));
190   wclrtoeol (main_w);
191   menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
192   menu_pad_string (buf, sizeof (buf));
193   print_enriched_string (menu->color (menu->oldcurrent),
194                          (unsigned char *) buf, 1);
195
196   /* now draw the new one to reflect the change */
197   menu_make_entry (buf, sizeof (buf), menu, menu->current);
198   menu_pad_string (buf, sizeof (buf));
199   wattrset (main_w, menu->color (menu->current));
200   ADDCOLOR(main_w, MT_COLOR_INDICATOR);
201   BKGDSET(main_w, MT_COLOR_INDICATOR);
202   CLEARLINE(main_w, menu->current - menu->top + menu->offset);
203   wmove (main_w, menu->current + menu->offset - menu->top, 0);
204   print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
205                          0);
206   SETCOLOR(main_w, MT_COLOR_NORMAL);
207   BKGDSET(main_w, MT_COLOR_NORMAL);
208
209   menu->redraw &= REDRAW_STATUS;
210 }
211
212 void menu_redraw_current (MUTTMENU * menu)
213 {
214   char buf[STRING];
215
216   wmove (main_w, menu->current + menu->offset - menu->top, 0);
217   menu_make_entry (buf, sizeof (buf), menu, menu->current);
218   menu_pad_string (buf, sizeof (buf));
219
220   wattrset (main_w, menu->color (menu->current));
221   ADDCOLOR(main_w, MT_COLOR_INDICATOR);
222   BKGDSET(main_w, MT_COLOR_INDICATOR);
223   wclrtoeol (main_w);
224   print_enriched_string (menu->color (menu->current), (unsigned char *) buf,
225                          0);
226   SETCOLOR(main_w, MT_COLOR_NORMAL);
227   BKGDSET(main_w, MT_COLOR_NORMAL);
228
229   menu->redraw &= REDRAW_STATUS;
230 }
231
232 static void menu_redraw_prompt (MUTTMENU * menu)
233 {
234   if (menu->dialog) {
235     if (option (OPTMSGERR)) {
236       mutt_sleep (1);
237       unset_option (OPTMSGERR);
238     }
239
240     if (*Errorbuf)
241       mutt_clear_error ();
242
243     SETCOLOR(main_w, MT_COLOR_NORMAL);
244     mvwaddstr (main_w, LINES - 1, 0, menu->prompt);
245     wclrtoeol (main_w);
246   }
247 }
248
249 void menu_check_recenter (MUTTMENU * menu)
250 {
251   int c = MIN (MenuContext, menu->pagelen / 2);
252   int old_top = menu->top;
253
254   if ((menu->pagelen <= 0) || (c < MenuContext)) {
255     if (menu->current < menu->top + c)
256       menu->top = menu->current - c;
257     else if (menu->current >= menu->top + menu->pagelen - c)
258       menu->top = menu->current - menu->pagelen + c + 1;
259   } else {
260     if (menu->current < menu->top + c)
261       menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
262     else if ((menu->current >= menu->top + menu->pagelen - c))
263       menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
264   }
265
266   menu->top = MAX (menu->top, 0);
267
268   if (menu->top != old_top)
269     menu->redraw |= REDRAW_INDEX;
270 }
271
272 void menu_jump (MUTTMENU * menu)
273 {
274   int n;
275   char buf[STRING];
276
277   if (menu->max) {
278     mutt_ungetch (LastKey, 0);
279     buf[0] = 0;
280     if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0]) {
281       n = atoi (buf) - 1;
282       if (n >= 0 && n < menu->max) {
283         menu->current = n;
284         menu->redraw = REDRAW_MOTION;
285       }
286       else
287         mutt_error _("Invalid index number.");
288     }
289   }
290   else
291     mutt_error _("No entries.");
292 }
293
294 void menu_next_line (MUTTMENU * menu)
295 {
296   if (menu->max) {
297     int c = MIN (MenuContext, menu->pagelen / 2);
298
299     if (menu->top + 1 < menu->max - c) {
300       menu->top++;
301       if (menu->current < menu->top + c && menu->current < menu->max - 1)
302         menu->current++;
303       menu->redraw = REDRAW_INDEX;
304     } else {
305       mutt_error _("You cannot scroll down farther.");
306     }
307   } else {
308     mutt_error _("No entries.");
309   }
310 }
311
312 void menu_prev_line (MUTTMENU * menu)
313 {
314   if (menu->top > 0) {
315     int c = MIN (MenuContext, menu->pagelen / 2);
316
317     menu->top--;
318     if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
319       menu->current--;
320     menu->redraw = REDRAW_INDEX;
321   }
322   else
323     mutt_error _("You cannot scroll up farther.");
324 }
325
326 /* 
327  * pageup:   jumplen == -pagelen
328  * pagedown: jumplen == pagelen
329  * halfup:   jumplen == -pagelen/2
330  * halfdown: jumplen == pagelen/2
331  */
332 #define DIRECTION ((neg * 2) + 1)
333 static void menu_length_jump (MUTTMENU *menu, int jumplen) {
334   int tmp, neg = (jumplen >= 0) ? 0 : -1;
335   int c = MIN (MenuContext, menu->pagelen / 2);
336
337   if (menu->max) {
338     /* possible to scroll? */
339     if (DIRECTION * menu->top < 
340         (tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/)))) {
341       menu->top += jumplen;
342
343       /* jumped too long? */
344       if (neg && DIRECTION * menu->top > tmp)
345         menu->top = tmp;
346
347       /* need to move the cursor? */
348       if ((DIRECTION * 
349            (tmp = (menu->current - (menu->top + 
350                                     (neg ? (menu->pagelen - 1) - c : c))))) < 0)
351         menu->current -= tmp;
352
353       menu->redraw = REDRAW_INDEX;
354     }
355     else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog) {
356       menu->current += jumplen;
357       menu->redraw = REDRAW_MOTION;
358     }
359     else
360       mutt_error (neg ? _("You are on the first page.")
361                   : _("You are on the last page."));
362
363     menu->current = MIN (menu->current, menu->max - 1);
364     menu->current = MAX (menu->current, 0);
365   }
366   else
367     mutt_error _("No entries.");
368 }
369 #undef DIRECTION
370
371 void menu_next_page (MUTTMENU *menu) {
372   menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
373 }
374
375 void menu_prev_page (MUTTMENU *menu) {
376   menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
377 }
378
379 void menu_half_down (MUTTMENU *menu) {
380   menu_length_jump (menu, menu->pagelen / 2);
381 }
382
383 void menu_half_up (MUTTMENU *menu) {
384   menu_length_jump (menu, 0 - menu->pagelen / 2);
385 }
386
387 void menu_top_page (MUTTMENU *menu) {
388   if (menu->current != menu->top) {
389     menu->current = menu->top;
390     menu->redraw = REDRAW_MOTION;
391   }
392 }
393
394 void menu_bottom_page (MUTTMENU *menu) {
395   if (menu->max) {
396     menu->current = menu->top + menu->pagelen - 1;
397     if (menu->current > menu->max - 1)
398       menu->current = menu->max - 1;
399     menu->redraw = REDRAW_MOTION;
400   }
401   else
402     mutt_error _("No entries.");
403 }
404
405 void menu_middle_page (MUTTMENU *menu) {
406   int i;
407
408   if (menu->max) {
409     i = menu->top + menu->pagelen;
410     if (i > menu->max - 1)
411       i = menu->max - 1;
412     menu->current = menu->top + (i - menu->top) / 2;
413     menu->redraw = REDRAW_MOTION;
414   }
415   else
416     mutt_error _("No entries.");
417 }
418
419 void menu_first_entry (MUTTMENU *menu) {
420   if (menu->max) {
421     menu->current = 0;
422     menu->redraw = REDRAW_MOTION;
423   }
424   else
425     mutt_error _("No entries.");
426 }
427
428 void menu_last_entry (MUTTMENU *menu) {
429   if (menu->max) {
430     menu->current = menu->max - 1;
431     menu->redraw = REDRAW_MOTION;
432   }
433   else
434     mutt_error _("No entries.");
435 }
436
437 void menu_current_top (MUTTMENU * menu)
438 {
439   if (menu->max) {
440     menu->top = menu->current;
441     menu->redraw = REDRAW_INDEX;
442   }
443   else
444     mutt_error _("No entries.");
445 }
446
447 void menu_current_middle (MUTTMENU * menu)
448 {
449   if (menu->max) {
450     menu->top = menu->current - menu->pagelen / 2;
451     if (menu->top < 0)
452       menu->top = 0;
453     menu->redraw = REDRAW_INDEX;
454   }
455   else
456     mutt_error _("No entries.");
457 }
458
459 void menu_current_bottom (MUTTMENU * menu)
460 {
461   if (menu->max) {
462     menu->top = menu->current - menu->pagelen + 1;
463     if (menu->top < 0)
464       menu->top = 0;
465     menu->redraw = REDRAW_INDEX;
466   }
467   else
468     mutt_error _("No entries.");
469 }
470
471 static void menu_next_entry (MUTTMENU * menu)
472 {
473   if (menu->current < menu->max - 1) {
474     menu->current++;
475     menu->redraw = REDRAW_MOTION;
476   }
477   else
478     mutt_error _("You are on the last entry.");
479 }
480
481 static void menu_prev_entry (MUTTMENU * menu)
482 {
483   if (menu->current) {
484     menu->current--;
485     menu->redraw = REDRAW_MOTION;
486   }
487   else
488     mutt_error _("You are on the first entry.");
489 }
490
491 static int default_color (int i __attribute__ ((unused)))
492 {
493   return ColorDefs[MT_COLOR_NORMAL];
494 }
495
496 static int menu_search_generic (MUTTMENU * m, regex_t * re, int n)
497 {
498   char buf[LONG_STRING];
499
500   menu_make_entry (buf, sizeof (buf), m, n);
501   return (regexec (re, buf, 0, NULL, 0));
502 }
503
504 MUTTMENU *mutt_new_menu (void)
505 {
506   MUTTMENU *p = p_new(MUTTMENU, 1);
507
508   p->current = 0;
509   p->top = 0;
510   p->offset = 1;
511   p->redraw = REDRAW_FULL;
512   p->pagelen = LINES - 3;
513   p->color = default_color;
514   p->search = menu_search_generic;
515   return (p);
516 }
517
518 void mutt_menuDestroy (MUTTMENU ** p)
519 {
520   int i;
521
522   p_delete(&(*p)->searchBuf);
523
524   if ((*p)->dialog) {
525     for (i = 0; i < (*p)->max; i++)
526       p_delete(&(*p)->dialog[i]);
527
528     p_delete(&(*p)->dialog);
529   }
530
531   p_delete(p);
532 }
533
534 #define M_SEARCH_UP   1
535 #define M_SEARCH_DOWN 2
536
537 static int menu_search (MUTTMENU * menu, int op)
538 {
539   int r;
540   int searchDir;
541   regex_t re;
542   char buf[STRING];
543
544   if (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE) {
545     m_strcpy(buf, sizeof(buf), NONULL(menu->searchBuf));
546     if (mutt_get_field ((op == OP_SEARCH) ? _("Search for: ") :
547                         _("Reverse search for: "),
548                         buf, sizeof (buf), M_CLEAR) != 0 || !buf[0])
549       return (-1);
550     m_strreplace(&menu->searchBuf, buf);
551     menu->searchDir = (op == OP_SEARCH) ? M_SEARCH_DOWN : M_SEARCH_UP;
552   }
553   else {
554     if (!menu->searchBuf) {
555       mutt_error _("No search pattern.");
556
557       return (-1);
558     }
559   }
560
561   searchDir = (menu->searchDir == M_SEARCH_UP) ? -1 : 1;
562   if (op == OP_SEARCH_OPPOSITE)
563     searchDir = -searchDir;
564
565   if ((r =
566        REGCOMP (&re, menu->searchBuf,
567                 REG_NOSUB | mutt_which_case (menu->searchBuf))) != 0) {
568     regerror (r, &re, buf, sizeof (buf));
569     regfree (&re);
570     mutt_error ("%s", buf);
571     return (-1);
572   }
573
574   r = menu->current + searchDir;
575   while (r >= 0 && r < menu->max) {
576     if (menu->search (menu, &re, r) == 0) {
577       regfree (&re);
578       return r;
579     }
580
581     r += searchDir;
582   }
583
584   regfree (&re);
585   mutt_error _("Not found.");
586
587   return (-1);
588 }
589
590 static int menu_dialog_translate_op (int i)
591 {
592   switch (i) {
593   case OP_NEXT_ENTRY:
594     return OP_NEXT_LINE;
595   case OP_PREV_ENTRY:
596     return OP_PREV_LINE;
597   case OP_CURRENT_TOP:
598   case OP_FIRST_ENTRY:
599     return OP_TOP_PAGE;
600   case OP_CURRENT_BOTTOM:
601   case OP_LAST_ENTRY:
602     return OP_BOTTOM_PAGE;
603   case OP_CURRENT_MIDDLE:
604     return OP_MIDDLE_PAGE;
605   }
606
607   return i;
608 }
609
610 static int menu_dialog_dokey (MUTTMENU * menu, int *ip)
611 {
612   event_t ch;
613   char *p;
614
615   ch = mutt_getch ();
616
617   if (ch.ch == -1) {
618     *ip = -1;
619     return 0;
620   }
621
622   if (ch.ch && (p = strchr (menu->keys, ch.ch))) {
623     *ip = OP_MAX + (p - menu->keys + 1);
624     return 0;
625   }
626   else {
627     mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
628     return -1;
629   }
630 }
631
632 int menu_redraw (MUTTMENU * menu)
633 {
634   /* See if all or part of the screen needs to be updated.  */
635   if (menu->redraw & REDRAW_FULL) {
636     menu_redraw_full (menu);
637     /* allow the caller to do any local configuration */
638     return (OP_REDRAW);
639   }
640
641   if (!menu->dialog)
642     menu_check_recenter (menu);
643
644   if (menu->redraw & REDRAW_STATUS)
645     menu_redraw_status (menu);
646   if (menu->redraw & REDRAW_INDEX)
647     menu_redraw_index (menu);
648   else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
649     menu_redraw_motion (menu);
650   else if (menu->redraw == REDRAW_CURRENT)
651     menu_redraw_current (menu);
652
653   if (menu->dialog)
654     menu_redraw_prompt (menu);
655
656   return OP_NULL;
657 }
658
659 int mutt_menuLoop (MUTTMENU * menu)
660 {
661   int i = OP_NULL;
662
663   for (;;) {
664     if (option (OPTMENUCALLER)) {
665       unset_option (OPTMENUCALLER);
666       return OP_NULL;
667     }
668
669
670     mutt_curs_set (0);
671     imap_keepalive ();
672
673     if (menu_redraw (menu) == OP_REDRAW)
674       return OP_REDRAW;
675
676     menu->oldcurrent = menu->current;
677     mutt_refresh ();
678
679     /* try to catch dialog keys before ops */
680     if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
681       return i;
682
683     i = km_dokey (menu->menu);
684     if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) {
685       if (menu->tagged) {
686         mvwaddstr (main_w, LINES - 1, 0, "Tag-");
687         wclrtoeol (main_w);
688         i = km_dokey (menu->menu);
689         menu->tagprefix = 1;
690         CLEARLINE(main_w, LINES - 1);
691       }
692       else if (i == OP_TAG_PREFIX) {
693         mutt_error _("No tagged entries.");
694
695         i = -1;
696       }
697       else {                    /* None tagged, OP_TAG_PREFIX_COND */
698
699         event_t tmp;
700
701         while (UngetCount > 0) {
702           tmp = mutt_getch ();
703           if (tmp.op == OP_END_COND)
704             break;
705         }
706         mutt_message _("Nothing to do.");
707
708         i = -1;
709       }
710     }
711     else if (menu->tagged && option (OPTAUTOTAG))
712       menu->tagprefix = 1;
713     else
714       menu->tagprefix = 0;
715
716     mutt_curs_set (1);
717
718     if (SigWinch) {
719       ui_layout_resize();
720       menu->redraw = REDRAW_FULL;
721     }
722
723     if (i == -1)
724       continue;
725
726     if (!menu->dialog)
727       mutt_clear_error ();
728
729     /* Convert menubar movement to scrolling */
730     if (menu->dialog)
731       i = menu_dialog_translate_op (i);
732
733     switch (i) {
734     case OP_NEXT_ENTRY:
735       menu_next_entry (menu);
736       break;
737     case OP_PREV_ENTRY:
738       menu_prev_entry (menu);
739       break;
740     case OP_HALF_DOWN:
741       menu_half_down (menu);
742       break;
743     case OP_HALF_UP:
744       menu_half_up (menu);
745       break;
746     case OP_NEXT_PAGE:
747       menu_next_page (menu);
748       break;
749     case OP_PREV_PAGE:
750       menu_prev_page (menu);
751       break;
752     case OP_NEXT_LINE:
753       menu_next_line (menu);
754       break;
755     case OP_PREV_LINE:
756       menu_prev_line (menu);
757       break;
758     case OP_FIRST_ENTRY:
759       menu_first_entry (menu);
760       break;
761     case OP_LAST_ENTRY:
762       menu_last_entry (menu);
763       break;
764     case OP_TOP_PAGE:
765       menu_top_page (menu);
766       break;
767     case OP_MIDDLE_PAGE:
768       menu_middle_page (menu);
769       break;
770     case OP_BOTTOM_PAGE:
771       menu_bottom_page (menu);
772       break;
773     case OP_CURRENT_TOP:
774       menu_current_top (menu);
775       break;
776     case OP_CURRENT_MIDDLE:
777       menu_current_middle (menu);
778       break;
779     case OP_CURRENT_BOTTOM:
780       menu_current_bottom (menu);
781       break;
782     case OP_SEARCH:
783     case OP_SEARCH_REVERSE:
784     case OP_SEARCH_NEXT:
785     case OP_SEARCH_OPPOSITE:
786       if (menu->search && !menu->dialog) {      /* Searching dialogs won't work */
787         menu->oldcurrent = menu->current;
788         if ((menu->current = menu_search (menu, i)) != -1)
789           menu->redraw = REDRAW_MOTION;
790         else
791           menu->current = menu->oldcurrent;
792       }
793       else
794         mutt_error _("Search is not implemented for this menu.");
795       break;
796
797     case OP_JUMP:
798       if (menu->dialog)
799         mutt_error (_("Jumping is not implemented for dialogs."));
800
801       else
802         menu_jump (menu);
803       break;
804
805     case OP_ENTER_COMMAND:
806       CurrentMenu = menu->menu;
807       mutt_enter_command ();
808       if (option (OPTFORCEREDRAWINDEX)) {
809         menu->redraw = REDRAW_FULL;
810         unset_option (OPTFORCEREDRAWINDEX);
811         unset_option (OPTFORCEREDRAWPAGER);
812       }
813       break;
814
815     case OP_TAG:
816       if (menu->tag && !menu->dialog) {
817         if (menu->tagprefix && !option (OPTAUTOTAG)) {
818           for (i = 0; i < menu->max; i++)
819             menu->tagged += menu->tag (menu, i, 0);
820           menu->redraw = REDRAW_INDEX;
821         }
822         else if (menu->max) {
823           int t = menu->tag (menu, menu->current, -1);
824
825           menu->tagged += t;
826           if (t && option (OPTRESOLVE) && menu->current < menu->max - 1) {
827             menu->current++;
828             menu->redraw = REDRAW_MOTION_RESYNCH;
829           }
830           else
831             menu->redraw = REDRAW_CURRENT;
832         }
833         else
834           mutt_error _("No entries.");
835       }
836       else
837         mutt_error _("Tagging is not supported.");
838       break;
839
840     case OP_SHELL_ESCAPE:
841       mutt_shell_escape ();
842       MAYBE_REDRAW (menu->redraw);
843       break;
844
845     case OP_WHAT_KEY:
846       mutt_what_key ();
847       break;
848
849     case OP_REDRAW:
850       clearok (main_w, TRUE);
851       menu->redraw = REDRAW_FULL;
852       break;
853
854     case OP_HELP:
855       mutt_help (menu->menu);
856       menu->redraw = REDRAW_FULL;
857       break;
858
859     case OP_NULL:
860       km_error_key (menu->menu);
861       break;
862
863     case OP_END_COND:
864       break;
865
866     default:
867       return (i);
868     }
869   }
870   /* not reached */
871 }