📄 curs_main.c
字号:
/* * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H# include "config.h"#endif#include "mutt.h"#include "mutt_curses.h"#include "mutt_menu.h"#include "attach.h"#include "mailbox.h"#include "mapping.h"#include "sort.h"#include "buffy.h"#include "mx.h"#ifdef USE_POP#include "pop.h"#endif#ifdef USE_IMAP#include "imap_private.h"#endif#include "mutt_crypt.h"#include <ctype.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>#include <string.h>#include <sys/stat.h>#include <errno.h>static const char *No_mailbox_is_open = N_("No mailbox is open.");static const char *There_are_no_messages = N_("There are no messages.");static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");static const char *Function_not_permitted_in_attach_message_mode = N_("Function not permitted in attach-message mode.");static const char *No_visible = N_("No visible messages.");#define CHECK_IN_MAILBOX if (!Context) \ { \ mutt_flushinp (); \ mutt_error _(No_mailbox_is_open); \ break; \ }#define CHECK_MSGCOUNT if (!Context) \ { \ mutt_flushinp (); \ mutt_error _(No_mailbox_is_open); \ break; \ } \ else if (!Context->msgcount) \ { \ mutt_flushinp (); \ mutt_error _(There_are_no_messages); \ break; \ }#define CHECK_VISIBLE if (Context && menu->current >= Context->vcount) \ {\ mutt_flushinp (); \ mutt_error _(No_visible); \ break; \ } #define CHECK_READONLY if (Context->readonly) \ { \ mutt_flushinp (); \ mutt_error _(Mailbox_is_read_only); \ break; \ }#ifdef USE_IMAP /* the error message returned here could be better. */#define CHECK_IMAP_ACL(aclbit) if (Context->magic == M_IMAP) \ if (mutt_bit_isset (((IMAP_DATA *)Context->data)->capabilities, ACL) \ && !mutt_bit_isset(((IMAP_DATA *)Context->data)->rights,aclbit)){ \ mutt_flushinp(); \ mutt_error ("Operation not permitted by the IMAP ACL for this mailbox"); \ break; \ }#else#define CHECK_IMAP_ACL(aclbit) /**/#endif#define CHECK_ATTACH if(option(OPTATTACHMSG)) \ {\ mutt_flushinp (); \ mutt_error _(Function_not_permitted_in_attach_message_mode); \ break; \ }#define CURHDR Context->hdrs[Context->v2r[menu->current]]#define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]#define UNREAD(h) mutt_thread_contains_unread (Context, h)extern size_t UngetCount;void index_make_entry (char *s, size_t l, MUTTMENU *menu, int num){ format_flag flag = M_FORMAT_MAKEPRINT | M_FORMAT_ARROWCURSOR | M_FORMAT_INDEX; int edgemsgno, reverse = Sort & SORT_REVERSE; HEADER *h = Context->hdrs[Context->v2r[num]]; THREAD *tmp; if ((Sort & SORT_MASK) == SORT_THREADS && h->tree) { flag |= M_FORMAT_TREE; /* display the thread tree */ if (h->display_subject) flag |= M_FORMAT_FORCESUBJ; else { if (reverse) { if (menu->top + menu->pagelen > menu->max) edgemsgno = Context->v2r[menu->max - 1]; else edgemsgno = Context->v2r[menu->top + menu->pagelen - 1]; } else edgemsgno = Context->v2r[menu->top]; for (tmp = h->thread->parent; tmp; tmp = tmp->parent) { if (!tmp->message) continue; /* if no ancestor is visible on current screen, provisionally force * subject... */ if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno) { flag |= M_FORMAT_FORCESUBJ; break; } else if (tmp->message->virtual >= 0) break; } if (flag & M_FORMAT_FORCESUBJ) { for (tmp = h->thread->prev; tmp; tmp = tmp->prev) { if (!tmp->message) continue; /* ...but if a previous sibling is available, don't force it */ if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno < edgemsgno) break; else if (tmp->message->virtual >= 0) { flag &= ~M_FORMAT_FORCESUBJ; break; } } } } } _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);}int index_color (int index_no){ HEADER *h = Context->hdrs[Context->v2r[index_no]]; if (h && h->pair) return h->pair; mutt_set_header_color (Context, h); return h->pair;}static int ci_next_undeleted (int msgno){ int i; for (i=msgno+1; i < Context->vcount; i++) if (! Context->hdrs[Context->v2r[i]]->deleted) return (i); return (-1);}static int ci_previous_undeleted (int msgno){ int i; for (i=msgno-1; i>=0; i--) if (! Context->hdrs[Context->v2r[i]]->deleted) return (i); return (-1);}/* Return the index of the first new message, or failing that, the first * unread message. */static int ci_first_message (void){ int old = -1, i; if (Context && Context->msgcount) { for (i=0; i < Context->vcount; i++) { if (! Context->hdrs[Context->v2r[i]]->read && ! Context->hdrs[Context->v2r[i]]->deleted) { if (! Context->hdrs[Context->v2r[i]]->old) return (i); else if (old == -1) old = i; } } if (old != -1) return (old); /* If Sort is reverse and not threaded, the latest message is first. * If Sort is threaded, the latest message is first iff exactly one * of Sort and SortAux are reverse. */ if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) || ((Sort & SORT_MASK) == SORT_THREADS && ((Sort ^ SortAux) & SORT_REVERSE))) return 0; else return (Context->vcount ? Context->vcount - 1 : 0); } return 0;}/* This should be in mx.c, but it only gets used here. */static int mx_toggle_write (CONTEXT *ctx){ if (!ctx) return -1; if (ctx->readonly) { mutt_error _("Cannot toggle write on a readonly mailbox!"); return -1; } if (ctx->dontwrite) { ctx->dontwrite = 0; mutt_message _("Changes to folder will be written on folder exit."); } else { ctx->dontwrite = 1; mutt_message _("Changes to folder will not be written."); } return 0;}static void update_index (MUTTMENU *menu, CONTEXT *ctx, int check, int oldcount, int index_hint){ /* store pointers to the newly added messages */ HEADER **save_new = NULL; int j; /* take note of the current message */ if (oldcount) { if (menu->current < Context->vcount) menu->oldcurrent = index_hint; else oldcount = 0; /* invalid message number! */ } /* We are in a limited view. Check if the new message(s) satisfy * the limit criteria. If they do, set their virtual msgno so that * they will be visible in the limited view */ if (Context->pattern) {#define THIS_BODY Context->hdrs[j]->content for (j = (check == M_REOPENED) ? 0 : oldcount; j < Context->msgcount; j++) { if (mutt_pattern_exec (Context->limit_pattern, M_MATCH_FULL_ADDRESS, Context, Context->hdrs[j])) { Context->hdrs[j]->virtual = Context->vcount; Context->v2r[Context->vcount] = j; Context->hdrs[j]->limited = 1; Context->vcount++; Context->vsize += THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset; } }#undef THIS_BODY } /* save the list of new messages */ if (oldcount && check != M_REOPENED && ((Sort & SORT_MASK) == SORT_THREADS)) { save_new = (HEADER **) safe_malloc (sizeof (HEADER *) * (Context->msgcount - oldcount)); for (j = oldcount; j < Context->msgcount; j++) save_new[j-oldcount] = Context->hdrs[j]; } /* if the mailbox was reopened, need to rethread from scratch */ mutt_sort_headers (Context, (check == M_REOPENED)); /* uncollapse threads with new mail */ if ((Sort & SORT_MASK) == SORT_THREADS) { if (check == M_REOPENED) { THREAD *h, *j; Context->collapsed = 0; for (h = Context->tree; h; h = h->next) { for (j = h; !j->message; j = j->child) ; mutt_uncollapse_thread (Context, j->message); } mutt_set_virtual (Context); } else if (oldcount) { for (j = 0; j < Context->msgcount - oldcount; j++) { int k; for (k = 0; k < Context->msgcount; k++) { HEADER *h = Context->hdrs[k]; if (h == save_new[j] && (!Context->pattern || h->limited)) mutt_uncollapse_thread (Context, h); } } FREE (&save_new); mutt_set_virtual (Context); } } menu->current = -1; if (oldcount) { /* restore the current message to the message it was pointing to */ for (j = 0; j < Context->vcount; j++) { if (Context->hdrs[Context->v2r[j]]->index == menu->oldcurrent) { menu->current = j; break; } } } if (menu->current < 0) menu->current = ci_first_message (); }static void resort_index (MUTTMENU *menu){ int i; HEADER *current = CURHDR; menu->current = -1; mutt_sort_headers (Context, 0); /* Restore the current message */ for (i = 0; i < Context->vcount; i++) { if (Context->hdrs[Context->v2r[i]] == current) { menu->current = i; break; } } if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0) menu->current = mutt_parent_message (Context, current); if (menu->current < 0) menu->current = ci_first_message (); menu->redraw = REDRAW_INDEX | REDRAW_STATUS;}struct mapping_t IndexHelp[] = { { N_("Quit"), OP_QUIT }, { N_("Del"), OP_DELETE }, { N_("Undel"), OP_UNDELETE }, { N_("Save"), OP_SAVE }, { N_("Mail"), OP_MAIL }, { N_("Reply"), OP_REPLY }, { N_("Group"), OP_GROUP_REPLY }, { N_("Help"), OP_HELP }, { NULL }};/* This function handles the message index window as well as commands returned * from the pager (MENU_PAGER). */int mutt_index_menu (void){ char buf[LONG_STRING], helpstr[SHORT_STRING]; int op = OP_NULL; int done = 0; /* controls when to exit the "event" loop */ int i = 0, j; int tag = 0; /* has the tag-prefix command been pressed? */ int newcount = -1; int oldcount = -1; int rc = -1; MUTTMENU *menu; char *cp; /* temporary variable. */ int index_hint; /* used to restore cursor position */ int do_buffy_notify = 1; int close = 0; /* did we OP_QUIT or OP_EXIT out of this menu? */ int attach_msg = option(OPTATTACHMSG); menu = mutt_new_menu (); menu->menu = MENU_MAIN; menu->offset = 1; menu->pagelen = LINES - 3; menu->make_entry = index_make_entry; menu->color = index_color; menu->current = ci_first_message (); menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN, IndexHelp); if (!attach_msg) mutt_buffy_check(1); /* force the buffy check after we enter the folder */ FOREVER { tag = 0; /* clear the tag-prefix */ menu->max = Context ? Context->vcount : 0; oldcount = Context ? Context->msgcount : 0; /* check if we need to resort the index because just about * any 'op' below could do mutt_enter_command(), either here or * from any new menu launched, and change $sort/$sort_aux */ if (option (OPTNEEDRESORT) && Context && Context->msgcount) resort_index (menu); if (option (OPTREDRAWTREE) && Context && Context->msgcount && (Sort & SORT_MASK) == SORT_THREADS) { mutt_draw_tree (Context); menu->redraw |= REDRAW_STATUS; unset_option (OPTREDRAWTREE); } if (Context && !attach_msg) { int check; /* check for new mail in the mailbox. If nonzero, then something has * changed about the file (either we got new mail or the file was * modified underneath us.) */#ifdef USE_IMAP imap_allow_reopen (Context);#endif index_hint = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ? CURHDR->index : 0; if ((check = mx_check_mailbox (Context, &index_hint, 0)) < 0) { if (!Context->path) { /* fatal error occurred */ FREE (&Context); menu->redraw = REDRAW_FULL; } set_option (OPTSEARCHINVALID); } else if (check == M_NEW_MAIL || check == M_REOPENED || check == M_FLAGS) { update_index (menu, Context, check, oldcount, index_hint); /* notify the user of new mail */ if (check == M_REOPENED) mutt_error _("Mailbox was externally modified. Flags may be wrong."); else if (check == M_NEW_MAIL) { mutt_message _("New mail in this mailbox."); if (option (OPTBEEPNEW)) beep (); } else if (check == M_FLAGS) mutt_message _("Mailbox was externally modified."); /* avoid the message being overwritten by buffy */ do_buffy_notify = 0; menu->redraw = REDRAW_FULL; menu->max = Context->vcount; set_option (OPTSEARCHINVALID); } }#ifdef USE_IMAP imap_keepalive (); imap_disallow_reopen (Context);#endif if (!attach_msg) { /* check for new mail in the incoming folders */ oldcount = newcount; if ((newcount = mutt_buffy_check (0)) != oldcount) menu->redraw |= REDRAW_STATUS; if (do_buffy_notify) { if (mutt_buffy_notify () && option (OPTBEEPNEW)) beep (); } else do_buffy_notify = 1; } if (op != -1) mutt_curs_set (0); if (menu->redraw & REDRAW_FULL) { menu_redraw_full (menu); mutt_show_error (); } if (menu->menu == MENU_MAIN) { if (Context && Context->hdrs && !(menu->current >= Context->vcount)) { menu_check_recenter (menu);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -