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