NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
mutt_thread.c File Reference

Create/manipulate threading in emails. More...

#include "config.h"
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "mutt.h"
#include "mutt_thread.h"
#include "mx.h"
#include "options.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

enum UseThreads mutt_thread_style (void)
 Which threading style is active? More...
 
const char * get_use_threads_str (enum UseThreads value)
 Convert UseThreads enum to string. More...
 
int sort_validator (const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
 Validate values of "sort" - Implements ConfigDef::validator() -. More...
 
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, void *arg)
 qsort_r function for comparing email threads More...
 
static 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

static const struct Mapping UseThreadsMethods []
 Choices for '$use_threads' for the index. More...
 
struct EnumDef UseThreadsTypeDef
 

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

◆ mutt_thread_style()

enum UseThreads mutt_thread_style ( void  )

Which threading style is active?

Return values
UT_FLATNo threading in use
UT_THREADSNormal threads (root above subthread)
UT_REVERSEReverse threads (subthread above root)
Note
UT_UNSET is never returned; rather, this function considers the interaction between $use_threads and $sort.

Definition at line 89 of file mutt_thread.c.

90 {
91  const unsigned char c_use_threads =
92  cs_subset_enum(NeoMutt->sub, "use_threads");
93  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
94  if (c_use_threads > UT_FLAT)
95  return c_use_threads;
96  if ((c_sort & SORT_MASK) != SORT_THREADS)
97  return UT_FLAT;
98  if (c_sort & SORT_REVERSE)
99  return UT_REVERSE;
100  return UT_THREADS;
101 }
Normal threading (root above subthreads)
Definition: mutt_thread.h:81
Container for Accounts, Notifications.
Definition: neomutt.h:36
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
Sort by email threads.
Definition: sort2.h:49
Unthreaded.
Definition: mutt_thread.h:80
unsigned char cs_subset_enum(const struct ConfigSubset *sub, const char *name)
Get a enumeration config item by name.
Definition: helpers.c:97
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:79
Reverse threading (subthreads above root)
Definition: mutt_thread.h:82
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_use_threads_str()

const char* get_use_threads_str ( enum UseThreads  value)

Convert UseThreads enum to string.

Parameters
valueValue to convert
Return values
stringform of value

Definition at line 108 of file mutt_thread.c.

109 {
110  return mutt_map_get_name(value, UseThreadsMethods);
111 }
static const struct Mapping UseThreadsMethods[]
Choices for &#39;$use_threads&#39; for the index.
Definition: mutt_thread.c:61
const char * mutt_map_get_name(int val, const struct Mapping *map)
Lookup a string for a constant.
Definition: mapping.c:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ is_visible()

static bool is_visible ( struct Email e)
static

Is the message visible?

Parameters
eEmail
Return values
trueThe message is not hidden in some way

Definition at line 132 of file mutt_thread.c.

133 {
134  return e->vnum >= 0 || (e->collapsed && e->visible);
135 }
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
trueThe subject should be displayed

Definition at line 142 of file mutt_thread.c.

143 {
144  struct MuttThread *tmp = NULL;
145  struct MuttThread *tree = e->thread;
146 
147  /* if the user disabled subject hiding, display it */
148  const bool c_hide_thread_subject =
149  cs_subset_bool(NeoMutt->sub, "hide_thread_subject");
150  if (!c_hide_thread_subject)
151  return true;
152 
153  /* if our subject is different from our parent's, display it */
154  if (e->subject_changed)
155  return true;
156 
157  /* if our subject is different from that of our closest previously displayed
158  * sibling, display the subject */
159  for (tmp = tree->prev; tmp; tmp = tmp->prev)
160  {
161  e = tmp->message;
162  if (e && is_visible(e))
163  {
164  if (e->subject_changed)
165  return true;
166  break;
167  }
168  }
169 
170  /* if there is a parent-to-child subject change anywhere between us and our
171  * closest displayed ancestor, display the subject */
172  for (tmp = tree->parent; tmp; tmp = tmp->parent)
173  {
174  e = tmp->message;
175  if (e)
176  {
177  if (is_visible(e))
178  return false;
179  if (e->subject_changed)
180  return true;
181  }
182  }
183 
184  /* if we have no visible parent or previous sibling, display the subject */
185  return true;
186 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
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:132
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
Container for Accounts, Notifications.
Definition: neomutt.h:36
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
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ 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 192 of file mutt_thread.c.

193 {
194  if (!tctx || !tctx->mailbox)
195  return;
196 
197  struct Mailbox *m = tctx->mailbox;
198 
199  const bool reverse = (mutt_thread_style() == UT_REVERSE);
200  struct MuttThread *tree = tctx->tree;
201  struct Email **array = m->emails + (reverse ? m->msg_count - 1 : 0);
202 
203  while (tree)
204  {
205  while (!tree->message)
206  tree = tree->child;
207 
208  *array = tree->message;
209  array += reverse ? -1 : 1;
210 
211  if (tree->child)
212  tree = tree->child;
213  else
214  {
215  while (tree)
216  {
217  if (tree->next)
218  {
219  tree = tree->next;
220  break;
221  }
222  else
223  tree = tree->parent;
224  }
225  }
226  }
227 }
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:51
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
enum UseThreads mutt_thread_style(void)
Which threading style is active?
Definition: mutt_thread.c:89
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
Reverse threading (subthreads above root)
Definition: mutt_thread.h:82
+ Here is the call graph for this function:
+ 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 241 of file mutt_thread.c.

242 {
243  if (!tree)
244  return;
245 
246  struct MuttThread *tmp = NULL;
247  struct MuttThread *orig_tree = tree;
248  const bool c_hide_top_missing =
249  cs_subset_bool(NeoMutt->sub, "hide_top_missing");
250  const bool c_hide_missing = cs_subset_bool(NeoMutt->sub, "hide_missing");
251  int hide_top_missing = c_hide_top_missing && !c_hide_missing;
252  const bool c_hide_top_limited =
253  cs_subset_bool(NeoMutt->sub, "hide_top_limited");
254  const bool c_hide_limited = cs_subset_bool(NeoMutt->sub, "hide_limited");
255  int hide_top_limited = c_hide_top_limited && !c_hide_limited;
256  int depth = 0;
257 
258  /* we walk each level backwards to make it easier to compute next_subtree_visible */
259  while (tree->next)
260  tree = tree->next;
261  *max_depth = 0;
262 
263  while (true)
264  {
265  if (depth > *max_depth)
266  *max_depth = depth;
267 
268  tree->subtree_visible = 0;
269  if (tree->message)
270  {
271  FREE(&tree->message->tree);
272  if (is_visible(tree->message))
273  {
274  tree->deep = true;
275  tree->visible = true;
277  for (tmp = tree; tmp; tmp = tmp->parent)
278  {
279  if (tmp->subtree_visible)
280  {
281  tmp->deep = true;
282  tmp->subtree_visible = 2;
283  break;
284  }
285  else
286  tmp->subtree_visible = 1;
287  }
288  }
289  else
290  {
291  tree->visible = false;
292  tree->deep = !c_hide_limited;
293  }
294  }
295  else
296  {
297  tree->visible = false;
298  tree->deep = !c_hide_missing;
299  }
300  tree->next_subtree_visible =
301  tree->next && (tree->next->next_subtree_visible || tree->next->subtree_visible);
302  if (tree->child)
303  {
304  depth++;
305  tree = tree->child;
306  while (tree->next)
307  tree = tree->next;
308  }
309  else if (tree->prev)
310  tree = tree->prev;
311  else
312  {
313  while (tree && !tree->prev)
314  {
315  depth--;
316  tree = tree->parent;
317  }
318  if (!tree)
319  break;
320  tree = tree->prev;
321  }
322  }
323 
324  /* now fix up for the OPTHIDETOP* options if necessary */
325  if (hide_top_limited || hide_top_missing)
326  {
327  tree = orig_tree;
328  while (true)
329  {
330  if (!tree->visible && tree->deep && (tree->subtree_visible < 2) &&
331  ((tree->message && hide_top_limited) || (!tree->message && hide_top_missing)))
332  {
333  tree->deep = false;
334  }
335  if (!tree->deep && tree->child && tree->subtree_visible)
336  tree = tree->child;
337  else if (tree->next)
338  tree = tree->next;
339  else
340  {
341  while (tree && !tree->next)
342  tree = tree->parent;
343  if (!tree)
344  break;
345  tree = tree->next;
346  }
347  }
348  }
349 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
static bool is_visible(struct Email *e)
Is the message visible?
Definition: mutt_thread.c:132
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
Container for Accounts, Notifications.
Definition: neomutt.h:36
static bool need_display_subject(struct Email *e)
Determines whether to display a message&#39;s subject.
Definition: mutt_thread.c:142
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
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
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ 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 356 of file mutt_thread.c.

357 {
358  struct ThreadsContext *tctx = mutt_mem_calloc(1, sizeof(struct ThreadsContext));
359  tctx->mailbox = m;
360  tctx->tree = NULL;
361  tctx->hash = NULL;
362  return tctx;
363 }
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:51
The "current" threading state.
Definition: mutt_thread.c:49
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
struct HashTable * hash
Hash table for threads.
Definition: mutt_thread.c:53
+ 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 369 of file mutt_thread.c.

370 {
371  (*tctx)->mailbox = NULL;
372  mutt_hash_free(&(*tctx)->hash);
373  FREE(tctx);
374 }
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:449
#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 388 of file mutt_thread.c.

389 {
390  char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree = NULL;
391  const bool reverse = (mutt_thread_style() == UT_REVERSE);
392  enum TreeChar corner = reverse ? MUTT_TREE_ULCORNER : MUTT_TREE_LLCORNER;
393  enum TreeChar vtee = reverse ? MUTT_TREE_BTEE : MUTT_TREE_TTEE;
394  const bool c_narrow_tree = cs_subset_bool(NeoMutt->sub, "narrow_tree");
395  int depth = 0, start_depth = 0, max_depth = 0, width = c_narrow_tree ? 1 : 2;
396  struct MuttThread *nextdisp = NULL, *pseudo = NULL, *parent = NULL;
397 
398  struct MuttThread *tree = tctx->tree;
399 
400  /* Do the visibility calculations and free the old thread chars.
401  * From now on we can simply ignore invisible subtrees */
402  calculate_visibility(tree, &max_depth);
403  pfx = mutt_mem_malloc((width * max_depth) + 2);
404  arrow = mutt_mem_malloc((width * max_depth) + 2);
405  while (tree)
406  {
407  if (depth != 0)
408  {
409  myarrow = arrow + (depth - start_depth - ((start_depth != 0) ? 0 : 1)) * width;
410  const bool c_hide_limited = cs_subset_bool(NeoMutt->sub, "hide_limited");
411  const bool c_hide_missing = cs_subset_bool(NeoMutt->sub, "hide_missing");
412  if (start_depth == depth)
413  myarrow[0] = nextdisp ? MUTT_TREE_LTEE : corner;
414  else if (parent->message && !c_hide_limited)
415  myarrow[0] = MUTT_TREE_HIDDEN;
416  else if (!parent->message && !c_hide_missing)
417  myarrow[0] = MUTT_TREE_MISSING;
418  else
419  myarrow[0] = vtee;
420  if (width == 2)
421  {
422  myarrow[1] = pseudo ? MUTT_TREE_STAR :
424  }
425  if (tree->visible)
426  {
427  myarrow[width] = MUTT_TREE_RARROW;
428  myarrow[width + 1] = 0;
429  new_tree = mutt_mem_malloc(((size_t) depth * width) + 2);
430  if (start_depth > 1)
431  {
432  strncpy(new_tree, pfx, (size_t) width * (start_depth - 1));
433  mutt_str_copy(new_tree + (start_depth - 1) * width, arrow,
434  (1 + depth - start_depth) * width + 2);
435  }
436  else
437  mutt_str_copy(new_tree, arrow, ((size_t) depth * width) + 2);
438  tree->message->tree = new_tree;
439  }
440  }
441  if (tree->child && (depth != 0))
442  {
443  mypfx = pfx + (depth - 1) * width;
444  mypfx[0] = nextdisp ? MUTT_TREE_VLINE : MUTT_TREE_SPACE;
445  if (width == 2)
446  mypfx[1] = MUTT_TREE_SPACE;
447  }
448  parent = tree;
449  nextdisp = NULL;
450  pseudo = NULL;
451  do
452  {
453  if (tree->child && tree->subtree_visible)
454  {
455  if (tree->deep)
456  depth++;
457  if (tree->visible)
458  start_depth = depth;
459  tree = tree->child;
460 
461  /* we do this here because we need to make sure that the first child thread
462  * of the old tree that we deal with is actually displayed if any are,
463  * or we might set the parent variable wrong while going through it. */
464  while (!tree->subtree_visible && tree->next)
465  tree = tree->next;
466  }
467  else
468  {
469  while (!tree->next && tree->parent)
470  {
471  if (tree == pseudo)
472  pseudo = NULL;
473  if (tree == nextdisp)
474  nextdisp = NULL;
475  if (tree->visible)
476  start_depth = depth;
477  tree = tree->parent;
478  if (tree->deep)
479  {
480  if (start_depth == depth)
481  start_depth--;
482  depth--;
483  }
484  }
485  if (tree == pseudo)
486  pseudo = NULL;
487  if (tree == nextdisp)
488  nextdisp = NULL;
489  if (tree->visible)
490  start_depth = depth;
491  tree = tree->next;
492  if (!tree)
493  break;
494  }
495  if (!pseudo && tree->fake_thread)
496  pseudo = tree;
497  if (!nextdisp && tree->next_subtree_visible)
498  nextdisp = tree;
499  } while (!tree->deep);
500  }
501 
502  FREE(&pfx);
503  FREE(&arrow);
504 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
Star character (for threads)
Definition: mutt_thread.h:49
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Ampersand character (for threads)
Definition: mutt_thread.h:50
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
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
enum UseThreads mutt_thread_style(void)
Which threading style is active?
Definition: mutt_thread.c:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
Lower left corner.
Definition: mutt_thread.h:42
Top T-piece.
Definition: mutt_thread.h:52
TreeChar
Tree characters for menus.
Definition: mutt_thread.h:40
Vertical line.
Definition: mutt_thread.h:46
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:48
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:53
Left T-piece.
Definition: mutt_thread.h:44
Upper left corner.
Definition: mutt_thread.h:43
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
Horizontal line.
Definition: mutt_thread.h:45
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:749
#define FREE(x)
Definition: memory.h:40
bool visible
Is this Thread visible?
Definition: thread.h:40
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Blank space.
Definition: mutt_thread.h:47
Reverse threading (subthreads above root)
Definition: mutt_thread.h:82
static void calculate_visibility(struct MuttThread *tree, int *max_depth)
Are tree nodes visible.
Definition: mutt_thread.c:241
Question mark.
Definition: mutt_thread.h:54
Equals (for threads)
Definition: mutt_thread.h:51
+ 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 516 of file mutt_thread.c.

517 {
518  struct MuttThread *start = cur;
519  struct Envelope *env = NULL;
520  time_t thisdate;
521  int rc = 0;
522 
523  while (true)
524  {
525  while (!cur->message)
526  cur = cur->child;
527 
528  if (dateptr)
529  {
530  const bool c_thread_received =
531  cs_subset_bool(NeoMutt->sub, "thread_received");
532  thisdate = c_thread_received ? cur->message->received : cur->message->date_sent;
533  if ((*dateptr == 0) || (thisdate < *dateptr))
534  *dateptr = thisdate;
535  }
536 
537  env = cur->message->env;
538  const bool c_sort_re = cs_subset_bool(NeoMutt->sub, "sort_re");
539  if (env->real_subj && ((env->real_subj != env->subject) || !c_sort_re))
540  {
541  struct ListNode *np = NULL;
542  STAILQ_FOREACH(np, subjects, entries)
543  {
544  rc = mutt_str_cmp(env->real_subj, np->data);
545  if (rc >= 0)
546  break;
547  }
548  if (!np)
549  mutt_list_insert_head(subjects, env->real_subj);
550  else if (rc > 0)
551  mutt_list_insert_after(subjects, np, env->real_subj);
552  }
553 
554  while (!cur->next && (cur != start))
555  {
556  cur = cur->parent;
557  }
558  if (cur == start)
559  break;
560  cur = cur->next;
561  }
562 }
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
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
Container for Accounts, Notifications.
Definition: neomutt.h:36
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:352
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
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
A List node for strings.
Definition: list.h:34
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 573 of file mutt_thread.c.

574 {
575  if (!m)
576  return NULL;
577 
578  struct HashElem *ptr = NULL;
579  struct MuttThread *tmp = NULL, *last = NULL;
580  struct ListHead subjects = STAILQ_HEAD_INITIALIZER(subjects);
581  time_t date = 0;
582 
583  make_subject_list(&subjects, cur, &date);
584 
585  struct ListNode *np = NULL;
586  STAILQ_FOREACH(np, &subjects, entries)
587  {
588  for (ptr = mutt_hash_find_bucket(m->subj_hash, np->data); ptr; ptr = ptr->next)
589  {
590  const bool c_thread_received =
591  cs_subset_bool(NeoMutt->sub, "thread_received");
592  tmp = ((struct Email *) ptr->data)->thread;
593  if ((tmp != cur) && /* don't match the same message */
594  !tmp->fake_thread && /* don't match pseudo threads */
595  tmp->message->subject_changed && /* only match interesting replies */
596  !is_descendant(tmp, cur) && /* don't match in the same thread */
597  (date >= (c_thread_received ? tmp->message->received : tmp->message->date_sent)) &&
598  (!last || (c_thread_received ?
599  (last->message->received < tmp->message->received) :
600  (last->message->date_sent < tmp->message->date_sent))) &&
601  tmp->message->env->real_subj &&
602  mutt_str_equal(np->data, tmp->message->env->real_subj))
603  {
604  last = tmp; /* best match so far */
605  }
606  }
607  }
608 
609  mutt_list_clear(&subjects);
610  return last;
611 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
The envelope/body of an email.
Definition: email.h:37
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
Container for Accounts, Notifications.
Definition: neomutt.h:36
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:352
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:324
The item stored in a Hash Table.
Definition: hash.h:43
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
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:516
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
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 618 of file mutt_thread.c.

619 {
620  if (!m)
621  return NULL;
622 
623  struct HashTable *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_ALLOW_DUPS);
624 
625  for (int i = 0; i < m->msg_count; i++)
626  {
627  struct Email *e = m->emails[i];
628  if (!e || !e->env)
629  continue;
630  if (e->env->real_subj)
631  mutt_hash_insert(hash, e->env->real_subj, e);
632  }
633 
634  return hash;
635 }
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:87
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:103
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 643 of file mutt_thread.c.

644 {
645  if (!tctx || !tctx->mailbox)
646  return;
647 
648  struct Mailbox *m = tctx->mailbox;
649 
650  struct MuttThread *tree = tctx->tree;
651  struct MuttThread *top = tree;
652  struct MuttThread *tmp = NULL, *cur = NULL, *parent = NULL, *curchild = NULL,
653  *nextchild = NULL;
654 
655  if (!m->subj_hash)
656  m->subj_hash = make_subj_hash(m);
657 
658  while (tree)
659  {
660  cur = tree;
661  tree = tree->next;
662  parent = find_subject(m, cur);
663  if (parent)
664  {
665  cur->fake_thread = true;
666  unlink_message(&top, cur);
667  insert_message(&parent->child, parent, cur);
668  parent->sort_children = true;
669  tmp = cur;
670  while (true)
671  {
672  while (!tmp->message)
673  tmp = tmp->child;
674 
675  /* if the message we're attaching has pseudo-children, they
676  * need to be attached to its parent, so move them up a level.
677  * but only do this if they have the same real subject as the
678  * parent, since otherwise they rightly belong to the message
679  * we're attaching. */
680  if ((tmp == cur) || mutt_str_equal(tmp->message->env->real_subj,
681  parent->message->env->real_subj))
682  {
683  tmp->message->subject_changed = false;
684 
685  for (curchild = tmp->child; curchild;)
686  {
687  nextchild = curchild->next;
688  if (curchild->fake_thread)
689  {
690  unlink_message(&tmp->child, curchild);
691  insert_message(&parent->child, parent, curchild);
692  }
693  curchild = nextchild;
694  }
695  }
696 
697  while (!tmp->next && (tmp != cur))
698  {
699  tmp = tmp->parent;
700  }
701  if (tmp == cur)
702  break;
703  tmp = tmp->next;
704  }
705  }
706  }
707  tctx->tree = top;
708 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
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:618
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:51
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
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:573
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:102
+ 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 714 of file mutt_thread.c.

715 {
716  if (!tctx || !tctx->mailbox || !tctx->mailbox->emails || !tctx->tree)
717  return;
718 
719  for (int i = 0; i < tctx->mailbox->msg_count; i++)
720  {
721  struct Email *e = tctx->mailbox->emails[i];
722  if (!e)
723  break;
724 
725  /* mailbox may have been only partially read */
726  e->thread = NULL;
727  e->threaded = false;
728  }
729  tctx->tree = NULL;
730  mutt_hash_free(&tctx->hash);
731 }
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:449
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:51
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
bool threaded
Used for threading.
Definition: email.h:56
struct HashTable * hash
Hash table for threads.
Definition: mutt_thread.c:53
+ 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,
void *  arg 
)
static

qsort_r function for comparing email threads

Parameters
aFirst thread to compare
bSecond thread to compare
argThreadsContext for how to compare
Return values
<0a precedes b
0a and b are identical
>0b precedes a

Definition at line 742 of file mutt_thread.c.

743 {
744  const struct MuttThread *ta = *(struct MuttThread const *const *) a;
745  const struct MuttThread *tb = *(struct MuttThread const *const *) b;
746  const struct ThreadsContext *tctx = arg;
747  assert(ta->parent == tb->parent);
748  /* If c_sort ties, remember we are building the thread array in
749  * reverse from the index the mails had in the mailbox. */
750  if (ta->parent)
751  {
754  }
755  else
756  {
758  mx_type(tctx->mailbox), tctx->c_sort,
760  }
761 }
struct Email * sort_thread_key
Email that controls how top thread sorts.
Definition: thread.h:50
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:51
The "current" threading state.
Definition: mutt_thread.c:49
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct Email * sort_aux_key
Email that controls how subthread siblings sort.
Definition: thread.h:51
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:48
int mutt_compare_emails(const struct Email *a, const struct Email *b, enum MailboxType type, short sort, short sort_aux)
Compare two emails using up to two sort methods.
Definition: sort.c:328
short c_sort_aux
Last sort_aux method.
Definition: mutt_thread.c:55
short c_sort
Last sort method.
Definition: mutt_thread.c:54
An Email conversation.
Definition: thread.h:34
enum MailboxType mx_type(struct Mailbox *m)
Return the type of the Mailbox.
Definition: mx.c:1819
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:79
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_sort_subthreads()

static void mutt_sort_subthreads ( struct ThreadsContext tctx,
bool  init 
)
static

Sort the children of a thread.

Parameters
tctxThreading context
initIf true, rebuild the thread

Definition at line 768 of file mutt_thread.c.

769 {
770  struct MuttThread *thread = tctx->tree;
771  if (!thread)
772  return;
773 
774  struct MuttThread **array = NULL, *top = NULL, *tmp = NULL;
775  struct Email *sort_aux_key = NULL, *oldsort_aux_key = NULL;
776  struct Email *oldsort_thread_key = NULL;
777  int i, array_size;
778  bool sort_top = false;
779 
780  /* we put things into the array backwards to save some cycles,
781  * but we want to have to move less stuff around if we're
782  * resorting, so we sort backwards and then put them back
783  * in reverse order so they're forwards */
784  const bool reverse = (mutt_thread_style() == UT_REVERSE);
785  short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
786  short c_sort_aux = cs_subset_sort(NeoMutt->sub, "sort_aux");
787  if ((c_sort & SORT_MASK) == SORT_THREADS)
788  {
789  assert(!(c_sort & SORT_REVERSE) != reverse);
790  assert(cs_subset_enum(NeoMutt->sub, "use_threads") == UT_UNSET);
791  c_sort = c_sort_aux;
792  }
793  c_sort ^= SORT_REVERSE;
794  c_sort_aux ^= SORT_REVERSE;
795  if (init || tctx->c_sort != c_sort || tctx->c_sort_aux != c_sort_aux)
796  {
797  tctx->c_sort = c_sort;
798  tctx->c_sort_aux = c_sort_aux;
799  init = true;
800  }
801 
802  top = thread;
803 
804  array_size = 256;
805  array = mutt_mem_calloc(array_size, sizeof(struct MuttThread *));
806  while (true)
807  {
808  if (init || !thread->sort_thread_key || !thread->sort_aux_key)
809  {
810  thread->sort_thread_key = NULL;
811  thread->sort_aux_key = NULL;
812 
813  if (thread->parent)
814  thread->parent->sort_children = true;
815  else
816  sort_top = true;
817  }
818 
819  if (thread->child)
820  {
821  thread = thread->child;
822  continue;
823  }
824  else
825  {
826  /* if it has no children, it must be real. sort it on its own merits */
827  thread->sort_thread_key = thread->message;
828  thread->sort_aux_key = thread->message;
829 
830  if (thread->next)
831  {
832  thread = thread->next;
833  continue;
834  }
835  }
836 
837  while (!thread->next)
838  {
839  /* if it has siblings and needs to be sorted, sort it... */
840  if (thread->prev && (thread->parent ? thread->parent->sort_children : sort_top))
841  {
842  /* put them into the array */
843  for (i = 0; thread; i++, thread = thread->prev)
844  {
845  if (i >= array_size)
846  mutt_mem_realloc(&array, (array_size *= 2) * sizeof(struct MuttThread *));
847 
848  array[i] = thread;
849  }
850 
851  mutt_qsort_r((void *) array, i, sizeof(struct MuttThread *), compare_threads, tctx);
852 
853  /* attach them back together. make thread the last sibling. */
854  thread = array[0];
855  thread->next = NULL;
856  array[i - 1]->prev = NULL;
857 
858  if (thread->parent)
859  thread->parent->child = array[i - 1];
860  else
861  top = array[i - 1];
862 
863  while (--i)
864  {
865  array[i - 1]->prev = array[i];
866  array[i]->next = array[i - 1];
867  }
868  }
869 
870  if (thread->parent)
871  {
872  tmp = thread;
873  thread = thread->parent;
874 
875  if (!thread->sort_thread_key || !thread->sort_aux_key || thread->sort_children)
876  {
877  /* we just sorted its children */
878  thread->sort_children = false;
879 
880  oldsort_aux_key = thread->sort_aux_key;
881  oldsort_thread_key = thread->sort_thread_key;
882 
883  /* update sort keys. sort_aux_key will be the first or last
884  * sibling, as appropriate... */
885  thread->sort_aux_key = thread->message;
886  sort_aux_key = ((!(c_sort_aux & SORT_LAST)) ^ (!(c_sort_aux & SORT_REVERSE))) ?
887  thread->child->sort_aux_key :
888  tmp->sort_aux_key;
889 
890  if (c_sort_aux & SORT_LAST)
891  {
892  if (!thread->sort_aux_key ||
893  (mutt_compare_emails(thread->sort_aux_key, sort_aux_key, mx_type(tctx->mailbox),
894  c_sort_aux | SORT_REVERSE, SORT_ORDER) > 0))
895  {
896  thread->sort_aux_key = sort_aux_key;
897  }
898  }
899  else if (!thread->sort_aux_key)
900  thread->sort_aux_key = sort_aux_key;
901 
902  /* ...but sort_thread_key may require searching the entire
903  * list of siblings */
904  if ((c_sort_aux & ~SORT_REVERSE) == (c_sort & ~SORT_REVERSE))
905  {
906  thread->sort_thread_key = thread->sort_aux_key;
907  }
908  else
909  {
910  if (thread->message)
911  {
912  thread->sort_thread_key = thread->message;
913  }
914  else if (reverse != (!(c_sort_aux & SORT_REVERSE)))
915  {
916  thread->sort_thread_key = tmp->sort_thread_key;
917  }
918  else
919  {
920  thread->sort_thread_key = thread->child->sort_thread_key;
921  }
922  if (c_sort & SORT_LAST)
923  {
924  for (tmp = thread->child; tmp; tmp = tmp->next)
925  {
926  if (tmp->sort_thread_key == thread->sort_thread_key)
927  continue;
928  if ((mutt_compare_emails(thread->sort_thread_key, tmp->sort_thread_key,
929  mx_type(tctx->mailbox),
930  c_sort | SORT_REVERSE, SORT_ORDER) > 0))
931  {
932  thread->sort_thread_key = tmp->sort_thread_key;
933  }
934  }
935  }
936  }
937 
938  /* if a sort_key has changed, we need to resort it and siblings */
939  if ((oldsort_aux_key != thread->sort_aux_key) ||
940  (oldsort_thread_key != thread->sort_thread_key))
941  {
942  if (thread->parent)
943  thread->parent->sort_children = true;
944  else
945  sort_top = true;
946  }
947  }
948  }
949  else
950  {
951  FREE(&array);
952  tctx->tree = top;
953  return;
954  }
955  }
956 
957  thread = thread->next;
958  }
959 }
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 Email * sort_thread_key
Email that controls how top thread sorts.
Definition: thread.h:50
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:51
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
Not yet set by user, stick to legacy semantics.
Definition: mutt_thread.h:79
enum UseThreads mutt_thread_style(void)
Which threading style is active?
Definition: mutt_thread.c:89
struct Email * sort_aux_key
Email that controls how subthread siblings sort.
Definition: thread.h:51
Container for Accounts, Notifications.
Definition: neomutt.h:36
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:48
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
Sort by email threads.
Definition: sort2.h:49
void mutt_qsort_r(void *base, size_t nmemb, size_t size, qsort_r_compar_t compar, void *arg)
Sort an array, where the comparator has access to opaque data rather than requiring global variables...
Definition: qsort_r.c:68
int mutt_compare_emails(const struct Email *a, const struct Email *b, enum MailboxType type, short sort, short sort_aux)
Compare two emails using up to two sort methods.
Definition: sort.c:328
short c_sort_aux
Last sort_aux method.
Definition: mutt_thread.c:55
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
short c_sort
Last sort method.
Definition: mutt_thread.c:54
An Email conversation.
Definition: thread.h:34
bool sort_children
Sort the children.
Definition: thread.h:38
unsigned char cs_subset_enum(const struct ConfigSubset *sub, const char *name)
Get a enumeration config item by name.
Definition: helpers.c:97
#define FREE(x)
Definition: memory.h:40
enum MailboxType mx_type(struct Mailbox *m)
Return the type of the Mailbox.
Definition: mx.c:1819
static int compare_threads(const void *a, const void *b, void *arg)
qsort_r function for comparing email threads
Definition: mutt_thread.c:742
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:79
Reverse threading (subthreads above root)
Definition: mutt_thread.h:82
#define SORT_LAST
Sort thread by last-X, e.g. received date.
Definition: sort2.h:80
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
+ 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 966 of file mutt_thread.c.

967 {
968  if (!m)
969  return;
970 
971  for (int i = 0; i < m->msg_count; i++)
972  {
973  struct Email *e = m->emails[i];
974  if (!e || !e->thread)
975  continue;
976 
977  if (e->thread->check_subject)
978  e->thread->check_subject = false;
979  else if (!init)
980  continue;
981 
982  /* figure out which messages have subjects different than their parents' */
983  struct MuttThread *tmp = e->thread->parent;
984  while (tmp && !tmp->message)
985  {
986  tmp = tmp->parent;
987  }
988 
989  if (!tmp)
990  e->subject_changed = true;
991  else if (e->env->real_subj && tmp->message->env->real_subj)
992  {
994  }
995  else
996  {
997  e->subject_changed = (e->env->real_subj || tmp->message->env->real_subj);
998  }
999  }
1000 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
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 1007 of file mutt_thread.c.

1008 {
1009  if (!tctx || !tctx->mailbox)
1010  return;
1011 
1012  struct Mailbox *m = tctx->mailbox;
1013 
1014  struct Email *e = NULL;
1015  int i, using_refs = 0;
1016  struct MuttThread *thread = NULL, *tnew = NULL, *tmp = NULL;
1017  struct MuttThread top = { 0 };
1018  struct ListNode *ref = NULL;
1019 
1020  assert(m->msg_count > 0);
1021  if (!tctx->hash)
1022  init = true;
1023 
1024  if (init)
1025  {
1028  }
1029 
1030  /* we want a quick way to see if things are actually attached to the top of the
1031  * thread tree or if they're just dangling, so we attach everything to a top
1032  * node temporarily */
1033  top.parent = NULL;
1034  top.next = NULL;
1035  top.prev = NULL;
1036 
1037  top.child = tctx->tree;
1038  for (thread = tctx->tree; thread; thread = thread->next)
1039  thread->parent = &top;
1040 
1041  /* put each new message together with the matching messageless MuttThread if it
1042  * exists. otherwise, if there is a MuttThread that already has a message, thread
1043  * new message as an identical child. if we didn't attach the message to a
1044  * MuttThread, make a new one for it. */
1045  const bool c_duplicate_threads =
1046  cs_subset_bool(NeoMutt->sub, "duplicate_threads");
1047  for (i = 0; i < m->msg_count; i++)
1048  {
1049  e = m->emails[i];
1050  if (!e)
1051  continue;
1052 
1053  if (!e->thread)
1054  {
1055  if ((!init || c_duplicate_threads) && e->env->message_id)
1056  thread = mutt_hash_find(tctx->hash, e->env->message_id);
1057  else
1058  thread = NULL;
1059 
1060  if (thread && !thread->message)
1061  {
1062  /* this is a message which was missing before */
1063  thread->message = e;
1064  e->thread = thread;
1065  thread->check_subject = true;
1066 
1067  /* mark descendants as needing subject_changed checked */
1068  for (tmp = (thread->child ? thread->child : thread); tmp != thread;)
1069  {
1070  while (!tmp->message)
1071  tmp = tmp->child;
1072  tmp->check_subject = true;
1073  while (!tmp->next && (tmp != thread))
1074  tmp = tmp->parent;
1075  if (tmp != thread)
1076  tmp = tmp->next;
1077  }
1078 
1079  if (thread->parent)
1080  {
1081  /* remove threading info above it based on its children, which we'll
1082  * recalculate based on its headers. make sure not to leave
1083  * dangling missing messages. note that we haven't kept track
1084  * of what info came from its children and what from its siblings'
1085  * children, so we just remove the stuff that's definitely from it */
1086  do
1087  {
1088  tmp = thread->parent;
1089  unlink_message(&tmp->child, thread);
1090  thread->parent = NULL;
1091  thread->sort_thread_key = NULL;
1092  thread->sort_aux_key = NULL;
1093  thread->fake_thread = false;
1094  thread = tmp;
1095  } while (thread != &top && !thread->child && !thread->message);
1096  }
1097  }
1098  else
1099  {
1100  tnew = (c_duplicate_threads ? thread : NULL);
1101 
1102  thread = mutt_mem_calloc(1, sizeof(struct MuttThread));
1103  thread->message = e;
1104  thread->check_subject = true;
1105  e->thread = thread;
1106  mutt_hash_insert(tctx->hash, e->env->message_id ? e->env->message_id : "", thread);
1107 
1108  if (tnew)
1109  {
1110  if (tnew->duplicate_thread)
1111  tnew = tnew->parent;
1112 
1113  thread = e->thread;
1114 
1115  insert_message(&tnew->child, tnew, thread);
1116  thread->duplicate_thread = true;
1117  thread->message->threaded = true;
1118  }
1119  }
1120  }
1121  else
1122  {
1123  /* unlink pseudo-threads because they might be children of newly
1124  * arrived messages */
1125  thread = e->thread;
1126  for (tnew = thread->child; tnew;)
1127  {
1128  tmp = tnew->next;
1129  if (tnew->fake_thread)
1130  {
1131  unlink_message(&thread->child, tnew);
1132  insert_message(&top.child, &top, tnew);
1133  tnew->fake_thread = false;
1134  }
1135  tnew = tmp;
1136  }
1137  }
1138  }
1139 
1140  /* thread by references */
1141  for (i = 0; i < m->msg_count; i++)
1142  {
1143  e = m->emails[i];
1144  if (!e)
1145  break;
1146 
1147  if (e->threaded)
1148  continue;
1149  e->threaded = true;
1150 
1151  thread = e->thread;
1152  if (!thread)
1153  continue;
1154  using_refs = 0;
1155 
1156  while (true)
1157  {
1158  if (using_refs == 0)
1159  {
1160  /* look at the beginning of in-reply-to: */
1161  ref = STAILQ_FIRST(&e->env->in_reply_to);
1162  if (ref)
1163  using_refs = 1;
1164  else
1165  {
1166  ref = STAILQ_FIRST(&e->env->references);
1167  using_refs = 2;
1168  }
1169  }
1170  else if (using_refs == 1)
1171  {
1172  /* if there's no references header, use all the in-reply-to:
1173  * data that we have. otherwise, use the first reference
1174  * if it's different than the first in-reply-to, otherwise use
1175  * the second reference (since at least eudora puts the most
1176  * recent reference in in-reply-to and the rest in references) */
1177  if (STAILQ_EMPTY(&e->env->references))
1178  ref = STAILQ_NEXT(ref, entries);
1179  else
1180  {
1181  if (!mutt_str_equal(ref->data, STAILQ_FIRST(&e->env->references)->data))
1182  ref = STAILQ_FIRST(&e->env->references);
1183  else
1184  ref = STAILQ_NEXT(STAILQ_FIRST(&e->env->references), entries);
1185 
1186  using_refs = 2;
1187  }
1188  }
1189  else
1190  ref = STAILQ_NEXT(ref, entries); /* go on with references */
1191 
1192  if (!ref)
1193  break;
1194 
1195  tnew = mutt_hash_find(tctx->hash, ref->data);
1196  if (tnew)
1197  {
1198  if (tnew->duplicate_thread)
1199  tnew = tnew->parent;
1200  if (is_descendant(tnew, thread)) /* no loops! */
1201  continue;
1202  }
1203  else
1204  {
1205  tnew = mutt_mem_calloc(1, sizeof(struct MuttThread));
1206  mutt_hash_insert(tctx->hash, ref->data, tnew);
1207  }
1208 
1209  if (thread->parent)
1210  unlink_message(&top.child, thread);
1211  insert_message(&tnew->child, tnew, thread);
1212  thread = tnew;
1213  if (thread->message || (thread->parent && (thread->parent != &top)))
1214  break;
1215  }
1216 
1217  if (!thread->parent)
1218  insert_message(&top.child, &top, thread);
1219  }
1220 
1221  /* detach everything from the temporary top node */
1222  for (thread = top.child; thread; thread = thread->next)
1223  {
1224  thread->parent = NULL;
1225  }
1226  tctx->tree = top.child;
1227 
1228  check_subjects(tctx->mailbox, init);
1229 
1230  const bool c_strict_threads = cs_subset_bool(NeoMutt->sub, "strict_threads");
1231  if (!c_strict_threads)
1232  pseudo_threads(tctx);
1233 
1234  /* if $sort_aux or similar changed after the mailbox is sorted, then
1235  * all the subthreads need to be resorted */
1236  if (tctx->tree)
1237  {
1238  mutt_sort_subthreads(tctx, init || OptSortSubthreads);
1239  OptSortSubthreads = false;
1240 
1241  /* Put the list into an array. */
1242  linearize_tree(tctx);
1243 
1244  /* Draw the thread tree. */
1245  mutt_draw_tree(tctx);
1246  }
1247 }
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:904
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
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
void thread_hash_destructor(int type, void *obj, intptr_t data)
Hash Destructor callback - Implements hash_hdata_free_t.
Definition: thread.c:119
The envelope/body of an email.
Definition: email.h:37
struct Email * sort_thread_key
Email that controls how top thread sorts.
Definition: thread.h:50
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:51
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
bool threaded
Used for threading.
Definition: email.h:56
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct Email * sort_aux_key
Email that controls how subthread siblings sort.
Definition: thread.h:51
static void mutt_sort_subthreads(struct ThreadsContext *tctx, bool init)
Sort the children of a thread.
Definition: mutt_thread.c:768
static void linearize_tree(struct ThreadsContext *tctx)
Flatten an email thread.
Definition: mutt_thread.c:192
Container for Accounts, Notifications.
Definition: neomutt.h:36
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
static void pseudo_threads(struct ThreadsContext *tctx)
Thread messages by subject.
Definition: mutt_thread.c:643
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
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:103
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:400
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:102
#define STAILQ_EMPTY(head)
Definition: queue.h:348
void mutt_draw_tree(struct ThreadsContext *tctx)
Draw a tree of threaded emails.
Definition: mutt_thread.c:388
WHERE bool OptSortSubthreads
(pseudo) used when $sort_aux changes
Definition: options.h:53
bool check_subject
Should the Subject be checked?
Definition: thread.h:39
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
struct HashTable * hash
Hash table for threads.
Definition: mutt_thread.c:53
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:966
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:350
+ 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 1256 of file mutt_thread.c.

1257 {
1258  struct MuttThread *cur = NULL;
1259  struct Email *e_tmp = NULL;
1260 
1261  const enum UseThreads threaded = mutt_thread_style();
1262  if (threaded == UT_FLAT)
1263  {
1264  mutt_error(_("Threading is not enabled"));
1265  return e->vnum;
1266  }
1267 
1268  cur = e->thread;
1269 
1270  if (subthreads)
1271  {
1272  if (forwards ^ (threaded == UT_REVERSE))
1273  {
1274  while (!cur->next && cur->parent)
1275  cur = cur->parent;
1276  }
1277  else
1278  {
1279  while (!cur->prev && cur->parent)
1280  cur = cur->parent;
1281  }
1282  }
1283  else
1284  {
1285  while (cur->parent)
1286  cur = cur->parent;
1287  }
1288 
1289  if (forwards ^ (threaded == UT_REVERSE))
1290  {
1291  do
1292  {
1293  cur = cur->next;
1294  if (!cur)
1295  return -1;
1296  e_tmp = find_virtual(cur, false);
1297  } while (!e_tmp);
1298  }
1299  else
1300  {
1301  do
1302  {
1303  cur = cur->prev;
1304  if (!cur)
1305  return -1;
1306  e_tmp = find_virtual(cur, true);
1307  } while (!e_tmp);
1308  }
1309 
1310  return e_tmp->vnum;
1311 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:37
#define mutt_error(...)
Definition: logging.h:88
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
struct Email * find_virtual(struct MuttThread *cur, bool reverse)
Find an email with a Virtual message number.
Definition: thread.c:130
bool threaded
Used for threading.
Definition: email.h:56
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
enum UseThreads mutt_thread_style(void)
Which threading style is active?
Definition: mutt_thread.c:89
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
UseThreads
Which threading style is active, $use_threads.
Definition: mutt_thread.h:77
int vnum
Virtual message number.
Definition: email.h:88
Unthreaded.
Definition: mutt_thread.h:80
An Email conversation.
Definition: thread.h:34
Reverse threading (subthreads above root)
Definition: mutt_thread.h:82
+ 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 1320 of file mutt_thread.c.

1321 {
1322  if (!e)
1323  return -1;
1324 
1325  struct MuttThread *thread = NULL;
1326  struct Email *e_parent = NULL;
1327 
1328  if (!mutt_using_threads())
1329  {
1330  mutt_error(_("Threading is not enabled"));
1331  return e->vnum;
1332  }
1333 
1334  /* Root may be the current message */
1335  if (find_root)
1336  e_parent = e;
1337 
1338  for (thread = e->thread->parent; thread; thread = thread->parent)
1339  {
1340  e = thread->message;
1341  if (e)
1342  {
1343  e_parent = e;
1344  if (!find_root)
1345  break;
1346  }
1347  }
1348 
1349  if (!e_parent)
1350  {
1351  mutt_error(_("Parent message is not available"));
1352  return -1;
1353  }
1354  if (!is_visible(e_parent))
1355  {
1356  if (find_root)
1357  mutt_error(_("Root message is not visible in this limited view"));
1358  else
1359  mutt_error(_("Parent message is not visible in this limited view"));
1360  return -1;
1361  }
1362  return e_parent->vnum;
1363 }
The envelope/body of an email.
Definition: email.h:37
#define mutt_error(...)
Definition: logging.h:88
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:132
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
int vnum
Virtual message number.
Definition: email.h:88
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
#define mutt_using_threads()
Definition: mutt_thread.h:95
An Email conversation.
Definition: thread.h:34
+ 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 1370 of file mutt_thread.c.

1371 {
1372  if (!m)
1373  return 0;
1374 
1375  off_t vsize = 0;
1376  const int padding = mx_msg_padding_size(m);
1377 
1378  m->vcount = 0;
1379 
1380  for (int i = 0; i < m->msg_count; i++)
1381  {
1382  struct Email *e = m->emails[i];
1383  if (!e)
1384  break;
1385 
1386  if (e->vnum >= 0)
1387  {
1388  e->vnum = m->vcount;
1389  m->v2r[m->vcount] = i;
1390  m->vcount++;
1391  vsize += e->body->length + e->body->offset - e->body->hdr_offset + padding;
1392  }
1393  }
1394 
1395  return vsize;
1396 }
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:1540
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 1404 of file mutt_thread.c.

1405 {
1406  struct MuttThread *thread = NULL, *top = NULL;
1407  struct Email *e_root = NULL;
1408  const enum UseThreads threaded = mutt_thread_style();
1409  int final, reverse = (threaded == UT_REVERSE), minmsgno;
1410  int num_hidden = 0, new_mail = 0, old_mail = 0;
1411  bool flagged = false;
1412  int min_unread_msgno = INT_MAX, min_unread = e_cur->vnum;
1413 
1414  if (threaded == UT_FLAT)
1415  {
1416  mutt_error(_("Threading is not enabled"));
1417  return e_cur->vnum;
1418  }
1419 
1420  if (!e_cur->thread)
1421  {
1422  return e_cur->vnum;
1423  }
1424 
1425  final = e_cur->vnum;
1426  thread = e_cur->thread;
1427  while (thread->parent)
1428  thread = thread->parent;
1429  top = thread;
1430  while (!thread->message)
1431  thread = thread->child;
1432  e_cur = thread->message;
1433  minmsgno = e_cur->msgno;
1434 
1435  if (!e_cur->read && e_cur->visible)
1436  {
1437  if (e_cur->old)
1438  old_mail = 2;
1439  else
1440  new_mail = 1;
1441  if (e_cur->msgno < min_unread_msgno)
1442  {
1443  min_unread = e_cur->vnum;
1444  min_unread_msgno = e_cur->msgno;
1445  }
1446  }
1447 
1448  if (e_cur->flagged && e_cur->visible)
1449  flagged = true;
1450 
1451  if ((e_cur->vnum == -1) && e_cur->visible)
1452  num_hidden++;
1453 
1455  {
1456  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1457  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1458  if (e_cur->vnum != -1)
1459  {
1460  e_root = e_cur;
1461  if (flag & MUTT_THREAD_COLLAPSE)
1462  final = e_root->vnum;
1463  }
1464  }
1465 
1466  if ((thread == top) && !(thread = thread->child))
1467  {
1468  /* return value depends on action requested */
1469  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1470  {
1471  e_cur->num_hidden = num_hidden;
1472  return final;
1473  }
1474  if (flag & MUTT_THREAD_UNREAD)
1475  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1476  if (flag & MUTT_THREAD_NEXT_UNREAD)
1477  return min_unread;
1478  if (flag & MUTT_THREAD_FLAGGED)
1479  return flagged;
1480  }
1481 
1482  while (true)
1483  {
1484  e_cur = thread->message;
1485 
1486  if (e_cur)
1487  {
1488  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1489  {
1490  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1491  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1492  if (!e_root && e_cur->visible)
1493  {
1494  e_root = e_cur;
1495  if (flag & MUTT_THREAD_COLLAPSE)
1496  final = e_root->vnum;
1497  }
1498 
1499  if (reverse && (flag & MUTT_THREAD_COLLAPSE) &&
1500  (e_cur->msgno < minmsgno) && e_cur->visible)
1501  {
1502  minmsgno = e_cur->msgno;
1503  final = e_cur->vnum;
1504  }
1505 
1506  if (flag & MUTT_THREAD_COLLAPSE)
1507  {
1508  if (e_cur != e_root)
1509  e_cur->vnum = -1;
1510  }
1511  else
1512  {
1513  if (e_cur->visible)
1514  e_cur->vnum = e_cur->msgno;
1515  }
1516  }
1517 
1518  if (!e_cur->read && e_cur->visible)
1519  {
1520  if (e_cur->old)
1521  old_mail = 2;
1522  else
1523  new_mail = 1;
1524  if (e_cur->msgno < min_unread_msgno)
1525  {
1526  min_unread = e_cur->vnum;
1527  min_unread_msgno = e_cur->msgno;
1528  }
1529  }
1530 
1531  if (e_cur->flagged && e_cur->visible)
1532  flagged = true;
1533 
1534  if ((e_cur->vnum == -1) && e_cur->visible)
1535  num_hidden++;
1536  }
1537 
1538  if (thread->child)
1539  thread = thread->child;
1540  else if (thread->next)
1541  thread = thread->next;
1542  else
1543  {
1544  bool done = false;
1545  while (!thread->next)
1546  {
1547  thread = thread->parent;
1548  if (thread == top)
1549  {
1550  done = true;
1551  break;
1552  }
1553  }
1554  if (done)
1555  break;
1556  thread = thread->next;
1557  }
1558  }
1559 
1560  /* re-traverse the thread and store num_hidden in all headers, with or
1561  * without a virtual index. this will allow ~v to match all collapsed
1562  * messages when switching sort order to non-threaded. */
1563  if (flag & MUTT_THREAD_COLLAPSE)
1564  {
1565  thread = top;
1566  while (true)
1567  {
1568  e_cur = thread->message;
1569  if (e_cur)
1570  e_cur->num_hidden = num_hidden + 1;
1571 
1572  if (thread->child)
1573  thread = thread->child;
1574  else if (thread->next)
1575  thread = thread->next;
1576  else
1577  {
1578  bool done = false;
1579  while (!thread->next)
1580  {
1581  thread = thread->parent;
1582  if (thread == top)
1583  {
1584  done = true;
1585  break;
1586  }
1587  }
1588  if (done)
1589  break;
1590  thread = thread->next;
1591  }
1592  }
1593  }
1594 
1595  /* return value depends on action requested */
1596  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1597  return final;
1598  if (flag & MUTT_THREAD_UNREAD)
1599  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1600  if (flag & MUTT_THREAD_NEXT_UNREAD)
1601  return min_unread;
1602  if (flag & MUTT_THREAD_FLAGGED)
1603  return flagged;
1604 
1605  return 0;
1606 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:37
#define mutt_error(...)
Definition: logging.h:88
#define MUTT_THREAD_UNCOLLAPSE
Uncollapse an email thread.
Definition: mutt_thread.h:63
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
bool threaded
Used for threading.
Definition: email.h:56
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
enum UseThreads mutt_thread_style(void)
Which threading style is active?
Definition: mutt_thread.c:89
bool read
Email is read.
Definition: email.h:51
bool old
Email is seen, but unread.
Definition: email.h:50
UseThreads
Which threading style is active, $use_threads.
Definition: mutt_thread.h:77
#define MUTT_THREAD_COLLAPSE
Collapse an email thread.
Definition: mutt_thread.h:62
#define MUTT_THREAD_FLAGGED
Count flagged emails in a thread.
Definition: mutt_thread.h:66
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool visible
Is this message part of the view?
Definition: email.h:74
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:65
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
Unthreaded.
Definition: mutt_thread.h:80
An Email conversation.
Definition: thread.h:34
bool flagged
Marked important?
Definition: email.h:43
Reverse threading (subthreads above root)
Definition: mutt_thread.h:82
int pair
Color-pair to use when displaying in the index.
Definition: email.h:80
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:64
+ Here is the call graph for this function:

◆ 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 1615 of file mutt_thread.c.

1616 {
1617  if (!m || !e)
1618  return 1;
1619 
1620  struct MuttThread *threads[2];
1621  int rc;
1622 
1623  const enum UseThreads threaded = mutt_thread_style();
1624  if ((threaded == UT_FLAT) || !e->thread)
1625  return 1;
1626 
1627  threads[0] = e->thread;
1628  while (threads[0]->parent)
1629  threads[0] = threads[0]->parent;
1630 
1631  threads[1] = (mit == MIT_POSITION) ? e->thread : threads[0]->next;
1632 
1633  for (int i = 0; i < (((mit == MIT_POSITION) || !threads[1]) ? 1 : 2); i++)
1634  {
1635  while (!threads[i]->message)
1636  threads[i] = threads[i]->child;
1637  }
1638 
1639  if (threaded == UT_REVERSE)
1640  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1641  else
1642  {
1643  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1644  threads[0]->message->msgno;
1645  }
1646 
1647  if (mit == MIT_POSITION)
1648  rc += 1;
1649 
1650  return rc;
1651 }
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
enum UseThreads mutt_thread_style(void)
Which threading style is active?
Definition: mutt_thread.c:89
UseThreads
Which threading style is active, $use_threads.
Definition: mutt_thread.h:77
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
Unthreaded.
Definition: mutt_thread.h:80
An Email conversation.
Definition: thread.h:34
Reverse threading (subthreads above root)
Definition: mutt_thread.h:82
int msgno
Number displayed to the user.
Definition: email.h:87
+ Here is the call graph for this function:
+ 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 1658 of file mutt_thread.c.

1659 {
1660  struct HashTable *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NO_FLAGS);
1661 
1662  for (int i = 0; i < m->msg_count; i++)
1663  {
1664  struct Email *e = m->emails[i];
1665  if (!e || !e->env)
1666  continue;
1667 
1668  if (e->env->message_id)
1669  mutt_hash_insert(hash, e->env->message_id, e);
1670  }
1671 
1672  return hash;
1673 }
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:87
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:100
+ 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 1682 of file mutt_thread.c.

1683 {
1684  if (child == parent)
1685  return false;
1686 
1687  mutt_break_thread(child);
1689  mutt_set_flag(m, child, MUTT_TAG, false);
1690 
1691  child->changed = true;
1692  child->env->changed |= MUTT_ENV_CHANGED_IRT;
1693  return true;
1694 }
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
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:233
Tagged messages.
Definition: mutt.h:99
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 1703 of file mutt_thread.c.

1704 {
1705  if (!parent || !children || !m)
1706  return false;
1707 
1708  bool changed = false;
1709 
1710  struct EmailNode *en = NULL;
1711  STAILQ_FOREACH(en, children, entries)
1712  {
1713  changed |= link_threads(parent, en->email, m);
1714  }
1715 
1716  return changed;
1717 }
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:1682
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct Email * email
Email in the list.
Definition: email.h:131
List of Emails.
Definition: email.h:129
+ 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 1723 of file mutt_thread.c.

1724 {
1725  struct MuttThread *thread = NULL;
1726  struct MuttThread *top = tctx->tree;
1727  while ((thread = top))
1728  {
1729  while (!thread->message)
1730  thread = thread->child;
1731 
1732  struct Email *e = thread->message;
1733  if (e->collapsed)
1735  top = top->next;
1736  }
1737 }
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:88
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
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 1744 of file mutt_thread.c.

1745 {
1746  struct MuttThread *thread = NULL;
1747  struct MuttThread *top = tctx->tree;
1748  while ((thread = top))
1749  {
1750  while (!thread->message)
1751  thread = thread->child;
1752 
1753  struct Email *e = thread->message;
1754 
1755  if (e->collapsed != collapse)
1756  {
1757  if (e->collapsed)
1759  else if (mutt_thread_can_collapse(e))
1761  }
1762  top = top->next;
1763  }
1764 }
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:89
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:88
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
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:1772
+ 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 1772 of file mutt_thread.c.

1773 {
1774  const bool c_collapse_flagged =
1775  cs_subset_bool(NeoMutt->sub, "collapse_flagged");
1776  const bool c_collapse_unread =
1777  cs_subset_bool(NeoMutt->sub, "collapse_unread");
1778  return (c_collapse_unread || !mutt_thread_contains_unread(e)) &&
1779  (c_collapse_flagged || !mutt_thread_contains_flagged(e));
1780 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define mutt_thread_contains_unread(e)
Definition: mutt_thread.h:90
#define mutt_thread_contains_flagged(e)
Definition: mutt_thread.h:91
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ UseThreadsMethods

const struct Mapping UseThreadsMethods[]
static
Initial value:
= {
{ "unset", UT_UNSET },
{ "flat", UT_FLAT },
{ "threads", UT_THREADS },
{ "reverse", UT_REVERSE },
{ "no", UT_FLAT },
{ "yes", UT_THREADS },
{ NULL, 0 },
}
Normal threading (root above subthreads)
Definition: mutt_thread.h:81
Not yet set by user, stick to legacy semantics.
Definition: mutt_thread.h:79
Unthreaded.
Definition: mutt_thread.h:80
Reverse threading (subthreads above root)
Definition: mutt_thread.h:82

Choices for '$use_threads' for the index.

Definition at line 61 of file mutt_thread.c.

◆ UseThreadsTypeDef

struct EnumDef UseThreadsTypeDef
Initial value:
= {
"use_threads_type",
4,
}
static const struct Mapping UseThreadsMethods[]
Choices for &#39;$use_threads&#39; for the index.
Definition: mutt_thread.c:61
Mapping between user-readable string and a constant.
Definition: mapping.h:31

Definition at line 74 of file mutt_thread.c.