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