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