NeoMutt  2022-04-29-145-g9b6a0e
Teaching an old dog new tricks
DOXYGEN
mutt_thread.h File Reference

Create/manipulate threading in emails. More...

#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include "config/lib.h"
+ Include dependency graph for mutt_thread.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define MUTT_THREAD_NO_FLAGS   0
 No flags are set. More...
 
#define MUTT_THREAD_COLLAPSE   (1 << 0)
 Collapse an email thread. More...
 
#define MUTT_THREAD_UNCOLLAPSE   (1 << 1)
 Uncollapse an email thread. More...
 
#define MUTT_THREAD_UNREAD   (1 << 2)
 Count unread emails in a thread. More...
 
#define MUTT_THREAD_NEXT_UNREAD   (1 << 3)
 Find the next unread email. More...
 
#define MUTT_THREAD_FLAGGED   (1 << 4)
 Count flagged emails in a thread. More...
 
#define mutt_collapse_thread(e)   mutt_traverse_thread(e, MUTT_THREAD_COLLAPSE)
 
#define mutt_uncollapse_thread(e)   mutt_traverse_thread(e, MUTT_THREAD_UNCOLLAPSE)
 
#define mutt_thread_contains_unread(e)   mutt_traverse_thread(e, MUTT_THREAD_UNREAD)
 
#define mutt_thread_contains_flagged(e)   mutt_traverse_thread(e, MUTT_THREAD_FLAGGED)
 
#define mutt_thread_next_unread(e)   mutt_traverse_thread(e, MUTT_THREAD_NEXT_UNREAD)
 
#define mutt_using_threads()   (mutt_thread_style() > UT_FLAT)
 
#define mutt_next_thread(e)   mutt_aside_thread(e, true, false)
 
#define mutt_previous_thread(e)   mutt_aside_thread(e, false, false)
 
#define mutt_next_subthread(e)   mutt_aside_thread(e, true, true)
 
#define mutt_previous_subthread(e)   mutt_aside_thread(e, false, true)
 

Typedefs

typedef uint8_t MuttThreadFlags
 Flags, e.g. MUTT_THREAD_COLLAPSE. More...
 

Enumerations

enum  TreeChar {
  MUTT_TREE_LLCORNER = 1 , MUTT_TREE_ULCORNER , MUTT_TREE_LTEE , MUTT_TREE_HLINE ,
  MUTT_TREE_VLINE , MUTT_TREE_SPACE , MUTT_TREE_RARROW , MUTT_TREE_STAR ,
  MUTT_TREE_HIDDEN , MUTT_TREE_EQUALS , MUTT_TREE_TTEE , MUTT_TREE_BTEE ,
  MUTT_TREE_MISSING , MUTT_TREE_MAX , MUTT_SPECIAL_INDEX = MUTT_TREE_MAX
}
 Tree characters for menus. More...
 
enum  MessageInThread { MIT_NUM_MESSAGES , MIT_POSITION }
 Flags for mutt_messages_in_thread() More...
 
enum  UseThreads { UT_UNSET , UT_FLAT , UT_THREADS , UT_REVERSE }
 Which threading style is active, $use_threads. More...
 

Functions

int mutt_traverse_thread (struct Email *e, MuttThreadFlags flag)
 Recurse through an email thread, matching messages. More...
 
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...
 
int mutt_aside_thread (struct Email *e, bool forwards, bool subthreads)
 Find the next/previous (sub)thread. 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_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...
 
void mutt_clear_threads (struct ThreadsContext *tctx)
 Clear the threading of message in a mailbox. More...
 
void mutt_draw_tree (struct ThreadsContext *tctx)
 Draw a tree of threaded emails. More...
 
bool mutt_link_threads (struct Email *parent, struct EmailList *children, struct Mailbox *m)
 Forcibly link threads together. More...
 
struct HashTablemutt_make_id_hash (struct Mailbox *m)
 Create a Hash Table for message-ids. More...
 
int mutt_messages_in_thread (struct Mailbox *m, struct Email *e, enum MessageInThread mit)
 Count the messages in a 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...
 
void mutt_sort_threads (struct ThreadsContext *tctx, bool init)
 Sort email threads. More...
 

Variables

struct EnumDef UseThreadsTypeDef
 

Detailed Description

Create/manipulate threading in emails.

Authors
  • Richard Russon

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.h.

Macro Definition Documentation

◆ MUTT_THREAD_NO_FLAGS

#define MUTT_THREAD_NO_FLAGS   0

No flags are set.

Definition at line 63 of file mutt_thread.h.

◆ MUTT_THREAD_COLLAPSE

#define MUTT_THREAD_COLLAPSE   (1 << 0)

Collapse an email thread.

Definition at line 64 of file mutt_thread.h.

◆ MUTT_THREAD_UNCOLLAPSE

#define MUTT_THREAD_UNCOLLAPSE   (1 << 1)

Uncollapse an email thread.

Definition at line 65 of file mutt_thread.h.

◆ MUTT_THREAD_UNREAD

#define MUTT_THREAD_UNREAD   (1 << 2)

Count unread emails in a thread.

Definition at line 66 of file mutt_thread.h.

◆ MUTT_THREAD_NEXT_UNREAD

#define MUTT_THREAD_NEXT_UNREAD   (1 << 3)

Find the next unread email.

Definition at line 67 of file mutt_thread.h.

◆ MUTT_THREAD_FLAGGED

#define MUTT_THREAD_FLAGGED   (1 << 4)

Count flagged emails in a thread.

Definition at line 68 of file mutt_thread.h.

◆ mutt_collapse_thread

#define mutt_collapse_thread (   e)    mutt_traverse_thread(e, MUTT_THREAD_COLLAPSE)

Definition at line 93 of file mutt_thread.h.

◆ mutt_uncollapse_thread

#define mutt_uncollapse_thread (   e)    mutt_traverse_thread(e, MUTT_THREAD_UNCOLLAPSE)

Definition at line 94 of file mutt_thread.h.

◆ mutt_thread_contains_unread

#define mutt_thread_contains_unread (   e)    mutt_traverse_thread(e, MUTT_THREAD_UNREAD)

Definition at line 95 of file mutt_thread.h.

◆ mutt_thread_contains_flagged

#define mutt_thread_contains_flagged (   e)    mutt_traverse_thread(e, MUTT_THREAD_FLAGGED)

Definition at line 96 of file mutt_thread.h.

◆ mutt_thread_next_unread

#define mutt_thread_next_unread (   e)    mutt_traverse_thread(e, MUTT_THREAD_NEXT_UNREAD)

Definition at line 97 of file mutt_thread.h.

◆ mutt_using_threads

#define mutt_using_threads ( )    (mutt_thread_style() > UT_FLAT)

Definition at line 100 of file mutt_thread.h.

◆ mutt_next_thread

#define mutt_next_thread (   e)    mutt_aside_thread(e, true, false)

Definition at line 106 of file mutt_thread.h.

◆ mutt_previous_thread

#define mutt_previous_thread (   e)    mutt_aside_thread(e, false, false)

Definition at line 107 of file mutt_thread.h.

◆ mutt_next_subthread

#define mutt_next_subthread (   e)    mutt_aside_thread(e, true, true)

Definition at line 108 of file mutt_thread.h.

◆ mutt_previous_subthread

#define mutt_previous_subthread (   e)    mutt_aside_thread(e, false, true)

Definition at line 109 of file mutt_thread.h.

Typedef Documentation

◆ MuttThreadFlags

typedef uint8_t MuttThreadFlags

Flags, e.g. MUTT_THREAD_COLLAPSE.

Definition at line 62 of file mutt_thread.h.

Enumeration Type Documentation

◆ TreeChar

enum TreeChar

Tree characters for menus.

See also
linearize_tree(), print_enriched_string()
Enumerator
MUTT_TREE_LLCORNER 

Lower left corner.

MUTT_TREE_ULCORNER 

Upper left corner.

MUTT_TREE_LTEE 

Left T-piece.

MUTT_TREE_HLINE 

Horizontal line.

MUTT_TREE_VLINE 

Vertical line.

MUTT_TREE_SPACE 

Blank space.

MUTT_TREE_RARROW 

Right arrow.

MUTT_TREE_STAR 

Star character (for threads)

MUTT_TREE_HIDDEN 

Ampersand character (for threads)

MUTT_TREE_EQUALS 

Equals (for threads)

MUTT_TREE_TTEE 

Top T-piece.

MUTT_TREE_BTEE 

Bottom T-piece.

MUTT_TREE_MISSING 

Question mark.

MUTT_TREE_MAX 
MUTT_SPECIAL_INDEX 

Colour indicator.

Definition at line 42 of file mutt_thread.h.

43 {
44  MUTT_TREE_LLCORNER = 1,
58 
60 };
@ MUTT_TREE_MAX
Definition: mutt_thread.h:57
@ MUTT_TREE_LLCORNER
Lower left corner.
Definition: mutt_thread.h:44
@ MUTT_TREE_RARROW
Right arrow.
Definition: mutt_thread.h:50
@ MUTT_SPECIAL_INDEX
Colour indicator.
Definition: mutt_thread.h:59
@ MUTT_TREE_ULCORNER
Upper left corner.
Definition: mutt_thread.h:45
@ MUTT_TREE_EQUALS
Equals (for threads)
Definition: mutt_thread.h:53
@ MUTT_TREE_HIDDEN
Ampersand character (for threads)
Definition: mutt_thread.h:52
@ MUTT_TREE_STAR
Star character (for threads)
Definition: mutt_thread.h:51
@ MUTT_TREE_LTEE
Left T-piece.
Definition: mutt_thread.h:46
@ MUTT_TREE_VLINE
Vertical line.
Definition: mutt_thread.h:48
@ MUTT_TREE_MISSING
Question mark.
Definition: mutt_thread.h:56
@ MUTT_TREE_TTEE
Top T-piece.
Definition: mutt_thread.h:54
@ MUTT_TREE_HLINE
Horizontal line.
Definition: mutt_thread.h:47
@ MUTT_TREE_SPACE
Blank space.
Definition: mutt_thread.h:49
@ MUTT_TREE_BTEE
Bottom T-piece.
Definition: mutt_thread.h:55

◆ MessageInThread

Flags for mutt_messages_in_thread()

Enumerator
MIT_NUM_MESSAGES 

How many messages are in the thread.

MIT_POSITION 

Our position in the thread.

Definition at line 73 of file mutt_thread.h.

74 {
76  MIT_POSITION,
77 };
@ MIT_NUM_MESSAGES
How many messages are in the thread.
Definition: mutt_thread.h:75
@ MIT_POSITION
Our position in the thread.
Definition: mutt_thread.h:76

◆ UseThreads

enum UseThreads

Which threading style is active, $use_threads.

Enumerator
UT_UNSET 

Not yet set by user, stick to legacy semantics.

UT_FLAT 

Unthreaded.

UT_THREADS 

Normal threading (root above subthreads)

UT_REVERSE 

Reverse threading (subthreads above root)

Definition at line 82 of file mutt_thread.h.

83 {
84  UT_UNSET,
85  UT_FLAT,
86  UT_THREADS,
87  UT_REVERSE,
88 };
@ UT_FLAT
Unthreaded.
Definition: mutt_thread.h:85
@ UT_UNSET
Not yet set by user, stick to legacy semantics.
Definition: mutt_thread.h:84
@ UT_THREADS
Normal threading (root above subthreads)
Definition: mutt_thread.h:86
@ UT_REVERSE
Reverse threading (subthreads above root)
Definition: mutt_thread.h:87

Function Documentation

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

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

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

90 {
91  const unsigned char c_use_threads = cs_subset_enum(NeoMutt->sub, "use_threads");
92  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
93  if (c_use_threads > UT_FLAT)
94  return c_use_threads;
95  if ((c_sort & SORT_MASK) != SORT_THREADS)
96  return UT_FLAT;
97  if (c_sort & SORT_REVERSE)
98  return UT_REVERSE;
99  return UT_THREADS;
100 }
unsigned char cs_subset_enum(const struct ConfigSubset *sub, const char *name)
Get a enumeration config item by name.
Definition: helpers.c:97
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:78
@ SORT_THREADS
Sort by email threads.
Definition: sort2.h:49
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:79
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ 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
ptrString form of value

Definition at line 107 of file mutt_thread.c.

108 {
109  return mutt_map_get_name(value, UseThreadsMethods);
110 }
const char * mutt_map_get_name(int val, const struct Mapping *map)
Lookup a string for a constant.
Definition: mapping.c:42
static const struct Mapping UseThreadsMethods[]
Choices for '$use_threads' for the index.
Definition: mutt_thread.c:61
+ 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 1249 of file mutt_thread.c.

1250 {
1251  struct MuttThread *cur = NULL;
1252  struct Email *e_tmp = NULL;
1253 
1254  const enum UseThreads threaded = mutt_thread_style();
1255  if (threaded == UT_FLAT)
1256  {
1257  mutt_error(_("Threading is not enabled"));
1258  return e->vnum;
1259  }
1260 
1261  cur = e->thread;
1262 
1263  if (subthreads)
1264  {
1265  if (forwards ^ (threaded == UT_REVERSE))
1266  {
1267  while (!cur->next && cur->parent)
1268  cur = cur->parent;
1269  }
1270  else
1271  {
1272  while (!cur->prev && cur->parent)
1273  cur = cur->parent;
1274  }
1275  }
1276  else
1277  {
1278  while (cur->parent)
1279  cur = cur->parent;
1280  }
1281 
1282  if (forwards ^ (threaded == UT_REVERSE))
1283  {
1284  do
1285  {
1286  cur = cur->next;
1287  if (!cur)
1288  return -1;
1289  e_tmp = find_virtual(cur, false);
1290  } while (!e_tmp);
1291  }
1292  else
1293  {
1294  do
1295  {
1296  cur = cur->prev;
1297  if (!cur)
1298  return -1;
1299  e_tmp = find_virtual(cur, true);
1300  } while (!e_tmp);
1301  }
1302 
1303  return e_tmp->vnum;
1304 }
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
struct Email * find_virtual(struct MuttThread *cur, bool reverse)
Find an email with a Virtual message number.
Definition: thread.c:130
+ Here is the call 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
ptrThreading context

Definition at line 352 of file mutt_thread.c.

353 {
354  struct ThreadsContext *tctx = mutt_mem_calloc(1, sizeof(struct ThreadsContext));
355  tctx->mailbox = m;
356  tctx->tree = NULL;
357  tctx->hash = NULL;
358  return tctx;
359 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
The "current" threading state.
Definition: mutt_thread.c:50
struct MuttThread * tree
Top of thread tree.
Definition: mutt_thread.c:52
struct HashTable * hash
Hash table for threads.
Definition: mutt_thread.c:53
struct Mailbox * mailbox
Current mailbox.
Definition: mutt_thread.c:51
+ 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 365 of file mutt_thread.c.

366 {
367  (*tctx)->mailbox = NULL;
368  mutt_hash_free(&(*tctx)->hash);
369  FREE(tctx);
370 }
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
#define FREE(x)
Definition: memory.h:43
+ 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 1716 of file mutt_thread.c.

1717 {
1718  struct MuttThread *thread = NULL;
1719  struct MuttThread *top = tctx->tree;
1720  while ((thread = top))
1721  {
1722  while (!thread->message)
1723  thread = thread->child;
1724 
1725  struct Email *e = thread->message;
1726  if (e->collapsed)
1728  top = top->next;
1729  }
1730 }
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:93

◆ mutt_thread_collapse()

void mutt_thread_collapse ( struct ThreadsContext tctx,
bool  collapse 
)

Toggle collapse.

Parameters
tctxThreading context
collapseCollapse / uncollapse

Definition at line 1737 of file mutt_thread.c.

1738 {
1739  struct MuttThread *thread = NULL;
1740  struct MuttThread *top = tctx->tree;
1741  while ((thread = top))
1742  {
1743  while (!thread->message)
1744  thread = thread->child;
1745 
1746  struct Email *e = thread->message;
1747 
1748  if (e->collapsed != collapse)
1749  {
1750  if (e->collapsed)
1752  else if (mutt_thread_can_collapse(e))
1754  }
1755  top = top->next;
1756  }
1757 }
bool mutt_thread_can_collapse(struct Email *e)
Check whether a thread can be collapsed.
Definition: mutt_thread.c:1765
#define mutt_uncollapse_thread(e)
Definition: mutt_thread.h:94
+ 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 1765 of file mutt_thread.c.

1766 {
1767  const bool c_collapse_flagged = cs_subset_bool(NeoMutt->sub, "collapse_flagged");
1768  const bool c_collapse_unread = cs_subset_bool(NeoMutt->sub, "collapse_unread");
1769  return (c_collapse_unread || !mutt_thread_contains_unread(e)) &&
1770  (c_collapse_flagged || !mutt_thread_contains_flagged(e));
1771 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define mutt_thread_contains_flagged(e)
Definition: mutt_thread.h:96
#define mutt_thread_contains_unread(e)
Definition: mutt_thread.h:95
+ 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 708 of file mutt_thread.c.

709 {
710  if (!tctx || !tctx->mailbox || !tctx->mailbox->emails || !tctx->tree)
711  return;
712 
713  for (int i = 0; i < tctx->mailbox->msg_count; i++)
714  {
715  struct Email *e = tctx->mailbox->emails[i];
716  if (!e)
717  break;
718 
719  /* mailbox may have been only partially read */
720  e->thread = NULL;
721  e->threaded = false;
722  }
723  tctx->tree = NULL;
724  mutt_hash_free(&tctx->hash);
725 }
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
+ 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 384 of file mutt_thread.c.

385 {
386  char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree = NULL;
387  const bool reverse = (mutt_thread_style() == UT_REVERSE);
388  enum TreeChar corner = reverse ? MUTT_TREE_ULCORNER : MUTT_TREE_LLCORNER;
389  enum TreeChar vtee = reverse ? MUTT_TREE_BTEE : MUTT_TREE_TTEE;
390  const bool c_narrow_tree = cs_subset_bool(NeoMutt->sub, "narrow_tree");
391  int depth = 0, start_depth = 0, max_depth = 0, width = c_narrow_tree ? 1 : 2;
392  struct MuttThread *nextdisp = NULL, *pseudo = NULL, *parent = NULL;
393 
394  struct MuttThread *tree = tctx->tree;
395 
396  /* Do the visibility calculations and free the old thread chars.
397  * From now on we can simply ignore invisible subtrees */
398  calculate_visibility(tree, &max_depth);
399  pfx = mutt_mem_malloc((width * max_depth) + 2);
400  arrow = mutt_mem_malloc((width * max_depth) + 2);
401  while (tree)
402  {
403  if (depth != 0)
404  {
405  myarrow = arrow + (depth - start_depth - ((start_depth != 0) ? 0 : 1)) * width;
406  const bool c_hide_limited = cs_subset_bool(NeoMutt->sub, "hide_limited");
407  const bool c_hide_missing = cs_subset_bool(NeoMutt->sub, "hide_missing");
408  if (start_depth == depth)
409  myarrow[0] = nextdisp ? MUTT_TREE_LTEE : corner;
410  else if (parent->message && !c_hide_limited)
411  myarrow[0] = MUTT_TREE_HIDDEN;
412  else if (!parent->message && !c_hide_missing)
413  myarrow[0] = MUTT_TREE_MISSING;
414  else
415  myarrow[0] = vtee;
416  if (width == 2)
417  {
418  myarrow[1] = pseudo ? MUTT_TREE_STAR :
420  }
421  if (tree->visible)
422  {
423  myarrow[width] = MUTT_TREE_RARROW;
424  myarrow[width + 1] = 0;
425  new_tree = mutt_mem_malloc(((size_t) depth * width) + 2);
426  if (start_depth > 1)
427  {
428  strncpy(new_tree, pfx, (size_t) width * (start_depth - 1));
429  mutt_str_copy(new_tree + (start_depth - 1) * width, arrow,
430  (1 + depth - start_depth) * width + 2);
431  }
432  else
433  mutt_str_copy(new_tree, arrow, ((size_t) depth * width) + 2);
434  tree->message->tree = new_tree;
435  }
436  }
437  if (tree->child && (depth != 0))
438  {
439  mypfx = pfx + (depth - 1) * width;
440  mypfx[0] = nextdisp ? MUTT_TREE_VLINE : MUTT_TREE_SPACE;
441  if (width == 2)
442  mypfx[1] = MUTT_TREE_SPACE;
443  }
444  parent = tree;
445  nextdisp = NULL;
446  pseudo = NULL;
447  do
448  {
449  if (tree->child && tree->subtree_visible)
450  {
451  if (tree->deep)
452  depth++;
453  if (tree->visible)
454  start_depth = depth;
455  tree = tree->child;
456 
457  /* we do this here because we need to make sure that the first child thread
458  * of the old tree that we deal with is actually displayed if any are,
459  * or we might set the parent variable wrong while going through it. */
460  while (!tree->subtree_visible && tree->next)
461  tree = tree->next;
462  }
463  else
464  {
465  while (!tree->next && tree->parent)
466  {
467  if (tree == pseudo)
468  pseudo = NULL;
469  if (tree == nextdisp)
470  nextdisp = NULL;
471  if (tree->visible)
472  start_depth = depth;
473  tree = tree->parent;
474  if (tree->deep)
475  {
476  if (start_depth == depth)
477  start_depth--;
478  depth--;
479  }
480  }
481  if (tree == pseudo)
482  pseudo = NULL;
483  if (tree == nextdisp)
484  nextdisp = NULL;
485  if (tree->visible)
486  start_depth = depth;
487  tree = tree->next;
488  if (!tree)
489  break;
490  }
491  if (!pseudo && tree->fake_thread)
492  pseudo = tree;
493  if (!nextdisp && tree->next_subtree_visible)
494  nextdisp = tree;
495  } while (!tree->deep);
496  }
497 
498  FREE(&pfx);
499  FREE(&arrow);
500 }
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
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:629
static void calculate_visibility(struct MuttThread *tree, int *max_depth)
Are tree nodes visible.
Definition: mutt_thread.c:239
TreeChar
Tree characters for menus.
Definition: mutt_thread.h:43
char * tree
Character string to print thread tree.
Definition: email.h:124
bool visible
Is this Thread visible?
Definition: thread.h:40
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
bool deep
Is the Thread deeply nested?
Definition: thread.h:41
unsigned int subtree_visible
Is this Thread subtree visible?
Definition: thread.h:42
bool duplicate_thread
Duplicated Email in Thread.
Definition: thread.h:37
bool next_subtree_visible
Is the next Thread subtree visible?
Definition: thread.h:43
+ 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 1696 of file mutt_thread.c.

1697 {
1698  if (!parent || !children || !m)
1699  return false;
1700 
1701  bool changed = false;
1702 
1703  struct EmailNode *en = NULL;
1704  STAILQ_FOREACH(en, children, entries)
1705  {
1706  changed |= link_threads(parent, en->email, m);
1707  }
1708 
1709  return changed;
1710 }
static bool link_threads(struct Email *parent, struct Email *child, struct Mailbox *m)
Forcibly link messages together.
Definition: mutt_thread.c:1675
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
List of Emails.
Definition: email.h:131
struct Email * email
Email in the list.
Definition: email.h:132
+ 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 1651 of file mutt_thread.c.

1652 {
1653  struct HashTable *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NO_FLAGS);
1654 
1655  for (int i = 0; i < m->msg_count; i++)
1656  {
1657  struct Email *e = m->emails[i];
1658  if (!e || !e->env)
1659  continue;
1660 
1661  if (e->env->message_id)
1662  mutt_hash_insert(hash, e->env->message_id, e);
1663  }
1664 
1665  return hash;
1666 }
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:335
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:259
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:109
struct Envelope * env
Envelope information.
Definition: email.h:66
char * message_id
Message ID.
Definition: envelope.h:73
A Hash Table.
Definition: hash.h:97
+ Here is the call graph for this function:
+ Here is the caller 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 1608 of file mutt_thread.c.

1609 {
1610  if (!m || !e)
1611  return 1;
1612 
1613  struct MuttThread *threads[2];
1614  int rc;
1615 
1616  const enum UseThreads threaded = mutt_thread_style();
1617  if ((threaded == UT_FLAT) || !e->thread)
1618  return 1;
1619 
1620  threads[0] = e->thread;
1621  while (threads[0]->parent)
1622  threads[0] = threads[0]->parent;
1623 
1624  threads[1] = (mit == MIT_POSITION) ? e->thread : threads[0]->next;
1625 
1626  for (int i = 0; i < (((mit == MIT_POSITION) || !threads[1]) ? 1 : 2); i++)
1627  {
1628  while (!threads[i]->message)
1629  threads[i] = threads[i]->child;
1630  }
1631 
1632  if (threaded == UT_REVERSE)
1633  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1634  else
1635  {
1636  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1637  threads[0]->message->msgno;
1638  }
1639 
1640  if (mit == MIT_POSITION)
1641  rc += 1;
1642 
1643  return rc;
1644 }
+ Here is the call graph for this function:
+ Here is the caller 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 1313 of file mutt_thread.c.

1314 {
1315  if (!e)
1316  return -1;
1317 
1318  struct MuttThread *thread = NULL;
1319  struct Email *e_parent = NULL;
1320 
1321  if (!mutt_using_threads())
1322  {
1323  mutt_error(_("Threading is not enabled"));
1324  return e->vnum;
1325  }
1326 
1327  /* Root may be the current message */
1328  if (find_root)
1329  e_parent = e;
1330 
1331  for (thread = e->thread->parent; thread; thread = thread->parent)
1332  {
1333  e = thread->message;
1334  if (e)
1335  {
1336  e_parent = e;
1337  if (!find_root)
1338  break;
1339  }
1340  }
1341 
1342  if (!e_parent)
1343  {
1344  mutt_error(_("Parent message is not available"));
1345  return -1;
1346  }
1347  if (!is_visible(e_parent))
1348  {
1349  if (find_root)
1350  mutt_error(_("Root message is not visible in this limited view"));
1351  else
1352  mutt_error(_("Parent message is not visible in this limited view"));
1353  return -1;
1354  }
1355  return e_parent->vnum;
1356 }
static bool is_visible(struct Email *e)
Is the message visible?
Definition: mutt_thread.c:131
#define mutt_using_threads()
Definition: mutt_thread.h:100
+ 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 1363 of file mutt_thread.c.

1364 {
1365  if (!m)
1366  return 0;
1367 
1368  off_t vsize = 0;
1369  const int padding = mx_msg_padding_size(m);
1370 
1371  m->vcount = 0;
1372 
1373  for (int i = 0; i < m->msg_count; i++)
1374  {
1375  struct Email *e = m->emails[i];
1376  if (!e)
1377  break;
1378 
1379  if (e->vnum >= 0)
1380  {
1381  e->vnum = m->vcount;
1382  m->v2r[m->vcount] = i;
1383  m->vcount++;
1384  vsize += e->body->length + e->body->offset - e->body->hdr_offset + padding;
1385  }
1386  }
1387 
1388  return vsize;
1389 }
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1549
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:80
struct Body * body
List of MIME parts.
Definition: email.h:67
int vcount
The number of virtual messages.
Definition: mailbox.h:99
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:98
+ 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 1001 of file mutt_thread.c.

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

Variable Documentation

◆ UseThreadsTypeDef

struct EnumDef UseThreadsTypeDef
extern

Definition at line 1 of file mutt_thread.c.