]> git.llucax.com Git - software/mutt-debian.git/blob - menu.c
debian/extra/rc/compressed-folders.rc: added support for xz-compressed folders (Close...
[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     mutt_error ("%s", buf);
753     return (-1);
754   }
755
756   r = menu->current + searchDir;
757 search_next:
758   if (wrap)
759     mutt_message (_("Search wrapped to top."));
760   while (r >= 0 && r < menu->max)
761   {
762     if (menu->search (menu, &re, r) == 0)
763     {
764       regfree (&re);
765       return r;
766     }
767
768     r += searchDir;
769   }
770
771   if (option (OPTWRAPSEARCH) && wrap++ == 0)
772   {
773     r = searchDir == 1 ? 0 : menu->max - 1;
774     goto search_next;
775   }
776   regfree (&re);
777   mutt_error _("Not found.");
778   return (-1);
779 }
780
781 static int menu_dialog_translate_op (int i)
782 {
783   switch (i)
784   {
785     case OP_NEXT_ENTRY:   
786       return OP_NEXT_LINE;
787     case OP_PREV_ENTRY:   
788       return OP_PREV_LINE;
789     case OP_CURRENT_TOP:   case OP_FIRST_ENTRY:  
790       return OP_TOP_PAGE;
791     case OP_CURRENT_BOTTOM:    case OP_LAST_ENTRY:        
792       return OP_BOTTOM_PAGE;
793     case OP_CURRENT_MIDDLE: 
794       return OP_MIDDLE_PAGE; 
795   }
796   
797   return i;
798 }
799
800 static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
801 {
802   event_t ch;
803   char *p;
804
805   ch = mutt_getch ();
806
807   if (ch.ch < 0)
808   {
809     *ip = -1;
810     return 0;
811   }
812
813   if (ch.ch && (p = strchr (menu->keys, ch.ch)))
814   {
815     *ip = OP_MAX + (p - menu->keys + 1);
816     return 0;
817   }
818   else
819   {
820     mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
821     return -1;
822   }
823 }
824
825 int menu_redraw (MUTTMENU *menu)
826 {
827   /* See if all or part of the screen needs to be updated.  */
828   if (menu->redraw & REDRAW_FULL)
829   {
830     menu_redraw_full (menu);
831     /* allow the caller to do any local configuration */
832     return (OP_REDRAW);
833   }
834   
835   if (!menu->dialog)
836     menu_check_recenter (menu);
837   
838   if (menu->redraw & REDRAW_STATUS)
839     menu_redraw_status (menu);
840   if (menu->redraw & REDRAW_INDEX)
841     menu_redraw_index (menu);
842   else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
843     menu_redraw_motion (menu);
844   else if (menu->redraw == REDRAW_CURRENT)
845     menu_redraw_current (menu);
846   
847   if (menu->dialog)
848     menu_redraw_prompt (menu);
849   
850   return OP_NULL;
851 }
852
853 int mutt_menuLoop (MUTTMENU *menu)
854 {
855   int i = OP_NULL;
856
857   FOREVER
858   {
859     if (option (OPTMENUCALLER))
860     {
861       unset_option (OPTMENUCALLER);
862       return OP_NULL;
863     }
864     
865     
866     mutt_curs_set (0);
867
868     if (menu_redraw (menu) == OP_REDRAW)
869       return OP_REDRAW;
870     
871     menu->oldcurrent = menu->current;
872
873
874     /* move the cursor out of the way */
875     
876     
877     if (option (OPTARROWCURSOR))
878       move (menu->current - menu->top + menu->offset, 2);
879     else if (option (OPTBRAILLEFRIENDLY))
880       move (menu->current - menu->top + menu->offset, 0);
881     else
882       move (menu->current - menu->top + menu->offset, COLS - 1);
883
884     mutt_refresh ();
885     
886     /* try to catch dialog keys before ops */
887     if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
888       return i;
889                     
890     i = km_dokey (menu->menu);
891     if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
892     {
893       if (menu->tagged)
894       {
895         mvaddstr (LINES - 1, 0, "Tag-");
896         clrtoeol ();
897         i = km_dokey (menu->menu);
898         menu->tagprefix = 1;
899         CLEARLINE (LINES - 1);
900       }
901       else if (i == OP_TAG_PREFIX)
902       {
903         mutt_error _("No tagged entries.");
904         i = -1;
905       }
906       else /* None tagged, OP_TAG_PREFIX_COND */
907       {
908         event_t tmp;
909         while(UngetCount>0)
910         {
911           tmp=mutt_getch();
912           if(tmp.op==OP_END_COND)break;
913         }
914         mutt_message _("Nothing to do.");
915         i = -1;
916       }
917     }
918     else if (menu->tagged && option (OPTAUTOTAG))
919       menu->tagprefix = 1;
920     else
921       menu->tagprefix = 0;
922
923     mutt_curs_set (1);
924
925 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
926     if (SigWinch)
927     {
928       mutt_resize_screen ();
929       menu->redraw = REDRAW_FULL;
930       SigWinch = 0;
931       clearok(stdscr,TRUE);/*force complete redraw*/
932     }
933 #endif
934
935     if (i == -1)
936       continue;
937
938     if (!menu->dialog)
939       mutt_clear_error ();
940
941     /* Convert menubar movement to scrolling */
942     if (menu->dialog) 
943       i = menu_dialog_translate_op (i);
944
945     switch (i)
946     {
947       case OP_NEXT_ENTRY:
948         menu_next_entry (menu);
949         break;
950       case OP_PREV_ENTRY:
951         menu_prev_entry (menu);
952         break;
953       case OP_HALF_DOWN:
954         menu_half_down (menu);
955         break;
956       case OP_HALF_UP:
957         menu_half_up (menu);
958         break;
959       case OP_NEXT_PAGE:
960         menu_next_page (menu);
961         break;
962       case OP_PREV_PAGE:
963         menu_prev_page (menu);
964         break;
965       case OP_NEXT_LINE:
966         menu_next_line (menu);
967         break;
968       case OP_PREV_LINE:
969         menu_prev_line (menu);
970         break;
971       case OP_FIRST_ENTRY:
972         menu_first_entry (menu);
973         break;
974       case OP_LAST_ENTRY:
975         menu_last_entry (menu);
976         break;
977       case OP_TOP_PAGE:
978         menu_top_page (menu);
979         break;
980       case OP_MIDDLE_PAGE:
981         menu_middle_page (menu);
982         break;
983       case OP_BOTTOM_PAGE:
984         menu_bottom_page (menu);
985         break;
986       case OP_CURRENT_TOP:
987         menu_current_top (menu);
988         break;
989       case OP_CURRENT_MIDDLE:
990         menu_current_middle (menu);
991         break;
992       case OP_CURRENT_BOTTOM:
993         menu_current_bottom (menu);
994         break;
995       case OP_SEARCH:
996       case OP_SEARCH_REVERSE:
997       case OP_SEARCH_NEXT:
998       case OP_SEARCH_OPPOSITE:
999         if (menu->search && !menu->dialog) /* Searching dialogs won't work */
1000         {
1001           menu->oldcurrent = menu->current;
1002           if ((menu->current = menu_search (menu, i)) != -1)
1003             menu->redraw = REDRAW_MOTION;
1004           else
1005             menu->current = menu->oldcurrent;
1006         }
1007         else
1008           mutt_error _("Search is not implemented for this menu.");
1009         break;
1010
1011       case OP_JUMP:
1012         if (menu->dialog)
1013           mutt_error _("Jumping is not implemented for dialogs.");
1014         else
1015           menu_jump (menu);
1016         break;
1017
1018       case OP_ENTER_COMMAND:
1019         CurrentMenu = menu->menu;
1020         mutt_enter_command ();
1021         if (option (OPTFORCEREDRAWINDEX))
1022         {
1023           menu->redraw = REDRAW_FULL;
1024           unset_option (OPTFORCEREDRAWINDEX);
1025           unset_option (OPTFORCEREDRAWPAGER);
1026         }
1027         break;
1028
1029       case OP_TAG:
1030         if (menu->tag && !menu->dialog)
1031         {
1032           if (menu->tagprefix && !option (OPTAUTOTAG))
1033           {
1034             for (i = 0; i < menu->max; i++)
1035               menu->tagged += menu->tag (menu, i, 0);
1036             menu->redraw = REDRAW_INDEX;
1037           }
1038           else if (menu->max)
1039           {
1040             int i = menu->tag (menu, menu->current, -1);
1041             menu->tagged += i;
1042             if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1043             {
1044               menu->current++;
1045               menu->redraw = REDRAW_MOTION_RESYNCH;
1046             }
1047             else
1048               menu->redraw = REDRAW_CURRENT;
1049           }
1050           else
1051             mutt_error _("No entries.");
1052         }
1053         else
1054           mutt_error _("Tagging is not supported.");
1055         break;
1056
1057       case OP_SHELL_ESCAPE:
1058         mutt_shell_escape ();
1059         MAYBE_REDRAW (menu->redraw);
1060         break;
1061
1062       case OP_WHAT_KEY:
1063         mutt_what_key ();
1064         break;
1065
1066       case OP_REDRAW:
1067         clearok (stdscr, TRUE);
1068         menu->redraw = REDRAW_FULL;
1069         break;
1070
1071       case OP_HELP:
1072         mutt_help (menu->menu);
1073         menu->redraw = REDRAW_FULL;
1074         break;
1075
1076       case OP_NULL:
1077         km_error_key (menu->menu);
1078         break;
1079
1080       case OP_END_COND:
1081         break;
1082
1083       default:
1084         return (i);
1085     }
1086   }
1087   /* not reached */
1088 }