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