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