NeoMutt  2020-08-21-74-g346364
Teaching an old dog new tricks
DOXYGEN
mutt_thread.c File Reference

Create/manipulate threading in emails. More...

#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, int flag)
 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 }
bool visible
Is this message part of the view?
Definition: email.h:74
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
int vnum
Virtual message number.
Definition: email.h:88
+ 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 }
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
static bool is_visible(struct Email *e)
Is the message visible?
Definition: mutt_thread.c:73
bool C_HideThreadSubject
Config: Hide subjects that are similar to that of the parent message.
Definition: mutt_thread.c:50
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool subject_changed
Used for threading.
Definition: email.h:55
+ 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 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:63
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
A mailbox.
Definition: mailbox.h:81
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:82
+ 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 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
bool C_HideMissing
Config: Don&#39;t indicate missing messages, in the thread tree.
Definition: mutt_thread.c:49
static bool is_visible(struct Email *e)
Is the message visible?
Definition: mutt_thread.c:73
bool deep
Is the Thread deeply nested?
Definition: thread.h:41
bool display_subject
Used for threading.
Definition: email.h:57
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool next_subtree_visible
Is the next Thread subtree visible?
Definition: thread.h:43
bool C_HideLimited
Config: Don&#39;t indicate hidden messages, in the thread tree.
Definition: mutt_thread.c:48
static bool need_display_subject(struct Email *e)
Determines whether to display a message&#39;s subject.
Definition: mutt_thread.c:83
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
bool C_HideTopLimited
Config: Don&#39;t indicate hidden top message, in the thread tree.
Definition: mutt_thread.c:51
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool C_HideTopMissing
Config: Don&#39;t indicate missing top message, in the thread tree.
Definition: mutt_thread.c:52
unsigned int subtree_visible
Is this Thread subtree visible?
Definition: thread.h:42
char * tree
Character string to print thread tree.
Definition: email.h:94
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
#define FREE(x)
Definition: memory.h:40
bool visible
Is this Thread visible?
Definition: thread.h:40
+ 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 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:63
The "current" threading state.
Definition: mutt_thread.c:61
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
struct HashTable * hash
Hash table for threads.
Definition: mutt_thread.c:65
+ 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 }
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:447
#define FREE(x)
Definition: memory.h:40
+ 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 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
Star character (for threads)
Definition: mutt_thread.h:63
Ampersand character (for threads)
Definition: mutt_thread.h:64
bool C_HideMissing
Config: Don&#39;t indicate missing messages, in the thread tree.
Definition: mutt_thread.c:49
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
bool deep
Is the Thread deeply nested?
Definition: thread.h:41
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool next_subtree_visible
Is the next Thread subtree visible?
Definition: thread.h:43
Lower left corner.
Definition: mutt_thread.h:56
bool C_HideLimited
Config: Don&#39;t indicate hidden messages, in the thread tree.
Definition: mutt_thread.c:48
Top T-piece.
Definition: mutt_thread.h:66
TreeChar
Tree characters for menus.
Definition: mutt_thread.h:54
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
Vertical line.
Definition: mutt_thread.h:60
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool duplicate_thread
Duplicated Email in Thread.
Definition: thread.h:37
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
Right arrow.
Definition: mutt_thread.h:62
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
unsigned int subtree_visible
Is this Thread subtree visible?
Definition: thread.h:42
Bottom T-piece.
Definition: mutt_thread.h:67
Left T-piece.
Definition: mutt_thread.h:58
Upper left corner.
Definition: mutt_thread.h:57
char * tree
Character string to print thread tree.
Definition: email.h:94
bool C_NarrowTree
Config: Draw a narrower thread tree in the index.
Definition: mutt_thread.c:53
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
Horizontal line.
Definition: mutt_thread.h:59
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
#define FREE(x)
Definition: memory.h:40
bool visible
Is this Thread visible?
Definition: thread.h:40
Blank space.
Definition: mutt_thread.h:61
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:82
static void calculate_visibility(struct MuttThread *tree, int *max_depth)
Are tree nodes visible.
Definition: mutt_thread.c:179
Question mark.
Definition: mutt_thread.h:68
Equals (for threads)
Definition: mutt_thread.h:65
+ 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 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:567
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool C_SortRe
Config: Sort method for the sidebar.
Definition: mutt_thread.c:54
struct Envelope * env
Envelope information.
Definition: email.h:90
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:82
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
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
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:45
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
char * data
String.
Definition: list.h:36
char * subject
Email&#39;s subject.
Definition: envelope.h:66
A List node for strings.
Definition: list.h:34
bool C_ThreadReceived
Config: Sort threaded messages by their received date.
Definition: mutt_thread.c:56
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
The header of an Email.
Definition: envelope.h:54
+ 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 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
The envelope/body of an email.
Definition: email.h:37
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct HashElem * next
Linked List.
Definition: hash.h:48
struct Envelope * env
Envelope information.
Definition: email.h:90
struct HashTable * subj_hash
Hash Table by subject.
Definition: mailbox.h:128
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:82
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
void * data
User-supplied data.
Definition: hash.h:47
char * data
String.
Definition: list.h:36
bool subject_changed
Used for threading.
Definition: email.h:55
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
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
The item stored in a Hash Table.
Definition: hash.h:43
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
bool is_descendant(struct MuttThread *a, struct MuttThread *b)
Is one thread a descendant of another.
Definition: thread.c:44
void mutt_list_clear(struct ListHead *h)
Free a list, but NOT its strings.
Definition: list.c:167
A List node for strings.
Definition: list.h:34
bool C_ThreadReceived
Config: Sort threaded messages by their received date.
Definition: mutt_thread.c:56
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
+ 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 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
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
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
A Hash Table.
Definition: hash.h:84
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct Envelope * env
Envelope information.
Definition: email.h:90
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:100
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:251
+ 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);
590  insert_message(&parent->child, parent, 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,
604  parent->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 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
static struct HashTable * make_subj_hash(struct Mailbox *m)
Create a Hash Table for the email subjects.
Definition: mutt_thread.c:541
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:63
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
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
struct Envelope * env
Envelope information.
Definition: email.h:90
struct HashTable * subj_hash
Hash Table by subject.
Definition: mailbox.h:128
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
A mailbox.
Definition: mailbox.h:81
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
void unlink_message(struct MuttThread **old, struct MuttThread *cur)
Break the message out of the thread.
Definition: thread.c:64
An Email conversation.
Definition: thread.h:34
bool subject_changed
Used for threading.
Definition: email.h:55
void insert_message(struct MuttThread **add, struct MuttThread *parent, struct MuttThread *cur)
Insert a message into a thread.
Definition: thread.c:94
+ 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 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:447
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:63
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
bool threaded
Used for threading.
Definition: email.h:56
struct HashTable * hash
Hash table for threads.
Definition: mutt_thread.c:65
+ 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 }
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
int(* sort_t)(const void *a, const void *b)
Prototype for a function to compare two emails.
Definition: sort.h:50
struct Email * sort_key
Email that this Thread is sorted against.
Definition: thread.h:50
An Email conversation.
Definition: thread.h:34
sort_t mutt_get_sort_func(enum SortType method)
Get the sort function for a given sort id.
Definition: sort.c:324
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:81
+ 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 */
729  thread->sort_key = thread->message;
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 
776  if (!thread->sort_key || thread->sort_children)
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;
787  thread->sort_key = thread->message;
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 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
static int compare_threads(const void *a, const void *b)
Sorting function for email threads.
Definition: mutt_thread.c:664
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
struct Email * sort_key
Email that this Thread is sorted against.
Definition: thread.h:50
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool sort_children
Sort the children.
Definition: thread.h:38
#define FREE(x)
Definition: memory.h:40
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:82
#define SORT_LAST
Sort thread by last-X, e.g. received date.
Definition: sort2.h:83
+ 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 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct Envelope * env
Envelope information.
Definition: email.h:90
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool subject_changed
Used for threading.
Definition: email.h:55
bool check_subject
Should the Subject be checked?
Definition: thread.h:39
+ 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 C_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 }
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
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
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
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int msg_count
Total number of messages.
Definition: mailbox.h:91
void thread_hash_destructor(int type, void *obj, intptr_t data)
Hash Destructor callback - Implements hash_hdata_free_t.
Definition: thread.c:111
The envelope/body of an email.
Definition: email.h:37
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:63
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
bool threaded
Used for threading.
Definition: email.h:56
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
static void linearize_tree(struct ThreadsContext *tctx)
Flatten an email thread.
Definition: mutt_thread.c:131
bool C_DuplicateThreads
Config: Highlight messages with duplicated message IDs.
Definition: mutt_thread.c:47
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
char * message_id
Message ID.
Definition: envelope.h:69
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
struct Envelope * env
Envelope information.
Definition: email.h:90
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
bool C_StrictThreads
Config: Thread messages using &#39;In-Reply-To&#39; and &#39;References&#39; headers.
Definition: mutt_thread.c:55
static void pseudo_threads(struct ThreadsContext *tctx)
Thread messages by subject.
Definition: mutt_thread.c:566
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
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool duplicate_thread
Duplicated Email in Thread.
Definition: thread.h:37
WHERE short C_SortAux
Config: Secondary sort method for the index.
Definition: sort.h:61
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:100
struct Email * sort_key
Email that this Thread is sorted against.
Definition: thread.h:50
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
A mailbox.
Definition: mailbox.h:81
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
void unlink_message(struct MuttThread **old, struct MuttThread *cur)
Break the message out of the thread.
Definition: thread.c:64
An Email conversation.
Definition: thread.h:34
char * data
String.
Definition: list.h:36
void insert_message(struct MuttThread **add, struct MuttThread *parent, struct MuttThread *cur)
Insert a message into a thread.
Definition: thread.c:94
void mutt_sort_subthreads(struct ThreadsContext *tctx, bool init)
Sort the children of a thread.
Definition: mutt_thread.c:687
#define STAILQ_EMPTY(head)
Definition: queue.h:345
void mutt_draw_tree(struct ThreadsContext *tctx)
Draw a tree of threaded emails.
Definition: mutt_thread.c:320
bool check_subject
Should the Subject be checked?
Definition: thread.h:39
struct HashTable * hash
Hash table for threads.
Definition: mutt_thread.c:65
static void check_subjects(struct Mailbox *m, bool init)
Find out which emails&#39; subjects differ from their parent&#39;s.
Definition: mutt_thread.c:829
bool is_descendant(struct MuttThread *a, struct MuttThread *b)
Is one thread a descendant of another.
Definition: thread.c:44
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
A List node for strings.
Definition: list.h:34
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:251
#define STAILQ_FIRST(head)
Definition: queue.h:347
+ 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 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:37
struct Email * find_virtual(struct MuttThread *cur, int reverse)
Find an email with a Virtual message number.
Definition: thread.c:122
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
Sort by email threads.
Definition: sort2.h:52
int vnum
Virtual message number.
Definition: email.h:88
An Email conversation.
Definition: thread.h:34
#define mutt_error(...)
Definition: logging.h:84
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:82
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:81
+ 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 }
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
static bool is_visible(struct Email *e)
Is the message visible?
Definition: mutt_thread.c:73
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
Sort by email threads.
Definition: sort2.h:52
int vnum
Virtual message number.
Definition: email.h:88
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
#define mutt_error(...)
Definition: logging.h:84
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:81
+ 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 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
struct Body * body
List of MIME parts.
Definition: email.h:91
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
int vcount
The number of virtual messages.
Definition: mailbox.h:102
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
int vnum
Virtual message number.
Definition: email.h:88
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1569
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
+ 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 */
1330  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
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  {
1349  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
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 */
1457  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
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 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:37
#define MUTT_THREAD_UNCOLLAPSE
Uncollapse an email thread.
Definition: mutt_thread.h:77
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool read
Email is read.
Definition: email.h:51
bool old
Email is seen, but unread.
Definition: email.h:50
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
#define MUTT_THREAD_COLLAPSE
Collapse an email thread.
Definition: mutt_thread.h:76
#define MUTT_THREAD_FLAGGED
Count flagged emails in a thread.
Definition: mutt_thread.h:80
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool visible
Is this message part of the view?
Definition: email.h:74
Sort by email threads.
Definition: sort2.h:52
size_t num_hidden
Number of hidden messages in this view (only valid when collapsed is set)
Definition: email.h:75
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
int vnum
Virtual message number.
Definition: email.h:88
#define MUTT_THREAD_NEXT_UNREAD
Find the next unread email.
Definition: mutt_thread.h:79
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool flagged
Marked important?
Definition: email.h:43
#define mutt_error(...)
Definition: logging.h:84
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:82
int pair
Color-pair to use when displaying in the index.
Definition: email.h:80
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:81
int msgno
Number displayed to the user.
Definition: email.h:87
#define MUTT_THREAD_UNREAD
Count unread emails in a thread.
Definition: mutt_thread.h:78

◆ mutt_messages_in_thread()

int mutt_messages_in_thread ( struct Mailbox m,
struct Email e,
int  flag 
)

Count the messages in a thread.

Parameters
mMailbox
eEmail
flagFlag, see notes below
Return values
numNumber of message / Our position

If flag is 0, we want to know how many messages are in the thread. If flag is 1, we want to know our position in the thread.

Definition at line 1479 of file mutt_thread.c.

1480 {
1481  if (!m || !e)
1482  return 1;
1483 
1484  struct MuttThread *threads[2];
1485  int rc;
1486 
1487  if (((C_Sort & SORT_MASK) != SORT_THREADS) || !e->thread)
1488  return 1;
1489 
1490  threads[0] = e->thread;
1491  while (threads[0]->parent)
1492  threads[0] = threads[0]->parent;
1493 
1494  threads[1] = flag ? e->thread : threads[0]->next;
1495 
1496  for (int i = 0; i < ((flag || !threads[1]) ? 1 : 2); i++)
1497  {
1498  while (!threads[i]->message)
1499  threads[i] = threads[i]->child;
1500  }
1501 
1502  if (C_Sort & SORT_REVERSE)
1503  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1504  else
1505  {
1506  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1507  threads[0]->message->msgno;
1508  }
1509 
1510  if (flag)
1511  rc += 1;
1512 
1513  return rc;
1514 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
int msg_count
Total number of messages.
Definition: mailbox.h:91
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
Sort by email threads.
Definition: sort2.h:52
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:82
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:81
int msgno
Number displayed to the user.
Definition: email.h:87
+ 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 1521 of file mutt_thread.c.

1522 {
1523  struct HashTable *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NO_FLAGS);
1524 
1525  for (int i = 0; i < m->msg_count; i++)
1526  {
1527  struct Email *e = m->emails[i];
1528  if (!e || !e->env)
1529  continue;
1530 
1531  if (e->env->message_id)
1532  mutt_hash_insert(hash, e->env->message_id, e);
1533  }
1534 
1535  return hash;
1536 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
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
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
A Hash Table.
Definition: hash.h:84
char * message_id
Message ID.
Definition: envelope.h:69
struct Envelope * env
Envelope information.
Definition: email.h:90
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:251
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:97
+ 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 1545 of file mutt_thread.c.

1546 {
1547  if (child == parent)
1548  return false;
1549 
1550  mutt_break_thread(child);
1552  mutt_set_flag(m, child, MUTT_TAG, false);
1553 
1554  child->changed = true;
1555  child->env->changed |= MUTT_ENV_CHANGED_IRT;
1556  return true;
1557 }
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:67
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
bool changed
Email has been edited.
Definition: email.h:48
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
char * message_id
Message ID.
Definition: envelope.h:69
struct Envelope * env
Envelope information.
Definition: email.h:90
void mutt_break_thread(struct Email *e)
Break the email Thread.
Definition: thread.c:225
Tagged messages.
Definition: mutt.h:103
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:45
#define MUTT_ENV_CHANGED_IRT
In-Reply-To changed to link/break threads.
Definition: envelope.h:32
+ 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 1566 of file mutt_thread.c.

1567 {
1568  if (!parent || !children || !m)
1569  return false;
1570 
1571  bool changed = false;
1572 
1573  struct EmailNode *en = NULL;
1574  STAILQ_FOREACH(en, children, entries)
1575  {
1576  changed |= link_threads(parent, en->email, m);
1577  }
1578 
1579  return changed;
1580 }
bool changed
Email has been edited.
Definition: email.h:48
static bool link_threads(struct Email *parent, struct Email *child, struct Mailbox *m)
Forcibly link messages together.
Definition: mutt_thread.c:1545
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
struct Email * email
Email in the list.
Definition: email.h:127
List of Emails.
Definition: email.h:125
+ 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 1586 of file mutt_thread.c.

1587 {
1588  struct MuttThread *thread = NULL;
1589  struct MuttThread *top = tctx->tree;
1590  while ((thread = top))
1591  {
1592  while (!thread->message)
1593  thread = thread->child;
1594 
1595  struct Email *e = thread->message;
1596  if (e->collapsed)
1598  top = top->next;
1599  }
1600 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:37
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:83
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
+ 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 1607 of file mutt_thread.c.

1608 {
1609  struct MuttThread *thread = NULL;
1610  struct MuttThread *top = tctx->tree;
1611  while ((thread = top))
1612  {
1613  while (!thread->message)
1614  thread = thread->child;
1615 
1616  struct Email *e = thread->message;
1617 
1618  if (e->collapsed != collapse)
1619  {
1620  if (e->collapsed)
1622  else if (mutt_thread_can_collapse(e))
1624  }
1625  top = top->next;
1626  }
1627 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:37
#define mutt_uncollapse_thread(e)
Definition: mutt_thread.h:84
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:83
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:64
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool mutt_thread_can_collapse(struct Email *e)
Check whether a thread can be collapsed.
Definition: mutt_thread.c:1635
+ 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 1635 of file mutt_thread.c.

1636 {
1639 }
bool C_CollapseUnread
Config: Prevent the collapse of threads with unread emails.
Definition: mutt_thread.c:46
#define mutt_thread_contains_unread(e)
Definition: mutt_thread.h:85
#define mutt_thread_contains_flagged(e)
Definition: mutt_thread.h:86
bool C_CollapseFlagged
Config: Prevent the collapse of threads with flagged emails.
Definition: mutt_thread.c:45
+ 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.