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