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