NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
thread.h File Reference

Create/manipulate threading in emails. More...

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

Go to the source code of this file.

Data Structures

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

Macros

#define mutt_collapse_thread(e)
 
#define mutt_uncollapse_thread(e)
 
#define mutt_thread_contains_unread(e)
 
#define mutt_thread_contains_flagged(e)
 
#define mutt_thread_next_unread(e)
 
#define mutt_using_threads()
 
#define mutt_next_thread(e)
 
#define mutt_previous_thread(e)
 
#define mutt_next_subthread(e)
 
#define mutt_previous_subthread(e)
 

Typedefs

typedef uint8_t MuttThreadFlags
 

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  MuttThreadFlag {
  MUTT_THREAD_NONE = 0 , MUTT_THREAD_COLLAPSE = 1U << 0 , MUTT_THREAD_UNCOLLAPSE = 1U << 1 , MUTT_THREAD_UNREAD = 1U << 2 ,
  MUTT_THREAD_NEXT_UNREAD = 1U << 3 , MUTT_THREAD_FLAGGED = 1U << 4
}
 Flags, e.g. More...
 
enum  MessageInThread { MIT_NUM_MESSAGES , MIT_POSITION }
 Result selectors 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.
 
enum UseThreads mutt_thread_style (void)
 Which threading style is active?
 
const char * get_use_threads_str (enum UseThreads value)
 Convert UseThreads enum to string.
 
int sort_validator (const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
 Validate the "sort" config variable - Implements ConfigDef::validator() -.
 
int mutt_aside_thread (struct Email *e, bool forwards, bool subthreads)
 Find the next/previous (sub)thread.
 
struct ThreadsContextmutt_thread_ctx_init (struct MailboxView *mv)
 Initialize a threading context.
 
void mutt_thread_ctx_free (struct ThreadsContext **ptr)
 Finalize a threading context.
 
void mutt_thread_collapse_collapsed (struct ThreadsContext *tctx)
 Re-collapse threads marked as collapsed.
 
void mutt_thread_collapse (struct ThreadsContext *tctx, bool collapse)
 Toggle collapse.
 
bool mutt_thread_can_collapse (struct Email *e)
 Check whether a thread can be collapsed.
 
void mutt_clear_threads (struct ThreadsContext *tctx)
 Clear the threading of message in a mailbox.
 
void mutt_draw_tree (struct ThreadsContext *tctx)
 Draw a tree of threaded emails.
 
bool mutt_link_threads (struct Email *parent, struct EmailArray *children, struct Mailbox *m)
 Forcibly link threads together.
 
struct HashTablemutt_make_id_hash (struct Mailbox *m)
 Create a Hash Table for Message-IDs.
 
int mutt_messages_in_thread (struct Mailbox *m, struct Email *e, enum MessageInThread mit)
 Count the messages in a thread.
 
int mutt_parent_message (struct Email *e, bool find_root, int count)
 Find the parent of a message.
 
off_t mutt_set_vnum (struct Mailbox *m)
 Set the virtual index number of all the messages in a mailbox.
 
void mutt_sort_threads (struct ThreadsContext *tctx, bool init)
 Sort email threads.
 

Variables

const struct EnumDef UseThreadsTypeDef
 Data for the $use_threads enumeration.
 

Detailed Description

Create/manipulate threading in emails.

Authors
  • Richard Russon
  • Pietro Cerutti
  • Eric Blake

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

Macro Definition Documentation

◆ mutt_collapse_thread

#define mutt_collapse_thread ( e)
Value:
int mutt_traverse_thread(struct Email *e_cur, MuttThreadFlags flag)
Recurse through an email thread, matching messages.
Definition thread.c:1549
@ MUTT_THREAD_COLLAPSE
Collapse an email thread.
Definition thread.h:81

Definition at line 112 of file thread.h.

◆ mutt_uncollapse_thread

#define mutt_uncollapse_thread ( e)
Value:
@ MUTT_THREAD_UNCOLLAPSE
Uncollapse an email thread.
Definition thread.h:82

Definition at line 113 of file thread.h.

◆ mutt_thread_contains_unread

#define mutt_thread_contains_unread ( e)
Value:
@ MUTT_THREAD_UNREAD
Count unread emails in a thread.
Definition thread.h:83

Definition at line 114 of file thread.h.

◆ mutt_thread_contains_flagged

#define mutt_thread_contains_flagged ( e)
Value:
@ MUTT_THREAD_FLAGGED
Count flagged emails in a thread.
Definition thread.h:85

Definition at line 115 of file thread.h.

◆ mutt_thread_next_unread

#define mutt_thread_next_unread ( e)
Value:
@ MUTT_THREAD_NEXT_UNREAD
Find the next unread email.
Definition thread.h:84

Definition at line 116 of file thread.h.

◆ mutt_using_threads

#define mutt_using_threads ( )
Value:
enum UseThreads mutt_thread_style(void)
Which threading style is active?
Definition thread.c:79
@ UT_FLAT
Unthreaded.
Definition thread.h:104

Definition at line 119 of file thread.h.

◆ mutt_next_thread

#define mutt_next_thread ( e)
Value:
mutt_aside_thread(e, true, false)
int mutt_aside_thread(struct Email *e, bool forwards, bool subthreads)
Find the next/previous (sub)thread.
Definition thread.c:1389

Definition at line 124 of file thread.h.

◆ mutt_previous_thread

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

Definition at line 125 of file thread.h.

◆ mutt_next_subthread

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

Definition at line 126 of file thread.h.

◆ mutt_previous_subthread

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

Definition at line 127 of file thread.h.

Typedef Documentation

◆ MuttThreadFlags

typedef uint8_t MuttThreadFlags

Definition at line 87 of file 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 55 of file thread.h.

56{
71
73};
@ MUTT_TREE_MAX
Definition thread.h:70
@ MUTT_TREE_LLCORNER
Lower left corner.
Definition thread.h:57
@ MUTT_TREE_RARROW
Right arrow.
Definition thread.h:63
@ MUTT_SPECIAL_INDEX
Colour indicator.
Definition thread.h:72
@ MUTT_TREE_ULCORNER
Upper left corner.
Definition thread.h:58
@ MUTT_TREE_EQUALS
Equals (for threads)
Definition thread.h:66
@ MUTT_TREE_HIDDEN
Ampersand character (for threads)
Definition thread.h:65
@ MUTT_TREE_STAR
Star character (for threads)
Definition thread.h:64
@ MUTT_TREE_LTEE
Left T-piece.
Definition thread.h:59
@ MUTT_TREE_VLINE
Vertical line.
Definition thread.h:61
@ MUTT_TREE_MISSING
Question mark.
Definition thread.h:69
@ MUTT_TREE_TTEE
Top T-piece.
Definition thread.h:67
@ MUTT_TREE_HLINE
Horizontal line.
Definition thread.h:60
@ MUTT_TREE_SPACE
Blank space.
Definition thread.h:62
@ MUTT_TREE_BTEE
Bottom T-piece.
Definition thread.h:68

◆ MuttThreadFlag

Flags, e.g.

MUTT_THREAD_COLLAPSE

Enumerator
MUTT_THREAD_NONE 

No flags are set.

MUTT_THREAD_COLLAPSE 

Collapse an email thread.

MUTT_THREAD_UNCOLLAPSE 

Uncollapse an email thread.

MUTT_THREAD_UNREAD 

Count unread emails in a thread.

MUTT_THREAD_NEXT_UNREAD 

Find the next unread email.

MUTT_THREAD_FLAGGED 

Count flagged emails in a thread.

Definition at line 78 of file thread.h.

79{
81 MUTT_THREAD_COLLAPSE = 1U << 0,
82 MUTT_THREAD_UNCOLLAPSE = 1U << 1,
83 MUTT_THREAD_UNREAD = 1U << 2,
84 MUTT_THREAD_NEXT_UNREAD = 1U << 3,
85 MUTT_THREAD_FLAGGED = 1U << 4,
86};
@ MUTT_THREAD_NONE
No flags are set.
Definition thread.h:80

◆ MessageInThread

Result selectors 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 92 of file thread.h.

93{
96};
@ MIT_NUM_MESSAGES
How many messages are in the thread.
Definition thread.h:94
@ MIT_POSITION
Our position in the thread.
Definition thread.h:95

◆ 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 101 of file thread.h.

102{
103 UT_UNSET,
104 UT_FLAT,
105 UT_THREADS,
106 UT_REVERSE,
107};
@ UT_UNSET
Not yet set by user, stick to legacy semantics.
Definition thread.h:103
@ UT_THREADS
Normal threading (root above subthreads)
Definition thread.h:105
@ UT_REVERSE
Reverse threading (subthreads above root)
Definition thread.h:106

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 1549 of file thread.c.

1550{
1551 struct MuttThread *thread = NULL;
1552 struct MuttThread *top = NULL;
1553 struct Email *e_root = NULL;
1554 const enum UseThreads threaded = mutt_thread_style();
1555 int final;
1556 int reverse = (threaded == UT_REVERSE);
1557 int minmsgno;
1558 int num_hidden = 0;
1559 int new_mail = 0;
1560 int old_mail = 0;
1561 bool flagged = false;
1562 int min_unread_msgno = INT_MAX;
1563 int min_unread = e_cur->vnum;
1564
1565 if (threaded == UT_FLAT)
1566 {
1567 mutt_warning(_("Threading is not enabled"));
1568 return e_cur->vnum;
1569 }
1570
1571 if (!e_cur->thread)
1572 {
1573 return e_cur->vnum;
1574 }
1575
1576 final = e_cur->vnum;
1577 thread = e_cur->thread;
1578 while (thread->parent)
1579 thread = thread->parent;
1580 top = thread;
1581 while (!thread->message)
1582 thread = thread->child;
1583 e_cur = thread->message;
1584 minmsgno = e_cur->msgno;
1585
1586 if (!e_cur->read && e_cur->visible)
1587 {
1588 if (e_cur->old)
1589 old_mail = 2;
1590 else
1591 new_mail = 1;
1592 if (e_cur->msgno < min_unread_msgno)
1593 {
1594 min_unread = e_cur->vnum;
1595 min_unread_msgno = e_cur->msgno;
1596 }
1597 }
1598
1599 if (e_cur->flagged && e_cur->visible)
1600 flagged = true;
1601
1602 if ((e_cur->vnum == -1) && e_cur->visible)
1603 num_hidden++;
1604
1606 {
1607 e_cur->attr_color = NULL; /* force index entry's color to be re-evaluated */
1608 e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1609 if (e_cur->vnum != -1)
1610 {
1611 e_root = e_cur;
1612 if (flag & MUTT_THREAD_COLLAPSE)
1613 final = e_root->vnum;
1614 }
1615 }
1616
1617 if ((thread == top) && !(thread = thread->child))
1618 {
1619 /* return value depends on action requested */
1621 {
1622 e_cur->num_hidden = num_hidden;
1623 return final;
1624 }
1625 if (flag & MUTT_THREAD_UNREAD)
1626 return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1627 if (flag & MUTT_THREAD_NEXT_UNREAD)
1628 return min_unread;
1629 if (flag & MUTT_THREAD_FLAGGED)
1630 return flagged;
1631 }
1632
1633 while (true)
1634 {
1635 e_cur = thread->message;
1636
1637 if (e_cur)
1638 {
1640 {
1641 e_cur->attr_color = NULL; /* force index entry's color to be re-evaluated */
1642 e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1643 if (!e_root && e_cur->visible)
1644 {
1645 e_root = e_cur;
1646 if (flag & MUTT_THREAD_COLLAPSE)
1647 final = e_root->vnum;
1648 }
1649
1650 if (reverse && (flag & MUTT_THREAD_COLLAPSE) &&
1651 (e_cur->msgno < minmsgno) && e_cur->visible)
1652 {
1653 minmsgno = e_cur->msgno;
1654 final = e_cur->vnum;
1655 }
1656
1657 if (flag & MUTT_THREAD_COLLAPSE)
1658 {
1659 if (e_cur != e_root)
1660 e_cur->vnum = -1;
1661 }
1662 else
1663 {
1664 if (e_cur->visible)
1665 e_cur->vnum = e_cur->msgno;
1666 }
1667 }
1668
1669 if (!e_cur->read && e_cur->visible)
1670 {
1671 if (e_cur->old)
1672 old_mail = 2;
1673 else
1674 new_mail = 1;
1675 if (e_cur->msgno < min_unread_msgno)
1676 {
1677 min_unread = e_cur->vnum;
1678 min_unread_msgno = e_cur->msgno;
1679 }
1680 }
1681
1682 if (e_cur->flagged && e_cur->visible)
1683 flagged = true;
1684
1685 if ((e_cur->vnum == -1) && e_cur->visible)
1686 num_hidden++;
1687 }
1688
1689 if (thread->child)
1690 {
1691 thread = thread->child;
1692 }
1693 else if (thread->next)
1694 {
1695 thread = thread->next;
1696 }
1697 else
1698 {
1699 bool done = false;
1700 while (!thread->next)
1701 {
1702 thread = thread->parent;
1703 if (thread == top)
1704 {
1705 done = true;
1706 break;
1707 }
1708 }
1709 if (done)
1710 break;
1711 thread = thread->next;
1712 }
1713 }
1714
1715 /* re-traverse the thread and store num_hidden in all headers, with or
1716 * without a virtual index. this will allow ~v to match all collapsed
1717 * messages when switching sort order to non-threaded. */
1718 if (flag & MUTT_THREAD_COLLAPSE)
1719 {
1720 thread = top;
1721 while (true)
1722 {
1723 e_cur = thread->message;
1724 if (e_cur)
1725 e_cur->num_hidden = num_hidden + 1;
1726
1727 if (thread->child)
1728 {
1729 thread = thread->child;
1730 }
1731 else if (thread->next)
1732 {
1733 thread = thread->next;
1734 }
1735 else
1736 {
1737 bool done = false;
1738 while (!thread->next)
1739 {
1740 thread = thread->parent;
1741 if (thread == top)
1742 {
1743 done = true;
1744 break;
1745 }
1746 }
1747 if (done)
1748 break;
1749 thread = thread->next;
1750 }
1751 }
1752 }
1753
1754 /* return value depends on action requested */
1756 return final;
1757 if (flag & MUTT_THREAD_UNREAD)
1758 return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1759 if (flag & MUTT_THREAD_NEXT_UNREAD)
1760 return min_unread;
1761 if (flag & MUTT_THREAD_FLAGGED)
1762 return flagged;
1763
1764 return 0;
1765}
#define mutt_warning(...)
Definition logging2.h:92
UseThreads
Which threading style is active, $use_threads.
Definition thread.h:102
#define _(a)
Definition message.h:28
The envelope/body of an email.
Definition email.h:39
bool read
Email is read.
Definition email.h:50
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:49
size_t num_hidden
Number of hidden messages in this view (only valid when collapsed is set)
Definition email.h:123
bool flagged
Marked important?
Definition email.h:47
bool threaded
Used for threading.
Definition email.h:108
const struct AttrColor * attr_color
Color-pair to use when displaying in the index.
Definition email.h:112
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:34
struct MuttThread * parent
Parent of this Thread.
Definition thread.h:44
struct MuttThread * child
Child of this Thread.
Definition thread.h:45
struct Email * message
Email this Thread refers to.
Definition thread.h:49
struct MuttThread * next
Next sibling Thread.
Definition thread.h:46
+ 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 79 of file thread.c.

80{
81 const unsigned char c_use_threads = cs_subset_enum(NeoMutt->sub, "use_threads");
82 const enum EmailSortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
83 if (c_use_threads > UT_FLAT)
84 return c_use_threads;
85 if ((c_sort & SORT_MASK) != EMAIL_SORT_THREADS)
86 return UT_FLAT;
87 if (c_sort & SORT_REVERSE)
88 return UT_REVERSE;
89 return UT_THREADS;
90}
unsigned char cs_subset_enum(const struct ConfigSubset *sub, const char *name)
Get a enumeration config item by name.
Definition helpers.c:71
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition helpers.c:266
#define SORT_MASK
Mask for the sort id.
Definition sort.h:39
#define SORT_REVERSE
Reverse the order of the sort.
Definition sort.h:40
EmailSortType
Methods for sorting Emails.
Definition sort.h:53
@ EMAIL_SORT_THREADS
Sort by email threads.
Definition sort.h:62
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ 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
ptrString form of value

Definition at line 97 of file thread.c.

98{
100}
static const struct Mapping UseThreadsMethods[]
Choices for '$use_threads' for the index.
Definition thread.c:50
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:

◆ 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
-1Error

Definition at line 1389 of file thread.c.

1390{
1391 if (!e)
1392 return -1;
1393
1394 struct MuttThread *cur = NULL;
1395 struct Email *e_tmp = NULL;
1396
1397 const enum UseThreads threaded = mutt_thread_style();
1398 if (threaded == UT_FLAT)
1399 {
1400 mutt_warning(_("Threading is not enabled"));
1401 return e->vnum;
1402 }
1403
1404 cur = e->thread;
1405
1406 if (subthreads)
1407 {
1408 if (forwards ^ (threaded == UT_REVERSE))
1409 {
1410 while (!cur->next && cur->parent)
1411 cur = cur->parent;
1412 }
1413 else
1414 {
1415 while (!cur->prev && cur->parent)
1416 cur = cur->parent;
1417 }
1418 }
1419 else
1420 {
1421 while (cur->parent)
1422 cur = cur->parent;
1423 }
1424
1425 if (forwards ^ (threaded == UT_REVERSE))
1426 {
1427 do
1428 {
1429 cur = cur->next;
1430 if (!cur)
1431 return -1;
1432 e_tmp = find_virtual(cur, false);
1433 } while (!e_tmp);
1434 }
1435 else
1436 {
1437 do
1438 {
1439 cur = cur->prev;
1440 if (!cur)
1441 return -1;
1442 e_tmp = find_virtual(cur, true);
1443 } while (!e_tmp);
1444 }
1445
1446 return e_tmp->vnum;
1447}
struct Email * find_virtual(struct MuttThread *cur, bool reverse)
Find an email with a Virtual message number.
Definition thread.c:124
struct MuttThread * prev
Previous sibling Thread.
Definition thread.h:47
+ 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 MailboxView * mv)

Initialize a threading context.

Parameters
mvMailbox view
Return values
ptrThreading context

Definition at line 370 of file thread.c.

371{
372 struct ThreadsContext *tctx = MUTT_MEM_CALLOC(1, struct ThreadsContext);
373 tctx->mailbox_view = mv;
374 return tctx;
375}
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
The "current" threading state.
Definition thread.h:42
struct MailboxView * mailbox_view
Current mailbox.
Definition thread.h:43
+ Here is the caller graph for this function:

◆ mutt_thread_ctx_free()

void mutt_thread_ctx_free ( struct ThreadsContext ** ptr)

Finalize a threading context.

Parameters
ptrThreading context to free

Definition at line 381 of file thread.c.

382{
383 if (!ptr || !*ptr)
384 {
385 return;
386 }
387
388 struct ThreadsContext *tctx = *ptr;
389
390 mutt_hash_free(&tctx->hash);
391
392 FREE(ptr);
393}
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:460
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
struct HashTable * hash
Hash Table: "Message-ID" -> MuttThread.
Definition thread.h:45
+ 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 1891 of file thread.c.

1892{
1893 struct MuttThread *thread = NULL;
1894 struct MuttThread *top = tctx->tree;
1895 while ((thread = top))
1896 {
1897 while (!thread->message)
1898 thread = thread->child;
1899
1900 struct Email *e = thread->message;
1901 if (e->collapsed)
1903 top = top->next;
1904 }
1905}
#define mutt_collapse_thread(e)
Definition thread.h:112
struct MuttThread * tree
Top of thread tree.
Definition thread.h:44
+ 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 1912 of file thread.c.

1913{
1914 struct MuttThread *thread = NULL;
1915 struct MuttThread *top = tctx->tree;
1916 while ((thread = top))
1917 {
1918 while (!thread->message)
1919 thread = thread->child;
1920
1921 struct Email *e = thread->message;
1922
1923 if (e->collapsed != collapse)
1924 {
1925 if (e->collapsed)
1927 else if (mutt_thread_can_collapse(e))
1929 }
1930 top = top->next;
1931 }
1932}
bool mutt_thread_can_collapse(struct Email *e)
Check whether a thread can be collapsed.
Definition thread.c:1940
#define mutt_uncollapse_thread(e)
Definition thread.h:113
+ 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 1940 of file thread.c.

1941{
1942 const bool c_collapse_flagged = cs_subset_bool(NeoMutt->sub, "collapse_flagged");
1943 const bool c_collapse_unread = cs_subset_bool(NeoMutt->sub, "collapse_unread");
1944 return (c_collapse_unread || !mutt_thread_contains_unread(e)) &&
1945 (c_collapse_flagged || !mutt_thread_contains_flagged(e));
1946}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
#define mutt_thread_contains_flagged(e)
Definition thread.h:115
#define mutt_thread_contains_unread(e)
Definition thread.h:114
+ 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 810 of file thread.c.

811{
812 if (!tctx || !tctx->tree)
813 return;
814
815 struct MailboxView *mv = tctx->mailbox_view;
816 if (!mv)
817 return;
818
819 struct Mailbox *m = mv->mailbox;
820 if (!m || !m->emails)
821 return;
822
823 for (int i = 0; i < m->msg_count; i++)
824 {
825 struct Email *e = m->emails[i];
826 if (!e)
827 {
828 // Keep processing, in case there are other holes in the Email array
829 continue;
830 }
831
832 /* mailbox may have been only partially read */
833 e->thread = NULL;
834 e->threaded = false;
835 }
836 tctx->tree = NULL;
837 mutt_hash_free(&tctx->hash);
838}
View of a Mailbox.
Definition mview.h:40
struct Mailbox * mailbox
Current Mailbox.
Definition mview.h:51
A mailbox.
Definition mailbox.h:81
int msg_count
Total number of messages.
Definition mailbox.h:90
struct Email ** emails
Array of Emails.
Definition mailbox.h:98
+ 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 465 of file thread.c.

466{
467 if (!tctx || !tctx->tree)
468 return;
469
470 char *pfx = NULL;
471 char *mypfx = NULL;
472 char *arrow = NULL;
473 char *myarrow = NULL;
474 char *new_tree = NULL;
475 const bool reverse = (mutt_thread_style() == UT_REVERSE);
476 enum TreeChar corner = reverse ? MUTT_TREE_ULCORNER : MUTT_TREE_LLCORNER;
477 enum TreeChar vtee = reverse ? MUTT_TREE_BTEE : MUTT_TREE_TTEE;
478 const bool c_narrow_tree = cs_subset_bool(NeoMutt->sub, "narrow_tree");
479 int depth = 0;
480 int start_depth = 0;
481 int max_depth = 0;
482 int width = c_narrow_tree ? 1 : 2;
483 struct MuttThread *nextdisp = NULL;
484 struct MuttThread *pseudo = NULL;
485 struct MuttThread *parent = NULL;
486
487 struct MuttThread *tree = tctx->tree;
488
489 /* Verify and repair thread<->message back-pointers before traversal */
491
492 /* Do the visibility calculations and free the old thread chars.
493 * From now on we can simply ignore invisible subtrees */
494 calculate_visibility(tree, &max_depth);
495 pfx = MUTT_MEM_MALLOC((width * max_depth) + 2, char);
496 arrow = MUTT_MEM_MALLOC((width * max_depth) + 2, char);
497 const bool c_hide_limited = cs_subset_bool(NeoMutt->sub, "hide_limited");
498 const bool c_hide_missing = cs_subset_bool(NeoMutt->sub, "hide_missing");
499 while (tree)
500 {
501 if (depth != 0)
502 {
503 myarrow = arrow + (depth - start_depth - ((start_depth != 0) ? 0 : 1)) * width;
504 if (start_depth == depth)
505 myarrow[0] = nextdisp ? MUTT_TREE_LTEE : corner;
506 else if (parent->message && !c_hide_limited)
507 myarrow[0] = MUTT_TREE_HIDDEN;
508 else if (!parent->message && !c_hide_missing)
509 myarrow[0] = MUTT_TREE_MISSING;
510 else
511 myarrow[0] = vtee;
512 if (width == 2)
513 {
514 myarrow[1] = pseudo ? MUTT_TREE_STAR :
516 }
517 if (tree->visible)
518 {
519 myarrow[width] = MUTT_TREE_RARROW;
520 myarrow[width + 1] = 0;
521 new_tree = MUTT_MEM_MALLOC(((size_t) depth * width) + 2, char);
522 if (start_depth > 1)
523 {
524 strncpy(new_tree, pfx, (size_t) width * (start_depth - 1));
525 mutt_str_copy(new_tree + (start_depth - 1) * width, arrow,
526 (1 + depth - start_depth) * width + 2);
527 }
528 else
529 {
530 mutt_str_copy(new_tree, arrow, ((size_t) depth * width) + 2);
531 }
532 tree->message->tree = new_tree;
533 }
534 }
535 if (tree->child && (depth != 0))
536 {
537 mypfx = pfx + (depth - 1) * width;
538 mypfx[0] = nextdisp ? MUTT_TREE_VLINE : MUTT_TREE_SPACE;
539 if (width == 2)
540 mypfx[1] = MUTT_TREE_SPACE;
541 }
542 parent = tree;
543 nextdisp = NULL;
544 pseudo = NULL;
545 do
546 {
547 if (tree->child && tree->subtree_visible)
548 {
549 if (tree->deep)
550 depth++;
551 if (tree->visible)
552 start_depth = depth;
553 tree = tree->child;
554
555 /* we do this here because we need to make sure that the first child thread
556 * of the old tree that we deal with is actually displayed if any are,
557 * or we might set the parent variable wrong while going through it. */
558 while (!tree->subtree_visible && tree->next)
559 tree = tree->next;
560 }
561 else
562 {
563 while (!tree->next && tree->parent)
564 {
565 if (tree == pseudo)
566 pseudo = NULL;
567 if (tree == nextdisp)
568 nextdisp = NULL;
569 if (tree->visible)
570 start_depth = depth;
571 tree = tree->parent;
572 if (tree->deep)
573 {
574 if (start_depth == depth)
575 start_depth--;
576 depth--;
577 }
578 }
579 if (tree == pseudo)
580 pseudo = NULL;
581 if (tree == nextdisp)
582 nextdisp = NULL;
583 if (tree->visible)
584 start_depth = depth;
585 tree = tree->next;
586 if (!tree)
587 break;
588 }
589 if (!pseudo && tree->fake_thread)
590 pseudo = tree;
591 if (!nextdisp && tree->next_subtree_visible)
592 nextdisp = tree;
593 } while (!tree->deep);
594 }
595
596 FREE(&pfx);
597 FREE(&arrow);
598}
static void calculate_visibility(struct MuttThread *tree, int *max_depth)
Are tree nodes visible.
Definition thread.c:239
static int thread_check_integrity(struct MuttThread *tree)
Verify and repair thread<->message back-pointers.
Definition thread.c:408
TreeChar
Tree characters for menus.
Definition thread.h:56
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
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:587
char * tree
Character string to print thread tree.
Definition email.h:125
bool visible
Is this Thread visible?
Definition thread.h:42
bool fake_thread
Emails grouped by Subject.
Definition thread.h:38
bool deep
Is the Thread deeply nested?
Definition thread.h:36
unsigned int subtree_visible
Is this Thread subtree visible?
Definition thread.h:41
bool duplicate_thread
Duplicated Email in Thread.
Definition thread.h:37
bool next_subtree_visible
Is the next Thread subtree visible?
Definition thread.h:39
+ 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 EmailArray * children,
struct Mailbox * m )

Forcibly link threads together.

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

Definition at line 1870 of file thread.c.

1871{
1872 if (!parent || !children || !m)
1873 return false;
1874
1875 bool changed = false;
1876
1877 struct Email **ep = NULL;
1878 ARRAY_FOREACH(ep, children)
1879 {
1880 struct Email *e = *ep;
1881 changed |= link_threads(parent, e, m);
1882 }
1883
1884 return changed;
1885}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
static bool link_threads(struct Email *parent, struct Email *child, struct Mailbox *m)
Forcibly link messages together.
Definition thread.c:1849
bool changed
Email has been edited.
Definition email.h:77
+ 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 1825 of file thread.c.

1826{
1827 struct HashTable *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NONE);
1828
1829 for (int i = 0; i < m->msg_count; i++)
1830 {
1831 struct Email *e = m->emails[i];
1832 if (!e || !e->env)
1833 continue;
1834
1835 if (e->env->message_id)
1836 mutt_hash_insert(hash, e->env->message_id, e);
1837 }
1838
1839 return hash;
1840}
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:338
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition hash.c:262
@ MUTT_HASH_NONE
No flags are set.
Definition hash.h:115
struct Envelope * env
Envelope information.
Definition email.h:68
char * message_id
Message ID.
Definition envelope.h:73
A Hash Table.
Definition hash.h:99
+ 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 1774 of file thread.c.

1775{
1776 if (!m || !e)
1777 return 1;
1778
1779 struct MuttThread *threads[2] = { 0 };
1780 int rc;
1781
1782 const enum UseThreads threaded = mutt_thread_style();
1783 if ((threaded == UT_FLAT) || !e->thread)
1784 return 1;
1785
1786 threads[0] = e->thread;
1787 while (threads[0]->parent)
1788 threads[0] = threads[0]->parent;
1789
1790 threads[1] = (mit == MIT_POSITION) ? e->thread : threads[0]->next;
1791
1792 for (int i = 0; i < (((mit == MIT_POSITION) || !threads[1]) ? 1 : 2); i++)
1793 {
1794 /* Descend to the first node that references a real Email.
1795 * Guard against a malformed or partially-built tree:
1796 * a node with no message and no child would otherwise cause a NULL dereference. */
1797 while (threads[i] && !threads[i]->message)
1798 threads[i] = threads[i]->child;
1799
1800 if (!threads[i] || !threads[i]->message)
1801 return 1;
1802 }
1803
1804 if (threaded == UT_REVERSE)
1805 {
1806 rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1807 }
1808 else
1809 {
1810 rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1811 threads[0]->message->msgno;
1812 }
1813
1814 if (mit == MIT_POSITION)
1815 rc += 1;
1816
1817 return rc;
1818}
+ 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,
int count )

Find the parent of a message.

Parameters
eCurrent Email
find_rootIf true, find the root message
countNumber of generations to go up (1 for direct parent, 0 = count as 1)
Return values
>=0Virtual index number of parent/root message
-1Error

Definition at line 1457 of file thread.c.

1458{
1459 if (!e)
1460 return -1;
1461
1462 struct MuttThread *thread = NULL;
1463 struct Email *e_parent = NULL;
1464
1465 if (!mutt_using_threads())
1466 {
1467 mutt_warning(_("Threading is not enabled"));
1468 return e->vnum;
1469 }
1470
1471 /* Normalize count to at least 1 */
1472 if (count <= 0)
1473 count = 1;
1474
1475 /* Root may be the current message */
1476 if (find_root)
1477 e_parent = e;
1478
1479 for (thread = e->thread->parent; thread; thread = thread->parent)
1480 {
1481 e = thread->message;
1482 if (e)
1483 {
1484 e_parent = e;
1485 if (!find_root)
1486 {
1487 count--;
1488 if (count == 0)
1489 break;
1490 }
1491 }
1492 }
1493
1494 if (!e_parent)
1495 {
1496 mutt_error(_("Parent message is not available"));
1497 return -1;
1498 }
1499 if (!is_visible(e_parent))
1500 {
1501 if (find_root)
1502 mutt_error(_("Root message is not visible in this limited view"));
1503 else
1504 mutt_error(_("Parent message is not visible in this limited view"));
1505 return -1;
1506 }
1507 return e_parent->vnum;
1508}
#define mutt_error(...)
Definition logging2.h:94
static bool is_visible(struct Email *e)
Is the message visible?
Definition thread.c:120
#define mutt_using_threads()
Definition thread.h:119
+ 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
numSize in bytes of all messages shown

Definition at line 1515 of file thread.c.

1516{
1517 if (!m)
1518 return 0;
1519
1520 off_t vsize = 0;
1521 const int padding = mx_msg_padding_size(m);
1522
1523 m->vcount = 0;
1524
1525 for (int i = 0; i < m->msg_count; i++)
1526 {
1527 struct Email *e = m->emails[i];
1528 if (!e)
1529 break;
1530
1531 if (e->vnum >= 0)
1532 {
1533 e->vnum = m->vcount;
1534 m->v2r[m->vcount] = i;
1535 m->vcount++;
1536 vsize += e->body->length + e->body->offset - e->body->hdr_offset + padding;
1537 }
1538 }
1539
1540 return vsize;
1541}
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition mx.c:1510
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:81
struct Body * body
List of MIME parts.
Definition email.h:69
int vcount
The number of virtual messages.
Definition mailbox.h:101
int * v2r
Mapping from virtual to real msgno.
Definition mailbox.h:100
+ 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 1130 of file thread.c.

1131{
1132 if (!tctx || !tctx->mailbox_view)
1133 return;
1134
1135 struct MailboxView *mv = tctx->mailbox_view;
1136 struct Mailbox *m = mv->mailbox;
1137
1138 struct Email *e = NULL;
1139 int i;
1140 int using_refs = 0;
1141 struct MuttThread *thread = NULL;
1142 struct MuttThread *tnew = NULL;
1143 struct MuttThread *tmp = NULL;
1144 struct MuttThread top = { 0 };
1145 struct ListNode *ref = NULL;
1146
1147 ASSERT(m->msg_count > 0);
1148 if (!tctx->hash)
1149 init = true;
1150
1151 if (init)
1152 {
1155 }
1156
1157 /* we want a quick way to see if things are actually attached to the top of the
1158 * thread tree or if they're just dangling, so we attach everything to a top
1159 * node temporarily */
1160 top.parent = NULL;
1161 top.next = NULL;
1162 top.prev = NULL;
1163
1164 top.child = tctx->tree;
1165 for (thread = tctx->tree; thread; thread = thread->next)
1166 thread->parent = &top;
1167
1168 /* put each new message together with the matching messageless MuttThread if it
1169 * exists. otherwise, if there is a MuttThread that already has a message, thread
1170 * new message as an identical child. if we didn't attach the message to a
1171 * MuttThread, make a new one for it. */
1172 const bool c_duplicate_threads = cs_subset_bool(NeoMutt->sub, "duplicate_threads");
1173 for (i = 0; i < m->msg_count; i++)
1174 {
1175 e = m->emails[i];
1176 if (!e)
1177 continue;
1178
1179 if (e->thread)
1180 {
1181 /* unlink pseudo-threads because they might be children of newly
1182 * arrived messages */
1183 thread = e->thread;
1184 for (tnew = thread->child; tnew;)
1185 {
1186 tmp = tnew->next;
1187 if (tnew->fake_thread)
1188 {
1189 unlink_message(&thread->child, tnew);
1190 insert_message(&top.child, &top, tnew);
1191 tnew->fake_thread = false;
1192 }
1193 tnew = tmp;
1194 }
1195 }
1196 else
1197 {
1198 if ((!init || c_duplicate_threads) && e->env->message_id)
1199 thread = mutt_hash_find(tctx->hash, e->env->message_id);
1200 else
1201 thread = NULL;
1202
1203 if (thread && !thread->message)
1204 {
1205 /* this is a message which was missing before */
1206 thread->message = e;
1207 e->thread = thread;
1208 thread->check_subject = true;
1209
1210 /* mark descendants as needing subject_changed checked */
1211 for (tmp = (thread->child ? thread->child : thread); tmp != thread;)
1212 {
1213 while (!tmp->message)
1214 tmp = tmp->child;
1215 tmp->check_subject = true;
1216 while (!tmp->next && (tmp != thread))
1217 tmp = tmp->parent;
1218 if (tmp != thread)
1219 tmp = tmp->next;
1220 }
1221
1222 if (thread->parent)
1223 {
1224 /* remove threading info above it based on its children, which we'll
1225 * recalculate based on its headers. make sure not to leave
1226 * dangling missing messages. note that we haven't kept track
1227 * of what info came from its children and what from its siblings'
1228 * children, so we just remove the stuff that's definitely from it */
1229 do
1230 {
1231 tmp = thread->parent;
1232 unlink_message(&tmp->child, thread);
1233 thread->parent = NULL;
1234 thread->sort_thread_key = NULL;
1235 thread->sort_aux_key = NULL;
1236 thread->fake_thread = false;
1237 thread = tmp;
1238 } while (thread != &top && !thread->child && !thread->message);
1239 }
1240 }
1241 else
1242 {
1243 tnew = (c_duplicate_threads ? thread : NULL);
1244
1245 thread = MUTT_MEM_CALLOC(1, struct MuttThread);
1246 thread->message = e;
1247 thread->check_subject = true;
1248 e->thread = thread;
1249 mutt_hash_insert(tctx->hash, e->env->message_id ? e->env->message_id : "", thread);
1250
1251 if (tnew)
1252 {
1253 if (tnew->duplicate_thread)
1254 tnew = tnew->parent;
1255
1256 thread = e->thread;
1257
1258 insert_message(&tnew->child, tnew, thread);
1259 thread->duplicate_thread = true;
1260 thread->message->threaded = true;
1261 }
1262 }
1263 }
1264 }
1265
1266 /* thread by references */
1267 for (i = 0; i < m->msg_count; i++)
1268 {
1269 e = m->emails[i];
1270 if (!e)
1271 break;
1272
1273 if (e->threaded)
1274 continue;
1275 e->threaded = true;
1276
1277 thread = e->thread;
1278 if (!thread)
1279 continue;
1280 using_refs = 0;
1281
1282 while (true)
1283 {
1284 if (using_refs == 0)
1285 {
1286 /* look at the beginning of in-reply-to: */
1287 ref = STAILQ_FIRST(&e->env->in_reply_to);
1288 if (ref)
1289 {
1290 using_refs = 1;
1291 }
1292 else
1293 {
1294 ref = STAILQ_FIRST(&e->env->references);
1295 using_refs = 2;
1296 }
1297 }
1298 else if (using_refs == 1)
1299 {
1300 /* if there's no references header, use all the in-reply-to:
1301 * data that we have. otherwise, use the first reference
1302 * if it's different than the first in-reply-to, otherwise use
1303 * the second reference (since at least eudora puts the most
1304 * recent reference in in-reply-to and the rest in references) */
1305 if (STAILQ_EMPTY(&e->env->references))
1306 {
1307 ref = STAILQ_NEXT(ref, entries);
1308 }
1309 else
1310 {
1311 if (!mutt_str_equal(ref->data, STAILQ_FIRST(&e->env->references)->data))
1312 ref = STAILQ_FIRST(&e->env->references);
1313 else
1314 ref = STAILQ_NEXT(STAILQ_FIRST(&e->env->references), entries);
1315
1316 using_refs = 2;
1317 }
1318 }
1319 else
1320 {
1321 ref = STAILQ_NEXT(ref, entries); /* go on with references */
1322 }
1323
1324 if (!ref)
1325 break;
1326
1327 tnew = mutt_hash_find(tctx->hash, ref->data);
1328 if (tnew)
1329 {
1330 if (tnew->duplicate_thread)
1331 tnew = tnew->parent;
1332 if (is_descendant(tnew, thread)) /* no loops! */
1333 continue;
1334 }
1335 else
1336 {
1337 tnew = MUTT_MEM_CALLOC(1, struct MuttThread);
1338 mutt_hash_insert(tctx->hash, ref->data, tnew);
1339 }
1340
1341 if (thread->parent)
1342 unlink_message(&top.child, thread);
1343 insert_message(&tnew->child, tnew, thread);
1344 thread = tnew;
1345 if (thread->message || (thread->parent && (thread->parent != &top)))
1346 break;
1347 }
1348
1349 if (!thread->parent)
1350 insert_message(&top.child, &top, thread);
1351 }
1352
1353 /* detach everything from the temporary top node */
1354 for (thread = top.child; thread; thread = thread->next)
1355 {
1356 thread->parent = NULL;
1357 }
1358 tctx->tree = top.child;
1359
1360 check_subjects(mv, init);
1361
1362 const bool c_strict_threads = cs_subset_bool(NeoMutt->sub, "strict_threads");
1363 if (!c_strict_threads)
1364 pseudo_threads(tctx);
1365
1366 /* if $sort_aux or similar changed after the mailbox is sorted, then
1367 * all the subthreads need to be resorted */
1368 if (tctx->tree)
1369 {
1371 OptSortSubthreads = false;
1372
1373 /* Put the list into an array. */
1374 linearize_tree(tctx);
1375
1376 /* Draw the thread tree. */
1377 mutt_draw_tree(tctx);
1378 }
1379}
void unlink_message(struct MuttThread **old, struct MuttThread *cur)
Break the message out of the thread.
Definition thread.c:66
bool is_descendant(const struct MuttThread *a, const struct MuttThread *b)
Is one thread a descendant of another.
Definition thread.c:46
void insert_message(struct MuttThread **add, struct MuttThread *parent, struct MuttThread *cur)
Insert a message into a thread.
Definition thread.c:104
bool OptSortSubthreads
(pseudo) used when $sort_aux changes
Definition globals.c:57
static void thread_hash_destructor(int type, void *obj, intptr_t data)
Free our hash table data - Implements hash_hdata_free_t -.
Definition thread.c:1120
static void linearize_tree(struct ThreadsContext *tctx)
Flatten an email thread.
Definition thread.c:186
static void mutt_sort_subthreads(struct ThreadsContext *tctx, bool init)
Sort the children of a thread.
Definition thread.c:871
void mutt_draw_tree(struct ThreadsContext *tctx)
Draw a tree of threaded emails.
Definition thread.c:465
static void pseudo_threads(struct ThreadsContext *tctx)
Thread messages by subject.
Definition thread.c:736
static void check_subjects(struct MailboxView *mv, bool init)
Find out which emails' subjects differ from their parent's.
Definition thread.c:1078
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:365
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:304
@ MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition hash.h:118
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
#define STAILQ_FIRST(head)
Definition queue.h:388
#define STAILQ_EMPTY(head)
Definition queue.h:382
#define STAILQ_NEXT(elm, field)
Definition queue.h:439
#define ASSERT(COND)
Definition signal2.h:59
struct ListHead references
message references (in reverse order)
Definition envelope.h:83
struct ListHead in_reply_to
in-reply-to header content
Definition envelope.h:84
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
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:35
struct Email * sort_thread_key
Email that controls how top thread sorts.
Definition thread.h:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ UseThreadsTypeDef

const struct EnumDef UseThreadsTypeDef
extern

Data for the $use_threads enumeration.

Definition at line 64 of file thread.c.

64 {
65 "use_threads_type",
66 4,
67 (struct Mapping *) &UseThreadsMethods,
68};
Mapping between user-readable string and a constant.
Definition mapping.h:33