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