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