presentation/QuickMailSelection.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "QuickMailSelection.h" | ||
| 2 | #include "service/MailSelectionService.h" | ||
| 3 | |||
| 4 |
2/2✓ Branch 3 → 4 taken 801 times.
✓ Branch 4 → 5 taken 801 times.
|
801 | QuickMailSelection::QuickMailSelection(QObject *parent) : QObject(parent) {} |
| 5 | 801 | QuickMailSelection::~QuickMailSelection() { | |
| 6 | 801 | MailSelectionService::shared()->cancel(&m_captureOwner); | |
| 7 | 801 | MailSelectionService::shared()->cancel(&m_membershipOwner); | |
| 8 | 801 | } | |
| 9 | 1042 | void QuickMailSelection::configure(QString databasePath, MailSelectionQuery query, | |
| 10 | std::function<bool(const MailSelectionSnapshot &)> admit, | ||
| 11 | MailSelectionKey initialSingle) { | ||
| 12 | // A filter/folder intent publishes its final scope and surviving current | ||
| 13 | // mail together. Clearing and then re-selecting used to notify every | ||
| 14 | // selection consumer twice before the next list frame. | ||
| 15 | 1042 | invalidatePending(false); | |
| 16 | 1042 | m_databasePath = std::move(databasePath); | |
| 17 | 1042 | m_query = std::move(query); | |
| 18 | 1042 | m_admit = std::move(admit); | |
| 19 |
2/2✓ Branch 10 → 11 taken 83 times.
✓ Branch 10 → 12 taken 215 times.
|
298 | m_single = initialSingle.folderId == m_query.folderId && initialSingle.uid >= 0 |
| 20 |
2/2✓ Branch 9 → 10 taken 298 times.
✓ Branch 9 → 12 taken 744 times.
|
1340 | ? initialSingle : MailSelectionKey{}; |
| 21 | 1042 | m_anchor = m_single; | |
| 22 | 1042 | m_snapshot.reset(); | |
| 23 | 1042 | refreshMembership(); | |
| 24 | 1042 | } | |
| 25 | 1168 | void QuickMailSelection::cancelPending() { | |
| 26 | 1168 | invalidatePending(true); | |
| 27 | 1168 | } | |
| 28 | 3938 | void QuickMailSelection::invalidatePending(bool publish) { | |
| 29 |
3/4✓ Branch 2 → 3 taken 3934 times.
✓ Branch 2 → 5 taken 4 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 3934 times.
|
3938 | const bool visibleChange = m_busy || !m_toggles.isEmpty(); |
| 30 | 3938 | ++m_generation; | |
| 31 | 3938 | MailSelectionService::shared()->cancel(&m_captureOwner); | |
| 32 | 3938 | m_busy = false; | |
| 33 | 3938 | m_toggles.clear(); | |
| 34 |
4/4✓ Branch 10 → 11 taken 1168 times.
✓ Branch 10 → 13 taken 2770 times.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 1167 times.
|
3938 | if (publish && visibleChange) emit changed(); |
| 35 | 3938 | } | |
| 36 | 805 | void QuickMailSelection::clear() { | |
| 37 | 805 | invalidatePending(false); | |
| 38 | 805 | m_single = {}; | |
| 39 | 805 | m_anchor = {}; | |
| 40 | 805 | m_snapshot.reset(); | |
| 41 | 805 | refreshMembership(); | |
| 42 | 1610 | } | |
| 43 | 17356 | qint64 QuickMailSelection::count() const { | |
| 44 |
4/4✓ Branch 3 → 4 taken 804 times.
✓ Branch 3 → 6 taken 16552 times.
✓ Branch 6 → 7 taken 10415 times.
✓ Branch 6 → 8 taken 6137 times.
|
17356 | return m_snapshot ? m_snapshot->count : m_single.uid >= 0 ? 1 : 0; |
| 45 | } | ||
| 46 | 48134 | bool QuickMailSelection::contains(qint64 uid) const { | |
| 47 |
6/6✓ Branch 3 → 4 taken 16782 times.
✓ Branch 3 → 6 taken 31352 times.
✓ Branch 6 → 7 taken 31351 times.
✓ Branch 6 → 9 taken 1 time.
✓ Branch 7 → 8 taken 3316 times.
✓ Branch 7 → 9 taken 28035 times.
|
48134 | return m_snapshot ? m_membership.value(uid, false) : uid >= 0 && uid == m_single.uid; |
| 48 | } | ||
| 49 | 914 | void QuickMailSelection::select(MailSelectionKey key, Qt::KeyboardModifiers modifiers) { | |
| 50 |
4/4✓ Branch 2 → 3 taken 913 times.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 912 times.
|
914 | if (key.uid < 0 || key.folderId != m_query.folderId) return; |
| 51 |
5/6✓ Branch 6 → 7 taken 10 times.
✓ Branch 6 → 9 taken 902 times.
✓ Branch 7 → 8 taken 10 times.
✗ Branch 7 → 9 not taken.
✓ Branch 10 → 11 taken 10 times.
✓ Branch 10 → 39 taken 902 times.
|
912 | if (modifiers.testFlag(Qt::ShiftModifier) && m_anchor.uid >= 0) { |
| 52 |
1/1✓ Branch 11 → 12 taken 10 times.
|
10 | invalidatePending(false); |
| 53 | 10 | auto query = m_query; | |
| 54 | // F-41: the range follows the presented order — the rows the list | ||
| 55 | // visibly shows between the anchor and the clicked row — not the | ||
| 56 | // date/UID span, which selected unrelated messages whenever the list | ||
| 57 | // was sorted by sender/subject or threaded. | ||
| 58 |
2/2✓ Branch 14 → 15 taken 7 times.
✓ Branch 14 → 26 taken 3 times.
|
10 | if (m_visibleRange) { |
| 59 | const QList<qint64> visible = | ||
| 60 |
1/1✓ Branch 15 → 16 taken 7 times.
|
7 | m_visibleRange(m_anchor, key); |
| 61 |
3/6✓ Branch 17 → 18 taken 7 times.
✗ Branch 17 → 21 not taken.
✓ Branch 19 → 20 taken 7 times.
✗ Branch 19 → 21 not taken.
✓ Branch 22 → 23 taken 7 times.
✗ Branch 22 → 24 not taken.
|
14 | if (!visible.isEmpty() && |
| 62 | 7 | visible.size() <= MailSelectionQuery::MaximumExplicitUids) | |
| 63 | 7 | query.explicitUids = visible; | |
| 64 | 7 | } | |
| 65 | 10 | query.range = true; | |
| 66 | 10 | query.first = m_anchor; | |
| 67 | 10 | query.last = key; | |
| 68 | 10 | m_busy = true; | |
| 69 | 10 | const auto generation = m_generation; | |
| 70 |
1/1✓ Branch 28 → 29 taken 10 times.
|
10 | emit changed(); |
| 71 |
3/3✓ Branch 29 → 30 taken 10 times.
✓ Branch 31 → 32 taken 10 times.
✓ Branch 33 → 34 taken 10 times.
|
20 | MailSelectionService::shared()->capture(&m_captureOwner, m_databasePath, query, |
| 72 | 20 | [this, generation, key](auto result) { | |
| 73 |
2/3✓ Branch 4 → 5 taken 10 times.
✓ Branch 6 → 7 taken 10 times.
✗ Branch 6 → 11 not taken.
|
10 | if (accept(std::move(result), generation)) { |
| 74 |
1/1✓ Branch 8 → 9 taken 10 times.
|
10 | emit currentRequested(key); |
| 75 | 10 | startToggle(); | |
| 76 | } | ||
| 77 | 10 | }); | |
| 78 |
2/2✓ Branch 40 → 41 taken 16 times.
✓ Branch 40 → 48 taken 886 times.
|
912 | } else if (modifiers.testFlag(Qt::ControlModifier)) { |
| 79 |
1/2✗ Branch 42 → 43 not taken.
✓ Branch 42 → 45 taken 16 times.
|
16 | if (m_toggles.size() >= 64) { emit failed(); return; } |
| 80 | 16 | m_anchor = key; | |
| 81 | 16 | m_toggles.append(key); | |
| 82 | 16 | startToggle(); | |
| 83 | } else { | ||
| 84 | 886 | invalidatePending(false); | |
| 85 | 886 | m_snapshot.reset(); | |
| 86 | 886 | m_single = m_anchor = key; | |
| 87 | 886 | refreshMembership(); | |
| 88 |
1/1✓ Branch 54 → 55 taken 886 times.
|
886 | emit currentRequested(key); |
| 89 | } | ||
| 90 | } | ||
| 91 | 28 | void QuickMailSelection::selectAll() { | |
| 92 |
5/6✓ Branch 3 → 4 taken 27 times.
✓ Branch 3 → 5 taken 1 time.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 27 times.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 27 times.
|
28 | if (m_query.accountId.isEmpty() || m_query.folderId <= 0) return; |
| 93 | 27 | invalidatePending(false); | |
| 94 | 27 | m_busy = true; | |
| 95 | 27 | const auto generation = m_generation; | |
| 96 | 27 | emit changed(); | |
| 97 |
1/1✓ Branch 14 → 15 taken 27 times.
|
54 | MailSelectionService::shared()->capture(&m_captureOwner, m_databasePath, m_query, |
| 98 | 54 | [this, generation](auto result) { | |
| 99 |
3/3✓ Branch 4 → 5 taken 23 times.
✓ Branch 6 → 7 taken 22 times.
✓ Branch 6 → 8 taken 1 time.
|
23 | if (accept(std::move(result), generation)) startToggle(); |
| 100 | 23 | }); | |
| 101 | } | ||
| 102 | 45 | bool QuickMailSelection::accept(MailSelectionResult result, quint64 generation) { | |
| 103 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 45 times.
|
45 | if (generation != m_generation) return false; |
| 104 | 45 | m_busy = false; | |
| 105 |
1/2✓ Branch 6 → 7 taken 45 times.
✗ Branch 6 → 12 not taken.
|
45 | if (result.outcome != MailSelectionResult::Outcome::Ready || !result.snapshot || |
| 106 |
6/8✓ Branch 4 → 5 taken 45 times.
✗ Branch 4 → 12 not taken.
✓ Branch 8 → 9 taken 45 times.
✗ Branch 8 → 12 not taken.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 44 times.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 17 taken 44 times.
|
90 | !m_admit || !m_admit(*result.snapshot)) { fail(); return false; } |
| 107 | 44 | m_single = {}; | |
| 108 | 44 | m_snapshot = std::move(result.snapshot); | |
| 109 | 44 | refreshMembership(); | |
| 110 | 44 | return true; | |
| 111 | 44 | } | |
| 112 | 63 | void QuickMailSelection::startToggle() { | |
| 113 |
6/6✓ Branch 2 → 3 taken 60 times.
✓ Branch 2 → 5 taken 3 times.
✓ Branch 4 → 5 taken 45 times.
✓ Branch 4 → 6 taken 15 times.
✓ Branch 7 → 8 taken 48 times.
✓ Branch 7 → 9 taken 15 times.
|
71 | if (m_busy || m_toggles.isEmpty()) return; |
| 114 |
1/1✓ Branch 9 → 10 taken 15 times.
|
15 | const auto key = m_toggles.takeFirst(); |
| 115 |
8/8✓ Branch 11 → 12 taken 10 times.
✓ Branch 11 → 15 taken 5 times.
✓ Branch 12 → 13 taken 9 times.
✓ Branch 12 → 14 taken 1 time.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 7 times.
✓ Branch 16 → 17 taken 3 times.
✓ Branch 16 → 28 taken 12 times.
|
15 | if (!m_snapshot && (m_single.uid < 0 || m_single.uid == key.uid)) { |
| 116 |
2/2✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 1 time.
|
3 | m_single = m_single.uid == key.uid ? MailSelectionKey{} : key; |
| 117 |
1/1✓ Branch 22 → 23 taken 3 times.
|
3 | refreshMembership(); |
| 118 |
1/1✓ Branch 24 → 25 taken 3 times.
|
3 | emit currentRequested(key); |
| 119 |
1/1✓ Branch 26 → 27 taken 3 times.
|
3 | startToggle(); |
| 120 | 3 | return; | |
| 121 | } | ||
| 122 | 12 | m_busy = true; | |
| 123 | 12 | const auto generation = m_generation; | |
| 124 |
1/1✓ Branch 28 → 29 taken 12 times.
|
12 | emit changed(); |
| 125 |
3/3✓ Branch 30 → 31 taken 5 times.
✓ Branch 30 → 35 taken 7 times.
✓ Branch 32 → 33 taken 5 times.
|
12 | if (m_snapshot) { toggle(key, generation); return; } |
| 126 | 7 | auto query = m_query; | |
| 127 | // The current single mail may have become read after it was selected. | ||
| 128 | // Materializing that explicit identity must not apply a later unread filter. | ||
| 129 | 7 | query.text.clear(); | |
| 130 | 7 | query.unreadOnly = query.starredOnly = query.attachmentsOnly = false; | |
| 131 | 7 | query.range = true; | |
| 132 | 7 | query.first = query.last = m_single; | |
| 133 |
3/3✓ Branch 39 → 40 taken 7 times.
✓ Branch 41 → 42 taken 7 times.
✓ Branch 43 → 44 taken 7 times.
|
14 | MailSelectionService::shared()->capture(&m_captureOwner, m_databasePath, query, |
| 134 | 14 | [this, generation, key](auto result) { | |
| 135 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 7 times.
|
7 | if (generation != m_generation) return; |
| 136 |
1/2✓ Branch 6 → 7 taken 7 times.
✗ Branch 6 → 12 not taken.
|
7 | if (result.outcome != MailSelectionResult::Outcome::Ready || !result.snapshot || |
| 137 |
4/8✓ Branch 4 → 5 taken 7 times.
✗ Branch 4 → 12 not taken.
✓ Branch 8 → 9 taken 7 times.
✗ Branch 8 → 12 not taken.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 7 times.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 17 taken 7 times.
|
14 | !m_admit || !m_admit(*result.snapshot)) { fail(); return; } |
| 138 | 7 | m_snapshot = std::move(result.snapshot); | |
| 139 |
1/1✓ Branch 20 → 21 taken 7 times.
|
7 | toggle(key, generation); |
| 140 | }); | ||
| 141 | 15 | } | |
| 142 | 12 | void QuickMailSelection::toggle(MailSelectionKey key, quint64 generation) { | |
| 143 |
2/2✓ Branch 4 → 5 taken 12 times.
✓ Branch 6 → 7 taken 12 times.
|
24 | MailSelectionService::shared()->toggle(&m_captureOwner, m_snapshot, key.uid, |
| 144 | 24 | [this, generation, key](auto result) { | |
| 145 |
2/3✓ Branch 4 → 5 taken 12 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 12 times.
|
12 | if (!accept(std::move(result), generation)) return; |
| 146 |
1/1✓ Branch 9 → 10 taken 12 times.
|
12 | emit currentRequested(key); |
| 147 | 12 | startToggle(); | |
| 148 | }); | ||
| 149 | 12 | } | |
| 150 | 1653 | void QuickMailSelection::watch(const QList<qint64> &uids) { | |
| 151 | // Bound pathological viewport requests independently of mailbox size. | ||
| 152 |
3/3✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 1652 times.
✓ Branch 4 → 5 taken 1 time.
|
2472 | if (uids.size() > 4096) { emit failed(); return; } |
| 153 |
1/1✓ Branch 8 → 9 taken 1652 times.
|
1652 | const QSet<qint64> visible(uids.begin(), uids.end()); |
| 154 |
2/2✓ Branch 10 → 11 taken 819 times.
✓ Branch 10 → 12 taken 833 times.
|
1652 | if (visible == m_visible) return; |
| 155 | 833 | m_visible = visible; | |
| 156 | 833 | ++m_viewGeneration; | |
| 157 |
2/2✓ Branch 13 → 14 taken 833 times.
✓ Branch 14 → 15 taken 833 times.
|
833 | MailSelectionService::shared()->cancel(&m_membershipOwner); |
| 158 | 833 | m_readingMembership = false; | |
| 159 |
3/3✓ Branch 15 → 16 taken 833 times.
✓ Branch 26 → 17 taken 192 times.
✓ Branch 26 → 27 taken 833 times.
|
1025 | for (auto it = m_membership.begin(); it != m_membership.end();) { |
| 160 |
3/3✓ Branch 19 → 20 taken 191 times.
✓ Branch 19 → 23 taken 1 time.
✓ Branch 21 → 22 taken 191 times.
|
192 | if (!m_visible.contains(it.key())) it = m_membership.erase(it); |
| 161 | 1 | else ++it; | |
| 162 | } | ||
| 163 |
1/1✓ Branch 27 → 28 taken 833 times.
|
833 | readMembership(); |
| 164 | 1652 | } | |
| 165 | 2780 | void QuickMailSelection::refreshMembership() { | |
| 166 | 2780 | ++m_viewGeneration; | |
| 167 | 2780 | MailSelectionService::shared()->cancel(&m_membershipOwner); | |
| 168 | 2780 | m_readingMembership = false; | |
| 169 | 2780 | m_membership.clear(); | |
| 170 | 2780 | emit changed(); | |
| 171 | 2780 | readMembership(); | |
| 172 | 2780 | } | |
| 173 | 3666 | void QuickMailSelection::readMembership() { | |
| 174 |
5/6✓ Branch 3 → 4 taken 109 times.
✓ Branch 3 → 5 taken 3557 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 109 times.
✓ Branch 7 → 8 taken 3557 times.
✓ Branch 7 → 9 taken 109 times.
|
3714 | if (!m_snapshot || m_readingMembership) return; |
| 175 | 109 | QList<qint64> missing; | |
| 176 |
4/4✓ Branch 9 → 10 taken 109 times.
✓ Branch 10 → 11 taken 109 times.
✓ Branch 21 → 12 taken 5673 times.
✓ Branch 21 → 22 taken 97 times.
|
5770 | for (const auto uid : m_visible) { |
| 177 |
3/3✓ Branch 14 → 15 taken 2547 times.
✓ Branch 14 → 16 taken 3126 times.
✓ Branch 15 → 16 taken 2547 times.
|
5673 | if (!m_membership.contains(uid)) missing.append(uid); |
| 178 |
2/2✓ Branch 17 → 18 taken 12 times.
✓ Branch 17 → 19 taken 5661 times.
|
5673 | if (missing.size() == MailSelectionReader::MaxBatch) break; |
| 179 | } | ||
| 180 |
2/2✓ Branch 23 → 24 taken 48 times.
✓ Branch 23 → 25 taken 61 times.
|
109 | if (missing.isEmpty()) return; |
| 181 | 61 | m_readingMembership = true; | |
| 182 | 61 | const auto generation = m_viewGeneration; | |
| 183 |
2/2✓ Branch 25 → 26 taken 61 times.
✓ Branch 29 → 30 taken 61 times.
|
122 | MailSelectionService::shared()->lookup(&m_membershipOwner, m_snapshot, missing, |
| 184 | 61 | [this, generation](auto result) { | |
| 185 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 53 times.
|
53 | if (generation != m_viewGeneration) return; |
| 186 | 53 | m_readingMembership = false; | |
| 187 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 53 times.
|
53 | if (result.outcome != MailSelectionResult::Outcome::Ready) { fail(); return; } |
| 188 | 53 | m_membership.insert(result.membership); | |
| 189 | 53 | emit changed(); | |
| 190 | 53 | readMembership(); | |
| 191 | }); | ||
| 192 | 109 | } | |
| 193 | 1 | void QuickMailSelection::fail() { | |
| 194 | 1 | clear(); | |
| 195 | 1 | emit failed(); | |
| 196 | 1 | } | |
| 197 |