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