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