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