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