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