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