📄 thread.c
字号:
/* * Copyright (C) 1996-2002 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 "sort.h"#include <string.h>#include <ctype.h>#define VISIBLE(hdr, ctx) (hdr->virtual >= 0 || (hdr->collapsed && (!ctx->pattern || hdr->limited)))/* determine whether a is a descendant of b */static int is_descendant (THREAD *a, THREAD *b){ while (a) { if (a == b) return (1); a = a->parent; } return (0);}/* Determines whether to display a message's subject. */static int need_display_subject (CONTEXT *ctx, HEADER *hdr){ THREAD *tmp, *tree = hdr->thread; /* if the user disabled subject hiding, display it */ if (!option (OPTHIDETHREADSUBJECT)) return (1); /* if our subject is different from our parent's, display it */ if (hdr->subject_changed) return (1); /* if our subject is different from that of our closest previously displayed * sibling, display the subject */ for (tmp = tree->prev; tmp; tmp = tmp->prev) { hdr = tmp->message; if (hdr && VISIBLE (hdr, ctx)) { if (hdr->subject_changed) return (1); else break; } } /* if there is a parent-to-child subject change anywhere between us and our * closest displayed ancestor, display the subject */ for (tmp = tree->parent; tmp; tmp = tmp->parent) { hdr = tmp->message; if (hdr) { if (VISIBLE (hdr, ctx)) return (0); else if (hdr->subject_changed) return (1); } } /* if we have no visible parent or previous sibling, display the subject */ return (1);}static void linearize_tree (CONTEXT *ctx){ THREAD *tree = ctx->tree; HEADER **array = ctx->hdrs + (Sort & SORT_REVERSE ? ctx->msgcount - 1 : 0); while (tree) { while (!tree->message) tree = tree->child; *array = tree->message; array += Sort & SORT_REVERSE ? -1 : 1; if (tree->child) tree = tree->child; else { while (tree) { if (tree->next) { tree = tree->next; break; } else tree = tree->parent; } } }}/* this calculates whether a node is the root of a subtree that has visible * nodes, whether a node itself is visible, whether, if invisible, it has * depth anyway, and whether any of its later siblings are roots of visible * subtrees. while it's at it, it frees the old thread display, so we can * skip parts of the tree in mutt_draw_tree() if we've decided here that we * don't care about them any more. */static void calculate_visibility (CONTEXT *ctx, int *max_depth){ THREAD *tmp, *tree = ctx->tree; int hide_top_missing = option (OPTHIDETOPMISSING) && !option (OPTHIDEMISSING); int hide_top_limited = option (OPTHIDETOPLIMITED) && !option (OPTHIDELIMITED); int depth = 0; /* we walk each level backwards to make it easier to compute next_subtree_visible */ while (tree->next) tree = tree->next; *max_depth = 0; FOREVER { if (depth > *max_depth) *max_depth = depth; tree->subtree_visible = 0; if (tree->message) { FREE (&tree->message->tree); if (VISIBLE (tree->message, ctx)) { tree->deep = 1; tree->visible = 1; tree->message->display_subject = need_display_subject (ctx, tree->message); for (tmp = tree; tmp; tmp = tmp->parent) { if (tmp->subtree_visible) { tmp->deep = 1; tmp->subtree_visible = 2; break; } else tmp->subtree_visible = 1; } } else { tree->visible = 0; tree->deep = !option (OPTHIDELIMITED); } } else { tree->visible = 0; tree->deep = !option (OPTHIDEMISSING); } tree->next_subtree_visible = tree->next && (tree->next->next_subtree_visible || tree->next->subtree_visible); if (tree->child) { depth++; tree = tree->child; while (tree->next) tree = tree->next; } else if (tree->prev) tree = tree->prev; else { while (tree && !tree->prev) { depth--; tree = tree->parent; } if (!tree) break; else tree = tree->prev; } } /* now fix up for the OPTHIDETOP* options if necessary */ if (hide_top_limited || hide_top_missing) { tree = ctx->tree; FOREVER { if (!tree->visible && tree->deep && tree->subtree_visible < 2 && ((tree->message && hide_top_limited) || (!tree->message && hide_top_missing))) tree->deep = 0; if (!tree->deep && tree->child && tree->subtree_visible) tree = tree->child; else if (tree->next) tree = tree->next; else { while (tree && !tree->next) tree = tree->parent; if (!tree) break; else tree = tree->next; } } }}/* Since the graphics characters have a value >255, I have to resort to * using escape sequences to pass the information to print_enriched_string(). * These are the macros M_TREE_* defined in mutt.h. * * ncurses should automatically use the default ASCII characters instead of * graphics chars on terminals which don't support them (see the man page * for curs_addch). */void mutt_draw_tree (CONTEXT *ctx){ char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree; char corner = (Sort & SORT_REVERSE) ? M_TREE_ULCORNER : M_TREE_LLCORNER; char vtee = (Sort & SORT_REVERSE) ? M_TREE_BTEE : M_TREE_TTEE; int depth = 0, start_depth = 0, max_depth = 0, width = option (OPTNARROWTREE) ? 1 : 2; THREAD *nextdisp = NULL, *pseudo = NULL, *parent = NULL, *tree = ctx->tree; /* Do the visibility calculations and free the old thread chars. * From now on we can simply ignore invisible subtrees */ calculate_visibility (ctx, &max_depth); pfx = safe_malloc (width * max_depth + 2); arrow = safe_malloc (width * max_depth + 2); while (tree) { if (depth) { myarrow = arrow + (depth - start_depth - (start_depth ? 0 : 1)) * width; if (depth && start_depth == depth) myarrow[0] = nextdisp ? M_TREE_LTEE : corner; else if (parent->message && !option (OPTHIDELIMITED)) myarrow[0] = M_TREE_HIDDEN; else if (!parent->message && !option (OPTHIDEMISSING)) myarrow[0] = M_TREE_MISSING; else myarrow[0] = vtee; if (width == 2) myarrow[1] = pseudo ? M_TREE_STAR : (tree->duplicate_thread ? M_TREE_EQUALS : M_TREE_HLINE); if (tree->visible) { myarrow[width] = M_TREE_RARROW; myarrow[width + 1] = 0; new_tree = safe_malloc ((2 + depth * width)); if (start_depth > 1) { strncpy (new_tree, pfx, (start_depth - 1) * width); strfcpy (new_tree + (start_depth - 1) * width, arrow, (1 + depth - start_depth) * width + 2); } else strfcpy (new_tree, arrow, 2 + depth * width); tree->message->tree = new_tree; } } if (tree->child && depth) { mypfx = pfx + (depth - 1) * width; mypfx[0] = nextdisp ? M_TREE_VLINE : M_TREE_SPACE; if (width == 2) mypfx[1] = M_TREE_SPACE; } parent = tree; nextdisp = NULL; pseudo = NULL; do { if (tree->child && tree->subtree_visible) { if (tree->deep) depth++; if (tree->visible) start_depth = depth; tree = tree->child; /* we do this here because we need to make sure that the first child thread * of the old tree that we deal with is actually displayed if any are, * or we might set the parent variable wrong while going through it. */ while (!tree->subtree_visible && tree->next) tree = tree->next; } else { while (!tree->next && tree->parent) { if (tree == pseudo) pseudo = NULL; if (tree == nextdisp) nextdisp = NULL; if (tree->visible) start_depth = depth; tree = tree->parent; if (tree->deep) { if (start_depth == depth) start_depth--; depth--; } } if (tree == pseudo) pseudo = NULL; if (tree == nextdisp) nextdisp = NULL; if (tree->visible) start_depth = depth; tree = tree->next; if (!tree) break; } if (!pseudo && tree->fake_thread) pseudo = tree; if (!nextdisp && tree->next_subtree_visible) nextdisp = tree; } while (!tree->deep); } FREE (&pfx); FREE (&arrow);}/* since we may be trying to attach as a pseudo-thread a THREAD that * has no message, we have to make a list of all the subjects of its * most immediate existing descendants. we also note the earliest * date on any of the parents and put it in *dateptr. */static LIST *make_subject_list (THREAD *cur, time_t *dateptr){ THREAD *start = cur; ENVELOPE *env; time_t thisdate; LIST *curlist, *oldlist, *newlist, *subjects = NULL; int rc = 0; FOREVER { while (!cur->message) cur = cur->child; if (dateptr) { thisdate = option (OPTTHREADRECEIVED) ? cur->message->received : cur->message->date_sent; if (!*dateptr || thisdate < *dateptr) *dateptr = thisdate; } env = cur->message->env; if (env->real_subj && ((env->real_subj != env->subject) || (!option (OPTSORTRE)))) { for (curlist = subjects, oldlist = NULL; curlist; oldlist = curlist, curlist = curlist->next) { rc = mutt_strcmp (env->real_subj, curlist->data); if (rc >= 0) break; } if (!curlist || rc > 0) { newlist = safe_calloc (1, sizeof (LIST)); newlist->data = env->real_subj; if (oldlist) { newlist->next = oldlist->next; oldlist->next = newlist; } else { newlist->next = subjects; subjects = newlist; } } } while (!cur->next && cur != start) { cur = cur->parent; } if (cur == start) break; cur = cur->next; } return (subjects);}/* find the best possible match for a parent mesage based upon subject. * if there are multiple matches, the one which was sent the latest, but * before the current message, is used. */static THREAD *find_subject (CONTEXT *ctx, THREAD *cur){ struct hash_elem *ptr; THREAD *tmp, *last = NULL; int hash; LIST *subjects = NULL, *oldlist; time_t date = 0; subjects = make_subject_list (cur, &date); while (subjects) { hash = hash_string ((unsigned char *) subjects->data, ctx->subj_hash->nelem); for (ptr = ctx->subj_hash->table[hash]; ptr; ptr = ptr->next) { tmp = ((HEADER *) ptr->data)->thread; if (tmp != cur && /* don't match the same message */ !tmp->fake_thread && /* don't match pseudo threads */ tmp->message->subject_changed && /* only match interesting replies */ !is_descendant (tmp, cur) && /* don't match in the same thread */ (date >= (option (OPTTHREADRECEIVED) ? tmp->message->received : tmp->message->date_sent)) && (!last || (option (OPTTHREADRECEIVED) ? (last->message->received < tmp->message->received) : (last->message->date_sent < tmp->message->date_sent))) && tmp->message->env->real_subj && mutt_strcmp (subjects->data, tmp->message->env->real_subj) == 0) last = tmp; /* best match so far */ } oldlist = subjects; subjects = subjects->next; FREE (&oldlist); } return (last);}/* remove cur and its descendants from their current location. * also make sure ancestors of cur no longer are sorted by the * fact that cur is their descendant. */static void unlink_message (THREAD **old, THREAD *cur){ THREAD *tmp; if (cur->prev) cur->prev->next = cur->next; else *old = cur->next; if (cur->next) cur->next->prev = cur->prev; if (cur->sort_key) { for (tmp = cur->parent; tmp && tmp->sort_key == cur->sort_key; tmp = tmp->parent) tmp->sort_key = NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -