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