NeoMutt  2021-02-05-89-gabe350
Teaching an old dog new tricks
DOXYGEN
mutt_thread.c File Reference
#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "mutt_thread.h"
#include "mx.h"
#include "protos.h"
#include "sort.h"
+ Include dependency graph for mutt_thread.c:

Go to the source code of this file.

Data Structures

struct  ThreadsContext
 The "current" threading state. More...
 

Functions

static bool is_visible (struct Email *e)
 Is the message visible? More...
 
static bool need_display_subject (struct Email *e)
 Determines whether to display a message's subject. More...
 
static void linearize_tree (struct ThreadsContext *tctx)
 Flatten an email thread. More...
 
static void calculate_visibility (struct MuttThread *tree, int *max_depth)
 Are tree nodes visible. More...
 
struct ThreadsContextmutt_thread_ctx_init (struct Mailbox *m)
 Initialize a threading context. More...
 
void mutt_thread_ctx_free (struct ThreadsContext **tctx)
 Finalize a threading context. More...
 
void mutt_draw_tree (struct ThreadsContext *tctx)
 Draw a tree of threaded emails. More...
 
static void make_subject_list (struct ListHead *subjects, struct MuttThread *cur, time_t *dateptr)
 Create a sorted list of all subjects in a thread. More...
 
static struct MuttThreadfind_subject (struct Mailbox *m, struct MuttThread *cur)
 Find the best possible match for a parent based on subject. More...
 
static struct HashTablemake_subj_hash (struct Mailbox *m)
 Create a Hash Table for the email subjects. More...
 
static void pseudo_threads (struct ThreadsContext *tctx)
 Thread messages by subject. More...
 
void mutt_clear_threads (struct ThreadsContext *tctx)
 Clear the threading of message in a mailbox. More...
 
static int compare_threads (const void *a, const void *b)
 Sorting function for email threads. More...
 
void mutt_sort_subthreads (struct ThreadsContext *tctx, bool init)
 Sort the children of a thread. More...
 
static void check_subjects (struct Mailbox *m, bool init)
 Find out which emails' subjects differ from their parent's. More...
 
void mutt_sort_threads (struct ThreadsContext *tctx, bool init)
 Sort email threads. More...
 
int mutt_aside_thread (struct Email *e, bool forwards, bool subthreads)
 Find the next/previous (sub)thread. More...
 
int mutt_parent_message (struct Email *e, bool find_root)
 Find the parent of a message. More...
 
off_t mutt_set_vnum (struct Mailbox *m)
 Set the virtual index number of all the messages in a mailbox. More...
 
int mutt_traverse_thread (struct Email *e_cur, MuttThreadFlags flag)
 Recurse through an email thread, matching messages. More...
 
int mutt_messages_in_thread (struct Mailbox *m, struct Email *e, enum MessageInThread mit)
 Count the messages in a thread. More...
 
struct HashTablemutt_make_id_hash (struct Mailbox *m)
 Create a Hash Table for message-ids. More...
 
static bool link_threads (struct Email *parent, struct Email *child, struct Mailbox *m)
 Forcibly link messages together. More...
 
bool mutt_link_threads (struct Email *parent, struct EmailList *children, struct Mailbox *m)
 Forcibly link threads together. More...
 
void mutt_thread_collapse_collapsed (struct ThreadsContext *tctx)
 re-collapse threads marked as collapsed More...
 
void mutt_thread_collapse (struct ThreadsContext *tctx, bool collapse)
 toggle collapse More...
 
bool mutt_thread_can_collapse (struct Email *e)
 Check whether a thread can be collapsed. More...
 

Variables

bool C_CollapseFlagged
 Config: Prevent the collapse of threads with flagged emails. More...
 
bool C_CollapseUnread
 Config: Prevent the collapse of threads with unread emails. More...
 
bool C_DuplicateThreads
 Config: Highlight messages with duplicated message IDs. More...
 
bool C_HideLimited
 Config: Don't indicate hidden messages, in the thread tree. More...
 
bool C_HideMissing
 Config: Don't indicate missing messages, in the thread tree. More...
 
bool C_HideThreadSubject
 Config: Hide subjects that are similar to that of the parent message. More...
 
bool C_HideTopLimited
 Config: Don't indicate hidden top message, in the thread tree. More...
 
bool C_HideTopMissing
 Config: Don't indicate missing top message, in the thread tree. More...
 
bool C_NarrowTree
 Config: Draw a narrower thread tree in the index. More...
 
bool C_SortRe
 Config: Sort method for the sidebar. More...
 
bool C_StrictThreads
 Config: Thread messages using 'In-Reply-To' and 'References' headers. More...
 
bool C_ThreadReceived
 Config: Sort threaded messages by their received date. More...
 

Detailed Description

Create/manipulate threading in emails

Authors
  • Michael R. Elkins

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, see http://www.gnu.org/licenses/.

Definition in file mutt_thread.c.

Function Documentation

◆ is_visible()

static bool is_visible ( struct Email e)
static

Is the message visible?

Parameters
eEmail
Return values
trueIf the message is not hidden in some way

Definition at line 73 of file mutt_thread.c.

74 {
75  return e->vnum >= 0 || (e->collapsed && e->visible);
76 }
+ Here is the caller graph for this function:

◆ need_display_subject()

static bool need_display_subject ( struct Email e)
static

Determines whether to display a message's subject.

Parameters
eEmail
Return values
trueIf the subject should be displayed

Definition at line 83 of file mutt_thread.c.

84 {
85  struct MuttThread *tmp = NULL;
86  struct MuttThread *tree = e->thread;
87 
88  /* if the user disabled subject hiding, display it */
90  return true;
91 
92  /* if our subject is different from our parent's, display it */
93  if (e->subject_changed)
94  return true;
95 
96  /* if our subject is different from that of our closest previously displayed
97  * sibling, display the subject */
98  for (tmp = tree->prev; tmp; tmp = tmp->prev)
99  {
100  e = tmp->message;
101  if (e && is_visible(e))
102  {
103  if (e->subject_changed)
104  return true;
105  break;
106  }
107  }
108 
109  /* if there is a parent-to-child subject change anywhere between us and our
110  * closest displayed ancestor, display the subject */
111  for (tmp = tree->parent; tmp; tmp = tmp->parent)
112  {
113  e = tmp->message;
114  if (e)
115  {
116  if (is_visible(e))
117  return false;
118  if (e->subject_changed)
119  return true;
120  }
121  }
122 
123  /* if we have no visible parent or previous sibling, display the subject */
124  return true;
125 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ linearize_tree()

static void linearize_tree ( struct ThreadsContext tctx)
static

Flatten an email thread.

Parameters
tctxThreading context

Definition at line 131 of file mutt_thread.c.

132 {
133  if (!tctx || !tctx->mailbox)
134  return;
135 
136  struct Mailbox *m = tctx->mailbox;
137 
138  struct MuttThread *tree = tctx->tree;
139  struct Email **array = m->emails + ((C_Sort & SORT_REVERSE) ? m->msg_count - 1 : 0);
140 
141  while (tree)
142  {
143  while (!tree->message)
144  tree = tree->child;
145 
146  *array = tree->message;
147  array += (C_Sort & SORT_REVERSE) ? -1 : 1;
148 
149  if (tree->child)
150  tree = tree->child;
151  else
152  {
153  while (tree)
154  {
155  if (tree->next)
156  {
157  tree = tree->next;
158  break;
159  }
160  else
161  tree = tree->parent;
162  }
163  }
164  }
165 }
+ Here is the caller graph for this function:

◆ calculate_visibility()

static void calculate_visibility ( struct MuttThread tree,
int *  max_depth 
)
static

Are tree nodes visible.

Parameters
treeThreads tree
max_depthMaximum depth to check

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.

Definition at line 179 of file mutt_thread.c.

180 {
181  if (!tree)
182  return;
183 
184  struct MuttThread *tmp = NULL;
185  struct MuttThread *orig_tree = tree;
186  int hide_top_missing = C_HideTopMissing && !C_HideMissing;
187  int hide_top_limited = C_HideTopLimited && !C_HideLimited;
188  int depth = 0;
189 
190  /* we walk each level backwards to make it easier to compute next_subtree_visible */
191  while (tree->next)
192  tree = tree->next;
193  *max_depth = 0;
194 
195  while (true)
196  {
197  if (depth > *max_depth)
198  *max_depth = depth;
199 
200  tree->subtree_visible = 0;
201  if (tree->message)
202  {
203  FREE(&tree->message->tree);
204  if (is_visible(tree->message))
205  {
206  tree->deep = true;
207  tree->visible = true;
209  for (tmp = tree; tmp; tmp = tmp->parent)
210  {
211  if (tmp->subtree_visible)
212  {
213  tmp->deep = true;
214  tmp->subtree_visible = 2;
215  break;
216  }
217  else
218  tmp->subtree_visible = 1;
219  }
220  }
221  else
222  {
223  tree->visible = false;
224  tree->deep = !C_HideLimited;
225  }
226  }
227  else
228  {
229  tree->visible = false;
230  tree->deep = !C_HideMissing;
231  }
232  tree->next_subtree_visible =
233  tree->next && (tree->next->next_subtree_visible || tree->next->subtree_visible);
234  if (tree->child)
235  {
236  depth++;
237  tree = tree->child;
238  while (tree->next)
239  tree = tree->next;
240  }
241  else if (tree->prev)
242  tree = tree->prev;
243  else
244  {
245  while (tree && !tree->prev)
246  {
247  depth--;
248  tree = tree->parent;
249  }
250  if (!tree)
251  break;
252  tree = tree->prev;
253  }
254  }
255 
256  /* now fix up for the OPTHIDETOP* options if necessary */
257  if (hide_top_limited || hide_top_missing)
258  {
259  tree = orig_tree;
260  while (true)
261  {
262  if (!tree->visible && tree->deep && (tree->subtree_visible < 2) &&
263  ((tree->message && hide_top_limited) || (!tree->message && hide_top_missing)))
264  {
265  tree->deep = false;
266  }
267  if (!tree->deep && tree->child && tree->subtree_visible)
268  tree = tree->child;
269  else if (tree->next)
270  tree = tree->next;
271  else
272  {
273  while (tree && !tree->next)
274  tree = tree->parent;
275  if (!tree)
276  break;
277  tree = tree->next;
278  }
279  }
280  }
281 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_thread_ctx_init()

struct ThreadsContext* mutt_thread_ctx_init ( struct Mailbox m)

Initialize a threading context.

Parameters
mCurrent mailbox
Return values
tctxThreading context

Definition at line 288 of file mutt_thread.c.

289 {
290  struct ThreadsContext *tctx = mutt_mem_calloc(1, sizeof(struct ThreadsContext));
291  tctx->mailbox = m;
292  tctx->tree = NULL;
293  tctx->hash = NULL;
294  return tctx;
295 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_thread_ctx_free()

void mutt_thread_ctx_free ( struct ThreadsContext **  tctx)

Finalize a threading context.

Parameters
tctxThreading context to finalize

Definition at line 301 of file mutt_thread.c.

302 {
303  (*tctx)->mailbox = NULL;
304  mutt_hash_free(&(*tctx)->hash);
305  FREE(tctx);
306 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_draw_tree()

void mutt_draw_tree ( struct ThreadsContext tctx)

Draw a tree of threaded emails.

Parameters
tctxThreading context

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 MUTT_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).

Definition at line 320 of file mutt_thread.c.

321 {
322  char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree = NULL;
325  int depth = 0, start_depth = 0, max_depth = 0, width = C_NarrowTree ? 1 : 2;
326  struct MuttThread *nextdisp = NULL, *pseudo = NULL, *parent = NULL;
327 
328  struct MuttThread *tree = tctx->tree;
329 
330  /* Do the visibility calculations and free the old thread chars.
331  * From now on we can simply ignore invisible subtrees */
332  calculate_visibility(tree, &max_depth);
333  pfx = mutt_mem_malloc((width * max_depth) + 2);
334  arrow = mutt_mem_malloc((width * max_depth) + 2);
335  while (tree)
336  {
337  if (depth != 0)
338  {
339  myarrow = arrow + (depth - start_depth - ((start_depth != 0) ? 0 : 1)) * width;
340  if (start_depth == depth)
341  myarrow[0] = nextdisp ? MUTT_TREE_LTEE : corner;
342  else if (parent->message && !C_HideLimited)
343  myarrow[0] = MUTT_TREE_HIDDEN;
344  else if (!parent->message && !C_HideMissing)
345  myarrow[0] = MUTT_TREE_MISSING;
346  else
347  myarrow[0] = vtee;
348  if (width == 2)
349  {
350  myarrow[1] = pseudo ? MUTT_TREE_STAR :
352  }
353  if (tree->visible)
354  {
355  myarrow[width] = MUTT_TREE_RARROW;
356  myarrow[width + 1] = 0;
357  new_tree = mutt_mem_malloc(((size_t) depth * width) + 2);
358  if (start_depth > 1)
359  {
360  strncpy(new_tree, pfx, (size_t) width * (start_depth - 1));
361  mutt_str_copy(new_tree + (start_depth - 1) * width, arrow,
362  (1 + depth - start_depth) * width + 2);
363  }
364  else
365  mutt_str_copy(new_tree, arrow, ((size_t) depth * width) + 2);
366  tree->message->tree = new_tree;
367  }
368  }
369  if (tree->child && (depth != 0))
370  {
371  mypfx = pfx + (depth - 1) * width;
372  mypfx[0] = nextdisp ? MUTT_TREE_VLINE : MUTT_TREE_SPACE;
373  if (width == 2)
374  mypfx[1] = MUTT_TREE_SPACE;
375  }
376  parent = tree;
377  nextdisp = NULL;
378  pseudo = NULL;
379  do
380  {
381  if (tree->child && tree->subtree_visible)
382  {
383  if (tree->deep)
384  depth++;
385  if (tree->visible)
386  start_depth = depth;
387  tree = tree->child;
388 
389  /* we do this here because we need to make sure that the first child thread
390  * of the old tree that we deal with is actually displayed if any are,
391  * or we might set the parent variable wrong while going through it. */
392  while (!tree->subtree_visible && tree->next)
393  tree = tree->next;
394  }
395  else
396  {
397  while (!tree->next && tree->parent)
398  {
399  if (tree == pseudo)
400  pseudo = NULL;
401  if (tree == nextdisp)
402  nextdisp = NULL;
403  if (tree->visible)
404  start_depth = depth;
405  tree = tree->parent;
406  if (tree->deep)
407  {
408  if (start_depth == depth)
409  start_depth--;
410  depth--;
411  }
412  }
413  if (tree == pseudo)
414  pseudo = NULL;
415  if (tree == nextdisp)
416  nextdisp = NULL;
417  if (tree->visible)
418  start_depth = depth;
419  tree = tree->next;
420  if (!tree)
421  break;
422  }
423  if (!pseudo && tree->fake_thread)
424  pseudo = tree;
425  if (!nextdisp && tree->next_subtree_visible)
426  nextdisp = tree;
427  } while (!tree->deep);
428  }
429 
430  FREE(&pfx);
431  FREE(&arrow);
432 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ make_subject_list()

static void make_subject_list ( struct ListHead *  subjects,
struct MuttThread cur,
time_t *  dateptr 
)
static

Create a sorted list of all subjects in a thread.

Parameters
[out]subjectsString List of subjects
[in]curEmail Thread
[out]dateptrEarliest date found in thread

Since we may be trying to attach as a pseudo-thread a MuttThread that has no message, we have to make a list of all the subjects of its most immediate existing descendants.

Definition at line 444 of file mutt_thread.c.

445 {
446  struct MuttThread *start = cur;
447  struct Envelope *env = NULL;
448  time_t thisdate;
449  int rc = 0;
450 
451  while (true)
452  {
453  while (!cur->message)
454  cur = cur->child;
455 
456  if (dateptr)
457  {
458  thisdate = C_ThreadReceived ? cur->message->received : cur->message->date_sent;
459  if ((*dateptr == 0) || (thisdate < *dateptr))
460  *dateptr = thisdate;
461  }
462 
463  env = cur->message->env;
464  if (env->real_subj && ((env->real_subj != env->subject) || !C_SortRe))
465  {
466  struct ListNode *np = NULL;
467  STAILQ_FOREACH(np, subjects, entries)
468  {
469  rc = mutt_str_cmp(env->real_subj, np->data);
470  if (rc >= 0)
471  break;
472  }
473  if (!np)
474  mutt_list_insert_head(subjects, env->real_subj);
475  else if (rc > 0)
476  mutt_list_insert_after(subjects, np, env->real_subj);
477  }
478 
479  while (!cur->next && (cur != start))
480  {
481  cur = cur->parent;
482  }
483  if (cur == start)
484  break;
485  cur = cur->next;
486  }
487 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ find_subject()

static struct MuttThread* find_subject ( struct Mailbox m,
struct MuttThread cur 
)
static

Find the best possible match for a parent based on subject.

Parameters
mMailbox
curEmail to match
Return values
ptrBest match for a parent

If there are multiple matches, the one which was sent the latest, but before the current message, is used.

Definition at line 498 of file mutt_thread.c.

499 {
500  if (!m)
501  return NULL;
502 
503  struct HashElem *ptr = NULL;
504  struct MuttThread *tmp = NULL, *last = NULL;
505  struct ListHead subjects = STAILQ_HEAD_INITIALIZER(subjects);
506  time_t date = 0;
507 
508  make_subject_list(&subjects, cur, &date);
509 
510  struct ListNode *np = NULL;
511  STAILQ_FOREACH(np, &subjects, entries)
512  {
513  for (ptr = mutt_hash_find_bucket(m->subj_hash, np->data); ptr; ptr = ptr->next)
514  {
515  tmp = ((struct Email *) ptr->data)->thread;
516  if ((tmp != cur) && /* don't match the same message */
517  !tmp->fake_thread && /* don't match pseudo threads */
518  tmp->message->subject_changed && /* only match interesting replies */
519  !is_descendant(tmp, cur) && /* don't match in the same thread */
520  (date >= (C_ThreadReceived ? tmp->message->received : tmp->message->date_sent)) &&
521  (!last || (C_ThreadReceived ?
522  (last->message->received < tmp->message->received) :
523  (last->message->date_sent < tmp->message->date_sent))) &&
524  tmp->message->env->real_subj &&
525  mutt_str_equal(np->data, tmp->message->env->real_subj))
526  {
527  last = tmp; /* best match so far */
528  }
529  }
530  }
531 
532  mutt_list_clear(&subjects);
533  return last;
534 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ make_subj_hash()

static struct HashTable* make_subj_hash ( struct Mailbox m)
static

Create a Hash Table for the email subjects.

Parameters
mMailbox
Return values
ptrNewly allocated Hash Table

Definition at line 541 of file mutt_thread.c.

542 {
543  if (!m)
544  return NULL;
545 
546  struct HashTable *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_ALLOW_DUPS);
547 
548  for (int i = 0; i < m->msg_count; i++)
549  {
550  struct Email *e = m->emails[i];
551  if (!e || !e->env)
552  continue;
553  if (e->env->real_subj)
554  mutt_hash_insert(hash, e->env->real_subj, e);
555  }
556 
557  return hash;
558 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pseudo_threads()

static void pseudo_threads ( struct ThreadsContext tctx)
static

Thread messages by subject.

Parameters
tctxThreading context

Thread by subject things that didn't get threaded by message-id

Definition at line 566 of file mutt_thread.c.

567 {
568  if (!tctx || !tctx->mailbox)
569  return;
570 
571  struct Mailbox *m = tctx->mailbox;
572 
573  struct MuttThread *tree = tctx->tree;
574  struct MuttThread *top = tree;
575  struct MuttThread *tmp = NULL, *cur = NULL, *parent = NULL, *curchild = NULL,
576  *nextchild = NULL;
577 
578  if (!m->subj_hash)
579  m->subj_hash = make_subj_hash(m);
580 
581  while (tree)
582  {
583  cur = tree;
584  tree = tree->next;
585  parent = find_subject(m, cur);
586  if (parent)
587  {
588  cur->fake_thread = true;
589  unlink_message(&top, cur);
591  parent->sort_children = true;
592  tmp = cur;
593  while (true)
594  {
595  while (!tmp->message)
596  tmp = tmp->child;
597 
598  /* if the message we're attaching has pseudo-children, they
599  * need to be attached to its parent, so move them up a level.
600  * but only do this if they have the same real subject as the
601  * parent, since otherwise they rightly belong to the message
602  * we're attaching. */
603  if ((tmp == cur) || mutt_str_equal(tmp->message->env->real_subj,
605  {
606  tmp->message->subject_changed = false;
607 
608  for (curchild = tmp->child; curchild;)
609  {
610  nextchild = curchild->next;
611  if (curchild->fake_thread)
612  {
613  unlink_message(&tmp->child, curchild);
614  insert_message(&parent->child, parent, curchild);
615  }
616  curchild = nextchild;
617  }
618  }
619 
620  while (!tmp->next && (tmp != cur))
621  {
622  tmp = tmp->parent;
623  }
624  if (tmp == cur)
625  break;
626  tmp = tmp->next;
627  }
628  }
629  }
630  tctx->tree = top;
631 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_clear_threads()

void mutt_clear_threads ( struct ThreadsContext tctx)

Clear the threading of message in a mailbox.

Parameters
tctxThreading context

Definition at line 637 of file mutt_thread.c.

638 {
639  if (!tctx || !tctx->mailbox || !tctx->mailbox->emails || !tctx->tree)
640  return;
641 
642  for (int i = 0; i < tctx->mailbox->msg_count; i++)
643  {
644  struct Email *e = tctx->mailbox->emails[i];
645  if (!e)
646  break;
647 
648  /* mailbox may have been only partially read */
649  e->thread = NULL;
650  e->threaded = false;
651  }
652  tctx->tree = NULL;
653  mutt_hash_free(&tctx->hash);
654 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ compare_threads()

static int compare_threads ( const void *  a,
const void *  b 
)
static

Sorting function for email threads.

Parameters
aFirst thread to compare
bSecond thread to compare
Return values
-1a precedes b
0a and b are identical
1b precedes a

Definition at line 664 of file mutt_thread.c.

665 {
666  static sort_t sort_func = NULL;
667 
668  if (a && b)
669  {
670  return (*sort_func)(&(*((struct MuttThread const *const *) a))->sort_key,
671  &(*((struct MuttThread const *const *) b))->sort_key);
672  }
673  /* a hack to let us reset sort_func even though we can't
674  * have extra arguments because of qsort */
675  else
676  {
677  sort_func = mutt_get_sort_func(C_Sort & SORT_MASK);
678  return sort_func ? 1 : 0;
679  }
680 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_sort_subthreads()

void mutt_sort_subthreads ( struct ThreadsContext tctx,
bool  init 
)

Sort the children of a thread.

Parameters
tctxThreading context
initIf true, rebuild the thread

Definition at line 687 of file mutt_thread.c.

688 {
689  struct MuttThread *thread = tctx->tree;
690  if (!thread)
691  return;
692 
693  struct MuttThread **array = NULL, *sort_key = NULL, *top = NULL, *tmp = NULL;
694  struct Email *oldsort_key = NULL;
695  int i, array_size, sort_top = 0;
696 
697  /* we put things into the array backwards to save some cycles,
698  * but we want to have to move less stuff around if we're
699  * resorting, so we sort backwards and then put them back
700  * in reverse order so they're forwards */
701  C_Sort ^= SORT_REVERSE;
702  if (compare_threads(NULL, NULL) == 0)
703  return;
704 
705  top = thread;
706 
707  array_size = 256;
708  array = mutt_mem_calloc(array_size, sizeof(struct MuttThread *));
709  while (true)
710  {
711  if (init || !thread->sort_key)
712  {
713  thread->sort_key = NULL;
714 
715  if (thread->parent)
716  thread->parent->sort_children = true;
717  else
718  sort_top = 1;
719  }
720 
721  if (thread->child)
722  {
723  thread = thread->child;
724  continue;
725  }
726  else
727  {
728  /* if it has no children, it must be real. sort it on its own merits */
730 
731  if (thread->next)
732  {
733  thread = thread->next;
734  continue;
735  }
736  }
737 
738  while (!thread->next)
739  {
740  /* if it has siblings and needs to be sorted, sort it... */
741  if (thread->prev && (thread->parent ? thread->parent->sort_children : sort_top))
742  {
743  /* put them into the array */
744  for (i = 0; thread; i++, thread = thread->prev)
745  {
746  if (i >= array_size)
747  mutt_mem_realloc(&array, (array_size *= 2) * sizeof(struct MuttThread *));
748 
749  array[i] = thread;
750  }
751 
752  qsort((void *) array, i, sizeof(struct MuttThread *), *compare_threads);
753 
754  /* attach them back together. make thread the last sibling. */
755  thread = array[0];
756  thread->next = NULL;
757  array[i - 1]->prev = NULL;
758 
759  if (thread->parent)
760  thread->parent->child = array[i - 1];
761  else
762  top = array[i - 1];
763 
764  while (--i)
765  {
766  array[i - 1]->prev = array[i];
767  array[i]->next = array[i - 1];
768  }
769  }
770 
771  if (thread->parent)
772  {
773  tmp = thread;
774  thread = thread->parent;
775 
777  {
778  /* make sort_key the first or last sibling, as appropriate */
779  sort_key = ((!(C_Sort & SORT_LAST)) ^ (!(C_Sort & SORT_REVERSE))) ?
780  thread->child :
781  tmp;
782 
783  /* we just sorted its children */
784  thread->sort_children = false;
785 
786  oldsort_key = thread->sort_key;
788 
789  if (C_Sort & SORT_LAST)
790  {
791  if (!thread->sort_key ||
792  ((((C_Sort & SORT_REVERSE) ? 1 : -1) *
793  compare_threads((void *) &thread, (void *) &sort_key)) > 0))
794  {
795  thread->sort_key = sort_key->sort_key;
796  }
797  }
798  else if (!thread->sort_key)
799  thread->sort_key = sort_key->sort_key;
800 
801  /* if its sort_key has changed, we need to resort it and siblings */
802  if (oldsort_key != thread->sort_key)
803  {
804  if (thread->parent)
805  thread->parent->sort_children = true;
806  else
807  sort_top = 1;
808  }
809  }
810  }
811  else
812  {
813  C_Sort ^= SORT_REVERSE;
814  FREE(&array);
815  tctx->tree = top;
816  return;
817  }
818  }
819 
820  thread = thread->next;
821  }
822 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_subjects()

static void check_subjects ( struct Mailbox m,
bool  init 
)
static

Find out which emails' subjects differ from their parent's.

Parameters
mMailbox
initIf true, rebuild the thread

Definition at line 829 of file mutt_thread.c.

830 {
831  if (!m)
832  return;
833 
834  for (int i = 0; i < m->msg_count; i++)
835  {
836  struct Email *e = m->emails[i];
837  if (!e || !e->thread)
838  continue;
839 
840  if (e->thread->check_subject)
841  e->thread->check_subject = false;
842  else if (!init)
843  continue;
844 
845  /* figure out which messages have subjects different than their parents' */
846  struct MuttThread *tmp = e->thread->parent;
847  while (tmp && !tmp->message)
848  {
849  tmp = tmp->parent;
850  }
851 
852  if (!tmp)
853  e->subject_changed = true;
854  else if (e->env->real_subj && tmp->message->env->real_subj)
855  {
857  }
858  else
859  {
860  e->subject_changed = (e->env->real_subj || tmp->message->env->real_subj);
861  }
862  }
863 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_sort_threads()

void mutt_sort_threads ( struct ThreadsContext tctx,
bool  init 
)

Sort email threads.

Parameters
tctxThreading context
initIf true, rebuild the thread

Definition at line 870 of file mutt_thread.c.

871 {
872  if (!tctx || !tctx->mailbox)
873  return;
874 
875  struct Mailbox *m = tctx->mailbox;
876 
877  struct Email *e = NULL;
878  int i, oldsort, using_refs = 0;
879  struct MuttThread *thread = NULL, *tnew = NULL, *tmp = NULL;
880  struct MuttThread top = { 0 };
881  struct ListNode *ref = NULL;
882 
883  /* Set `$sort` to the secondary method to support the set sort_aux=reverse-*
884  * settings. The sorting functions just look at the value of SORT_REVERSE */
885  oldsort = C_Sort;
886  C_Sort = C_SortAux;
887 
888  if (!tctx->hash)
889  init = true;
890 
891  if (init)
892  {
895  }
896 
897  /* we want a quick way to see if things are actually attached to the top of the
898  * thread tree or if they're just dangling, so we attach everything to a top
899  * node temporarily */
900  top.parent = NULL;
901  top.next = NULL;
902  top.prev = NULL;
903 
904  top.child = tctx->tree;
905  for (thread = tctx->tree; thread; thread = thread->next)
906  thread->parent = &top;
907 
908  /* put each new message together with the matching messageless MuttThread if it
909  * exists. otherwise, if there is a MuttThread that already has a message, thread
910  * new message as an identical child. if we didn't attach the message to a
911  * MuttThread, make a new one for it. */
912  for (i = 0; i < m->msg_count; i++)
913  {
914  e = m->emails[i];
915  if (!e)
916  continue;
917 
918  if (!e->thread)
919  {
920  if ((!init || C_DuplicateThreads) && e->env->message_id)
921  thread = mutt_hash_find(tctx->hash, e->env->message_id);
922  else
923  thread = NULL;
924 
925  if (thread && !thread->message)
926  {
927  /* this is a message which was missing before */
928  thread->message = e;
929  e->thread = thread;
930  thread->check_subject = true;
931 
932  /* mark descendants as needing subject_changed checked */
933  for (tmp = (thread->child ? thread->child : thread); tmp != thread;)
934  {
935  while (!tmp->message)
936  tmp = tmp->child;
937  tmp->check_subject = true;
938  while (!tmp->next && (tmp != thread))
939  tmp = tmp->parent;
940  if (tmp != thread)
941  tmp = tmp->next;
942  }
943 
944  if (thread->parent)
945  {
946  /* remove threading info above it based on its children, which we'll
947  * recalculate based on its headers. make sure not to leave
948  * dangling missing messages. note that we haven't kept track
949  * of what info came from its children and what from its siblings'
950  * children, so we just remove the stuff that's definitely from it */
951  do
952  {
953  tmp = thread->parent;
954  unlink_message(&tmp->child, thread);
955  thread->parent = NULL;
956  thread->sort_key = NULL;
957  thread->fake_thread = false;
958  thread = tmp;
959  } while (thread != &top && !thread->child && !thread->message);
960  }
961  }
962  else
963  {
964  tnew = (C_DuplicateThreads ? thread : NULL);
965 
966  thread = mutt_mem_calloc(1, sizeof(struct MuttThread));
967  thread->message = e;
968  thread->check_subject = true;
969  e->thread = thread;
970  mutt_hash_insert(tctx->hash, e->env->message_id ? e->env->message_id : "", thread);
971 
972  if (tnew)
973  {
974  if (tnew->duplicate_thread)
975  tnew = tnew->parent;
976 
977  thread = e->thread;
978 
979  insert_message(&tnew->child, tnew, thread);
980  thread->duplicate_thread = true;
981  thread->message->threaded = true;
982  }
983  }
984  }
985  else
986  {
987  /* unlink pseudo-threads because they might be children of newly
988  * arrived messages */
989  thread = e->thread;
990  for (tnew = thread->child; tnew;)
991  {
992  tmp = tnew->next;
993  if (tnew->fake_thread)
994  {
995  unlink_message(&thread->child, tnew);
996  insert_message(&top.child, &top, tnew);
997  tnew->fake_thread = false;
998  }
999  tnew = tmp;
1000  }
1001  }
1002  }
1003 
1004  /* thread by references */
1005  for (i = 0; i < m->msg_count; i++)
1006  {
1007  e = m->emails[i];
1008  if (!e)
1009  break;
1010 
1011  if (e->threaded)
1012  continue;
1013  e->threaded = true;
1014 
1015  thread = e->thread;
1016  if (!thread)
1017  continue;
1018  using_refs = 0;
1019 
1020  while (true)
1021  {
1022  if (using_refs == 0)
1023  {
1024  /* look at the beginning of in-reply-to: */
1025  ref = STAILQ_FIRST(&e->env->in_reply_to);
1026  if (ref)
1027  using_refs = 1;
1028  else
1029  {
1030  ref = STAILQ_FIRST(&e->env->references);
1031  using_refs = 2;
1032  }
1033  }
1034  else if (using_refs == 1)
1035  {
1036  /* if there's no references header, use all the in-reply-to:
1037  * data that we have. otherwise, use the first reference
1038  * if it's different than the first in-reply-to, otherwise use
1039  * the second reference (since at least eudora puts the most
1040  * recent reference in in-reply-to and the rest in references) */
1041  if (STAILQ_EMPTY(&e->env->references))
1042  ref = STAILQ_NEXT(ref, entries);
1043  else
1044  {
1045  if (!mutt_str_equal(ref->data, STAILQ_FIRST(&e->env->references)->data))
1046  ref = STAILQ_FIRST(&e->env->references);
1047  else
1048  ref = STAILQ_NEXT(STAILQ_FIRST(&e->env->references), entries);
1049 
1050  using_refs = 2;
1051  }
1052  }
1053  else
1054  ref = STAILQ_NEXT(ref, entries); /* go on with references */
1055 
1056  if (!ref)
1057  break;
1058 
1059  tnew = mutt_hash_find(tctx->hash, ref->data);
1060  if (tnew)
1061  {
1062  if (tnew->duplicate_thread)
1063  tnew = tnew->parent;
1064  if (is_descendant(tnew, thread)) /* no loops! */
1065  continue;
1066  }
1067  else
1068  {
1069  tnew = mutt_mem_calloc(1, sizeof(struct MuttThread));
1070  mutt_hash_insert(tctx->hash, ref->data, tnew);
1071  }
1072 
1073  if (thread->parent)
1074  unlink_message(&top.child, thread);
1075  insert_message(&tnew->child, tnew, thread);
1076  thread = tnew;
1077  if (thread->message || (thread->parent && (thread->parent != &top)))
1078  break;
1079  }
1080 
1081  if (!thread->parent)
1082  insert_message(&top.child, &top, thread);
1083  }
1084 
1085  /* detach everything from the temporary top node */
1086  for (thread = top.child; thread; thread = thread->next)
1087  {
1088  thread->parent = NULL;
1089  }
1090  tctx->tree = top.child;
1091 
1092  check_subjects(tctx->mailbox, init);
1093 
1094  if (!C_StrictThreads)
1095  pseudo_threads(tctx);
1096 
1097  if (tctx->tree)
1098  {
1099  mutt_sort_subthreads(tctx, init);
1100 
1101  /* restore the oldsort order. */
1102  C_Sort = oldsort;
1103 
1104  /* Put the list into an array. */
1105  linearize_tree(tctx);
1106 
1107  /* Draw the thread tree. */
1108  mutt_draw_tree(tctx);
1109  }
1110 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_aside_thread()

int mutt_aside_thread ( struct Email e,
bool  forwards,
bool  subthreads 
)

Find the next/previous (sub)thread.

Parameters
eSearch from this Email
forwardsDirection to search: 'true' forwards, 'false' backwards
subthreadsSearch subthreads: 'true' subthread, 'false' not
Return values
numIndex into the virtual email table

Definition at line 1119 of file mutt_thread.c.

1120 {
1121  struct MuttThread *cur = NULL;
1122  struct Email *e_tmp = NULL;
1123 
1124  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1125  {
1126  mutt_error(_("Threading is not enabled"));
1127  return e->vnum;
1128  }
1129 
1130  cur = e->thread;
1131 
1132  if (subthreads)
1133  {
1134  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1135  {
1136  while (!cur->next && cur->parent)
1137  cur = cur->parent;
1138  }
1139  else
1140  {
1141  while (!cur->prev && cur->parent)
1142  cur = cur->parent;
1143  }
1144  }
1145  else
1146  {
1147  while (cur->parent)
1148  cur = cur->parent;
1149  }
1150 
1151  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1152  {
1153  do
1154  {
1155  cur = cur->next;
1156  if (!cur)
1157  return -1;
1158  e_tmp = find_virtual(cur, 0);
1159  } while (!e_tmp);
1160  }
1161  else
1162  {
1163  do
1164  {
1165  cur = cur->prev;
1166  if (!cur)
1167  return -1;
1168  e_tmp = find_virtual(cur, 1);
1169  } while (!e_tmp);
1170  }
1171 
1172  return e_tmp->vnum;
1173 }
+ Here is the call graph for this function:

◆ mutt_parent_message()

int mutt_parent_message ( struct Email e,
bool  find_root 
)

Find the parent of a message.

Parameters
eCurrent Email
find_rootIf true, find the root message
Return values
>=0Virtual index number of parent/root message
-1Error

Definition at line 1182 of file mutt_thread.c.

1183 {
1184  if (!e)
1185  return -1;
1186 
1187  struct MuttThread *thread = NULL;
1188  struct Email *e_parent = NULL;
1189 
1190  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1191  {
1192  mutt_error(_("Threading is not enabled"));
1193  return e->vnum;
1194  }
1195 
1196  /* Root may be the current message */
1197  if (find_root)
1198  e_parent = e;
1199 
1200  for (thread = e->thread->parent; thread; thread = thread->parent)
1201  {
1202  e = thread->message;
1203  if (e)
1204  {
1205  e_parent = e;
1206  if (!find_root)
1207  break;
1208  }
1209  }
1210 
1211  if (!e_parent)
1212  {
1213  mutt_error(_("Parent message is not available"));
1214  return -1;
1215  }
1216  if (!is_visible(e_parent))
1217  {
1218  if (find_root)
1219  mutt_error(_("Root message is not visible in this limited view"));
1220  else
1221  mutt_error(_("Parent message is not visible in this limited view"));
1222  return -1;
1223  }
1224  return e_parent->vnum;
1225 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_set_vnum()

off_t mutt_set_vnum ( struct Mailbox m)

Set the virtual index number of all the messages in a mailbox.

Parameters
mMailbox
Return values
mumSize in bytes of all messages shown

Definition at line 1232 of file mutt_thread.c.

1233 {
1234  if (!m)
1235  return 0;
1236 
1237  off_t vsize = 0;
1238  const int padding = mx_msg_padding_size(m);
1239 
1240  m->vcount = 0;
1241 
1242  for (int i = 0; i < m->msg_count; i++)
1243  {
1244  struct Email *e = m->emails[i];
1245  if (!e)
1246  break;
1247 
1248  if (e->vnum >= 0)
1249  {
1250  e->vnum = m->vcount;
1251  m->v2r[m->vcount] = i;
1252  m->vcount++;
1253  vsize += e->body->length + e->body->offset - e->body->hdr_offset + padding;
1254  }
1255  }
1256 
1257  return vsize;
1258 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_traverse_thread()

int mutt_traverse_thread ( struct Email e_cur,
MuttThreadFlags  flag 
)

Recurse through an email thread, matching messages.

Parameters
e_curCurrent Email
flagFlag to set, see MuttThreadFlags
Return values
numNumber of matches

Definition at line 1266 of file mutt_thread.c.

1267 {
1268  struct MuttThread *thread = NULL, *top = NULL;
1269  struct Email *e_root = NULL;
1270  int final, reverse = (C_Sort & SORT_REVERSE), minmsgno;
1271  int num_hidden = 0, new_mail = 0, old_mail = 0;
1272  bool flagged = false;
1273  int min_unread_msgno = INT_MAX, min_unread = e_cur->vnum;
1274 
1275  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1276  {
1277  mutt_error(_("Threading is not enabled"));
1278  return e_cur->vnum;
1279  }
1280 
1281  if (!e_cur->thread)
1282  {
1283  return e_cur->vnum;
1284  }
1285 
1286  final = e_cur->vnum;
1287  thread = e_cur->thread;
1288  while (thread->parent)
1289  thread = thread->parent;
1290  top = thread;
1291  while (!thread->message)
1292  thread = thread->child;
1293  e_cur = thread->message;
1294  minmsgno = e_cur->msgno;
1295 
1296  if (!e_cur->read && e_cur->visible)
1297  {
1298  if (e_cur->old)
1299  old_mail = 2;
1300  else
1301  new_mail = 1;
1302  if (e_cur->msgno < min_unread_msgno)
1303  {
1304  min_unread = e_cur->vnum;
1305  min_unread_msgno = e_cur->msgno;
1306  }
1307  }
1308 
1309  if (e_cur->flagged && e_cur->visible)
1310  flagged = true;
1311 
1312  if ((e_cur->vnum == -1) && e_cur->visible)
1313  num_hidden++;
1314 
1316  {
1317  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1318  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1319  if (e_cur->vnum != -1)
1320  {
1321  e_root = e_cur;
1322  if (flag & MUTT_THREAD_COLLAPSE)
1323  final = e_root->vnum;
1324  }
1325  }
1326 
1327  if ((thread == top) && !(thread = thread->child))
1328  {
1329  /* return value depends on action requested */
1331  {
1332  e_cur->num_hidden = num_hidden;
1333  return final;
1334  }
1335  if (flag & MUTT_THREAD_UNREAD)
1336  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1337  if (flag & MUTT_THREAD_NEXT_UNREAD)
1338  return min_unread;
1339  if (flag & MUTT_THREAD_FLAGGED)
1340  return flagged;
1341  }
1342 
1343  while (true)
1344  {
1345  e_cur = thread->message;
1346 
1347  if (e_cur)
1348  {
1350  {
1351  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1352  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1353  if (!e_root && e_cur->visible)
1354  {
1355  e_root = e_cur;
1356  if (flag & MUTT_THREAD_COLLAPSE)
1357  final = e_root->vnum;
1358  }
1359 
1360  if (reverse && (flag & MUTT_THREAD_COLLAPSE) &&
1361  (e_cur->msgno < minmsgno) && e_cur->visible)
1362  {
1363  minmsgno = e_cur->msgno;
1364  final = e_cur->vnum;
1365  }
1366 
1367  if (flag & MUTT_THREAD_COLLAPSE)
1368  {
1369  if (e_cur != e_root)
1370  e_cur->vnum = -1;
1371  }
1372  else
1373  {
1374  if (e_cur->visible)
1375  e_cur->vnum = e_cur->msgno;
1376  }
1377  }
1378 
1379  if (!e_cur->read && e_cur->visible)
1380  {
1381  if (e_cur->old)
1382  old_mail = 2;
1383  else
1384  new_mail = 1;
1385  if (e_cur->msgno < min_unread_msgno)
1386  {
1387  min_unread = e_cur->vnum;
1388  min_unread_msgno = e_cur->msgno;
1389  }
1390  }
1391 
1392  if (e_cur->flagged && e_cur->visible)
1393  flagged = true;
1394 
1395  if ((e_cur->vnum == -1) && e_cur->visible)
1396  num_hidden++;
1397  }
1398 
1399  if (thread->child)
1400  thread = thread->child;
1401  else if (thread->next)
1402  thread = thread->next;
1403  else
1404  {
1405  bool done = false;
1406  while (!thread->next)
1407  {
1408  thread = thread->parent;
1409  if (thread == top)
1410  {
1411  done = true;
1412  break;
1413  }
1414  }
1415  if (done)
1416  break;
1417  thread = thread->next;
1418  }
1419  }
1420 
1421  /* re-traverse the thread and store num_hidden in all headers, with or
1422  * without a virtual index. this will allow ~v to match all collapsed
1423  * messages when switching sort order to non-threaded. */
1424  if (flag & MUTT_THREAD_COLLAPSE)
1425  {
1426  thread = top;
1427  while (true)
1428  {
1429  e_cur = thread->message;
1430  if (e_cur)
1431  e_cur->num_hidden = num_hidden + 1;
1432 
1433  if (thread->child)
1434  thread = thread->child;
1435  else if (thread->next)
1436  thread = thread->next;
1437  else
1438  {
1439  bool done = false;
1440  while (!thread->next)
1441  {
1442  thread = thread->parent;
1443  if (thread == top)
1444  {
1445  done = true;
1446  break;
1447  }
1448  }
1449  if (done)
1450  break;
1451  thread = thread->next;
1452  }
1453  }
1454  }
1455 
1456  /* return value depends on action requested */
1458  return final;
1459  if (flag & MUTT_THREAD_UNREAD)
1460  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1461  if (flag & MUTT_THREAD_NEXT_UNREAD)
1462  return min_unread;
1463  if (flag & MUTT_THREAD_FLAGGED)
1464  return flagged;
1465 
1466  return 0;
1467 }

◆ mutt_messages_in_thread()

int mutt_messages_in_thread ( struct Mailbox m,
struct Email e,
enum MessageInThread  mit 
)

Count the messages in a thread.

Parameters
mMailbox
eEmail
mitFlag, e.g. MIT_NUM_MESSAGES
Return values
numNumber of message / Our position

Definition at line 1476 of file mutt_thread.c.

1477 {
1478  if (!m || !e)
1479  return 1;
1480 
1481  struct MuttThread *threads[2];
1482  int rc;
1483 
1484  if (((C_Sort & SORT_MASK) != SORT_THREADS) || !e->thread)
1485  return 1;
1486 
1487  threads[0] = e->thread;
1488  while (threads[0]->parent)
1489  threads[0] = threads[0]->parent;
1490 
1491  threads[1] = (mit == MIT_POSITION) ? e->thread : threads[0]->next;
1492 
1493  for (int i = 0; i < (((mit == MIT_POSITION) || !threads[1]) ? 1 : 2); i++)
1494  {
1495  while (!threads[i]->message)
1496  threads[i] = threads[i]->child;
1497  }
1498 
1499  if (C_Sort & SORT_REVERSE)
1500  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1501  else
1502  {
1503  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1504  threads[0]->message->msgno;
1505  }
1506 
1507  if (mit == MIT_POSITION)
1508  rc += 1;
1509 
1510  return rc;
1511 }
+ Here is the caller graph for this function:

◆ mutt_make_id_hash()

struct HashTable* mutt_make_id_hash ( struct Mailbox m)

Create a Hash Table for message-ids.

Parameters
mMailbox
Return values
ptrNewly allocated Hash Table

Definition at line 1518 of file mutt_thread.c.

1519 {
1520  struct HashTable *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NO_FLAGS);
1521 
1522  for (int i = 0; i < m->msg_count; i++)
1523  {
1524  struct Email *e = m->emails[i];
1525  if (!e || !e->env)
1526  continue;
1527 
1528  if (e->env->message_id)
1529  mutt_hash_insert(hash, e->env->message_id, e);
1530  }
1531 
1532  return hash;
1533 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ link_threads()

static bool link_threads ( struct Email parent,
struct Email child,
struct Mailbox m 
)
static

Forcibly link messages together.

Parameters
parentParent Email
childChild Email
mMailbox
Return values
trueOn success

Definition at line 1542 of file mutt_thread.c.

1543 {
1544  if (child == parent)
1545  return false;
1546 
1547  mutt_break_thread(child);
1549  mutt_set_flag(m, child, MUTT_TAG, false);
1550 
1551  child->changed = true;
1552  child->env->changed |= MUTT_ENV_CHANGED_IRT;
1553  return true;
1554 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_link_threads()

bool mutt_link_threads ( struct Email parent,
struct EmailList *  children,
struct Mailbox m 
)

Forcibly link threads together.

Parameters
parentParent Email
childrenList of children Emails
mMailbox
Return values
trueOn success

Definition at line 1563 of file mutt_thread.c.

1564 {
1565  if (!parent || !children || !m)
1566  return false;
1567 
1568  bool changed = false;
1569 
1570  struct EmailNode *en = NULL;
1571  STAILQ_FOREACH(en, children, entries)
1572  {
1573  changed |= link_threads(parent, en->email, m);
1574  }
1575 
1576  return changed;
1577 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_thread_collapse_collapsed()

void mutt_thread_collapse_collapsed ( struct ThreadsContext tctx)

re-collapse threads marked as collapsed

Parameters
tctxThreading context

Definition at line 1583 of file mutt_thread.c.

1584 {
1585  struct MuttThread *thread = NULL;
1586  struct MuttThread *top = tctx->tree;
1587  while ((thread = top))
1588  {
1589  while (!thread->message)
1590  thread = thread->child;
1591 
1592  struct Email *e = thread->message;
1593  if (e->collapsed)
1595  top = top->next;
1596  }
1597 }
+ Here is the caller graph for this function:

◆ mutt_thread_collapse()

void mutt_thread_collapse ( struct ThreadsContext tctx,
bool  collapse 
)

toggle collapse

Parameters
tctxThreading context
collapseCollapse / uncollapse

Definition at line 1604 of file mutt_thread.c.

1605 {
1606  struct MuttThread *thread = NULL;
1607  struct MuttThread *top = tctx->tree;
1608  while ((thread = top))
1609  {
1610  while (!thread->message)
1611  thread = thread->child;
1612 
1613  struct Email *e = thread->message;
1614 
1615  if (e->collapsed != collapse)
1616  {
1617  if (e->collapsed)
1619  else if (mutt_thread_can_collapse(e))
1621  }
1622  top = top->next;
1623  }
1624 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_thread_can_collapse()

bool mutt_thread_can_collapse ( struct Email e)

Check whether a thread can be collapsed.

Parameters
eHead of the thread
Return values
trueCan be collapsed
falseCannot be collapsed

Definition at line 1632 of file mutt_thread.c.

1633 {
1636 }
+ Here is the caller graph for this function:

Variable Documentation

◆ C_CollapseFlagged

bool C_CollapseFlagged

Config: Prevent the collapse of threads with flagged emails.

Definition at line 45 of file mutt_thread.c.

◆ C_CollapseUnread

bool C_CollapseUnread

Config: Prevent the collapse of threads with unread emails.

Definition at line 46 of file mutt_thread.c.

◆ C_DuplicateThreads

bool C_DuplicateThreads

Config: Highlight messages with duplicated message IDs.

Definition at line 47 of file mutt_thread.c.

◆ C_HideLimited

bool C_HideLimited

Config: Don't indicate hidden messages, in the thread tree.

Definition at line 48 of file mutt_thread.c.

◆ C_HideMissing

bool C_HideMissing

Config: Don't indicate missing messages, in the thread tree.

Definition at line 49 of file mutt_thread.c.

◆ C_HideThreadSubject

bool C_HideThreadSubject

Config: Hide subjects that are similar to that of the parent message.

Definition at line 50 of file mutt_thread.c.

◆ C_HideTopLimited

bool C_HideTopLimited

Config: Don't indicate hidden top message, in the thread tree.

Definition at line 51 of file mutt_thread.c.

◆ C_HideTopMissing

bool C_HideTopMissing

Config: Don't indicate missing top message, in the thread tree.

Definition at line 52 of file mutt_thread.c.

◆ C_NarrowTree

bool C_NarrowTree

Config: Draw a narrower thread tree in the index.

Definition at line 53 of file mutt_thread.c.

◆ C_SortRe

bool C_SortRe

Config: Sort method for the sidebar.

Definition at line 54 of file mutt_thread.c.

◆ C_StrictThreads

bool C_StrictThreads

Config: Thread messages using 'In-Reply-To' and 'References' headers.

Definition at line 55 of file mutt_thread.c.

◆ C_ThreadReceived

bool C_ThreadReceived

Config: Sort threaded messages by their received date.

Definition at line 56 of file mutt_thread.c.

pseudo_threads
static void pseudo_threads(struct ThreadsContext *tctx)
Thread messages by subject.
Definition: mutt_thread.c:566
Body::hdr_offset
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
Envelope
The header of an Email.
Definition: envelope.h:54
link_threads
static bool link_threads(struct Email *parent, struct Email *child, struct Mailbox *m)
Forcibly link messages together.
Definition: mutt_thread.c:1542
Mailbox::subj_hash
struct HashTable * subj_hash
Hash Table by subject.
Definition: mailbox.h:128
Email::date_sent
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:82
Email::msgno
int msgno
Number displayed to the user.
Definition: email.h:87
Envelope::subject
char * subject
Email's subject.
Definition: envelope.h:66
MUTT_THREAD_FLAGGED
#define MUTT_THREAD_FLAGGED
Count flagged emails in a thread.
Definition: mutt_thread.h:80
ThreadsContext
The "current" threading state.
Definition: mutt_thread.c:61
Email::subject_changed
bool subject_changed
Used for threading.
Definition: email.h:55
mutt_hash_new
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:251
MUTT_ENV_CHANGED_IRT
#define MUTT_ENV_CHANGED_IRT
In-Reply-To changed to link/break threads.
Definition: envelope.h:32
insert_message
void insert_message(struct MuttThread **add, struct MuttThread *parent, struct MuttThread *cur)
Insert a message into a thread.
Definition: thread.c:94
mutt_mem_calloc
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
_
#define _(a)
Definition: message.h:28
Mailbox
A mailbox.
Definition: mailbox.h:81
SORT_THREADS
@ SORT_THREADS
Sort by email threads.
Definition: sort2.h:51
Envelope::in_reply_to
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
Envelope::message_id
char * message_id
Message ID.
Definition: envelope.h:69
Mailbox::v2r
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
MUTT_THREAD_NEXT_UNREAD
#define MUTT_THREAD_NEXT_UNREAD
Find the next unread email.
Definition: mutt_thread.h:79
Mailbox::emails
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
ListNode
A List node for strings.
Definition: list.h:34
Email::thread
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
thread_hash_destructor
void thread_hash_destructor(int type, void *obj, intptr_t data)
Hash Destructor callback - Implements hash_hdata_free_t.
Definition: thread.c:111
mutt_thread_contains_flagged
#define mutt_thread_contains_flagged(e)
Definition: mutt_thread.h:92
Body::offset
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
mutt_hash_set_destructor
void mutt_hash_set_destructor(struct HashTable *table, hash_hdata_free_t fn, intptr_t fn_data)
Set the destructor for a Hash Table.
Definition: hash.c:293
ThreadsContext::hash
struct HashTable * hash
Hash table for threads.
Definition: mutt_thread.c:65
mutt_list_insert_after
struct ListNode * mutt_list_insert_after(struct ListHead *h, struct ListNode *n, char *s)
Insert a string after a given ListNode.
Definition: list.c:84
HashElem::data
void * data
User-supplied data.
Definition: hash.h:47
HashElem::next
struct HashElem * next
Linked List.
Definition: hash.h:48
MUTT_TREE_LLCORNER
@ MUTT_TREE_LLCORNER
Lower left corner.
Definition: mutt_thread.h:56
Email::num_hidden
size_t num_hidden
Number of hidden messages in this view (only valid when collapsed is set)
Definition: email.h:75
EmailNode::email
struct Email * email
Email in the list.
Definition: email.h:127
is_descendant
bool is_descendant(struct MuttThread *a, struct MuttThread *b)
Is one thread a descendant of another.
Definition: thread.c:44
C_NarrowTree
bool C_NarrowTree
Config: Draw a narrower thread tree in the index.
Definition: mutt_thread.c:53
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
FREE
#define FREE(x)
Definition: memory.h:40
EmailNode
List of Emails.
Definition: email.h:125
SORT_REVERSE
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:81
STAILQ_FIRST
#define STAILQ_FIRST(head)
Definition: queue.h:347
mutt_sort_subthreads
void mutt_sort_subthreads(struct ThreadsContext *tctx, bool init)
Sort the children of a thread.
Definition: mutt_thread.c:687
mutt_get_sort_func
sort_t mutt_get_sort_func(enum SortType method)
Get the sort function for a given sort id.
Definition: sort.c:325
mutt_draw_tree
void mutt_draw_tree(struct ThreadsContext *tctx)
Draw a tree of threaded emails.
Definition: mutt_thread.c:320
SORT_MASK
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:80
C_CollapseFlagged
bool C_CollapseFlagged
Config: Prevent the collapse of threads with flagged emails.
Definition: mutt_thread.c:45
find_virtual
struct Email * find_virtual(struct MuttThread *cur, int reverse)
Find an email with a Virtual message number.
Definition: thread.c:122
STAILQ_EMPTY
#define STAILQ_EMPTY(head)
Definition: queue.h:345
SORT_LAST
#define SORT_LAST
Sort thread by last-X, e.g. received date.
Definition: sort2.h:82
check_subjects
static void check_subjects(struct Mailbox *m, bool init)
Find out which emails' subjects differ from their parent's.
Definition: mutt_thread.c:829
sort_t
int(* sort_t)(const void *a, const void *b)
Prototype for a function to compare two emails.
Definition: sort.h:50
Envelope::real_subj
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
MUTT_TREE_STAR
@ MUTT_TREE_STAR
Star character (for threads)
Definition: mutt_thread.h:63
STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
Mailbox::vcount
int vcount
The number of virtual messages.
Definition: mailbox.h:102
mutt_thread_can_collapse
bool mutt_thread_can_collapse(struct Email *e)
Check whether a thread can be collapsed.
Definition: mutt_thread.c:1632
Email::tree
char * tree
Character string to print thread tree.
Definition: email.h:94
MuttThread::prev
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
mutt_hash_find
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:354
MuttThread::parent
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
mutt_str_equal
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
MuttThread::child
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
MUTT_HASH_ALLOW_DUPS
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:100
MuttThread::next_subtree_visible
bool next_subtree_visible
Is the next Thread subtree visible?
Definition: thread.h:43
Email::old
bool old
Email is seen, but unread.
Definition: email.h:50
Email::received
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
MIT_POSITION
@ MIT_POSITION
Definition: mutt_thread.h:85
C_ThreadReceived
bool C_ThreadReceived
Config: Sort threaded messages by their received date.
Definition: mutt_thread.c:56
MUTT_TREE_MISSING
@ MUTT_TREE_MISSING
Question mark.
Definition: mutt_thread.h:68
make_subject_list
static void make_subject_list(struct ListHead *subjects, struct MuttThread *cur, time_t *dateptr)
Create a sorted list of all subjects in a thread.
Definition: mutt_thread.c:444
MUTT_TREE_RARROW
@ MUTT_TREE_RARROW
Right arrow.
Definition: mutt_thread.h:62
MUTT_TREE_HLINE
@ MUTT_TREE_HLINE
Horizontal line.
Definition: mutt_thread.h:59
mutt_hash_find_bucket
struct HashElem * mutt_hash_find_bucket(const struct HashTable *table, const char *strkey)
Find the HashElem in a Hash Table element using a key.
Definition: hash.c:401
Envelope::changed
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
Email::visible
bool visible
Is this message part of the view?
Definition: email.h:74
calculate_visibility
static void calculate_visibility(struct MuttThread *tree, int *max_depth)
Are tree nodes visible.
Definition: mutt_thread.c:179
MuttThread::fake_thread
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
Body::length
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
MUTT_TREE_BTEE
@ MUTT_TREE_BTEE
Bottom T-piece.
Definition: mutt_thread.h:67
mutt_list_insert_head
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:45
MuttThread::subtree_visible
unsigned int subtree_visible
Is this Thread subtree visible?
Definition: thread.h:42
MuttThread::message
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
Mailbox::msg_count
int msg_count
Total number of messages.
Definition: mailbox.h:91
mutt_hash_insert
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition: hash.c:327
MuttThread::duplicate_thread
bool duplicate_thread
Duplicated Email in Thread.
Definition: thread.h:37
MUTT_THREAD_UNREAD
#define MUTT_THREAD_UNREAD
Count unread emails in a thread.
Definition: mutt_thread.h:78
MUTT_TREE_ULCORNER
@ MUTT_TREE_ULCORNER
Upper left corner.
Definition: mutt_thread.h:57
C_HideMissing
bool C_HideMissing
Config: Don't indicate missing messages, in the thread tree.
Definition: mutt_thread.c:49
mutt_collapse_thread
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:89
Email::display_subject
bool display_subject
Used for threading.
Definition: email.h:57
mutt_mem_realloc
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
Email::env
struct Envelope * env
Envelope information.
Definition: email.h:90
MuttThread
An Email conversation.
Definition: thread.h:34
ThreadsContext::tree
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
ThreadsContext::mailbox
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:63
is_visible
static bool is_visible(struct Email *e)
Is the message visible?
Definition: mutt_thread.c:73
Email::flagged
bool flagged
Marked important?
Definition: email.h:43
Email::collapsed
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
make_subj_hash
static struct HashTable * make_subj_hash(struct Mailbox *m)
Create a Hash Table for the email subjects.
Definition: mutt_thread.c:541
mutt_uncollapse_thread
#define mutt_uncollapse_thread(e)
Definition: mutt_thread.h:90
MUTT_THREAD_UNCOLLAPSE
#define MUTT_THREAD_UNCOLLAPSE
Uncollapse an email thread.
Definition: mutt_thread.h:77
STAILQ_HEAD_INITIALIZER
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
MuttThread::sort_key
struct Email * sort_key
Email that this Thread is sorted against.
Definition: thread.h:50
mutt_str_cmp
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:567
MUTT_HASH_NO_FLAGS
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:97
HashElem
The item stored in a Hash Table.
Definition: hash.h:43
MUTT_TREE_EQUALS
@ MUTT_TREE_EQUALS
Equals (for threads)
Definition: mutt_thread.h:65
MUTT_TREE_TTEE
@ MUTT_TREE_TTEE
Top T-piece.
Definition: mutt_thread.h:66
C_Sort
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
linearize_tree
static void linearize_tree(struct ThreadsContext *tctx)
Flatten an email thread.
Definition: mutt_thread.c:131
mutt_mem_malloc
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
MuttThread::visible
bool visible
Is this Thread visible?
Definition: thread.h:40
C_DuplicateThreads
bool C_DuplicateThreads
Config: Highlight messages with duplicated message IDs.
Definition: mutt_thread.c:47
C_HideThreadSubject
bool C_HideThreadSubject
Config: Hide subjects that are similar to that of the parent message.
Definition: mutt_thread.c:50
mutt_list_clear
void mutt_list_clear(struct ListHead *h)
Free a list, but NOT its strings.
Definition: list.c:167
MUTT_TREE_LTEE
@ MUTT_TREE_LTEE
Left T-piece.
Definition: mutt_thread.h:58
MUTT_TAG
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:103
C_SortRe
bool C_SortRe
Config: Sort method for the sidebar.
Definition: mutt_thread.c:54
MuttThread::sort_children
bool sort_children
Sort the children.
Definition: thread.h:38
MUTT_TREE_HIDDEN
@ MUTT_TREE_HIDDEN
Ampersand character (for threads)
Definition: mutt_thread.h:64
MUTT_TREE_VLINE
@ MUTT_TREE_VLINE
Vertical line.
Definition: mutt_thread.h:60
MuttThread::check_subject
bool check_subject
Should the Subject be checked?
Definition: thread.h:39
Email::vnum
int vnum
Virtual message number.
Definition: email.h:88
ListNode::data
char * data
String.
Definition: list.h:36
need_display_subject
static bool need_display_subject(struct Email *e)
Determines whether to display a message's subject.
Definition: mutt_thread.c:83
unlink_message
void unlink_message(struct MuttThread **old, struct MuttThread *cur)
Break the message out of the thread.
Definition: thread.c:64
C_CollapseUnread
bool C_CollapseUnread
Config: Prevent the collapse of threads with unread emails.
Definition: mutt_thread.c:46
MuttThread::next
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
STAILQ_NEXT
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
C_HideTopLimited
bool C_HideTopLimited
Config: Don't indicate hidden top message, in the thread tree.
Definition: mutt_thread.c:51
C_HideLimited
bool C_HideLimited
Config: Don't indicate hidden messages, in the thread tree.
Definition: mutt_thread.c:48
mutt_break_thread
void mutt_break_thread(struct Email *e)
Break the email Thread.
Definition: thread.c:225
Email
The envelope/body of an email.
Definition: email.h:37
Email::threaded
bool threaded
Used for threading.
Definition: email.h:56
HashTable
A Hash Table.
Definition: hash.h:84
mutt_set_flag
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:67
C_SortAux
WHERE short C_SortAux
Config: Secondary sort method for the index.
Definition: sort.h:61
mx_msg_padding_size
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1554
compare_threads
static int compare_threads(const void *a, const void *b)
Sorting function for email threads.
Definition: mutt_thread.c:664
Envelope::references
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
MUTT_THREAD_COLLAPSE
#define MUTT_THREAD_COLLAPSE
Collapse an email thread.
Definition: mutt_thread.h:76
Email::read
bool read
Email is read.
Definition: email.h:51
mutt_hash_free
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:447
find_subject
static struct MuttThread * find_subject(struct Mailbox *m, struct MuttThread *cur)
Find the best possible match for a parent based on subject.
Definition: mutt_thread.c:498
Email::changed
bool changed
Email has been edited.
Definition: email.h:48
TreeChar
TreeChar
Tree characters for menus.
Definition: mutt_thread.h:54
MuttThread::deep
bool deep
Is the Thread deeply nested?
Definition: thread.h:41
Email::pair
int pair
Color-pair to use when displaying in the index.
Definition: email.h:80
mutt_thread_contains_unread
#define mutt_thread_contains_unread(e)
Definition: mutt_thread.h:91
Email::body
struct Body * body
List of MIME parts.
Definition: email.h:91
C_HideTopMissing
bool C_HideTopMissing
Config: Don't indicate missing top message, in the thread tree.
Definition: mutt_thread.c:52
MUTT_TREE_SPACE
@ MUTT_TREE_SPACE
Blank space.
Definition: mutt_thread.h:61
mutt_error
#define mutt_error(...)
Definition: logging.h:84
C_StrictThreads
bool C_StrictThreads
Config: Thread messages using 'In-Reply-To' and 'References' headers.
Definition: mutt_thread.c:55
mutt_str_copy
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:716