Better curses init.
[apps/madmutt.git] / lib-ui / curs_main.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * Parts were written/modified by:
6  * Nico Golde <nico@ngolde.de>
7  *
8  * This file is part of mutt-ng, see http://www.muttng.org/.
9  * It's licensed under the GNU General Public License,
10  * please see the file GPL in the top level source directory.
11  */
12
13 #include <lib-ui/lib-ui.h>
14
15 #include <lib-ui/sidebar.h>
16 #include <lib-mx/mx.h>
17 #include "pop.h"
18
19 #include "menu.h"
20
21 #include "mutt.h"
22 #include "crypt.h"
23 #include "pattern.h"
24 #include "alias.h"
25 #include "sort.h"
26 #include "recvattach.h"
27 #include "buffy.h"
28 #include "thread.h"
29 #include "score.h"
30
31 #include <imap/imap_private.h>
32
33 static const char *No_mailbox_is_open = N_("No mailbox is open.");
34 static const char *There_are_no_messages = N_("There are no messages.");
35 static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
36 static const char *Function_not_permitted_in_attach_message_mode =
37 N_("Function not permitted in attach-message mode.");
38 static const char *No_visible = N_("No visible messages.");
39
40 #define CHECK_IN_MAILBOX if (!Context) \
41         { \
42                 mutt_flushinp (); \
43                 mutt_error (_(No_mailbox_is_open)); \
44                 break; \
45         }
46
47 #define CHECK_MSGCOUNT if (!Context) \
48         { \
49                 mutt_flushinp (); \
50                 mutt_error(_(No_mailbox_is_open)); \
51                 break; \
52         } \
53         else if (!Context->msgcount) \
54         { \
55                   mutt_flushinp (); \
56                 mutt_error(_(There_are_no_messages)); \
57                 break; \
58         }
59
60 #define CHECK_VISIBLE if (Context && menu->current >= Context->vcount) \
61           {\
62                   mutt_flushinp (); \
63                   mutt_error(_(No_visible)); \
64                   break; \
65         }
66
67
68 #define CHECK_READONLY if (Context->readonly) \
69                         { \
70                                   mutt_flushinp (); \
71                                 mutt_error(_(Mailbox_is_read_only)); \
72                                 break; \
73                         }
74
75 #define CHECK_ATTACH if(option(OPTATTACHMSG)) \
76                      {\
77                         mutt_flushinp (); \
78                         mutt_error(_(Function_not_permitted_in_attach_message_mode)); \
79                         break; \
80                      }
81
82 #define CURHDR Context->hdrs[Context->v2r[menu->current]]
83 #define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
84 #define UNREAD(h) mutt_thread_contains_unread (Context, h)
85
86 extern size_t UngetCount;
87
88 void index_make_entry (char *s, ssize_t l, struct menu_t * menu, int num)
89 {
90   format_flag flag = M_FORMAT_MAKEPRINT | M_FORMAT_INDEX;
91   int edgemsgno, reverse = Sort & SORT_REVERSE;
92   HEADER *h = Context->hdrs[Context->v2r[num]];
93   THREAD *tmp;
94
95   if ((Sort & SORT_MASK) == SORT_THREADS && h->tree) {
96     flag |= M_FORMAT_TREE;      /* display the thread tree */
97     if (h->display_subject)
98       flag |= M_FORMAT_FORCESUBJ;
99     else {
100       if (reverse) {
101         if (menu->top + menu->pagelen > menu->max)
102           edgemsgno = Context->v2r[menu->max - 1];
103         else
104           edgemsgno = Context->v2r[menu->top + menu->pagelen - 1];
105       }
106       else
107         edgemsgno = Context->v2r[menu->top];
108
109       for (tmp = h->thread->parent; tmp; tmp = tmp->parent) {
110         if (!tmp->message)
111           continue;
112
113         /* if no ancestor is visible on current screen, provisionally force
114          * subject... */
115         if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno <
116             edgemsgno) {
117           flag |= M_FORMAT_FORCESUBJ;
118           break;
119         }
120         else if (tmp->message->virtual >= 0)
121           break;
122       }
123       if (flag & M_FORMAT_FORCESUBJ) {
124         for (tmp = h->thread->prev; tmp; tmp = tmp->prev) {
125           if (!tmp->message)
126             continue;
127
128           /* ...but if a previous sibling is available, don't force it */
129           if (reverse ? tmp->message->msgno >
130               edgemsgno : tmp->message->msgno < edgemsgno)
131             break;
132           else if (tmp->message->virtual >= 0) {
133             flag &= ~M_FORMAT_FORCESUBJ;
134             break;
135           }
136         }
137       }
138     }
139   }
140
141   _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
142 }
143
144 int index_color (int index_no)
145 {
146   HEADER *h = Context->hdrs[Context->v2r[index_no]];
147
148   if (h && h->pair)
149     return h->pair;
150
151   mutt_set_header_color (Context, h);
152   return h->pair;
153 }
154
155 static int ci_next_undeleted (int msgno)
156 {
157   int i;
158
159   for (i = msgno + 1; i < Context->vcount; i++)
160     if (!Context->hdrs[Context->v2r[i]]->deleted)
161       return (i);
162   return (-1);
163 }
164
165 static int ci_previous_undeleted (int msgno)
166 {
167   int i;
168
169   for (i = msgno - 1; i >= 0; i--)
170     if (!Context->hdrs[Context->v2r[i]]->deleted)
171       return (i);
172   return (-1);
173 }
174
175 /* Return the index of the first new message, or failing that, the first
176  * unread message.
177  */
178 static int ci_first_message (void)
179 {
180   int old = -1, i;
181
182   if (Context && Context->msgcount) {
183     for (i = 0; i < Context->vcount; i++) {
184       if (!Context->hdrs[Context->v2r[i]]->read &&
185           !Context->hdrs[Context->v2r[i]]->deleted) {
186         if (!Context->hdrs[Context->v2r[i]]->old)
187           return (i);
188         else if (old == -1)
189           old = i;
190       }
191     }
192     if (old != -1)
193       return (old);
194
195     /* If Sort is reverse and not threaded, the latest message is first.
196      * If Sort is threaded, the latest message is first iff exactly one
197      * of Sort and SortAux are reverse.
198      */
199     if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) ||
200         ((Sort & SORT_MASK) == SORT_THREADS &&
201          ((Sort ^ SortAux) & SORT_REVERSE)))
202       return 0;
203     else
204       return (Context->vcount ? Context->vcount - 1 : 0);
205   }
206   return 0;
207 }
208
209 /* This should be in mx.c, but it only gets used here. */
210 static int mx_toggle_write (CONTEXT * ctx)
211 {
212   if (!ctx)
213     return -1;
214
215   if (ctx->readonly) {
216     mutt_error (_("Cannot toggle write on a readonly mailbox!"));
217
218     return -1;
219   }
220
221   if (ctx->dontwrite) {
222     ctx->dontwrite = 0;
223     mutt_message (_("Changes to folder will be written on folder exit."));
224   }
225   else {
226     ctx->dontwrite = 1;
227     mutt_message (_("Changes to folder will not be written."));
228   }
229
230   return 0;
231 }
232
233 static void update_index (MUTTMENU * menu, CONTEXT * ctx __attribute__ ((unused)), int check,
234                           int oldcount, int index_hint)
235 {
236   /* store pointers to the newly added messages */
237   HEADER **save_new = NULL;
238   int j;
239
240   /* take note of the current message */
241   if (oldcount) {
242     if (menu->current < Context->vcount)
243       menu->oldcurrent = index_hint;
244     else
245       oldcount = 0;             /* invalid message number! */
246   }
247
248   /* We are in a limited view. Check if the new message(s) satisfy
249    * the limit criteria. If they do, set their virtual msgno so that
250    * they will be visible in the limited view */
251   if (Context->pattern) {
252 #define THIS_BODY Context->hdrs[j]->content
253     for (j = (check == M_REOPENED) ? 0 : oldcount; j < Context->msgcount; j++) {
254       if (mutt_pattern_exec
255           (Context->limit_pattern, M_MATCH_FULL_ADDRESS, Context,
256             Context->hdrs[j])) {
257         Context->hdrs[j]->virtual = Context->vcount;
258         Context->v2r[Context->vcount] = j;
259         Context->hdrs[j]->limited = 1;
260         Context->vcount++;
261         Context->vsize +=
262           THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset;
263       }
264     }
265 #undef THIS_BODY
266   }
267
268   /* save the list of new messages */
269   if (oldcount && check != M_REOPENED && ((Sort & SORT_MASK) == SORT_THREADS)) {
270     save_new = p_new(HEADER*, Context->msgcount - oldcount);
271     for (j = oldcount; j < Context->msgcount; j++)
272       save_new[j - oldcount] = Context->hdrs[j];
273   }
274
275   /* if the mailbox was reopened, need to rethread from scratch */
276   mutt_sort_headers (Context, (check == M_REOPENED));
277
278   /* uncollapse threads with new mail */
279   if ((Sort & SORT_MASK) == SORT_THREADS) {
280     if (check == M_REOPENED) {
281       THREAD *h, *c;
282
283       Context->collapsed = 0;
284
285       for (h = Context->tree; h; h = h->next) {
286         for (c = h; !c->message; c = c->child);
287         mutt_uncollapse_thread (Context, c->message);
288       }
289       mutt_set_virtual (Context);
290     }
291     else if (oldcount) {
292       for (j = 0; j < Context->msgcount - oldcount; j++) {
293         int k;
294
295         for (k = 0; k < Context->msgcount; k++) {
296           HEADER *h = Context->hdrs[k];
297
298           if (h == save_new[j] && (!Context->pattern || h->limited))
299             mutt_uncollapse_thread (Context, h);
300         }
301       }
302       p_delete(&save_new);
303       mutt_set_virtual (Context);
304     }
305   }
306
307   menu->current = -1;
308   if (oldcount) {
309     /* restore the current message to the message it was pointing to */
310     for (j = 0; j < Context->vcount; j++) {
311       if (Context->hdrs[Context->v2r[j]]->index == menu->oldcurrent) {
312         menu->current = j;
313         break;
314       }
315     }
316   }
317
318   if (menu->current < 0)
319     menu->current = ci_first_message ();
320 }
321
322 static void resort_index (MUTTMENU * menu)
323 {
324   int i;
325   HEADER *current = CURHDR;
326
327   menu->current = -1;
328   mutt_sort_headers (Context, 0);
329   /* Restore the current message */
330
331   for (i = 0; i < Context->vcount; i++) {
332     if (Context->hdrs[Context->v2r[i]] == current) {
333       menu->current = i;
334       break;
335     }
336   }
337
338   if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0)
339     menu->current = mutt_parent_message (Context, current);
340
341   if (menu->current < 0)
342     menu->current = ci_first_message ();
343
344   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
345 }
346
347 /* This function handles the message index window as well as commands returned
348  * from the pager (MENU_PAGER).
349  */
350 int mutt_index_menu (void)
351 {
352   char buf[LONG_STRING];
353   int flags;
354   int op = OP_NULL;
355   int done = 0;                 /* controls when to exit the "event" loop */
356   int i = 0, j;
357   int tag = 0;                  /* has the tag-prefix command been pressed? */
358   int newcount = -1;
359   int oldcount = -1;
360   int rc = -1;
361   MUTTMENU *menu;
362   const char *cp;               /* temporary variable. */
363   int index_hint;               /* used to restore cursor position */
364   int do_buffy_notify = 1;
365   int closed = 0;               /* did we OP_QUIT or OP_EXIT out of this menu? */
366   int attach_msg = option (OPTATTACHMSG);
367
368   menu = mutt_new_menu ();
369   menu->menu = MENU_MAIN;
370   menu->offset = 1;
371   menu->pagelen = LINES - 3;
372   menu->make_entry = (void *) index_make_entry;
373   menu->color = index_color;
374   menu->current = ci_first_message ();
375
376   if (!attach_msg) {
377     buffy_check (0);       /* force the buffy check after we enter the folder */
378     /* record folder we open to place sidebar indicator properly */
379     if (Context && Context->path)
380       sidebar_set_current (Context->path);
381   }
382
383   for (;;) {
384     tag = 0;                    /* clear the tag-prefix */
385
386     menu->max = Context ? Context->vcount : 0;
387     oldcount = Context ? Context->msgcount : 0;
388
389     /* check if we need to resort the index because just about
390      * any 'op' below could do mutt_enter_command(), either here or
391      * from any new menu launched, and change $sort/$sort_aux
392      */
393     if (option (OPTNEEDRESORT) && Context && Context->msgcount)
394       resort_index (menu);
395
396     if (option (OPTREDRAWTREE) && Context && Context->msgcount
397         && (Sort & SORT_MASK) == SORT_THREADS) {
398       mutt_draw_tree (Context);
399       menu->redraw |= REDRAW_STATUS;
400       unset_option (OPTREDRAWTREE);
401     }
402
403     if (Context && !attach_msg) {
404       int check;
405
406       /* check for new mail in the mailbox.  If nonzero, then something has
407        * changed about the file (either we got new mail or the file was
408        * modified underneath us.)
409        */
410
411       imap_allow_reopen (Context);
412
413       index_hint = (Context->vcount && menu->current >= 0
414                     && menu->current < Context->vcount) ? CURHDR->index : 0;
415
416       if ((check = mx_check_mailbox (Context, &index_hint, 0)) < 0) {
417         if (!Context->path) {
418           /* fatal error occurred */
419           p_delete(&Context);
420           menu->redraw = REDRAW_FULL;
421         }
422         set_option (OPTSEARCHINVALID);
423       }
424       else if (check == M_NEW_MAIL || check == M_REOPENED || check == M_FLAGS) {
425         update_index (menu, Context, check, oldcount, index_hint);
426
427         /* notify the user of new mail */
428         if (check == M_REOPENED)
429           mutt_error (_
430                       ("Mailbox was externally modified.  Flags may be wrong."));
431         else if (check == M_NEW_MAIL) {
432           /* on new mail: redraw sidebar */
433           sidebar_draw ();
434           mutt_message (_("New mail in this mailbox."));
435
436           if (mod_core.beep_new)
437             beep ();
438         }
439         else if (check == M_FLAGS)
440           mutt_message (_("Mailbox was externally modified."));
441
442         /* avoid the message being overwritten by buffy */
443         do_buffy_notify = 0;
444
445         menu->redraw = REDRAW_FULL;
446         menu->max = Context->vcount;
447
448         set_option (OPTSEARCHINVALID);
449       }
450     }
451
452     imap_keepalive ();
453     imap_disallow_reopen (Context);
454
455     if (!attach_msg) {
456       /* check for new mail in the incoming folders */
457       oldcount = newcount;
458       if ((newcount = buffy_check (0)) != oldcount) {
459         menu->redraw |= REDRAW_STATUS;
460         menu->redraw |= REDRAW_SIDEBAR;
461       }
462       if (do_buffy_notify) {
463         if (buffy_notify () && mod_core.beep_new)
464           beep ();
465       }
466       else
467         do_buffy_notify = 1;
468     }
469
470     if (op != -1)
471       mutt_curs_set (0);
472     if (menu->redraw & REDRAW_SIDEBAR)
473       sidebar_draw ();
474     if (menu->redraw & REDRAW_FULL) {
475       menu_redraw_full (menu);
476       sidebar_draw ();
477       mutt_show_error ();
478     }
479
480     if (menu->menu == MENU_MAIN) {
481       if (Context && Context->hdrs && !(menu->current >= Context->vcount)) {
482         menu_check_recenter (menu);
483
484         if (menu->redraw & REDRAW_INDEX) {
485           menu_redraw_index (menu);
486           menu->redraw |= REDRAW_STATUS;
487         }
488         else if (menu->redraw & (REDRAW_MOTION_RESYNCH | REDRAW_MOTION))
489           menu_redraw_motion (menu);
490         else if (menu->redraw & REDRAW_CURRENT)
491           menu_redraw_current (menu);
492       }
493
494       if (menu->redraw & REDRAW_STATUS) {
495         menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
496         CLEARLINE(main_w, 0);
497         SETCOLOR(main_w, MT_COLOR_STATUS);
498         BKGDSET(main_w, MT_COLOR_STATUS);
499         wmove(main_w, 0, 0);
500         mutt_paddstr (main_w, getmaxx(main_w), buf);
501         SETCOLOR(main_w, MT_COLOR_NORMAL);
502         BKGDSET(main_w, MT_COLOR_NORMAL);
503         sidebar_set_buffystats (Context);
504         menu->redraw &= ~REDRAW_STATUS;
505         if (option (OPTXTERMSETTITLES)) {
506           menu_status_line(buf, sizeof(buf), menu, NONULL(XtermTitle));
507           printf("\033]2;%s\007", buf);
508           menu_status_line(buf, sizeof(buf), menu, NONULL(XtermIcon));
509           printf("\033]1;%s\007", buf);
510           fflush(stdout);
511         }
512       }
513
514       menu->redraw = 0;
515       if (menu->current < menu->max)
516         menu->oldcurrent = menu->current;
517       else
518         menu->oldcurrent = -1;
519       mutt_refresh ();
520
521       if (SigWinch) {
522         mutt_flushinp ();
523         ui_layout_resize();
524         menu->redraw = REDRAW_FULL;
525         menu->menu = MENU_MAIN;
526         menu->top = 0;          /* so we scroll the right amount */
527         continue;
528       }
529
530       op = km_dokey (MENU_MAIN);
531       if (op == -1)
532         continue;               /* either user abort or timeout */
533
534       mutt_curs_set (1);
535
536       /* special handling for the tag-prefix function */
537       if (op == OP_TAG_PREFIX) {
538         if (!Context) {
539           mutt_error (_("No mailbox is open."));
540
541           continue;
542         }
543
544         if (!Context->tagged) {
545           mutt_error (_("No tagged messages."));
546
547           continue;
548         }
549         tag = 1;
550
551         /* give visual indication that the next command is a tag- command */
552         mvwaddstr (stdscr, LINES - 1, 0, "tag-");
553         wclrtoeol (stdscr);
554
555         /* get the real command */
556         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
557           /* abort tag sequence */
558           CLEARLINE(stdscr, LINES - 1);
559           continue;
560         }
561       }
562       else if (option (OPTAUTOTAG) && Context && Context->tagged)
563         tag = 1;
564
565       if (op == OP_TAG_PREFIX_COND) {
566         if (!Context) {
567           mutt_error (_("No mailbox is open."));
568
569           continue;
570         }
571
572         if (!Context->tagged) {
573           event_t tmp;
574
575           while (UngetCount > 0) {
576             tmp = mutt_getch ();
577             if (tmp.op == OP_END_COND)
578               break;
579           }
580           mutt_message (_("Nothing to do."));
581
582           continue;
583         }
584         tag = 1;
585
586         /* give visual indication that the next command is a tag- command */
587         mvwaddstr (stdscr, LINES - 1, 0, "tag-");
588         wclrtoeol (stdscr);
589
590         /* get the real command */
591         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
592           /* abort tag sequence */
593           CLEARLINE(stdscr, LINES - 1);
594           continue;
595         }
596       }
597
598       mutt_clear_error ();
599     } else {
600       if (menu->current < menu->max)
601         menu->oldcurrent = menu->current;
602       else
603         menu->oldcurrent = -1;
604
605       mutt_curs_set (1);        /* fallback from the pager */
606     }
607
608     switch (op) {
609
610       /* ----------------------------------------------------------------------
611        * movement commands
612        */
613
614     case OP_BOTTOM_PAGE:
615       menu_bottom_page (menu);
616       break;
617     case OP_FIRST_ENTRY:
618       menu_first_entry (menu);
619       break;
620     case OP_MIDDLE_PAGE:
621       menu_middle_page (menu);
622       break;
623     case OP_HALF_UP:
624       menu_half_up (menu);
625       break;
626     case OP_HALF_DOWN:
627       menu_half_down (menu);
628       break;
629     case OP_NEXT_LINE:
630       menu_next_line (menu);
631       break;
632     case OP_PREV_LINE:
633       menu_prev_line (menu);
634       break;
635     case OP_NEXT_PAGE:
636       menu_next_page (menu);
637       break;
638     case OP_PREV_PAGE:
639       menu_prev_page (menu);
640       break;
641     case OP_LAST_ENTRY:
642       menu_last_entry (menu);
643       break;
644     case OP_TOP_PAGE:
645       menu_top_page (menu);
646       break;
647     case OP_CURRENT_TOP:
648       menu_current_top (menu);
649       break;
650     case OP_CURRENT_MIDDLE:
651       menu_current_middle (menu);
652       break;
653     case OP_CURRENT_BOTTOM:
654       menu_current_bottom (menu);
655       break;
656
657     case OP_JUMP:
658       CHECK_MSGCOUNT;
659       CHECK_VISIBLE;
660       if (isdigit (LastKey))
661         mutt_ungetch (LastKey, 0);
662       buf[0] = 0;
663       if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0
664           || !buf[0])
665         break;
666
667       if (!isdigit ((unsigned char) buf[0])) {
668         mutt_error (_("Argument must be a message number."));
669
670         break;
671       }
672
673       i = atoi (buf);
674       if (i > 0 && i <= Context->msgcount) {
675         for (j = i - 1; j < Context->msgcount; j++) {
676           if (Context->hdrs[j]->virtual != -1)
677             break;
678         }
679         if (j >= Context->msgcount) {
680           for (j = i - 2; j >= 0; j--) {
681             if (Context->hdrs[j]->virtual != -1)
682               break;
683           }
684         }
685
686         if (j >= 0) {
687           menu->current = Context->hdrs[j]->virtual;
688           if (menu->menu == MENU_PAGER) {
689             op = OP_DISPLAY_MESSAGE;
690             continue;
691           }
692           else
693             menu->redraw = REDRAW_MOTION;
694         }
695         else
696           mutt_error (_("That message is not visible."));
697       }
698       else
699         mutt_error (_("Invalid message number."));
700
701       break;
702
703       /* --------------------------------------------------------------------
704        * `index' specific commands
705        */
706
707     case OP_MAIN_DELETE_PATTERN:
708       CHECK_MSGCOUNT;
709       CHECK_VISIBLE;
710       CHECK_READONLY;
711
712       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
713
714       CHECK_ATTACH;
715       mutt_pattern_func (M_DELETE, _("Delete messages matching: "));
716       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
717       break;
718
719     case OP_MAIN_FETCH_MAIL:
720       CHECK_ATTACH;
721       pop_fetch_mail ();
722       menu->redraw = REDRAW_FULL;
723       break;
724
725     case OP_HELP:
726       mutt_help (MENU_MAIN);
727       menu->redraw = REDRAW_FULL;
728       break;
729
730     case OP_MAIN_SHOW_LIMIT:
731       CHECK_IN_MAILBOX;
732       if (!Context->pattern)
733         mutt_message (_("No limit pattern is in effect."));
734
735       else {
736         char buffer[STRING];
737
738         /* i18n: ask for a limit to apply */
739         snprintf (buffer, sizeof (buffer), _("Limit: %s"), Context->pattern);
740         mutt_message ("%s", buffer);
741       }
742       break;
743
744     case OP_MAIN_LIMIT:
745     case OP_TOGGLE_READ:
746       CHECK_IN_MAILBOX;
747       menu->oldcurrent = (Context->vcount && menu->current >= 0
748                           && menu->current <
749                           Context->vcount) ? CURHDR->index : -1;
750       if (op == OP_TOGGLE_READ) {
751         char buffer[LONG_STRING];
752
753         if (m_strncmp (Context->pattern, "!~R!~D~s", 8) != 0) {
754           snprintf (buffer, sizeof (buffer), "!~R!~D~s%s",
755                     Context->pattern ? Context->pattern : ".*");
756           set_option (OPTHIDEREAD);
757         }
758         else {
759           m_strcpy(buf, sizeof(buf), Context->pattern + 8);
760           if (m_strncmp (buf, ".*", 2) == 0)
761             snprintf (buf, sizeof (buf), "~A");
762           unset_option (OPTHIDEREAD);
763         }
764         p_delete(&Context->pattern);
765         Context->pattern = m_strdup(buf);
766       }
767       if ((op == OP_TOGGLE_READ && mutt_pattern_func (M_LIMIT, NULL) == 0) ||
768           mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0)
769       {
770         if (menu->oldcurrent >= 0) {
771           /* try to find what used to be the current message */
772           menu->current = -1;
773           for (i = 0; i < Context->vcount; i++)
774             if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent) {
775               menu->current = i;
776               break;
777             }
778           if (menu->current < 0)
779             menu->current = 0;
780         }
781         else
782           menu->current = 0;
783         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
784         if (Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS)
785           mutt_draw_tree (Context);
786         menu->redraw = REDRAW_FULL;
787       }
788       if (Context->pattern)
789         mutt_message _("To view all messages, limit to \"all\".");
790       break;
791
792     case OP_QUIT:
793       closed = op;
794       if (attach_msg) {
795         done = 1;
796         break;
797       }
798
799       if (query_quadoption2(mod_core.quit, _("Quit Madmutt?")) == M_YES) {
800         int check;
801
802         oldcount = Context ? Context->msgcount : 0;
803
804         if (!Context
805             || (check = mx_close_mailbox (Context, &index_hint)) == 0)
806           done = 1;
807         else {
808           if (check == M_NEW_MAIL || check == M_REOPENED)
809             update_index (menu, Context, check, oldcount, index_hint);
810
811           menu->redraw = REDRAW_FULL;   /* new mail arrived? */
812           set_option (OPTSEARCHINVALID);
813         }
814       }
815       break;
816
817     case OP_REDRAW:
818       clearok (main_w, TRUE);
819       menu->redraw = REDRAW_FULL;
820       break;
821
822     case OP_SEARCH:
823     case OP_SEARCH_REVERSE:
824     case OP_SEARCH_NEXT:
825     case OP_SEARCH_OPPOSITE:
826       CHECK_MSGCOUNT;
827       CHECK_VISIBLE;
828       if ((menu->current = mutt_search_command (menu->current, op)) == -1)
829         menu->current = menu->oldcurrent;
830       else
831         menu->redraw = REDRAW_MOTION;
832       break;
833
834     case OP_SORT:
835     case OP_SORT_REVERSE:
836       if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0) {
837         if (Context && Context->msgcount) {
838           resort_index (menu);
839           set_option (OPTSEARCHINVALID);
840         }
841       }
842       break;
843
844     case OP_TAG:
845       CHECK_MSGCOUNT;
846       CHECK_VISIBLE;
847       if (tag && !option (OPTAUTOTAG)) {
848         for (j = 0; j < Context->vcount; j++)
849           mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_TAG, 0);
850         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
851       }
852       else {
853         mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged);
854         Context->last_tag = CURHDR->tagged ? CURHDR :
855           ((Context->last_tag == CURHDR && !CURHDR->tagged)
856            ? NULL : Context->last_tag);
857         menu->redraw = REDRAW_STATUS;
858         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
859           menu->current++;
860           menu->redraw |= REDRAW_MOTION_RESYNCH;
861         }
862         else
863           menu->redraw |= REDRAW_CURRENT;
864       }
865       break;
866
867     case OP_MAIN_TAG_PATTERN:
868       CHECK_MSGCOUNT;
869       CHECK_VISIBLE;
870       mutt_pattern_func (M_TAG, _("Tag messages matching: "));
871       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
872       break;
873
874     case OP_MAIN_UNDELETE_PATTERN:
875       CHECK_MSGCOUNT;
876       CHECK_VISIBLE;
877       CHECK_READONLY;
878
879       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
880
881       if (mutt_pattern_func (M_UNDELETE, _("Undelete messages matching: ")) ==
882           0)
883         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
884       break;
885
886     case OP_MAIN_UNTAG_PATTERN:
887       CHECK_MSGCOUNT;
888       CHECK_VISIBLE;
889       if (mutt_pattern_func (M_UNTAG, _("Untag messages matching: ")) == 0)
890         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
891       break;
892
893       /* --------------------------------------------------------------------
894        * The following operations can be performed inside of the pager.
895        */
896
897     case OP_MAIN_IMAP_FETCH:
898       if (Context->magic == M_IMAP)
899         imap_check_mailbox (Context, &index_hint, 1);
900       break;
901
902     case OP_MAIN_SYNC_FOLDER:
903       if (Context && !Context->msgcount)
904         break;
905
906       CHECK_MSGCOUNT;
907       CHECK_VISIBLE;
908       CHECK_READONLY;
909       {
910         int oldvcount = Context->vcount;
911         oldcount = Context->msgcount;
912         int dcount = 0;
913         int check;
914
915         /* calculate the number of messages _above_ the cursor,
916          * so we can keep the cursor on the current message
917          */
918         for (j = 0; j <= menu->current; j++) {
919           if (Context->hdrs[Context->v2r[j]]->deleted)
920             dcount++;
921         }
922
923         if ((check = mx_sync_mailbox (Context, &index_hint)) == 0) {
924           if (Context->vcount != oldvcount)
925             menu->current -= dcount;
926           set_option (OPTSEARCHINVALID);
927         }
928         else if (check == M_NEW_MAIL || check == M_REOPENED)
929           update_index (menu, Context, check, oldcount, index_hint);
930
931         /* 
932          * do a sanity check even if mx_sync_mailbox failed.
933          */
934
935         if (menu->current < 0 || menu->current >= Context->vcount)
936           menu->current = ci_first_message ();
937       }
938
939       /* check for a fatal error, or all messages deleted */
940       if (!Context->path)
941         p_delete(&Context);
942
943       /* if we were in the pager, redisplay the message */
944       if (menu->menu == MENU_PAGER) {
945         op = OP_DISPLAY_MESSAGE;
946         continue;
947       }
948       else
949         menu->redraw = REDRAW_FULL;
950       break;
951
952     case OP_SIDEBAR_OPEN:
953     case OP_MAIN_CHANGE_FOLDER:
954     case OP_MAIN_CHANGE_FOLDER_READONLY:
955       if (attach_msg || op == OP_MAIN_CHANGE_FOLDER_READONLY)
956         flags = M_READONLY;
957       else
958         flags = 0;
959
960       if (flags)
961         cp = _("Open mailbox in read-only mode");
962       else
963         cp = _("Open mailbox");
964
965       buf[0] = '\0';
966       if (Context && Context->path)
967         m_strcpy(buf, sizeof(buf), Context->path);
968       if (op != OP_SIDEBAR_OPEN)
969         buffy_next (buf, sizeof (buf));
970
971       if (op == OP_SIDEBAR_OPEN) {
972         m_strcpy(buf, sizeof(buf), sidebar_get_current());
973       }
974       else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) {
975         if (menu->menu == MENU_PAGER) {
976           op = OP_DISPLAY_MESSAGE;
977           continue;
978         } else
979           break;
980       }
981       if (!buf[0]) {
982         CLEARLINE(main_w, LINES - 1);
983         break;
984       }
985
986       mutt_expand_path (buf, sizeof (buf));
987       if (mx_get_magic (buf) <= 0) {
988         mutt_error (_("%s is not a mailbox."), buf);
989         break;
990       }
991       m_strreplace(&CurrentFolder, buf);
992
993       if (Context) {
994         int check;
995
996         if (Context->cinfo && Context->realpath)
997           m_strreplace(&LastFolder, Context->realpath);
998         else
999           m_strreplace(&LastFolder, Context->path);
1000         oldcount = Context ? Context->msgcount : 0;
1001
1002         if ((check = mx_close_mailbox (Context, &index_hint)) != 0) {
1003           if (check == M_NEW_MAIL || check == M_REOPENED)
1004             update_index (menu, Context, check, oldcount, index_hint);
1005
1006           set_option (OPTSEARCHINVALID);
1007           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1008           break;
1009         }
1010         p_delete(&Context);
1011       }
1012
1013       mutt_sleep (0);
1014
1015       /* Set CurrentMenu to MENU_MAIN before executing any folder
1016        * hooks so that all the index menu functions are available to
1017        * the exec command.
1018        */
1019
1020       CurrentMenu = MENU_MAIN;
1021       mutt_folder_hook (buf);
1022
1023       if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL) {
1024         menu->current = ci_first_message ();
1025       }
1026       else
1027         menu->current = 0;
1028       sidebar_set_current (buf);
1029
1030       mutt_clear_error ();
1031       buffy_check (0);     /* force the buffy check after we have changed
1032                                    the folder */
1033       menu->redraw = REDRAW_FULL;
1034       set_option (OPTSEARCHINVALID);
1035       break;
1036
1037     case OP_DISPLAY_MESSAGE:
1038     case OP_DISPLAY_HEADERS:   /* don't weed the headers */
1039
1040       CHECK_MSGCOUNT;
1041       CHECK_VISIBLE;
1042       /*
1043        * toggle the weeding of headers so that a user can press the key
1044        * again while reading the message.
1045        */
1046       if (op == OP_DISPLAY_HEADERS)
1047         toggle_option (OPTWEED);
1048
1049       unset_option (OPTNEEDRESORT);
1050
1051       if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed) {
1052         mutt_uncollapse_thread (Context, CURHDR);
1053         mutt_set_virtual (Context);
1054         if (option (OPTUNCOLLAPSEJUMP))
1055           menu->current = mutt_thread_next_unread (Context, CURHDR);
1056       }
1057
1058       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1059         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1060
1061       if ((op = mutt_display_message (CURHDR)) == -1) {
1062         unset_option (OPTNEEDRESORT);
1063         break;
1064       }
1065
1066       menu->menu = MENU_PAGER;
1067       menu->oldcurrent = menu->current;
1068       continue;
1069
1070     case OP_EXIT:
1071       closed = op;
1072       if (menu->menu == MENU_MAIN && attach_msg) {
1073         done = 1;
1074         break;
1075       }
1076
1077       if ((menu->menu == MENU_MAIN)
1078           && (query_quadoption2(mod_core.quit,
1079                                 _("Exit Madmutt without saving?")) == M_YES))
1080       {
1081         if (Context) {
1082           mx_fastclose_mailbox (Context);
1083           p_delete(&Context);
1084         }
1085         done = 1;
1086       }
1087       break;
1088
1089     case OP_EDIT_TYPE:
1090       CHECK_MSGCOUNT;
1091       CHECK_VISIBLE;
1092       CHECK_ATTACH;
1093       mutt_edit_content_type (CURHDR, CURHDR->content, NULL);
1094       /* if we were in the pager, redisplay the message */
1095       if (menu->menu == MENU_PAGER) {
1096         op = OP_DISPLAY_MESSAGE;
1097         continue;
1098       }
1099       else
1100         menu->redraw = REDRAW_CURRENT;
1101       break;
1102
1103     case OP_MAIN_BREAK_THREAD:
1104       CHECK_MSGCOUNT;
1105       CHECK_VISIBLE;
1106       CHECK_READONLY;
1107
1108       if ((Sort & SORT_MASK) != SORT_THREADS)
1109         mutt_error (_("Threading is not enabled."));
1110
1111       else {
1112         {
1113           HEADER *oldcur = CURHDR;
1114
1115           mutt_break_thread (CURHDR);
1116           mutt_sort_headers (Context, 1);
1117           menu->current = oldcur->virtual;
1118         }
1119
1120         Context->changed = 1;
1121         mutt_message _("Thread broken");
1122
1123         if (menu->menu == MENU_PAGER) {
1124           op = OP_DISPLAY_MESSAGE;
1125           continue;
1126         }
1127         else
1128           menu->redraw |= REDRAW_INDEX;
1129       }
1130       break;
1131
1132     case OP_MAIN_LINK_THREADS:
1133       CHECK_MSGCOUNT;
1134       CHECK_VISIBLE;
1135       CHECK_READONLY;
1136
1137       if ((Sort & SORT_MASK) != SORT_THREADS)
1138         mutt_error (_("Threading is not enabled."));
1139
1140       else if (!CURHDR->env->message_id)
1141         mutt_error (_("No Message-ID: header available to link thread"));
1142
1143       else if (!tag && (!Context->last_tag || !Context->last_tag->tagged))
1144         mutt_error (_("First, please tag a message to be linked here"));
1145
1146       else {
1147         HEADER *oldcur = CURHDR;
1148
1149         if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag,
1150                                Context)) {
1151           mutt_sort_headers (Context, 1);
1152           menu->current = oldcur->virtual;
1153
1154           Context->changed = 1;
1155           mutt_message _("Threads linked");
1156         }
1157         else
1158           mutt_error (_("No thread linked"));
1159       }
1160
1161       if (menu->menu == MENU_PAGER) {
1162         op = OP_DISPLAY_MESSAGE;
1163         continue;
1164       }
1165       else
1166         menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
1167       break;
1168
1169     case OP_MAIN_NEXT_UNDELETED:
1170       CHECK_MSGCOUNT;
1171       CHECK_VISIBLE;
1172       if (menu->current >= Context->vcount - 1) {
1173         if (menu->menu == MENU_MAIN)
1174           mutt_error (_("You are on the last message."));
1175
1176         break;
1177       }
1178       if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1179         menu->current = menu->oldcurrent;
1180         if (menu->menu == MENU_MAIN)
1181           mutt_error (_("No undeleted messages."));
1182       }
1183       else if (menu->menu == MENU_PAGER) {
1184         op = OP_DISPLAY_MESSAGE;
1185         continue;
1186       }
1187       else
1188         menu->redraw = REDRAW_MOTION;
1189       break;
1190
1191     case OP_NEXT_ENTRY:
1192       CHECK_MSGCOUNT;
1193       CHECK_VISIBLE;
1194       if (menu->current >= Context->vcount - 1) {
1195         if (menu->menu == MENU_MAIN)
1196           mutt_error (_("You are on the last message."));
1197
1198         break;
1199       }
1200       menu->current++;
1201       if (menu->menu == MENU_PAGER) {
1202         op = OP_DISPLAY_MESSAGE;
1203         continue;
1204       }
1205       else
1206         menu->redraw = REDRAW_MOTION;
1207       break;
1208
1209     case OP_MAIN_PREV_UNDELETED:
1210       CHECK_MSGCOUNT;
1211       CHECK_VISIBLE;
1212       if (menu->current < 1) {
1213         mutt_error (_("You are on the first message."));
1214
1215         break;
1216       }
1217       if ((menu->current = ci_previous_undeleted (menu->current)) == -1) {
1218         menu->current = menu->oldcurrent;
1219         if (menu->menu == MENU_MAIN)
1220           mutt_error (_("No undeleted messages."));
1221       }
1222       else if (menu->menu == MENU_PAGER) {
1223         op = OP_DISPLAY_MESSAGE;
1224         continue;
1225       }
1226       else
1227         menu->redraw = REDRAW_MOTION;
1228       break;
1229
1230     case OP_PREV_ENTRY:
1231       CHECK_MSGCOUNT;
1232       CHECK_VISIBLE;
1233       if (menu->current < 1) {
1234         if (menu->menu == MENU_MAIN)
1235           mutt_error (_("You are on the first message."));
1236
1237         break;
1238       }
1239       menu->current--;
1240       if (menu->menu == MENU_PAGER) {
1241         op = OP_DISPLAY_MESSAGE;
1242         continue;
1243       }
1244       else
1245         menu->redraw = REDRAW_MOTION;
1246       break;
1247
1248     case OP_DECRYPT_COPY:
1249     case OP_DECRYPT_SAVE:
1250     case OP_COPY_MESSAGE:
1251     case OP_SAVE:
1252     case OP_DECODE_COPY:
1253     case OP_DECODE_SAVE:
1254       CHECK_MSGCOUNT;
1255       CHECK_VISIBLE;
1256       if (mutt_save_message (tag ? NULL : CURHDR,
1257                              (op == OP_DECRYPT_SAVE) ||
1258                              (op == OP_SAVE) || (op == OP_DECODE_SAVE),
1259                              (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY),
1260                              (op == OP_DECRYPT_SAVE)
1261                              || (op == OP_DECRYPT_COPY)
1262                              || 0, &menu->redraw) == 0 && (op == OP_SAVE
1263                                                            || op ==
1264                                                            OP_DECODE_SAVE
1265                                                            || op ==
1266                                                            OP_DECRYPT_SAVE)
1267         ) {
1268         if (tag)
1269           menu->redraw |= REDRAW_INDEX;
1270         else if (option (OPTRESOLVE)) {
1271           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1272             menu->current = menu->oldcurrent;
1273             menu->redraw |= REDRAW_CURRENT;
1274           }
1275           else
1276             menu->redraw |= REDRAW_MOTION_RESYNCH;
1277         }
1278         else
1279           menu->redraw |= REDRAW_CURRENT;
1280       }
1281       break;
1282
1283     case OP_MAIN_NEXT_NEW:
1284     case OP_MAIN_NEXT_UNREAD:
1285     case OP_MAIN_PREV_NEW:
1286     case OP_MAIN_PREV_UNREAD:
1287     case OP_MAIN_NEXT_NEW_THEN_UNREAD:
1288     case OP_MAIN_PREV_NEW_THEN_UNREAD:
1289       {
1290         int first_unread = -1;
1291         int first_new = -1;
1292
1293         CHECK_MSGCOUNT;
1294         CHECK_VISIBLE;
1295
1296         i = menu->current;
1297         menu->current = -1;
1298         for (j = 0; j != Context->vcount; j++) {
1299 #define CURHDRi Context->hdrs[Context->v2r[i]]
1300           if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD
1301               || op == OP_MAIN_NEXT_NEW_THEN_UNREAD) {
1302             i++;
1303             if (i > Context->vcount - 1) {
1304               mutt_message _("Search wrapped to top.");
1305
1306               i = 0;
1307             }
1308           }
1309           else {
1310             i--;
1311             if (i < 0) {
1312               mutt_message _("Search wrapped to bottom.");
1313
1314               i = Context->vcount - 1;
1315             }
1316           }
1317
1318           if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS) {
1319             if (UNREAD (CURHDRi) && first_unread == -1)
1320               first_unread = i;
1321             if (UNREAD (CURHDRi) == 1 && first_new == -1)
1322               first_new = i;
1323           }
1324           else if ((!CURHDRi->deleted && !CURHDRi->read)) {
1325             if (first_unread == -1)
1326               first_unread = i;
1327             if ((!CURHDRi->old) && first_new == -1)
1328               first_new = i;
1329           }
1330
1331           if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) &&
1332               first_unread != -1)
1333             break;
1334           if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1335                op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1336                || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1337               && first_new != -1)
1338             break;
1339         }
1340 #undef CURHDRi
1341         if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1342              op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1343              || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1344             && first_new != -1)
1345           menu->current = first_new;
1346         else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
1347                   op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1348                   || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1349                  && first_unread != -1)
1350           menu->current = first_unread;
1351
1352         if (menu->current == -1) {
1353           menu->current = menu->oldcurrent;
1354           mutt_error ("%s%s.",
1355                       (op == OP_MAIN_NEXT_NEW
1356                        || op ==
1357                        OP_MAIN_PREV_NEW) ? _("No new messages") :
1358                       _("No unread messages"),
1359                       Context->pattern ? _(" in this limited view") : "");
1360         }
1361         else if (menu->menu == MENU_PAGER) {
1362           op = OP_DISPLAY_MESSAGE;
1363           continue;
1364         }
1365         else
1366           menu->redraw = REDRAW_MOTION;
1367         break;
1368       }
1369     case OP_FLAG_MESSAGE:
1370       CHECK_MSGCOUNT;
1371       CHECK_VISIBLE;
1372       CHECK_READONLY;
1373
1374       CHECK_MX_ACL (Context, ACL_WRITE, _("Flagging"));
1375
1376       if (tag) {
1377         for (j = 0; j < Context->vcount; j++) {
1378           if (Context->hdrs[Context->v2r[j]]->tagged)
1379             mutt_set_flag (Context, Context->hdrs[Context->v2r[j]],
1380                            M_FLAG, !Context->hdrs[Context->v2r[j]]->flagged);
1381         }
1382
1383         menu->redraw |= REDRAW_INDEX;
1384       }
1385       else {
1386         mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
1387         if (option (OPTRESOLVE)) {
1388           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1389             menu->current = menu->oldcurrent;
1390             menu->redraw = REDRAW_CURRENT;
1391           }
1392           else
1393             menu->redraw = REDRAW_MOTION_RESYNCH;
1394         }
1395         else
1396           menu->redraw = REDRAW_CURRENT;
1397       }
1398       menu->redraw |= REDRAW_STATUS;
1399       break;
1400
1401     case OP_TOGGLE_NEW:
1402       CHECK_MSGCOUNT;
1403       CHECK_VISIBLE;
1404       CHECK_READONLY;
1405
1406       CHECK_MX_ACL (Context, ACL_SEEN, _("Toggling"));
1407
1408       if (tag) {
1409         for (j = 0; j < Context->vcount; j++) {
1410           if (Context->hdrs[Context->v2r[j]]->tagged) {
1411             if (Context->hdrs[Context->v2r[j]]->read ||
1412                 Context->hdrs[Context->v2r[j]]->old)
1413               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_NEW,
1414                              1);
1415             else
1416               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ,
1417                              1);
1418           }
1419         }
1420         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1421       }
1422       else {
1423         if (CURHDR->read || CURHDR->old)
1424           mutt_set_flag (Context, CURHDR, M_NEW, 1);
1425         else
1426           mutt_set_flag (Context, CURHDR, M_READ, 1);
1427
1428         if (option (OPTRESOLVE)) {
1429           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1430             menu->current = menu->oldcurrent;
1431             menu->redraw = REDRAW_CURRENT;
1432           }
1433           else
1434             menu->redraw = REDRAW_MOTION_RESYNCH;
1435         }
1436         else
1437           menu->redraw = REDRAW_CURRENT;
1438         menu->redraw |= REDRAW_STATUS;
1439       }
1440       break;
1441
1442     case OP_TOGGLE_WRITE:
1443       CHECK_IN_MAILBOX;
1444       if (mx_toggle_write (Context) == 0)
1445         menu->redraw |= REDRAW_STATUS;
1446       break;
1447
1448     case OP_MAIN_NEXT_THREAD:
1449     case OP_MAIN_NEXT_SUBTHREAD:
1450     case OP_MAIN_PREV_THREAD:
1451     case OP_MAIN_PREV_SUBTHREAD:
1452       CHECK_MSGCOUNT;
1453       CHECK_VISIBLE;
1454       switch (op) {
1455       case OP_MAIN_NEXT_THREAD:
1456         menu->current = mutt_next_thread (CURHDR);
1457         break;
1458
1459       case OP_MAIN_NEXT_SUBTHREAD:
1460         menu->current = mutt_next_subthread (CURHDR);
1461         break;
1462
1463       case OP_MAIN_PREV_THREAD:
1464         menu->current = mutt_previous_thread (CURHDR);
1465         break;
1466
1467       case OP_MAIN_PREV_SUBTHREAD:
1468         menu->current = mutt_previous_subthread (CURHDR);
1469         break;
1470       }
1471
1472       if (menu->current < 0) {
1473         menu->current = menu->oldcurrent;
1474         if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
1475           mutt_error (_("No more threads."));
1476
1477         else
1478           mutt_error (_("You are on the first thread."));
1479       }
1480       else if (menu->menu == MENU_PAGER) {
1481         op = OP_DISPLAY_MESSAGE;
1482         continue;
1483       }
1484       else
1485         menu->redraw = REDRAW_MOTION;
1486       break;
1487
1488     case OP_MAIN_PARENT_MESSAGE:
1489       CHECK_MSGCOUNT;
1490       CHECK_VISIBLE;
1491
1492       if ((menu->current = mutt_parent_message (Context, CURHDR)) < 0) {
1493         menu->current = menu->oldcurrent;
1494       }
1495       else if (menu->menu == MENU_PAGER) {
1496         op = OP_DISPLAY_MESSAGE;
1497         continue;
1498       }
1499       else
1500         menu->redraw = REDRAW_MOTION;
1501       break;
1502
1503     case OP_MAIN_SET_FLAG:
1504     case OP_MAIN_CLEAR_FLAG:
1505       CHECK_MSGCOUNT;
1506       CHECK_VISIBLE;
1507       CHECK_READONLY;
1508
1509       if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) == 0) {
1510         menu->redraw = REDRAW_STATUS;
1511         if (tag)
1512           menu->redraw |= REDRAW_INDEX;
1513         else if (option (OPTRESOLVE)) {
1514           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1515             menu->current = menu->oldcurrent;
1516             menu->redraw |= REDRAW_CURRENT;
1517           }
1518           else
1519             menu->redraw |= REDRAW_MOTION_RESYNCH;
1520         }
1521         else
1522           menu->redraw |= REDRAW_CURRENT;
1523       }
1524       break;
1525
1526     case OP_MAIN_COLLAPSE_THREAD:
1527       CHECK_MSGCOUNT;
1528       CHECK_VISIBLE;
1529
1530       if ((Sort & SORT_MASK) != SORT_THREADS) {
1531         mutt_error (_("Threading is not enabled."));
1532
1533         break;
1534       }
1535
1536       if (CURHDR->collapsed) {
1537         menu->current = mutt_uncollapse_thread (Context, CURHDR);
1538         mutt_set_virtual (Context);
1539         if (option (OPTUNCOLLAPSEJUMP))
1540           menu->current = mutt_thread_next_unread (Context, CURHDR);
1541       }
1542       else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR)) {
1543         menu->current = mutt_collapse_thread (Context, CURHDR);
1544         mutt_set_virtual (Context);
1545       }
1546       else {
1547         mutt_error (_("Thread contains unread messages."));
1548
1549         break;
1550       }
1551
1552       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1553
1554       break;
1555
1556     case OP_MAIN_COLLAPSE_ALL:
1557       CHECK_MSGCOUNT;
1558       CHECK_VISIBLE;
1559
1560       if ((Sort & SORT_MASK) != SORT_THREADS) {
1561         mutt_error (_("Threading is not enabled."));
1562
1563         break;
1564       }
1565
1566       {
1567         HEADER *h, *base;
1568         THREAD *thread, *top;
1569         int final;
1570
1571         if (CURHDR->collapsed)
1572           final = mutt_uncollapse_thread (Context, CURHDR);
1573         else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1574           final = mutt_collapse_thread (Context, CURHDR);
1575         else
1576           final = CURHDR->virtual;
1577
1578         base = Context->hdrs[Context->v2r[final]];
1579
1580         top = Context->tree;
1581         Context->collapsed = !Context->collapsed;
1582         while ((thread = top) != NULL) {
1583           while (!thread->message)
1584             thread = thread->child;
1585           h = thread->message;
1586
1587           if (h->collapsed != Context->collapsed) {
1588             if (h->collapsed)
1589               mutt_uncollapse_thread (Context, h);
1590             else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
1591               mutt_collapse_thread (Context, h);
1592           }
1593           top = top->next;
1594         }
1595
1596         mutt_set_virtual (Context);
1597         for (j = 0; j < Context->vcount; j++) {
1598           if (Context->hdrs[Context->v2r[j]]->index == base->index) {
1599             menu->current = j;
1600             break;
1601           }
1602         }
1603
1604         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1605       }
1606       break;
1607
1608       /* --------------------------------------------------------------------
1609        * These functions are invoked directly from the internal-pager
1610        */
1611
1612     case OP_BOUNCE_MESSAGE:
1613       CHECK_ATTACH;
1614       CHECK_MSGCOUNT;
1615       CHECK_VISIBLE;
1616       ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
1617       break;
1618
1619     case OP_CREATE_ALIAS:
1620       mutt_create_alias (Context
1621                          && Context->vcount ? CURHDR->env : NULL, NULL);
1622       MAYBE_REDRAW (menu->redraw);
1623       menu->redraw |= REDRAW_CURRENT;
1624       break;
1625
1626     case OP_QUERY:
1627       CHECK_ATTACH;
1628       mutt_query_menu (NULL, 0);
1629       MAYBE_REDRAW (menu->redraw);
1630       break;
1631
1632     case OP_PURGE_MESSAGE:
1633     case OP_DELETE:
1634       CHECK_MSGCOUNT;
1635       CHECK_VISIBLE;
1636       CHECK_READONLY;
1637
1638       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1639
1640       if (tag) {
1641         mutt_tag_set_flag (M_DELETE, 1);
1642         mutt_tag_set_flag (M_PURGED, (op != OP_PURGE_MESSAGE) ? 0 : 1);
1643         if (option (OPTDELETEUNTAG))
1644           mutt_tag_set_flag (M_TAG, 0);
1645         menu->redraw = REDRAW_INDEX;
1646       }
1647       else {
1648         mutt_set_flag (Context, CURHDR, M_DELETE, 1);
1649         mutt_set_flag (Context, CURHDR, M_PURGED,
1650                        (op != OP_PURGE_MESSAGE) ? 0 : 1);
1651         if (option (OPTDELETEUNTAG))
1652           mutt_set_flag (Context, CURHDR, M_TAG, 0);
1653         if (option (OPTRESOLVE)) {
1654           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1655             menu->current = menu->oldcurrent;
1656             menu->redraw = REDRAW_CURRENT;
1657           }
1658           else if (menu->menu == MENU_PAGER) {
1659             op = OP_DISPLAY_MESSAGE;
1660             continue;
1661           }
1662           else
1663             menu->redraw |= REDRAW_MOTION_RESYNCH;
1664         }
1665         else
1666           menu->redraw = REDRAW_CURRENT;
1667       }
1668       menu->redraw |= REDRAW_STATUS;
1669       break;
1670
1671     case OP_DELETE_THREAD:
1672     case OP_DELETE_SUBTHREAD:
1673       CHECK_MSGCOUNT;
1674       CHECK_VISIBLE;
1675       CHECK_READONLY;
1676
1677       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1678
1679       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
1680                                  op == OP_DELETE_THREAD ? 0 : 1);
1681
1682       if (rc != -1) {
1683         if (option (OPTDELETEUNTAG))
1684           mutt_thread_set_flag (CURHDR, M_TAG, 0,
1685                                 op == OP_DELETE_THREAD ? 0 : 1);
1686         if (option (OPTRESOLVE))
1687           if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1688             menu->current = menu->oldcurrent;
1689         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1690       }
1691       break;
1692
1693     case OP_DISPLAY_ADDRESS:
1694       CHECK_MSGCOUNT;
1695       CHECK_VISIBLE;
1696       mutt_display_address (CURHDR->env);
1697       break;
1698
1699     case OP_ENTER_COMMAND:
1700       CurrentMenu = MENU_MAIN;
1701       mutt_enter_command ();
1702       mutt_check_rescore (Context);
1703       if (option (OPTFORCEREDRAWINDEX))
1704         menu->redraw = REDRAW_FULL;
1705       unset_option (OPTFORCEREDRAWINDEX);
1706       unset_option (OPTFORCEREDRAWPAGER);
1707       break;
1708
1709     case OP_EDIT_MESSAGE:
1710       CHECK_MSGCOUNT;
1711       CHECK_VISIBLE;
1712       CHECK_READONLY;
1713       CHECK_ATTACH;
1714
1715       CHECK_MX_ACL (Context, ACL_INSERT, _("Editing"));
1716
1717       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1718         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1719       mutt_edit_message (Context, tag ? NULL : CURHDR);
1720       menu->redraw = REDRAW_FULL;
1721
1722       break;
1723
1724     case OP_FORWARD_MESSAGE:
1725       CHECK_MSGCOUNT;
1726       CHECK_VISIBLE;
1727       CHECK_ATTACH;
1728
1729       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1730         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1731       ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
1732       menu->redraw = REDRAW_FULL;
1733       break;
1734
1735     case OP_GROUP_REPLY:
1736       CHECK_MSGCOUNT;
1737       CHECK_VISIBLE;
1738       CHECK_ATTACH;
1739
1740       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1741         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1742
1743       ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, Context,
1744                        tag ? NULL : CURHDR);
1745       menu->redraw = REDRAW_FULL;
1746       break;
1747
1748     case OP_LIST_REPLY:
1749       CHECK_ATTACH;
1750       CHECK_MSGCOUNT;
1751       CHECK_VISIBLE;
1752
1753       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1754         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1755
1756       ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, Context,
1757                        tag ? NULL : CURHDR);
1758       menu->redraw = REDRAW_FULL;
1759       break;
1760
1761     case OP_MAIL:
1762       CHECK_ATTACH;
1763       ci_send_message (0, NULL, NULL, Context, NULL);
1764       menu->redraw = REDRAW_FULL;
1765       break;
1766
1767     case OP_EXTRACT_KEYS:
1768       CHECK_MSGCOUNT;
1769       CHECK_VISIBLE;
1770       crypt_extract_keys_from_messages (tag ? NULL : CURHDR);
1771       menu->redraw = REDRAW_FULL;
1772       break;
1773
1774
1775     case OP_CHECK_TRADITIONAL:
1776       CHECK_MSGCOUNT;
1777       CHECK_VISIBLE;
1778       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1779         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1780
1781       if (menu->menu == MENU_PAGER) {
1782         op = OP_DISPLAY_MESSAGE;
1783         continue;
1784       }
1785       break;
1786
1787     case OP_PIPE:
1788       CHECK_MSGCOUNT;
1789       CHECK_VISIBLE;
1790       mutt_pipe_message (tag ? NULL : CURHDR);
1791       MAYBE_REDRAW (menu->redraw);
1792       break;
1793
1794     case OP_PRINT:
1795       CHECK_MSGCOUNT;
1796       CHECK_VISIBLE;
1797       mutt_print_message (tag ? NULL : CURHDR);
1798       break;
1799
1800     case OP_MAIN_READ_THREAD:
1801     case OP_MAIN_READ_SUBTHREAD:
1802       CHECK_MSGCOUNT;
1803       CHECK_VISIBLE;
1804       CHECK_READONLY;
1805
1806       CHECK_MX_ACL (Context, ACL_SEEN, _("Marking as read"));
1807
1808       rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
1809                                  op == OP_MAIN_READ_THREAD ? 0 : 1);
1810
1811       if (rc != -1) {
1812         if (option (OPTRESOLVE)) {
1813           if ((menu->current = (op == OP_MAIN_READ_THREAD ?
1814                                 mutt_next_thread (CURHDR) :
1815                                 mutt_next_subthread (CURHDR))) == -1)
1816             menu->current = menu->oldcurrent;
1817         }
1818         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1819       }
1820       break;
1821
1822     case OP_RECALL_MESSAGE:
1823       CHECK_ATTACH;
1824       ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
1825       menu->redraw = REDRAW_FULL;
1826       break;
1827
1828     case OP_RESEND:
1829       CHECK_ATTACH;
1830       CHECK_MSGCOUNT;
1831       CHECK_VISIBLE;
1832
1833       if (tag) {
1834         for (j = 0; j < Context->vcount; j++) {
1835           if (Context->hdrs[Context->v2r[j]]->tagged)
1836             mutt_resend_message (NULL, Context,
1837                                  Context->hdrs[Context->v2r[j]]);
1838         }
1839       }
1840       else
1841         mutt_resend_message (NULL, Context, CURHDR);
1842
1843       menu->redraw = REDRAW_FULL;
1844       break;
1845
1846     case OP_REPLY:
1847       CHECK_ATTACH;
1848       CHECK_MSGCOUNT;
1849       CHECK_VISIBLE;
1850
1851       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1852         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1853
1854       ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
1855       menu->redraw = REDRAW_FULL;
1856       break;
1857
1858     case OP_SHELL_ESCAPE:
1859       mutt_shell_escape ();
1860       MAYBE_REDRAW (menu->redraw);
1861       break;
1862
1863     case OP_TAG_THREAD:
1864     case OP_TAG_SUBTHREAD:
1865       CHECK_MSGCOUNT;
1866       CHECK_VISIBLE;
1867       rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
1868                                  op == OP_TAG_THREAD ? 0 : 1);
1869
1870       if (rc != -1) {
1871         if (option (OPTRESOLVE)) {
1872           menu->current = mutt_next_thread (CURHDR);
1873
1874           if (menu->current == -1)
1875             menu->current = menu->oldcurrent;
1876         }
1877         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1878       }
1879       break;
1880
1881     case OP_UNDELETE:
1882       CHECK_MSGCOUNT;
1883       CHECK_VISIBLE;
1884       CHECK_READONLY;
1885
1886       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
1887
1888       if (tag) {
1889         mutt_tag_set_flag (M_DELETE, 0);
1890         mutt_tag_set_flag (M_PURGED, 0);
1891         menu->redraw = REDRAW_INDEX;
1892       }
1893       else {
1894         mutt_set_flag (Context, CURHDR, M_DELETE, 0);
1895         mutt_set_flag (Context, CURHDR, M_PURGED, 0);
1896         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
1897           menu->current++;
1898           menu->redraw = REDRAW_MOTION_RESYNCH;
1899         }
1900         else
1901           menu->redraw = REDRAW_CURRENT;
1902       }
1903       menu->redraw |= REDRAW_STATUS;
1904       break;
1905
1906     case OP_UNDELETE_THREAD:
1907     case OP_UNDELETE_SUBTHREAD:
1908       CHECK_MSGCOUNT;
1909       CHECK_VISIBLE;
1910       CHECK_READONLY;
1911
1912       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
1913
1914       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
1915                                  op == OP_UNDELETE_THREAD ? 0 : 1)
1916         + mutt_thread_set_flag (CURHDR, M_PURGED, 0,
1917                                 op == OP_UNDELETE_THREAD ? 0 : 1);
1918
1919       if (rc > -1) {
1920         if (option (OPTRESOLVE)) {
1921           if (op == OP_UNDELETE_THREAD)
1922             menu->current = mutt_next_thread (CURHDR);
1923           else
1924             menu->current = mutt_next_subthread (CURHDR);
1925
1926           if (menu->current == -1)
1927             menu->current = menu->oldcurrent;
1928         }
1929         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1930       }
1931       break;
1932
1933     case OP_VERSION:
1934       mutt_version ();
1935       break;
1936
1937     case OP_BUFFY_LIST:
1938       if (option (OPTFORCEBUFFYCHECK))
1939         buffy_check (1);
1940       buffy_list ();
1941       menu->redraw = REDRAW_FULL;
1942       break;
1943
1944     case OP_VIEW_ATTACHMENTS:
1945       CHECK_MSGCOUNT;
1946       CHECK_VISIBLE;
1947       mutt_view_attachments (CURHDR);
1948       if (CURHDR->attach_del)
1949         Context->changed = 1;
1950       menu->redraw = REDRAW_FULL;
1951       break;
1952
1953     case OP_END_COND:
1954       break;
1955
1956     case OP_WHAT_KEY:
1957       mutt_what_key ();
1958       break;
1959
1960     case OP_SIDEBAR_SCROLL_UP:
1961     case OP_SIDEBAR_SCROLL_DOWN:
1962     case OP_SIDEBAR_NEXT:
1963     case OP_SIDEBAR_PREV:
1964     case OP_SIDEBAR_NEXT_NEW:
1965     case OP_SIDEBAR_PREV_NEW:
1966       sidebar_scroll (op);
1967       break;
1968
1969     default:
1970       if (menu->menu == MENU_MAIN)
1971         km_error_key (MENU_MAIN);
1972     }
1973
1974     if (menu->menu == MENU_PAGER) {
1975       menu->menu = MENU_MAIN;
1976       menu->redraw = REDRAW_FULL;
1977     }
1978
1979     if (done)
1980       break;
1981   }
1982
1983   if (!attach_msg) {
1984   /* Close all open IMAP connections */
1985     imap_logout_all ();
1986   }
1987
1988   mutt_menuDestroy (&menu);
1989   return (closed);
1990 }
1991
1992 void mutt_set_header_color (CONTEXT * ctx, HEADER * curhdr)
1993 {
1994   COLOR_LINE *color;
1995
1996   if (!curhdr)
1997     return;
1998
1999   for (color = ColorIndexList; color; color = color->next)
2000     if (mutt_pattern_exec
2001         (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr)) {
2002       curhdr->pair = color->pair;
2003       return;
2004     }
2005   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2006 }