Make the sidebar live in a proper independent window.
[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         SETCOLOR (MT_COLOR_STATUS);
503         BKGDSET (MT_COLOR_STATUS);
504         wmove(stdscr, option (OPTSTATUSONTOP) ? 0 : LINES - 2,SW);
505         mutt_paddstr (COLS-SW, buf);
506         SETCOLOR (MT_COLOR_NORMAL);
507         BKGDSET (MT_COLOR_NORMAL);
508         sidebar_set_buffystats (Context);
509         menu->redraw &= ~REDRAW_STATUS;
510         if (option (OPTXTERMSETTITLES)) {
511           menu_status_line(buf, sizeof(buf), menu, NONULL(XtermTitle));
512           printf("\033]2;%s\007", buf);
513           menu_status_line(buf, sizeof(buf), menu, NONULL(XtermIcon));
514           printf("\033]1;%s\007", buf);
515           fflush(stdout);
516         }
517       }
518
519       menu->redraw = 0;
520       if (menu->current < menu->max)
521         menu->oldcurrent = menu->current;
522       else
523         menu->oldcurrent = -1;
524
525       if (option (OPTBRAILLEFRIENDLY))
526          wmove (stdscr, menu->current - menu->top + menu->offset, 0);
527       else
528         wmove (stdscr, menu->current - menu->top + menu->offset, COLS - 1);
529       mutt_refresh ();
530
531       if (SigWinch) {
532         mutt_flushinp ();
533         ui_layout_resize();
534         menu->redraw = REDRAW_FULL;
535         menu->menu = MENU_MAIN;
536         menu->top = 0;          /* so we scroll the right amount */
537         continue;
538       }
539
540       op = km_dokey (MENU_MAIN);
541       if (op == -1)
542         continue;               /* either user abort or timeout */
543
544       mutt_curs_set (1);
545
546       /* special handling for the tag-prefix function */
547       if (op == OP_TAG_PREFIX) {
548         if (!Context) {
549           mutt_error (_("No mailbox is open."));
550
551           continue;
552         }
553
554         if (!Context->tagged) {
555           mutt_error (_("No tagged messages."));
556
557           continue;
558         }
559         tag = 1;
560
561         /* give visual indication that the next command is a tag- command */
562         mvwaddstr (stdscr, LINES - 1, 0, "tag-");
563         wclrtoeol (stdscr);
564
565         /* get the real command */
566         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
567           /* abort tag sequence */
568           CLEARLINE (LINES - 1);
569           continue;
570         }
571       }
572       else if (option (OPTAUTOTAG) && Context && Context->tagged)
573         tag = 1;
574
575       if (op == OP_TAG_PREFIX_COND) {
576         if (!Context) {
577           mutt_error (_("No mailbox is open."));
578
579           continue;
580         }
581
582         if (!Context->tagged) {
583           event_t tmp;
584
585           while (UngetCount > 0) {
586             tmp = mutt_getch ();
587             if (tmp.op == OP_END_COND)
588               break;
589           }
590           mutt_message (_("Nothing to do."));
591
592           continue;
593         }
594         tag = 1;
595
596         /* give visual indication that the next command is a tag- command */
597         mvwaddstr (stdscr, LINES - 1, 0, "tag-");
598         wclrtoeol (stdscr);
599
600         /* get the real command */
601         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
602           /* abort tag sequence */
603           CLEARLINE (LINES - 1);
604           continue;
605         }
606       }
607
608       mutt_clear_error ();
609     }
610     else {
611       if (menu->current < menu->max)
612         menu->oldcurrent = menu->current;
613       else
614         menu->oldcurrent = -1;
615
616       mutt_curs_set (1);        /* fallback from the pager */
617     }
618
619 #ifdef USE_NNTP
620     unset_option (OPTNEWS);     /* for any case */
621 #endif
622
623     switch (op) {
624
625       /* ----------------------------------------------------------------------
626        * movement commands
627        */
628
629     case OP_BOTTOM_PAGE:
630       menu_bottom_page (menu);
631       break;
632     case OP_FIRST_ENTRY:
633       menu_first_entry (menu);
634       break;
635     case OP_MIDDLE_PAGE:
636       menu_middle_page (menu);
637       break;
638     case OP_HALF_UP:
639       menu_half_up (menu);
640       break;
641     case OP_HALF_DOWN:
642       menu_half_down (menu);
643       break;
644     case OP_NEXT_LINE:
645       menu_next_line (menu);
646       break;
647     case OP_PREV_LINE:
648       menu_prev_line (menu);
649       break;
650     case OP_NEXT_PAGE:
651       menu_next_page (menu);
652       break;
653     case OP_PREV_PAGE:
654       menu_prev_page (menu);
655       break;
656     case OP_LAST_ENTRY:
657       menu_last_entry (menu);
658       break;
659     case OP_TOP_PAGE:
660       menu_top_page (menu);
661       break;
662     case OP_CURRENT_TOP:
663       menu_current_top (menu);
664       break;
665     case OP_CURRENT_MIDDLE:
666       menu_current_middle (menu);
667       break;
668     case OP_CURRENT_BOTTOM:
669       menu_current_bottom (menu);
670       break;
671
672 #ifdef USE_NNTP
673     case OP_GET_MESSAGE:
674     case OP_GET_PARENT:
675       CHECK_MSGCOUNT;
676       if (Context->magic == M_NNTP) {
677         HEADER *h;
678
679         if (op == OP_GET_MESSAGE) {
680           buf[0] = 0;
681           if (mutt_get_field (_("Enter Message-ID: "), buf, sizeof (buf), 0)
682               != 0 || !buf[0])
683             break;
684         }
685         else {
686           string_list_t *ref = CURHDR->env->references;
687
688           if (!ref) {
689             mutt_error (_("Article has no parent reference!"));
690
691             break;
692           }
693           m_strcpy(buf, sizeof(buf), ref->data);
694         }
695         if (!Context->id_hash)
696           Context->id_hash = mutt_make_id_hash (Context);
697         if ((h = hash_find (Context->id_hash, buf))) {
698           if (h->virtual != -1) {
699             menu->current = h->virtual;
700             menu->redraw = REDRAW_MOTION_RESYNCH;
701           }
702           else if (h->collapsed) {
703             mutt_uncollapse_thread (Context, h);
704             mutt_set_virtual (Context);
705             menu->current = h->virtual;
706             menu->redraw = REDRAW_MOTION_RESYNCH;
707           }
708           else
709             mutt_error (_("Message not visible in limited view."));
710         }
711         else {
712           if (nntp_check_msgid (Context, buf) == 0) {
713             h = Context->hdrs[Context->msgcount - 1];
714             mutt_sort_headers (Context, 0);
715             menu->current = h->virtual;
716             menu->redraw = REDRAW_FULL;
717           }
718           else
719             mutt_error (_("Article %s not found on server"), buf);
720         }
721       }
722       break;
723
724     case OP_GET_CHILDREN:
725     case OP_RECONSTRUCT_THREAD:
726       CHECK_MSGCOUNT;
727       if (Context->magic == M_NNTP) {
728         HEADER *h;
729         int old = CURHDR->index;
730
731         if (!CURHDR->env->message_id) {
732           mutt_error (_("No Message-ID. Unable to perform operation"));
733
734           break;
735         }
736
737         if (!Context->id_hash)
738           Context->id_hash = mutt_make_id_hash (Context);
739         m_strcpy(buf, sizeof(buf), CURHDR->env->message_id);
740
741         if (op == OP_RECONSTRUCT_THREAD) {
742           string_list_t *ref = CURHDR->env->references;
743
744           while (ref) {
745             nntp_check_msgid (Context, ref->data);
746             /* the last msgid in References is the root message */
747             if (!ref->next)
748               m_strcpy(buf, sizeof(buf), ref->data);
749             ref = ref->next;
750           }
751         }
752         mutt_message (_("Check for children of message..."));
753
754         if (nntp_check_children (Context, buf) == 0) {
755           mutt_sort_headers (Context, (op == OP_RECONSTRUCT_THREAD));
756           h = hash_find (Context->id_hash, buf);
757           /* if the root message was retrieved, move to it */
758           if (h)
759             menu->current = h->virtual;
760           else                  /* try to restore old position */
761             for (i = 0; i < Context->msgcount; i++)
762               if (Context->hdrs[i]->index == old) {
763                 menu->current = Context->hdrs[i]->virtual;
764                 /* As an added courtesy, recenter the menu
765                  * with the current entry at the middle of the screen */
766                 menu_check_recenter (menu);
767                 menu_current_middle (menu);
768               }
769         }
770         menu->redraw = REDRAW_FULL;
771         mutt_clear_error ();
772       }
773       break;
774 #endif
775
776     case OP_JUMP:
777
778       CHECK_MSGCOUNT;
779       CHECK_VISIBLE;
780       if (isdigit (LastKey))
781         mutt_ungetch (LastKey, 0);
782       buf[0] = 0;
783       if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0
784           || !buf[0])
785         break;
786
787       if (!isdigit ((unsigned char) buf[0])) {
788         mutt_error (_("Argument must be a message number."));
789
790         break;
791       }
792
793       i = atoi (buf);
794       if (i > 0 && i <= Context->msgcount) {
795         for (j = i - 1; j < Context->msgcount; j++) {
796           if (Context->hdrs[j]->virtual != -1)
797             break;
798         }
799         if (j >= Context->msgcount) {
800           for (j = i - 2; j >= 0; j--) {
801             if (Context->hdrs[j]->virtual != -1)
802               break;
803           }
804         }
805
806         if (j >= 0) {
807           menu->current = Context->hdrs[j]->virtual;
808           if (menu->menu == MENU_PAGER) {
809             op = OP_DISPLAY_MESSAGE;
810             continue;
811           }
812           else
813             menu->redraw = REDRAW_MOTION;
814         }
815         else
816           mutt_error (_("That message is not visible."));
817       }
818       else
819         mutt_error (_("Invalid message number."));
820
821       break;
822
823       /* --------------------------------------------------------------------
824        * `index' specific commands
825        */
826
827     case OP_MAIN_DELETE_PATTERN:
828
829       CHECK_MSGCOUNT;
830       CHECK_VISIBLE;
831       CHECK_READONLY;
832
833       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
834
835       CHECK_ATTACH;
836       mutt_pattern_func (M_DELETE, _("Delete messages matching: "));
837       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
838       break;
839
840     case OP_MAIN_FETCH_MAIL:
841
842       CHECK_ATTACH;
843       pop_fetch_mail ();
844       menu->redraw = REDRAW_FULL;
845       break;
846
847     case OP_HELP:
848
849       mutt_help (MENU_MAIN);
850       menu->redraw = REDRAW_FULL;
851       break;
852
853     case OP_MAIN_SHOW_LIMIT:
854       CHECK_IN_MAILBOX;
855       if (!Context->pattern)
856         mutt_message (_("No limit pattern is in effect."));
857
858       else {
859         char buffer[STRING];
860
861         /* i18n: ask for a limit to apply */
862         snprintf (buffer, sizeof (buffer), _("Limit: %s"), Context->pattern);
863         mutt_message ("%s", buffer);
864       }
865       break;
866
867     case OP_MAIN_LIMIT:
868     case OP_TOGGLE_READ:
869
870       CHECK_IN_MAILBOX;
871       menu->oldcurrent = (Context->vcount && menu->current >= 0
872                           && menu->current <
873                           Context->vcount) ? CURHDR->index : -1;
874       if (op == OP_TOGGLE_READ) {
875         char buffer[LONG_STRING];
876
877         if (m_strncmp (Context->pattern, "!~R!~D~s", 8) != 0) {
878           snprintf (buffer, sizeof (buffer), "!~R!~D~s%s",
879                     Context->pattern ? Context->pattern : ".*");
880           set_option (OPTHIDEREAD);
881         }
882         else {
883           m_strcpy(buf, sizeof(buf), Context->pattern + 8);
884           if (m_strncmp (buf, ".*", 2) == 0)
885             snprintf (buf, sizeof (buf), "~A");
886           unset_option (OPTHIDEREAD);
887         }
888         p_delete(&Context->pattern);
889         Context->pattern = m_strdup(buf);
890       }
891       if ((op == OP_TOGGLE_READ && mutt_pattern_func (M_LIMIT, NULL) == 0) ||
892           mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0)
893       {
894         if (menu->oldcurrent >= 0) {
895           /* try to find what used to be the current message */
896           menu->current = -1;
897           for (i = 0; i < Context->vcount; i++)
898             if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent) {
899               menu->current = i;
900               break;
901             }
902           if (menu->current < 0)
903             menu->current = 0;
904         }
905         else
906           menu->current = 0;
907         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
908         if (Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS)
909           mutt_draw_tree (Context);
910         menu->redraw = REDRAW_FULL;
911       }
912       if (Context->pattern)
913         mutt_message _("To view all messages, limit to \"all\".");
914       break;
915
916     case OP_QUIT:
917
918       closed = op;
919       if (attach_msg) {
920         done = 1;
921         break;
922       }
923
924       if (query_quadoption2(mod_core.quit, _("Quit Madmutt?")) == M_YES) {
925         int check;
926
927         oldcount = Context ? Context->msgcount : 0;
928
929         if (!Context
930             || (check = mx_close_mailbox (Context, &index_hint)) == 0)
931           done = 1;
932         else {
933           if (check == M_NEW_MAIL || check == M_REOPENED)
934             update_index (menu, Context, check, oldcount, index_hint);
935
936           menu->redraw = REDRAW_FULL;   /* new mail arrived? */
937           set_option (OPTSEARCHINVALID);
938         }
939       }
940       break;
941
942     case OP_REDRAW:
943
944       clearok (stdscr, TRUE);
945       menu->redraw = REDRAW_FULL;
946       break;
947
948     case OP_SEARCH:
949     case OP_SEARCH_REVERSE:
950     case OP_SEARCH_NEXT:
951     case OP_SEARCH_OPPOSITE:
952
953       CHECK_MSGCOUNT;
954       CHECK_VISIBLE;
955       if ((menu->current = mutt_search_command (menu->current, op)) == -1)
956         menu->current = menu->oldcurrent;
957       else
958         menu->redraw = REDRAW_MOTION;
959       break;
960
961     case OP_SORT:
962     case OP_SORT_REVERSE:
963
964       if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0) {
965         if (Context && Context->msgcount) {
966           resort_index (menu);
967           set_option (OPTSEARCHINVALID);
968         }
969       }
970       break;
971
972     case OP_TAG:
973
974       CHECK_MSGCOUNT;
975       CHECK_VISIBLE;
976       if (tag && !option (OPTAUTOTAG)) {
977         for (j = 0; j < Context->vcount; j++)
978           mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_TAG, 0);
979         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
980       }
981       else {
982         mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged);
983         Context->last_tag = CURHDR->tagged ? CURHDR :
984           ((Context->last_tag == CURHDR && !CURHDR->tagged)
985            ? NULL : Context->last_tag);
986         menu->redraw = REDRAW_STATUS;
987         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
988           menu->current++;
989           menu->redraw |= REDRAW_MOTION_RESYNCH;
990         }
991         else
992           menu->redraw |= REDRAW_CURRENT;
993       }
994       break;
995
996     case OP_MAIN_TAG_PATTERN:
997
998       CHECK_MSGCOUNT;
999       CHECK_VISIBLE;
1000       mutt_pattern_func (M_TAG, _("Tag messages matching: "));
1001       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1002       break;
1003
1004     case OP_MAIN_UNDELETE_PATTERN:
1005
1006       CHECK_MSGCOUNT;
1007       CHECK_VISIBLE;
1008       CHECK_READONLY;
1009
1010       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
1011
1012       if (mutt_pattern_func (M_UNDELETE, _("Undelete messages matching: ")) ==
1013           0)
1014         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1015       break;
1016
1017     case OP_MAIN_UNTAG_PATTERN:
1018
1019       CHECK_MSGCOUNT;
1020       CHECK_VISIBLE;
1021       if (mutt_pattern_func (M_UNTAG, _("Untag messages matching: ")) == 0)
1022         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1023       break;
1024
1025       /* --------------------------------------------------------------------
1026        * The following operations can be performed inside of the pager.
1027        */
1028
1029     case OP_MAIN_IMAP_FETCH:
1030       if (Context->magic == M_IMAP)
1031         imap_check_mailbox (Context, &index_hint, 1);
1032       break;
1033
1034     case OP_MAIN_SYNC_FOLDER:
1035
1036       if (Context && !Context->msgcount)
1037         break;
1038
1039       CHECK_MSGCOUNT;
1040       CHECK_VISIBLE;
1041       CHECK_READONLY;
1042       {
1043         int oldvcount = Context->vcount;
1044         oldcount = Context->msgcount;
1045         int dcount = 0;
1046         int check;
1047
1048         /* calculate the number of messages _above_ the cursor,
1049          * so we can keep the cursor on the current message
1050          */
1051         for (j = 0; j <= menu->current; j++) {
1052           if (Context->hdrs[Context->v2r[j]]->deleted)
1053             dcount++;
1054         }
1055
1056         if ((check = mx_sync_mailbox (Context, &index_hint)) == 0) {
1057           if (Context->vcount != oldvcount)
1058             menu->current -= dcount;
1059           set_option (OPTSEARCHINVALID);
1060         }
1061         else if (check == M_NEW_MAIL || check == M_REOPENED)
1062           update_index (menu, Context, check, oldcount, index_hint);
1063
1064         /* 
1065          * do a sanity check even if mx_sync_mailbox failed.
1066          */
1067
1068         if (menu->current < 0 || menu->current >= Context->vcount)
1069           menu->current = ci_first_message ();
1070       }
1071
1072       /* check for a fatal error, or all messages deleted */
1073       if (!Context->path)
1074         p_delete(&Context);
1075
1076       /* if we were in the pager, redisplay the message */
1077       if (menu->menu == MENU_PAGER) {
1078         op = OP_DISPLAY_MESSAGE;
1079         continue;
1080       }
1081       else
1082         menu->redraw = REDRAW_FULL;
1083       break;
1084
1085     case OP_SIDEBAR_OPEN:
1086     case OP_MAIN_CHANGE_FOLDER:
1087     case OP_MAIN_CHANGE_FOLDER_READONLY:
1088 #ifdef USE_NNTP
1089     case OP_MAIN_CHANGE_GROUP:
1090     case OP_MAIN_CHANGE_GROUP_READONLY:
1091 #endif
1092       if (attach_msg || option (OPTREADONLY) ||
1093 #ifdef USE_NNTP
1094           op == OP_MAIN_CHANGE_GROUP_READONLY ||
1095 #endif
1096           op == OP_MAIN_CHANGE_FOLDER_READONLY)
1097         flags = M_READONLY;
1098       else
1099         flags = 0;
1100
1101       if (flags)
1102         cp = _("Open mailbox in read-only mode");
1103       else
1104         cp = _("Open mailbox");
1105
1106       buf[0] = '\0';
1107 #ifdef USE_NNTP
1108       unset_option (OPTNEWS);
1109       if (op == OP_MAIN_CHANGE_GROUP || op == OP_MAIN_CHANGE_GROUP_READONLY) {
1110         set_option (OPTNEWS);
1111         if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)))
1112           break;
1113         if (flags)
1114           cp = _("Open newsgroup in read-only mode");
1115         else
1116           cp = _("Open newsgroup");
1117         nntp_buffy (buf, sizeof (buf));
1118       }
1119       else
1120 #endif
1121       {
1122         if (Context && Context->path)
1123           m_strcpy(buf, sizeof(buf), Context->path);
1124         if (op != OP_SIDEBAR_OPEN)
1125           buffy_next (buf, sizeof (buf));
1126       }
1127
1128       if (op == OP_SIDEBAR_OPEN) {
1129         m_strcpy(buf, sizeof(buf), sidebar_get_current());
1130       }
1131       else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) {
1132         if (menu->menu == MENU_PAGER) {
1133           op = OP_DISPLAY_MESSAGE;
1134           continue;
1135         } else
1136           break;
1137       }
1138       if (!buf[0]) {
1139         CLEARLINE (LINES - 1);
1140         break;
1141       }
1142
1143 #ifdef USE_NNTP
1144       if (option (OPTNEWS)) {
1145         unset_option (OPTNEWS);
1146         nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account);
1147       }
1148       else
1149 #endif
1150         mutt_expand_path (buf, sizeof (buf));
1151       if (mx_get_magic (buf) <= 0) {
1152         mutt_error (_("%s is not a mailbox."), buf);
1153         break;
1154       }
1155       m_strreplace(&CurrentFolder, buf);
1156
1157       if (Context) {
1158         int check;
1159
1160         if (Context->cinfo && Context->realpath)
1161           m_strreplace(&LastFolder, Context->realpath);
1162         else
1163           m_strreplace(&LastFolder, Context->path);
1164         oldcount = Context ? Context->msgcount : 0;
1165
1166         if ((check = mx_close_mailbox (Context, &index_hint)) != 0) {
1167           if (check == M_NEW_MAIL || check == M_REOPENED)
1168             update_index (menu, Context, check, oldcount, index_hint);
1169
1170           set_option (OPTSEARCHINVALID);
1171           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1172           break;
1173         }
1174         p_delete(&Context);
1175       }
1176
1177       mutt_sleep (0);
1178
1179       /* Set CurrentMenu to MENU_MAIN before executing any folder
1180        * hooks so that all the index menu functions are available to
1181        * the exec command.
1182        */
1183
1184       CurrentMenu = MENU_MAIN;
1185       mutt_folder_hook (buf);
1186
1187       if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL) {
1188         menu->current = ci_first_message ();
1189       }
1190       else
1191         menu->current = 0;
1192       sidebar_set_current (buf);
1193
1194       mutt_clear_error ();
1195       buffy_check (0);     /* force the buffy check after we have changed
1196                                    the folder */
1197       menu->redraw = REDRAW_FULL;
1198       set_option (OPTSEARCHINVALID);
1199       break;
1200
1201     case OP_DISPLAY_MESSAGE:
1202     case OP_DISPLAY_HEADERS:   /* don't weed the headers */
1203
1204       CHECK_MSGCOUNT;
1205       CHECK_VISIBLE;
1206       /*
1207        * toggle the weeding of headers so that a user can press the key
1208        * again while reading the message.
1209        */
1210       if (op == OP_DISPLAY_HEADERS)
1211         toggle_option (OPTWEED);
1212
1213       unset_option (OPTNEEDRESORT);
1214
1215       if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed) {
1216         mutt_uncollapse_thread (Context, CURHDR);
1217         mutt_set_virtual (Context);
1218         if (option (OPTUNCOLLAPSEJUMP))
1219           menu->current = mutt_thread_next_unread (Context, CURHDR);
1220       }
1221
1222       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1223         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1224
1225       if ((op = mutt_display_message (CURHDR)) == -1) {
1226         unset_option (OPTNEEDRESORT);
1227         break;
1228       }
1229
1230       menu->menu = MENU_PAGER;
1231       menu->oldcurrent = menu->current;
1232       continue;
1233
1234     case OP_EXIT:
1235
1236       closed = op;
1237       if (menu->menu == MENU_MAIN && attach_msg) {
1238         done = 1;
1239         break;
1240       }
1241
1242       if ((menu->menu == MENU_MAIN)
1243           && (query_quadoption2(mod_core.quit,
1244                                 _("Exit Madmutt without saving?")) == M_YES))
1245       {
1246         if (Context) {
1247           mx_fastclose_mailbox (Context);
1248           p_delete(&Context);
1249         }
1250         done = 1;
1251       }
1252       break;
1253
1254     case OP_EDIT_TYPE:
1255
1256       CHECK_MSGCOUNT;
1257       CHECK_VISIBLE;
1258       CHECK_ATTACH;
1259       mutt_edit_content_type (CURHDR, CURHDR->content, NULL);
1260       /* if we were in the pager, redisplay the message */
1261       if (menu->menu == MENU_PAGER) {
1262         op = OP_DISPLAY_MESSAGE;
1263         continue;
1264       }
1265       else
1266         menu->redraw = REDRAW_CURRENT;
1267       break;
1268
1269     case OP_MAIN_BREAK_THREAD:
1270
1271       CHECK_MSGCOUNT;
1272       CHECK_VISIBLE;
1273       CHECK_READONLY;
1274
1275       if ((Sort & SORT_MASK) != SORT_THREADS)
1276         mutt_error (_("Threading is not enabled."));
1277
1278       else {
1279         {
1280           HEADER *oldcur = CURHDR;
1281
1282           mutt_break_thread (CURHDR);
1283           mutt_sort_headers (Context, 1);
1284           menu->current = oldcur->virtual;
1285         }
1286
1287         Context->changed = 1;
1288         mutt_message _("Thread broken");
1289
1290         if (menu->menu == MENU_PAGER) {
1291           op = OP_DISPLAY_MESSAGE;
1292           continue;
1293         }
1294         else
1295           menu->redraw |= REDRAW_INDEX;
1296       }
1297       break;
1298
1299     case OP_MAIN_LINK_THREADS:
1300
1301       CHECK_MSGCOUNT;
1302       CHECK_VISIBLE;
1303       CHECK_READONLY;
1304
1305       if ((Sort & SORT_MASK) != SORT_THREADS)
1306         mutt_error (_("Threading is not enabled."));
1307
1308       else if (!CURHDR->env->message_id)
1309         mutt_error (_("No Message-ID: header available to link thread"));
1310
1311       else if (!tag && (!Context->last_tag || !Context->last_tag->tagged))
1312         mutt_error (_("First, please tag a message to be linked here"));
1313
1314       else {
1315         HEADER *oldcur = CURHDR;
1316
1317         if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag,
1318                                Context)) {
1319           mutt_sort_headers (Context, 1);
1320           menu->current = oldcur->virtual;
1321
1322           Context->changed = 1;
1323           mutt_message _("Threads linked");
1324         }
1325         else
1326           mutt_error (_("No thread linked"));
1327       }
1328
1329       if (menu->menu == MENU_PAGER) {
1330         op = OP_DISPLAY_MESSAGE;
1331         continue;
1332       }
1333       else
1334         menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
1335       break;
1336
1337     case OP_MAIN_NEXT_UNDELETED:
1338
1339       CHECK_MSGCOUNT;
1340       CHECK_VISIBLE;
1341       if (menu->current >= Context->vcount - 1) {
1342         if (menu->menu == MENU_MAIN)
1343           mutt_error (_("You are on the last message."));
1344
1345         break;
1346       }
1347       if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1348         menu->current = menu->oldcurrent;
1349         if (menu->menu == MENU_MAIN)
1350           mutt_error (_("No undeleted messages."));
1351       }
1352       else if (menu->menu == MENU_PAGER) {
1353         op = OP_DISPLAY_MESSAGE;
1354         continue;
1355       }
1356       else
1357         menu->redraw = REDRAW_MOTION;
1358       break;
1359
1360     case OP_NEXT_ENTRY:
1361
1362       CHECK_MSGCOUNT;
1363       CHECK_VISIBLE;
1364       if (menu->current >= Context->vcount - 1) {
1365         if (menu->menu == MENU_MAIN)
1366           mutt_error (_("You are on the last message."));
1367
1368         break;
1369       }
1370       menu->current++;
1371       if (menu->menu == MENU_PAGER) {
1372         op = OP_DISPLAY_MESSAGE;
1373         continue;
1374       }
1375       else
1376         menu->redraw = REDRAW_MOTION;
1377       break;
1378
1379     case OP_MAIN_PREV_UNDELETED:
1380
1381       CHECK_MSGCOUNT;
1382       CHECK_VISIBLE;
1383       if (menu->current < 1) {
1384         mutt_error (_("You are on the first message."));
1385
1386         break;
1387       }
1388       if ((menu->current = ci_previous_undeleted (menu->current)) == -1) {
1389         menu->current = menu->oldcurrent;
1390         if (menu->menu == MENU_MAIN)
1391           mutt_error (_("No undeleted messages."));
1392       }
1393       else if (menu->menu == MENU_PAGER) {
1394         op = OP_DISPLAY_MESSAGE;
1395         continue;
1396       }
1397       else
1398         menu->redraw = REDRAW_MOTION;
1399       break;
1400
1401     case OP_PREV_ENTRY:
1402
1403       CHECK_MSGCOUNT;
1404       CHECK_VISIBLE;
1405       if (menu->current < 1) {
1406         if (menu->menu == MENU_MAIN)
1407           mutt_error (_("You are on the first message."));
1408
1409         break;
1410       }
1411       menu->current--;
1412       if (menu->menu == MENU_PAGER) {
1413         op = OP_DISPLAY_MESSAGE;
1414         continue;
1415       }
1416       else
1417         menu->redraw = REDRAW_MOTION;
1418       break;
1419
1420     case OP_DECRYPT_COPY:
1421     case OP_DECRYPT_SAVE:
1422     case OP_COPY_MESSAGE:
1423     case OP_SAVE:
1424     case OP_DECODE_COPY:
1425     case OP_DECODE_SAVE:
1426       CHECK_MSGCOUNT;
1427       CHECK_VISIBLE;
1428       if (mutt_save_message (tag ? NULL : CURHDR,
1429                              (op == OP_DECRYPT_SAVE) ||
1430                              (op == OP_SAVE) || (op == OP_DECODE_SAVE),
1431                              (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY),
1432                              (op == OP_DECRYPT_SAVE)
1433                              || (op == OP_DECRYPT_COPY)
1434                              || 0, &menu->redraw) == 0 && (op == OP_SAVE
1435                                                            || op ==
1436                                                            OP_DECODE_SAVE
1437                                                            || op ==
1438                                                            OP_DECRYPT_SAVE)
1439         ) {
1440         if (tag)
1441           menu->redraw |= REDRAW_INDEX;
1442         else if (option (OPTRESOLVE)) {
1443           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1444             menu->current = menu->oldcurrent;
1445             menu->redraw |= REDRAW_CURRENT;
1446           }
1447           else
1448             menu->redraw |= REDRAW_MOTION_RESYNCH;
1449         }
1450         else
1451           menu->redraw |= REDRAW_CURRENT;
1452       }
1453       break;
1454
1455     case OP_MAIN_NEXT_NEW:
1456     case OP_MAIN_NEXT_UNREAD:
1457     case OP_MAIN_PREV_NEW:
1458     case OP_MAIN_PREV_UNREAD:
1459     case OP_MAIN_NEXT_NEW_THEN_UNREAD:
1460     case OP_MAIN_PREV_NEW_THEN_UNREAD:
1461
1462       {
1463         int first_unread = -1;
1464         int first_new = -1;
1465
1466         CHECK_MSGCOUNT;
1467         CHECK_VISIBLE;
1468
1469         i = menu->current;
1470         menu->current = -1;
1471         for (j = 0; j != Context->vcount; j++) {
1472 #define CURHDRi Context->hdrs[Context->v2r[i]]
1473           if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD
1474               || op == OP_MAIN_NEXT_NEW_THEN_UNREAD) {
1475             i++;
1476             if (i > Context->vcount - 1) {
1477               mutt_message _("Search wrapped to top.");
1478
1479               i = 0;
1480             }
1481           }
1482           else {
1483             i--;
1484             if (i < 0) {
1485               mutt_message _("Search wrapped to bottom.");
1486
1487               i = Context->vcount - 1;
1488             }
1489           }
1490
1491           if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS) {
1492             if (UNREAD (CURHDRi) && first_unread == -1)
1493               first_unread = i;
1494             if (UNREAD (CURHDRi) == 1 && first_new == -1)
1495               first_new = i;
1496           }
1497           else if ((!CURHDRi->deleted && !CURHDRi->read)) {
1498             if (first_unread == -1)
1499               first_unread = i;
1500             if ((!CURHDRi->old) && first_new == -1)
1501               first_new = i;
1502           }
1503
1504           if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) &&
1505               first_unread != -1)
1506             break;
1507           if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1508                op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1509                || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1510               && first_new != -1)
1511             break;
1512         }
1513 #undef CURHDRi
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           menu->current = first_new;
1519         else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
1520                   op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1521                   || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1522                  && first_unread != -1)
1523           menu->current = first_unread;
1524
1525         if (menu->current == -1) {
1526           menu->current = menu->oldcurrent;
1527           mutt_error ("%s%s.",
1528                       (op == OP_MAIN_NEXT_NEW
1529                        || op ==
1530                        OP_MAIN_PREV_NEW) ? _("No new messages") :
1531                       _("No unread messages"),
1532                       Context->pattern ? _(" in this limited view") : "");
1533         }
1534         else if (menu->menu == MENU_PAGER) {
1535           op = OP_DISPLAY_MESSAGE;
1536           continue;
1537         }
1538         else
1539           menu->redraw = REDRAW_MOTION;
1540         break;
1541       }
1542     case OP_FLAG_MESSAGE:
1543
1544       CHECK_MSGCOUNT;
1545       CHECK_VISIBLE;
1546       CHECK_READONLY;
1547
1548       CHECK_MX_ACL (Context, ACL_WRITE, _("Flagging"));
1549
1550       if (tag) {
1551         for (j = 0; j < Context->vcount; j++) {
1552           if (Context->hdrs[Context->v2r[j]]->tagged)
1553             mutt_set_flag (Context, Context->hdrs[Context->v2r[j]],
1554                            M_FLAG, !Context->hdrs[Context->v2r[j]]->flagged);
1555         }
1556
1557         menu->redraw |= REDRAW_INDEX;
1558       }
1559       else {
1560         mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
1561         if (option (OPTRESOLVE)) {
1562           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1563             menu->current = menu->oldcurrent;
1564             menu->redraw = REDRAW_CURRENT;
1565           }
1566           else
1567             menu->redraw = REDRAW_MOTION_RESYNCH;
1568         }
1569         else
1570           menu->redraw = REDRAW_CURRENT;
1571       }
1572       menu->redraw |= REDRAW_STATUS;
1573       break;
1574
1575     case OP_TOGGLE_NEW:
1576
1577       CHECK_MSGCOUNT;
1578       CHECK_VISIBLE;
1579       CHECK_READONLY;
1580
1581       CHECK_MX_ACL (Context, ACL_SEEN, _("Toggling"));
1582
1583       if (tag) {
1584         for (j = 0; j < Context->vcount; j++) {
1585           if (Context->hdrs[Context->v2r[j]]->tagged) {
1586             if (Context->hdrs[Context->v2r[j]]->read ||
1587                 Context->hdrs[Context->v2r[j]]->old)
1588               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_NEW,
1589                              1);
1590             else
1591               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ,
1592                              1);
1593           }
1594         }
1595         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1596       }
1597       else {
1598         if (CURHDR->read || CURHDR->old)
1599           mutt_set_flag (Context, CURHDR, M_NEW, 1);
1600         else
1601           mutt_set_flag (Context, CURHDR, M_READ, 1);
1602
1603         if (option (OPTRESOLVE)) {
1604           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1605             menu->current = menu->oldcurrent;
1606             menu->redraw = REDRAW_CURRENT;
1607           }
1608           else
1609             menu->redraw = REDRAW_MOTION_RESYNCH;
1610         }
1611         else
1612           menu->redraw = REDRAW_CURRENT;
1613         menu->redraw |= REDRAW_STATUS;
1614       }
1615       break;
1616
1617     case OP_TOGGLE_WRITE:
1618
1619       CHECK_IN_MAILBOX;
1620       if (mx_toggle_write (Context) == 0)
1621         menu->redraw |= REDRAW_STATUS;
1622       break;
1623
1624     case OP_MAIN_NEXT_THREAD:
1625     case OP_MAIN_NEXT_SUBTHREAD:
1626     case OP_MAIN_PREV_THREAD:
1627     case OP_MAIN_PREV_SUBTHREAD:
1628
1629       CHECK_MSGCOUNT;
1630       CHECK_VISIBLE;
1631       switch (op) {
1632       case OP_MAIN_NEXT_THREAD:
1633         menu->current = mutt_next_thread (CURHDR);
1634         break;
1635
1636       case OP_MAIN_NEXT_SUBTHREAD:
1637         menu->current = mutt_next_subthread (CURHDR);
1638         break;
1639
1640       case OP_MAIN_PREV_THREAD:
1641         menu->current = mutt_previous_thread (CURHDR);
1642         break;
1643
1644       case OP_MAIN_PREV_SUBTHREAD:
1645         menu->current = mutt_previous_subthread (CURHDR);
1646         break;
1647       }
1648
1649       if (menu->current < 0) {
1650         menu->current = menu->oldcurrent;
1651         if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
1652           mutt_error (_("No more threads."));
1653
1654         else
1655           mutt_error (_("You are on the first thread."));
1656       }
1657       else if (menu->menu == MENU_PAGER) {
1658         op = OP_DISPLAY_MESSAGE;
1659         continue;
1660       }
1661       else
1662         menu->redraw = REDRAW_MOTION;
1663       break;
1664
1665     case OP_MAIN_PARENT_MESSAGE:
1666
1667       CHECK_MSGCOUNT;
1668       CHECK_VISIBLE;
1669
1670       if ((menu->current = mutt_parent_message (Context, CURHDR)) < 0) {
1671         menu->current = menu->oldcurrent;
1672       }
1673       else if (menu->menu == MENU_PAGER) {
1674         op = OP_DISPLAY_MESSAGE;
1675         continue;
1676       }
1677       else
1678         menu->redraw = REDRAW_MOTION;
1679       break;
1680
1681     case OP_MAIN_SET_FLAG:
1682     case OP_MAIN_CLEAR_FLAG:
1683
1684       CHECK_MSGCOUNT;
1685       CHECK_VISIBLE;
1686       CHECK_READONLY;
1687
1688       if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) == 0) {
1689         menu->redraw = REDRAW_STATUS;
1690         if (tag)
1691           menu->redraw |= REDRAW_INDEX;
1692         else if (option (OPTRESOLVE)) {
1693           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1694             menu->current = menu->oldcurrent;
1695             menu->redraw |= REDRAW_CURRENT;
1696           }
1697           else
1698             menu->redraw |= REDRAW_MOTION_RESYNCH;
1699         }
1700         else
1701           menu->redraw |= REDRAW_CURRENT;
1702       }
1703       break;
1704
1705     case OP_MAIN_COLLAPSE_THREAD:
1706       CHECK_MSGCOUNT;
1707       CHECK_VISIBLE;
1708
1709       if ((Sort & SORT_MASK) != SORT_THREADS) {
1710         mutt_error (_("Threading is not enabled."));
1711
1712         break;
1713       }
1714
1715       if (CURHDR->collapsed) {
1716         menu->current = mutt_uncollapse_thread (Context, CURHDR);
1717         mutt_set_virtual (Context);
1718         if (option (OPTUNCOLLAPSEJUMP))
1719           menu->current = mutt_thread_next_unread (Context, CURHDR);
1720       }
1721       else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR)) {
1722         menu->current = mutt_collapse_thread (Context, CURHDR);
1723         mutt_set_virtual (Context);
1724       }
1725       else {
1726         mutt_error (_("Thread contains unread messages."));
1727
1728         break;
1729       }
1730
1731       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1732
1733       break;
1734
1735     case OP_MAIN_COLLAPSE_ALL:
1736       CHECK_MSGCOUNT;
1737       CHECK_VISIBLE;
1738
1739       if ((Sort & SORT_MASK) != SORT_THREADS) {
1740         mutt_error (_("Threading is not enabled."));
1741
1742         break;
1743       }
1744
1745       {
1746         HEADER *h, *base;
1747         THREAD *thread, *top;
1748         int final;
1749
1750         if (CURHDR->collapsed)
1751           final = mutt_uncollapse_thread (Context, CURHDR);
1752         else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1753           final = mutt_collapse_thread (Context, CURHDR);
1754         else
1755           final = CURHDR->virtual;
1756
1757         base = Context->hdrs[Context->v2r[final]];
1758
1759         top = Context->tree;
1760         Context->collapsed = !Context->collapsed;
1761         while ((thread = top) != NULL) {
1762           while (!thread->message)
1763             thread = thread->child;
1764           h = thread->message;
1765
1766           if (h->collapsed != Context->collapsed) {
1767             if (h->collapsed)
1768               mutt_uncollapse_thread (Context, h);
1769             else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
1770               mutt_collapse_thread (Context, h);
1771           }
1772           top = top->next;
1773         }
1774
1775         mutt_set_virtual (Context);
1776         for (j = 0; j < Context->vcount; j++) {
1777           if (Context->hdrs[Context->v2r[j]]->index == base->index) {
1778             menu->current = j;
1779             break;
1780           }
1781         }
1782
1783         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1784       }
1785       break;
1786
1787       /* --------------------------------------------------------------------
1788        * These functions are invoked directly from the internal-pager
1789        */
1790
1791     case OP_BOUNCE_MESSAGE:
1792
1793       CHECK_ATTACH;
1794       CHECK_MSGCOUNT;
1795       CHECK_VISIBLE;
1796       ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
1797       break;
1798
1799     case OP_CREATE_ALIAS:
1800
1801       mutt_create_alias (Context
1802                          && Context->vcount ? CURHDR->env : NULL, NULL);
1803       MAYBE_REDRAW (menu->redraw);
1804       menu->redraw |= REDRAW_CURRENT;
1805       break;
1806
1807     case OP_QUERY:
1808       CHECK_ATTACH;
1809       mutt_query_menu (NULL, 0);
1810       MAYBE_REDRAW (menu->redraw);
1811       break;
1812
1813     case OP_PURGE_MESSAGE:
1814     case OP_DELETE:
1815
1816       CHECK_MSGCOUNT;
1817       CHECK_VISIBLE;
1818       CHECK_READONLY;
1819
1820       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1821
1822       if (tag) {
1823         mutt_tag_set_flag (M_DELETE, 1);
1824         mutt_tag_set_flag (M_PURGED, (op != OP_PURGE_MESSAGE) ? 0 : 1);
1825         if (option (OPTDELETEUNTAG))
1826           mutt_tag_set_flag (M_TAG, 0);
1827         menu->redraw = REDRAW_INDEX;
1828       }
1829       else {
1830         mutt_set_flag (Context, CURHDR, M_DELETE, 1);
1831         mutt_set_flag (Context, CURHDR, M_PURGED,
1832                        (op != OP_PURGE_MESSAGE) ? 0 : 1);
1833         if (option (OPTDELETEUNTAG))
1834           mutt_set_flag (Context, CURHDR, M_TAG, 0);
1835         if (option (OPTRESOLVE)) {
1836           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1837             menu->current = menu->oldcurrent;
1838             menu->redraw = REDRAW_CURRENT;
1839           }
1840           else if (menu->menu == MENU_PAGER) {
1841             op = OP_DISPLAY_MESSAGE;
1842             continue;
1843           }
1844           else
1845             menu->redraw |= REDRAW_MOTION_RESYNCH;
1846         }
1847         else
1848           menu->redraw = REDRAW_CURRENT;
1849       }
1850       menu->redraw |= REDRAW_STATUS;
1851       break;
1852
1853     case OP_DELETE_THREAD:
1854     case OP_DELETE_SUBTHREAD:
1855
1856       CHECK_MSGCOUNT;
1857       CHECK_VISIBLE;
1858       CHECK_READONLY;
1859
1860       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1861
1862       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
1863                                  op == OP_DELETE_THREAD ? 0 : 1);
1864
1865       if (rc != -1) {
1866         if (option (OPTDELETEUNTAG))
1867           mutt_thread_set_flag (CURHDR, M_TAG, 0,
1868                                 op == OP_DELETE_THREAD ? 0 : 1);
1869         if (option (OPTRESOLVE))
1870           if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1871             menu->current = menu->oldcurrent;
1872         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1873       }
1874       break;
1875
1876 #ifdef USE_NNTP
1877     case OP_CATCHUP:
1878       if (Context && Context->magic == M_NNTP) {
1879         if (mutt_newsgroup_catchup (CurrentNewsSrv,
1880                                     ((nntp_data_t *) Context->data)->group))
1881           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1882       }
1883       break;
1884 #endif
1885
1886     case OP_DISPLAY_ADDRESS:
1887
1888       CHECK_MSGCOUNT;
1889       CHECK_VISIBLE;
1890       mutt_display_address (CURHDR->env);
1891       break;
1892
1893     case OP_ENTER_COMMAND:
1894
1895       CurrentMenu = MENU_MAIN;
1896       mutt_enter_command ();
1897       mutt_check_rescore (Context);
1898       if (option (OPTFORCEREDRAWINDEX))
1899         menu->redraw = REDRAW_FULL;
1900       unset_option (OPTFORCEREDRAWINDEX);
1901       unset_option (OPTFORCEREDRAWPAGER);
1902       break;
1903
1904     case OP_EDIT_MESSAGE:
1905
1906       CHECK_MSGCOUNT;
1907       CHECK_VISIBLE;
1908       CHECK_READONLY;
1909       CHECK_ATTACH;
1910
1911       CHECK_MX_ACL (Context, ACL_INSERT, _("Editing"));
1912
1913       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1914         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1915       mutt_edit_message (Context, tag ? NULL : CURHDR);
1916       menu->redraw = REDRAW_FULL;
1917
1918       break;
1919
1920     case OP_FORWARD_MESSAGE:
1921
1922       CHECK_MSGCOUNT;
1923       CHECK_VISIBLE;
1924       CHECK_ATTACH;
1925
1926       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1927         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1928       ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
1929       menu->redraw = REDRAW_FULL;
1930       break;
1931
1932     case OP_GROUP_REPLY:
1933
1934       CHECK_MSGCOUNT;
1935       CHECK_VISIBLE;
1936       CHECK_ATTACH;
1937
1938       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1939         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1940
1941       ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, Context,
1942                        tag ? NULL : CURHDR);
1943       menu->redraw = REDRAW_FULL;
1944       break;
1945
1946     case OP_LIST_REPLY:
1947
1948       CHECK_ATTACH;
1949       CHECK_MSGCOUNT;
1950       CHECK_VISIBLE;
1951
1952       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1953         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1954
1955       ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, Context,
1956                        tag ? NULL : CURHDR);
1957       menu->redraw = REDRAW_FULL;
1958       break;
1959
1960     case OP_MAIL:
1961
1962       CHECK_ATTACH;
1963       ci_send_message (0, NULL, NULL, Context, NULL);
1964       menu->redraw = REDRAW_FULL;
1965       break;
1966
1967     case OP_EXTRACT_KEYS:
1968       CHECK_MSGCOUNT;
1969       CHECK_VISIBLE;
1970       crypt_extract_keys_from_messages (tag ? NULL : CURHDR);
1971       menu->redraw = REDRAW_FULL;
1972       break;
1973
1974
1975     case OP_CHECK_TRADITIONAL:
1976       CHECK_MSGCOUNT;
1977       CHECK_VISIBLE;
1978       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
1979         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
1980
1981       if (menu->menu == MENU_PAGER) {
1982         op = OP_DISPLAY_MESSAGE;
1983         continue;
1984       }
1985       break;
1986
1987     case OP_PIPE:
1988       CHECK_MSGCOUNT;
1989       CHECK_VISIBLE;
1990       mutt_pipe_message (tag ? NULL : CURHDR);
1991       MAYBE_REDRAW (menu->redraw);
1992       break;
1993
1994     case OP_PRINT:
1995       CHECK_MSGCOUNT;
1996       CHECK_VISIBLE;
1997       mutt_print_message (tag ? NULL : CURHDR);
1998       break;
1999
2000     case OP_MAIN_READ_THREAD:
2001     case OP_MAIN_READ_SUBTHREAD:
2002       CHECK_MSGCOUNT;
2003       CHECK_VISIBLE;
2004       CHECK_READONLY;
2005
2006       CHECK_MX_ACL (Context, ACL_SEEN, _("Marking as read"));
2007
2008       rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
2009                                  op == OP_MAIN_READ_THREAD ? 0 : 1);
2010
2011       if (rc != -1) {
2012         if (option (OPTRESOLVE)) {
2013           if ((menu->current = (op == OP_MAIN_READ_THREAD ?
2014                                 mutt_next_thread (CURHDR) :
2015                                 mutt_next_subthread (CURHDR))) == -1)
2016             menu->current = menu->oldcurrent;
2017         }
2018         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2019       }
2020       break;
2021
2022     case OP_RECALL_MESSAGE:
2023
2024       CHECK_ATTACH;
2025       ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
2026       menu->redraw = REDRAW_FULL;
2027       break;
2028
2029     case OP_RESEND:
2030
2031       CHECK_ATTACH;
2032       CHECK_MSGCOUNT;
2033       CHECK_VISIBLE;
2034
2035       if (tag) {
2036         for (j = 0; j < Context->vcount; j++) {
2037           if (Context->hdrs[Context->v2r[j]]->tagged)
2038             mutt_resend_message (NULL, Context,
2039                                  Context->hdrs[Context->v2r[j]]);
2040         }
2041       }
2042       else
2043         mutt_resend_message (NULL, Context, CURHDR);
2044
2045       menu->redraw = REDRAW_FULL;
2046       break;
2047
2048 #ifdef USE_NNTP
2049     case OP_POST:
2050     case OP_FOLLOWUP:
2051     case OP_FORWARD_TO_GROUP:
2052
2053       CHECK_ATTACH;
2054       if ((op == OP_FOLLOWUP || op == OP_FORWARD_TO_GROUP) &&
2055           Context && Context->msgcount == 0) {
2056         mutt_error (_("There are no messages."));
2057         sleep (2);
2058       }
2059       else if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
2060                m_strcasecmp(CURHDR->env->followup_to, "poster") ||
2061                query_quadoption (OPT_FOLLOWUPTOPOSTER,
2062                                  _("Reply by mail as poster prefers?")) !=
2063                M_YES) {
2064         if (Context && Context->magic == M_NNTP
2065             && !((nntp_data_t *) Context->data)->allowed
2066             && query_quadoption (OPT_TOMODERATED,
2067                                  _
2068                                  ("Posting to this group not allowed, may be moderated. Continue?"))
2069             != M_YES)
2070           break;
2071         if (op == OP_POST)
2072           ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
2073         else {
2074           CHECK_MSGCOUNT;
2075           if (op == OP_FOLLOWUP)
2076             ci_send_message (SENDNEWS | SENDREPLY, NULL, NULL, Context,
2077                              tag ? NULL : CURHDR);
2078           else
2079             ci_send_message (SENDNEWS | SENDFORWARD, NULL, NULL, Context,
2080                              tag ? NULL : CURHDR);
2081         }
2082         menu->redraw = REDRAW_FULL;
2083         break;
2084       }
2085 #endif
2086
2087     case OP_REPLY:
2088
2089       CHECK_ATTACH;
2090       CHECK_MSGCOUNT;
2091       CHECK_VISIBLE;
2092
2093       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
2094         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2095
2096       ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2097       menu->redraw = REDRAW_FULL;
2098       break;
2099
2100     case OP_SHELL_ESCAPE:
2101
2102       mutt_shell_escape ();
2103       MAYBE_REDRAW (menu->redraw);
2104       break;
2105
2106     case OP_TAG_THREAD:
2107     case OP_TAG_SUBTHREAD:
2108
2109       CHECK_MSGCOUNT;
2110       CHECK_VISIBLE;
2111       rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
2112                                  op == OP_TAG_THREAD ? 0 : 1);
2113
2114       if (rc != -1) {
2115         if (option (OPTRESOLVE)) {
2116           menu->current = mutt_next_thread (CURHDR);
2117
2118           if (menu->current == -1)
2119             menu->current = menu->oldcurrent;
2120         }
2121         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2122       }
2123       break;
2124
2125     case OP_UNDELETE:
2126
2127       CHECK_MSGCOUNT;
2128       CHECK_VISIBLE;
2129       CHECK_READONLY;
2130
2131       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2132
2133       if (tag) {
2134         mutt_tag_set_flag (M_DELETE, 0);
2135         mutt_tag_set_flag (M_PURGED, 0);
2136         menu->redraw = REDRAW_INDEX;
2137       }
2138       else {
2139         mutt_set_flag (Context, CURHDR, M_DELETE, 0);
2140         mutt_set_flag (Context, CURHDR, M_PURGED, 0);
2141         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
2142           menu->current++;
2143           menu->redraw = REDRAW_MOTION_RESYNCH;
2144         }
2145         else
2146           menu->redraw = REDRAW_CURRENT;
2147       }
2148       menu->redraw |= REDRAW_STATUS;
2149       break;
2150
2151     case OP_UNDELETE_THREAD:
2152     case OP_UNDELETE_SUBTHREAD:
2153
2154       CHECK_MSGCOUNT;
2155       CHECK_VISIBLE;
2156       CHECK_READONLY;
2157
2158       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2159
2160       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
2161                                  op == OP_UNDELETE_THREAD ? 0 : 1)
2162         + mutt_thread_set_flag (CURHDR, M_PURGED, 0,
2163                                 op == OP_UNDELETE_THREAD ? 0 : 1);
2164
2165       if (rc > -1) {
2166         if (option (OPTRESOLVE)) {
2167           if (op == OP_UNDELETE_THREAD)
2168             menu->current = mutt_next_thread (CURHDR);
2169           else
2170             menu->current = mutt_next_subthread (CURHDR);
2171
2172           if (menu->current == -1)
2173             menu->current = menu->oldcurrent;
2174         }
2175         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2176       }
2177       break;
2178
2179     case OP_VERSION:
2180       mutt_version ();
2181       break;
2182
2183     case OP_BUFFY_LIST:
2184       if (option (OPTFORCEBUFFYCHECK))
2185         buffy_check (1);
2186       buffy_list ();
2187       menu->redraw = REDRAW_FULL;
2188       break;
2189
2190     case OP_VIEW_ATTACHMENTS:
2191       CHECK_MSGCOUNT;
2192       CHECK_VISIBLE;
2193       mutt_view_attachments (CURHDR);
2194       if (CURHDR->attach_del)
2195         Context->changed = 1;
2196       menu->redraw = REDRAW_FULL;
2197       break;
2198
2199     case OP_END_COND:
2200       break;
2201
2202     case OP_WHAT_KEY:
2203       mutt_what_key ();
2204       break;
2205
2206     case OP_SIDEBAR_SCROLL_UP:
2207     case OP_SIDEBAR_SCROLL_DOWN:
2208     case OP_SIDEBAR_NEXT:
2209     case OP_SIDEBAR_PREV:
2210     case OP_SIDEBAR_NEXT_NEW:
2211     case OP_SIDEBAR_PREV_NEW:
2212       sidebar_scroll (op);
2213       break;
2214     default:
2215       if (menu->menu == MENU_MAIN)
2216         km_error_key (MENU_MAIN);
2217     }
2218
2219     if (menu->menu == MENU_PAGER) {
2220       menu->menu = MENU_MAIN;
2221       menu->redraw = REDRAW_FULL;
2222     }
2223
2224     if (done)
2225       break;
2226   }
2227
2228   if (!attach_msg) {
2229   /* Close all open IMAP connections */
2230     imap_logout_all ();
2231 #ifdef USE_NNTP
2232   /* Close all open NNTP connections */
2233     nntp_logout_all ();
2234 #endif
2235   }
2236
2237   mutt_menuDestroy (&menu);
2238   return (closed);
2239 }
2240
2241 void mutt_set_header_color (CONTEXT * ctx, HEADER * curhdr)
2242 {
2243   COLOR_LINE *color;
2244
2245   if (!curhdr)
2246     return;
2247
2248   for (color = ColorIndexList; color; color = color->next)
2249     if (mutt_pattern_exec
2250         (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr)) {
2251       curhdr->pair = color->pair;
2252       return;
2253     }
2254   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2255 }