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