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