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