presentation/RecipientChipModel.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "RecipientChipModel.h" | ||
| 2 | |||
| 3 | #include "ComposeRecipients.h" | ||
| 4 | |||
| 5 | #include <QRegularExpression> | ||
| 6 | |||
| 7 | namespace { | ||
| 8 | |||
| 9 | // Name and address of one parsed mailbox header ("Name" <a@b> | a@b). | ||
| 10 | 839 | QPair<QString, QString> splitMailbox(const QString &header) { | |
| 11 | 839 | qsizetype left = -1; | |
| 12 | 839 | bool quoted = false; | |
| 13 |
2/2✓ Branch 21 → 3 taken 20210 times.
✓ Branch 21 → 22 taken 839 times.
|
21049 | for (qsizetype i = 0; i < header.size(); ++i) { |
| 14 |
2/2✓ Branch 7 → 8 taken 26 times.
✓ Branch 7 → 9 taken 20184 times.
|
20210 | if (header.at(i) == QLatin1Char('"')) quoted = !quoted; |
| 15 |
6/6✓ Branch 9 → 10 taken 20074 times.
✓ Branch 9 → 16 taken 136 times.
✓ Branch 14 → 15 taken 427 times.
✓ Branch 14 → 16 taken 19647 times.
✓ Branch 17 → 18 taken 427 times.
✓ Branch 17 → 19 taken 19783 times.
|
20210 | if (!quoted && header.at(i) == QLatin1Char('<')) left = i; |
| 16 | } | ||
| 17 |
3/3✓ Branch 22 → 23 taken 412 times.
✓ Branch 22 → 29 taken 427 times.
✓ Branch 24 → 25 taken 412 times.
|
839 | if (left < 0) return {QString(), header.trimmed()}; |
| 18 |
2/2✓ Branch 29 → 30 taken 427 times.
✓ Branch 30 → 31 taken 427 times.
|
427 | QString name = header.left(left).trimmed(); |
| 19 |
8/10✓ Branch 33 → 34 taken 427 times.
✗ Branch 33 → 43 not taken.
✓ Branch 36 → 37 taken 427 times.
✓ Branch 37 → 38 taken 13 times.
✓ Branch 37 → 43 taken 414 times.
✓ Branch 40 → 41 taken 13 times.
✓ Branch 41 → 42 taken 13 times.
✗ Branch 41 → 43 not taken.
✓ Branch 44 → 45 taken 13 times.
✓ Branch 44 → 72 taken 414 times.
|
427 | if (name.size() >= 2 && name.startsWith(QLatin1Char('"')) && name.endsWith(QLatin1Char('"'))) { |
| 20 |
1/1✓ Branch 46 → 47 taken 13 times.
|
13 | name = name.mid(1, name.size() - 2); |
| 21 |
1/1✓ Branch 55 → 56 taken 13 times.
|
26 | name.replace(QStringLiteral("\\\""), QStringLiteral("\"")); |
| 22 |
1/1✓ Branch 66 → 67 taken 13 times.
|
26 | name.replace(QStringLiteral("\\\\"), QStringLiteral("\\")); |
| 23 | } | ||
| 24 | 427 | const qsizetype right = header.indexOf(QLatin1Char('>'), left); | |
| 25 |
2/2✓ Branch 75 → 76 taken 427 times.
✓ Branch 76 → 77 taken 427 times.
|
427 | return {name, header.mid(left + 1, right - left - 1).trimmed()}; |
| 26 | 427 | } | |
| 27 | |||
| 28 | 207 | QString initialsOf(const QString &name, const QString &address) { | |
| 29 |
3/3✓ Branch 3 → 4 taken 116 times.
✓ Branch 3 → 8 taken 91 times.
✓ Branch 7 → 9 taken 116 times.
|
323 | const QString source = name.isEmpty() ? address.section(QLatin1Char('@'), 0, 0) : name; |
| 30 | 207 | QString initials; | |
| 31 |
6/6✓ Branch 14 → 15 taken 207 times.
✓ Branch 15 → 16 taken 207 times.
✓ Branch 19 → 20 taken 207 times.
✓ Branch 20 → 21 taken 207 times.
✓ Branch 35 → 22 taken 295 times.
✓ Branch 35 → 36 taken 121 times.
|
623 | for (const QString &part : source.split(QRegularExpression(QStringLiteral("[\\s._-]+")), Qt::SkipEmptyParts)) { |
| 32 |
3/3✓ Branch 23 → 24 taken 295 times.
✓ Branch 25 → 26 taken 3 times.
✓ Branch 25 → 27 taken 292 times.
|
295 | if (!part.front().isLetterOrNumber()) continue; |
| 33 |
2/2✓ Branch 27 → 28 taken 292 times.
✓ Branch 29 → 30 taken 292 times.
|
292 | initials += part.front().toUpper(); |
| 34 |
2/2✓ Branch 31 → 32 taken 86 times.
✓ Branch 31 → 33 taken 206 times.
|
292 | if (initials.size() == 2) break; |
| 35 | 207 | } | |
| 36 |
4/4✓ Branch 38 → 39 taken 1 time.
✓ Branch 38 → 42 taken 206 times.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 46 taken 206 times.
|
415 | return initials.isEmpty() ? QStringLiteral("?") : initials; |
| 37 | 207 | } | |
| 38 | |||
| 39 | // Splits at top-level separators (outside quotes and angle brackets). | ||
| 40 | 885 | QStringList topLevelParts(const QString &text) { | |
| 41 | 885 | QStringList parts; | |
| 42 | 885 | QString current; | |
| 43 | 885 | bool quoted = false, angle = false, escaped = false; | |
| 44 |
2/2✓ Branch 61 → 4 taken 5019 times.
✓ Branch 61 → 62 taken 885 times.
|
5904 | for (const QChar c : text) { |
| 45 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 5019 times.
|
5019 | if (escaped) { |
| 46 | ✗ | escaped = false; | |
| 47 | ✗ | current.append(c); | |
| 48 | ✗ | continue; | |
| 49 | } | ||
| 50 |
4/6✓ Branch 7 → 8 taken 61 times.
✓ Branch 7 → 13 taken 4958 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 61 times.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 5019 times.
|
5019 | if (quoted && c == QLatin1Char('\\')) escaped = true; |
| 51 |
2/2✓ Branch 19 → 20 taken 7 times.
✓ Branch 19 → 21 taken 5012 times.
|
5019 | else if (c == QLatin1Char('"')) quoted = !quoted; |
| 52 |
6/6✓ Branch 21 → 22 taken 4954 times.
✓ Branch 21 → 27 taken 58 times.
✓ Branch 25 → 26 taken 86 times.
✓ Branch 25 → 27 taken 4868 times.
✓ Branch 28 → 29 taken 86 times.
✓ Branch 28 → 30 taken 4926 times.
|
5012 | else if (!quoted && c == QLatin1Char('<')) angle = true; |
| 53 |
6/6✓ Branch 30 → 31 taken 4868 times.
✓ Branch 30 → 36 taken 58 times.
✓ Branch 34 → 35 taken 86 times.
✓ Branch 34 → 36 taken 4782 times.
✓ Branch 37 → 38 taken 86 times.
✓ Branch 37 → 39 taken 4840 times.
|
4926 | else if (!quoted && c == QLatin1Char('>')) angle = false; |
| 54 |
12/12✓ Branch 39 → 40 taken 4782 times.
✓ Branch 39 → 54 taken 58 times.
✓ Branch 40 → 41 taken 3194 times.
✓ Branch 40 → 54 taken 1588 times.
✓ Branch 44 → 45 taken 3151 times.
✓ Branch 44 → 53 taken 43 times.
✓ Branch 48 → 49 taken 3149 times.
✓ Branch 48 → 53 taken 2 times.
✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 54 taken 3148 times.
✓ Branch 55 → 56 taken 46 times.
✓ Branch 55 → 59 taken 4794 times.
|
4840 | else if (!quoted && !angle && (c == QLatin1Char(',') || c == QLatin1Char(';') || c == QLatin1Char('\n'))) { |
| 55 |
1/1✓ Branch 56 → 57 taken 46 times.
|
46 | parts.append(current); |
| 56 | 46 | current.clear(); | |
| 57 | 46 | continue; | |
| 58 | } | ||
| 59 |
1/1✓ Branch 59 → 60 taken 4973 times.
|
4973 | current.append(c); |
| 60 | } | ||
| 61 |
1/1✓ Branch 62 → 63 taken 885 times.
|
885 | parts.append(current); |
| 62 | 885 | return parts; | |
| 63 | 885 | } | |
| 64 | |||
| 65 | } // namespace | ||
| 66 | |||
| 67 | 707 | RecipientChipModel::RecipientChipModel(const QString &fieldName, QObject *parent) | |
| 68 | 707 | : QAbstractListModel(parent), m_fieldName(fieldName) {} | |
| 69 | |||
| 70 | 3250 | int RecipientChipModel::rowCount(const QModelIndex &parent) const { | |
| 71 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3250 times.
|
3250 | return parent.isValid() ? 0 : int(m_chips.size()); |
| 72 | } | ||
| 73 | |||
| 74 | 1862 | QVariant RecipientChipModel::data(const QModelIndex &index, int role) const { | |
| 75 |
3/6✓ Branch 3 → 4 taken 1862 times.
✗ Branch 3 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1862 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 1862 times.
|
1862 | if (!index.isValid() || index.row() >= m_chips.size()) return {}; |
| 76 | 1862 | const Chip &chip = m_chips.at(index.row()); | |
| 77 |
9/10✓ Branch 13 → 14 taken 207 times.
✓ Branch 13 → 37 taken 207 times.
✓ Branch 13 → 38 taken 206 times.
✓ Branch 13 → 39 taken 209 times.
✓ Branch 13 → 40 taken 207 times.
✓ Branch 13 → 44 taken 206 times.
✓ Branch 13 → 48 taken 207 times.
✓ Branch 13 → 50 taken 206 times.
✓ Branch 13 → 79 taken 207 times.
✗ Branch 13 → 132 not taken.
|
1862 | switch (role) { |
| 78 | 207 | case Qt::DisplayRole: | |
| 79 | case DisplayRole: | ||
| 80 |
5/5✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 28 taken 205 times.
✓ Branch 17 → 18 taken 2 times.
✓ Branch 19 → 20 taken 2 times.
✓ Branch 22 → 23 taken 2 times.
|
211 | if (chip.state == QLatin1String("group")) return tr("%1 (%2)").arg(chip.name).arg(chip.members.size()); |
| 81 |
2/2✓ Branch 30 → 31 taken 19 times.
✓ Branch 30 → 32 taken 186 times.
|
205 | if (chip.state == QLatin1String("invalid")) return chip.raw; |
| 82 |
2/2✓ Branch 33 → 34 taken 97 times.
✓ Branch 33 → 35 taken 89 times.
|
186 | return chip.name.isEmpty() ? chip.address : chip.name; |
| 83 | 207 | case NameRole: return chip.name; | |
| 84 | 206 | case AddressRole: return chip.address; | |
| 85 | 209 | case StateRole: return chip.state; | |
| 86 |
1/1✓ Branch 40 → 41 taken 207 times.
|
207 | case InitialsRole: return initialsOf(chip.name, chip.address); |
| 87 |
1/1✓ Branch 44 → 45 taken 206 times.
|
206 | case ColorIndexRole: return int(qHash(chip.address.toLower()) % 8); |
| 88 | 207 | case MemberCountRole: return int(chip.members.size()); | |
| 89 | 206 | case TooltipRole: | |
| 90 |
3/3✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 62 taken 205 times.
✓ Branch 56 → 57 taken 1 time.
|
207 | if (chip.state == QLatin1String("group")) return chip.members.join(QStringLiteral(", ")); |
| 91 |
4/4✓ Branch 64 → 65 taken 19 times.
✓ Branch 64 → 72 taken 186 times.
✓ Branch 65 → 66 taken 19 times.
✓ Branch 67 → 68 taken 19 times.
|
224 | if (chip.state == QLatin1String("invalid")) return tr("%1 is not a valid address").arg(chip.raw); |
| 92 |
3/3✓ Branch 73 → 74 taken 97 times.
✓ Branch 73 → 75 taken 89 times.
✓ Branch 75 → 76 taken 89 times.
|
186 | return chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address); |
| 93 | 207 | case AccessibleRole: { | |
| 94 |
4/4✓ Branch 79 → 80 taken 207 times.
✓ Branch 82 → 83 taken 207 times.
✓ Branch 85 → 86 taken 207 times.
✓ Branch 87 → 88 taken 207 times.
|
828 | const QString position = tr("recipient %1 of %2 in %3").arg(index.row() + 1).arg(m_chips.size()).arg(m_fieldName); |
| 95 |
2/2✓ Branch 93 → 94 taken 19 times.
✓ Branch 93 → 100 taken 188 times.
|
207 | if (chip.state == QLatin1String("invalid")) |
| 96 |
2/2✓ Branch 94 → 95 taken 19 times.
✓ Branch 95 → 96 taken 19 times.
|
19 | return tr("%1, not a valid address, %2").arg(chip.raw, position); |
| 97 |
2/2✓ Branch 102 → 103 taken 1 time.
✓ Branch 102 → 117 taken 187 times.
|
188 | if (chip.state == QLatin1String("group")) |
| 98 |
4/4✓ Branch 103 → 104 taken 1 time.
✓ Branch 105 → 106 taken 1 time.
✓ Branch 108 → 109 taken 1 time.
✓ Branch 110 → 111 taken 1 time.
|
4 | return tr("Group %1 with %2 people, %3").arg(chip.name).arg(chip.members.size()).arg(position); |
| 99 |
4/6✓ Branch 118 → 119 taken 97 times.
✓ Branch 118 → 121 taken 90 times.
✓ Branch 119 → 120 taken 97 times.
✓ Branch 120 → 123 taken 97 times.
✗ Branch 191 → 192 not taken.
✗ Branch 191 → 193 not taken.
|
561 | return chip.name.isEmpty() ? tr("%1, %2").arg(chip.address, position) |
| 100 |
6/8✓ Branch 121 → 122 taken 90 times.
✓ Branch 122 → 123 taken 90 times.
✓ Branch 125 → 126 taken 90 times.
✓ Branch 125 → 127 taken 97 times.
✓ Branch 127 → 128 taken 97 times.
✓ Branch 127 → 129 taken 90 times.
✗ Branch 187 → 188 not taken.
✗ Branch 187 → 189 not taken.
|
374 | : tr("%1, %2, %3").arg(chip.name, chip.address, position); |
| 101 | 207 | } | |
| 102 | ✗ | default: return {}; | |
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | 413 | QHash<int, QByteArray> RecipientChipModel::roleNames() const { | |
| 107 | ✗ | return {{DisplayRole, "display"}, {NameRole, "name"}, {AddressRole, "address"}, | |
| 108 | ✗ | {StateRole, "state"}, {InitialsRole, "initials"}, {ColorIndexRole, "colorIndex"}, | |
| 109 |
2/4✓ Branch 13 → 14 taken 3717 times.
✓ Branch 13 → 15 taken 413 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
|
4130 | {MemberCountRole, "memberCount"}, {TooltipRole, "tooltip"}, {AccessibleRole, "accessibleName"}}; |
| 110 | 413 | } | |
| 111 | |||
| 112 | 14 | bool RecipientChipModel::hasInvalid() const { | |
| 113 |
2/2✓ Branch 11 → 4 taken 12 times.
✓ Branch 11 → 12 taken 4 times.
|
16 | for (const Chip &chip : m_chips) |
| 114 |
2/2✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 9 taken 2 times.
|
12 | if (chip.state == QLatin1String("invalid")) return true; |
| 115 | 4 | return false; | |
| 116 | } | ||
| 117 | |||
| 118 | 2096 | QStringList RecipientChipModel::headers() const { | |
| 119 | 2096 | QStringList out; | |
| 120 |
2/2✓ Branch 22 → 4 taken 946 times.
✓ Branch 22 → 23 taken 2096 times.
|
3042 | for (const Chip &chip : m_chips) { |
| 121 |
2/2✓ Branch 7 → 8 taken 33 times.
✓ Branch 7 → 9 taken 913 times.
|
946 | if (chip.state == QLatin1String("invalid")) continue; |
| 122 |
3/3✓ Branch 11 → 12 taken 5 times.
✓ Branch 11 → 13 taken 908 times.
✓ Branch 12 → 20 taken 5 times.
|
913 | if (chip.state == QLatin1String("group")) out.append(chip.members); |
| 123 |
4/4✓ Branch 14 → 15 taken 434 times.
✓ Branch 14 → 16 taken 474 times.
✓ Branch 16 → 17 taken 474 times.
✓ Branch 17 → 18 taken 908 times.
|
908 | else out.append(chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address)); |
| 124 | } | ||
| 125 | 2096 | return out; | |
| 126 | ✗ | } | |
| 127 | |||
| 128 | 3339 | QString RecipientChipModel::draftText() const { | |
| 129 | 3339 | QStringList out; | |
| 130 |
2/2✓ Branch 22 → 4 taken 951 times.
✓ Branch 22 → 23 taken 3339 times.
|
4290 | for (const Chip &chip : m_chips) { |
| 131 |
3/3✓ Branch 7 → 8 taken 103 times.
✓ Branch 7 → 9 taken 848 times.
✓ Branch 8 → 20 taken 103 times.
|
951 | if (chip.state == QLatin1String("invalid")) out.append(chip.raw); |
| 132 |
3/3✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 847 times.
✓ Branch 12 → 20 taken 1 time.
|
848 | else if (chip.state == QLatin1String("group")) out.append(chip.members); |
| 133 |
4/4✓ Branch 14 → 15 taken 494 times.
✓ Branch 14 → 16 taken 353 times.
✓ Branch 16 → 17 taken 353 times.
✓ Branch 17 → 18 taken 847 times.
|
847 | else out.append(chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address)); |
| 134 | } | ||
| 135 |
1/1✓ Branch 26 → 27 taken 3339 times.
|
6678 | return out.join(QStringLiteral(", ")); |
| 136 | 3339 | } | |
| 137 | |||
| 138 | 1354 | QStringList RecipientChipModel::addresses() const { | |
| 139 | 1354 | QStringList out; | |
| 140 |
7/7✓ Branch 2 → 3 taken 1354 times.
✓ Branch 3 → 4 taken 1354 times.
✓ Branch 4 → 5 taken 1354 times.
✓ Branch 7 → 8 taken 607 times.
✓ Branch 8 → 9 taken 607 times.
✓ Branch 12 → 6 taken 607 times.
✓ Branch 12 → 13 taken 1354 times.
|
1961 | for (const QString &header : headers()) out.append(splitMailbox(header).second); |
| 141 | 1354 | return out; | |
| 142 | ✗ | } | |
| 143 | |||
| 144 | 142 | QStringList RecipientChipModel::invalidTexts() const { | |
| 145 | 142 | QStringList out; | |
| 146 |
2/2✓ Branch 11 → 4 taken 77 times.
✓ Branch 11 → 12 taken 142 times.
|
219 | for (const Chip &chip : m_chips) |
| 147 |
3/3✓ Branch 7 → 8 taken 16 times.
✓ Branch 7 → 9 taken 61 times.
✓ Branch 8 → 9 taken 16 times.
|
77 | if (chip.state == QLatin1String("invalid")) out.append(chip.raw); |
| 148 | 142 | return out; | |
| 149 | ✗ | } | |
| 150 | |||
| 151 |
2/2✓ Branch 2 → 3 taken 292 times.
✓ Branch 6 → 7 taken 292 times.
|
584 | QString RecipientChipModel::text() const { return headers().join(QStringLiteral(", ")); } |
| 152 | |||
| 153 | // A chip that differs only in letter case is the same person to every | ||
| 154 | // mail provider in practice, so the field shows it once (§4.5). The | ||
| 155 | // envelope keeps RFC 5321's case-sensitive local part (ComposeRecipients). | ||
| 156 | 232 | bool RecipientChipModel::containsAddress(const QString &address) const { | |
| 157 |
1/1✓ Branch 2 → 3 taken 232 times.
|
232 | const QString key = address.toCaseFolded(); |
| 158 |
5/5✓ Branch 3 → 4 taken 232 times.
✓ Branch 4 → 5 taken 232 times.
✓ Branch 5 → 6 taken 232 times.
✓ Branch 15 → 7 taken 56 times.
✓ Branch 15 → 16 taken 230 times.
|
286 | for (const QString &existing : addresses()) |
| 159 |
5/5✓ Branch 8 → 9 taken 56 times.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 54 times.
✓ Branch 18 → 19 taken 230 times.
✓ Branch 18 → 21 taken 2 times.
|
286 | if (existing.toCaseFolded() == key) return true; |
| 160 | 230 | return false; | |
| 161 | 232 | } | |
| 162 | |||
| 163 | 244 | RecipientChipModel::Chip RecipientChipModel::chipFor(const QString &mailbox) const { | |
| 164 | 244 | Chip chip; | |
| 165 |
1/1✓ Branch 2 → 3 taken 244 times.
|
244 | const auto parsed = ComposeRecipients::parse(mailbox); |
| 166 |
5/6✓ Branch 4 → 5 taken 226 times.
✓ Branch 4 → 8 taken 18 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 226 times.
✓ Branch 10 → 11 taken 18 times.
✓ Branch 10 → 22 taken 226 times.
|
244 | if (!parsed || parsed->headers.size() != 1) { |
| 167 | 18 | chip.state = QStringLiteral("invalid"); | |
| 168 |
1/1✓ Branch 17 → 18 taken 18 times.
|
18 | chip.raw = mailbox.trimmed(); |
| 169 | 18 | chip.address = chip.raw; | |
| 170 | 18 | return chip; | |
| 171 | } | ||
| 172 |
1/1✓ Branch 24 → 25 taken 226 times.
|
226 | const auto [name, address] = splitMailbox(parsed->headers.first()); |
| 173 | 226 | chip.name = name; | |
| 174 | 226 | chip.address = address; | |
| 175 |
12/18✓ Branch 30 → 31 taken 190 times.
✓ Branch 30 → 37 taken 36 times.
✓ Branch 31 → 32 taken 190 times.
✓ Branch 32 → 33 taken 190 times.
✓ Branch 33 → 34 taken 1 time.
✓ Branch 33 → 37 taken 189 times.
✓ Branch 42 → 43 taken 225 times.
✓ Branch 42 → 44 taken 1 time.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 46 taken 225 times.
✓ Branch 46 → 47 taken 190 times.
✓ Branch 46 → 48 taken 36 times.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
✗ Branch 57 → 58 not taken.
✗ Branch 57 → 59 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
|
452 | chip.state = m_known && m_known(address.toLower()) ? QStringLiteral("known") : QStringLiteral("new"); |
| 176 | 226 | return chip; | |
| 177 | 244 | } | |
| 178 | |||
| 179 | 238 | void RecipientChipModel::append(const Chip &chip) { | |
| 180 |
9/9✓ Branch 4 → 5 taken 221 times.
✓ Branch 4 → 11 taken 17 times.
✓ Branch 7 → 8 taken 219 times.
✓ Branch 7 → 11 taken 2 times.
✓ Branch 8 → 9 taken 219 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 218 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 15 taken 237 times.
|
238 | if (chip.state != QLatin1String("invalid") && chip.state != QLatin1String("group") && containsAddress(chip.address)) { |
| 181 | 1 | emit duplicateSkipped(chip.address); | |
| 182 | 1 | return; | |
| 183 | } | ||
| 184 |
1/1✓ Branch 18 → 19 taken 237 times.
|
237 | beginInsertRows({}, int(m_chips.size()), int(m_chips.size())); |
| 185 | 237 | m_chips.append(chip); | |
| 186 | 237 | endInsertRows(); | |
| 187 | } | ||
| 188 | |||
| 189 | 723 | void RecipientChipModel::setText(const QString &text) { | |
| 190 | 723 | beginResetModel(); | |
| 191 | 723 | m_chips.clear(); | |
| 192 | 723 | endResetModel(); | |
| 193 |
5/5✓ Branch 5 → 6 taken 723 times.
✓ Branch 6 → 7 taken 723 times.
✓ Branch 7 → 8 taken 723 times.
✓ Branch 21 → 9 taken 768 times.
✓ Branch 21 → 22 taken 723 times.
|
1491 | for (const QString &part : topLevelParts(text)) { |
| 194 |
3/3✓ Branch 10 → 11 taken 768 times.
✓ Branch 13 → 14 taken 548 times.
✓ Branch 13 → 15 taken 220 times.
|
768 | if (part.trimmed().isEmpty()) continue; |
| 195 |
2/2✓ Branch 15 → 16 taken 220 times.
✓ Branch 16 → 17 taken 220 times.
|
220 | append(chipFor(part)); |
| 196 | 723 | } | |
| 197 | 723 | emit changed(); | |
| 198 | 723 | } | |
| 199 | |||
| 200 | 162 | QString RecipientChipModel::commit(const QString &text, bool force) { | |
| 201 |
1/1✓ Branch 2 → 3 taken 162 times.
|
162 | const QStringList parts = topLevelParts(text); |
| 202 | 162 | QStringList rest; | |
| 203 |
2/2✓ Branch 32 → 4 taken 163 times.
✓ Branch 32 → 33 taken 162 times.
|
325 | for (qsizetype i = 0; i < parts.size(); ++i) { |
| 204 |
1/1✓ Branch 5 → 6 taken 163 times.
|
163 | const QString part = parts.at(i).trimmed(); |
| 205 |
2/2✓ Branch 7 → 8 taken 154 times.
✓ Branch 7 → 9 taken 9 times.
|
163 | if (part.isEmpty()) continue; |
| 206 |
1/1✓ Branch 9 → 10 taken 9 times.
|
9 | const Chip chip = chipFor(part); |
| 207 | // The last part is still being typed unless the commit is forced. | ||
| 208 |
6/6✓ Branch 12 → 13 taken 4 times.
✓ Branch 12 → 15 taken 5 times.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 3 times.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 19 taken 8 times.
|
9 | if (chip.state == QLatin1String("invalid") && !force) { |
| 209 |
1/1✓ Branch 17 → 18 taken 1 time.
|
1 | rest.append(part); |
| 210 | 1 | continue; | |
| 211 | } | ||
| 212 |
1/1✓ Branch 19 → 20 taken 8 times.
|
8 | append(chip); |
| 213 | 164 | } | |
| 214 |
1/1✓ Branch 33 → 34 taken 162 times.
|
162 | emit changed(); |
| 215 |
1/1✓ Branch 37 → 38 taken 162 times.
|
324 | return rest.join(QStringLiteral(", ")); |
| 216 | 162 | } | |
| 217 | |||
| 218 | 3 | bool RecipientChipModel::commitsOnSeparator(const QString &text) const { | |
| 219 |
1/1✓ Branch 2 → 3 taken 3 times.
|
3 | const QString trimmed = text.trimmed(); |
| 220 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 3 times.
|
3 | if (trimmed.isEmpty()) return false; |
| 221 |
1/1✓ Branch 6 → 7 taken 3 times.
|
3 | const auto parsed = ComposeRecipients::parse(trimmed); |
| 222 |
3/4✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 13 taken 1 time.
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 13 not taken.
|
3 | return parsed && parsed->headers.size() == 1; |
| 223 | 3 | } | |
| 224 | |||
| 225 | 5 | void RecipientChipModel::addCompletion(const QVariantMap &row) { | |
| 226 |
4/4✓ Branch 6 → 7 taken 5 times.
✓ Branch 7 → 8 taken 5 times.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 72 taken 3 times.
|
10 | if (row.value(QStringLiteral("group")).toBool()) { |
| 227 | 2 | Chip chip; | |
| 228 | 2 | chip.state = QStringLiteral("group"); | |
| 229 |
2/2✓ Branch 23 → 24 taken 2 times.
✓ Branch 24 → 25 taken 2 times.
|
4 | chip.name = row.value(QStringLiteral("name")).toString(); |
| 230 |
3/3✓ Branch 35 → 36 taken 2 times.
✓ Branch 36 → 37 taken 2 times.
✓ Branch 37 → 38 taken 2 times.
|
4 | const auto parsed = ComposeRecipients::parse(row.value(QStringLiteral("insert")).toString()); |
| 231 |
1/2✓ Branch 44 → 45 taken 2 times.
✗ Branch 44 → 58 not taken.
|
2 | if (parsed) { |
| 232 |
2/2✓ Branch 56 → 48 taken 6 times.
✓ Branch 56 → 57 taken 2 times.
|
8 | for (const QString &member : parsed->headers) |
| 233 |
5/5✓ Branch 49 → 50 taken 6 times.
✓ Branch 50 → 51 taken 6 times.
✓ Branch 52 → 53 taken 5 times.
✓ Branch 52 → 54 taken 1 time.
✓ Branch 53 → 54 taken 5 times.
|
6 | if (!containsAddress(splitMailbox(member).second)) chip.members.append(member); |
| 234 | } | ||
| 235 |
1/2✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 2 times.
|
2 | if (chip.members.isEmpty()) return; |
| 236 |
1/1✓ Branch 61 → 62 taken 2 times.
|
2 | append(chip); |
| 237 |
2/4✓ Branch 64 → 65 taken 2 times.
✗ Branch 64 → 66 not taken.
✓ Branch 68 → 69 taken 2 times.
✗ Branch 68 → 71 not taken.
|
2 | } else { |
| 238 |
5/5✓ Branch 76 → 77 taken 3 times.
✓ Branch 77 → 78 taken 3 times.
✓ Branch 82 → 83 taken 3 times.
✓ Branch 83 → 84 taken 3 times.
✓ Branch 84 → 85 taken 3 times.
|
9 | addMailbox(row.value(QStringLiteral("name")).toString(), row.value(QStringLiteral("email")).toString()); |
| 239 | 3 | return; | |
| 240 | } | ||
| 241 | 2 | emit changed(); | |
| 242 | } | ||
| 243 | |||
| 244 | 8 | void RecipientChipModel::addMailbox(const QString &name, const QString &address) { | |
| 245 |
5/5✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 7 times.
✓ Branch 5 → 6 taken 7 times.
✓ Branch 6 → 7 taken 8 times.
✓ Branch 7 → 8 taken 8 times.
|
8 | append(chipFor(name.isEmpty() ? address : ComposeRecipients::mailbox(name, address))); |
| 246 | 8 | emit changed(); | |
| 247 | 8 | } | |
| 248 | |||
| 249 | 4 | void RecipientChipModel::remove(int row) { | |
| 250 |
3/6✓ Branch 2 → 3 taken 4 times.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 4 times.
|
4 | if (row < 0 || row >= m_chips.size()) return; |
| 251 |
1/1✓ Branch 10 → 11 taken 4 times.
|
4 | beginRemoveRows({}, row, row); |
| 252 | 4 | m_chips.removeAt(row); | |
| 253 | 4 | endRemoveRows(); | |
| 254 | 4 | emit changed(); | |
| 255 | } | ||
| 256 | |||
| 257 | 1 | QString RecipientChipModel::take(int row) { | |
| 258 |
3/6✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
|
1 | if (row < 0 || row >= m_chips.size()) return {}; |
| 259 | 1 | const Chip chip = m_chips.at(row); | |
| 260 |
1/1✓ Branch 11 → 12 taken 1 time.
|
1 | remove(row); |
| 261 |
1/2✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 1 time.
|
1 | if (chip.state == QLatin1String("invalid")) return chip.raw; |
| 262 |
1/3✗ Branch 18 → 19 not taken.
✓ Branch 18 → 27 taken 1 time.
✗ Branch 22 → 23 not taken.
|
1 | if (chip.state == QLatin1String("group")) return chip.members.join(QStringLiteral(", ")); |
| 263 |
2/3✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 1 time.
✓ Branch 30 → 31 taken 1 time.
|
1 | return chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address); |
| 264 | 1 | } | |
| 265 | |||
| 266 | 2 | void RecipientChipModel::expandGroup(int row) { | |
| 267 |
4/8✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 9 not taken.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 2 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 2 times.
|
2 | if (row < 0 || row >= m_chips.size() || m_chips.at(row).state != QLatin1String("group")) return; |
| 268 | 2 | const QStringList members = m_chips.at(row).members; | |
| 269 |
1/1✓ Branch 16 → 17 taken 2 times.
|
2 | beginRemoveRows({}, row, row); |
| 270 |
1/1✓ Branch 17 → 18 taken 2 times.
|
2 | m_chips.removeAt(row); |
| 271 |
1/1✓ Branch 18 → 19 taken 2 times.
|
2 | endRemoveRows(); |
| 272 | 2 | QList<Chip> chips; | |
| 273 |
2/2✓ Branch 35 → 21 taken 5 times.
✓ Branch 35 → 36 taken 2 times.
|
7 | for (const QString &member : members) { |
| 274 |
1/1✓ Branch 22 → 23 taken 5 times.
|
5 | const Chip chip = chipFor(member); |
| 275 |
5/8✓ Branch 25 → 26 taken 5 times.
✗ Branch 25 → 29 not taken.
✓ Branch 26 → 27 taken 5 times.
✓ Branch 27 → 28 taken 5 times.
✗ Branch 27 → 29 not taken.
✓ Branch 30 → 31 taken 5 times.
✗ Branch 30 → 32 not taken.
✓ Branch 31 → 32 taken 5 times.
|
5 | if (chip.state != QLatin1String("invalid") && !containsAddress(chip.address)) chips.append(chip); |
| 276 | 5 | } | |
| 277 |
1/2✓ Branch 37 → 38 taken 2 times.
✗ Branch 37 → 48 not taken.
|
2 | if (!chips.isEmpty()) { |
| 278 |
1/1✓ Branch 40 → 41 taken 2 times.
|
2 | beginInsertRows({}, row, row + int(chips.size()) - 1); |
| 279 |
3/3✓ Branch 43 → 44 taken 5 times.
✓ Branch 46 → 42 taken 5 times.
✓ Branch 46 → 47 taken 2 times.
|
7 | for (qsizetype i = 0; i < chips.size(); ++i) m_chips.insert(row + i, chips.at(i)); |
| 280 |
1/1✓ Branch 47 → 48 taken 2 times.
|
2 | endInsertRows(); |
| 281 | } | ||
| 282 |
1/1✓ Branch 48 → 49 taken 2 times.
|
2 | emit changed(); |
| 283 | 2 | } | |
| 284 | |||
| 285 | 761 | QVariantMap RecipientChipModel::chipAt(int row) const { | |
| 286 |
6/6✓ Branch 2 → 3 taken 734 times.
✓ Branch 2 → 5 taken 27 times.
✓ Branch 4 → 5 taken 31 times.
✓ Branch 4 → 6 taken 703 times.
✓ Branch 7 → 8 taken 58 times.
✓ Branch 7 → 9 taken 703 times.
|
761 | if (row < 0 || row >= m_chips.size()) return {}; |
| 287 | 703 | const Chip &chip = m_chips.at(row); | |
| 288 | 2109 | return {{QStringLiteral("name"), chip.name}, {QStringLiteral("address"), chip.address}, | |
| 289 | 2109 | {QStringLiteral("state"), chip.state}, {QStringLiteral("members"), chip.members}, | |
| 290 |
2/4✓ Branch 32 → 33 taken 3515 times.
✓ Branch 32 → 34 taken 703 times.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
|
5624 | {QStringLiteral("raw"), chip.raw}}; |
| 291 | 4218 | } | |
| 292 | |||
| 293 | 2 | void RecipientChipModel::insertChip(int row, const QVariantMap &value) { | |
| 294 | 2 | Chip chip; | |
| 295 |
2/2✓ Branch 6 → 7 taken 2 times.
✓ Branch 7 → 8 taken 2 times.
|
4 | chip.name = value.value(QStringLiteral("name")).toString(); |
| 296 |
2/2✓ Branch 18 → 19 taken 2 times.
✓ Branch 19 → 20 taken 2 times.
|
4 | chip.address = value.value(QStringLiteral("address")).toString(); |
| 297 |
2/2✓ Branch 30 → 31 taken 2 times.
✓ Branch 31 → 32 taken 2 times.
|
4 | chip.state = value.value(QStringLiteral("state")).toString(); |
| 298 |
2/2✓ Branch 42 → 43 taken 2 times.
✓ Branch 43 → 44 taken 2 times.
|
4 | chip.members = value.value(QStringLiteral("members")).toStringList(); |
| 299 |
2/2✓ Branch 54 → 55 taken 2 times.
✓ Branch 55 → 56 taken 2 times.
|
4 | chip.raw = value.value(QStringLiteral("raw")).toString(); |
| 300 |
3/6✓ Branch 64 → 65 taken 2 times.
✗ Branch 64 → 69 not taken.
✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 69 not taken.
✓ Branch 70 → 71 taken 2 times.
✗ Branch 70 → 80 not taken.
|
2 | if (chip.state != QLatin1String("group") && chip.state != QLatin1String("invalid")) |
| 301 |
4/4✓ Branch 72 → 73 taken 1 time.
✓ Branch 72 → 74 taken 1 time.
✓ Branch 74 → 75 taken 1 time.
✓ Branch 75 → 76 taken 2 times.
|
2 | chip = chipFor(chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address)); |
| 302 |
5/9✓ Branch 82 → 83 taken 2 times.
✗ Branch 82 → 89 not taken.
✓ Branch 85 → 86 taken 2 times.
✗ Branch 85 → 89 not taken.
✓ Branch 86 → 87 taken 2 times.
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 2 times.
✗ Branch 90 → 91 not taken.
✓ Branch 90 → 93 taken 2 times.
|
2 | if (chip.state != QLatin1String("invalid") && chip.state != QLatin1String("group") && containsAddress(chip.address)) { |
| 303 | ✗ | emit duplicateSkipped(chip.address); | |
| 304 | ✗ | return; | |
| 305 | } | ||
| 306 |
1/1✓ Branch 94 → 95 taken 2 times.
|
2 | row = qBound(0, row, int(m_chips.size())); |
| 307 |
1/1✓ Branch 96 → 97 taken 2 times.
|
2 | beginInsertRows({}, row, row); |
| 308 |
1/1✓ Branch 97 → 98 taken 2 times.
|
2 | m_chips.insert(row, chip); |
| 309 |
1/1✓ Branch 98 → 99 taken 2 times.
|
2 | endInsertRows(); |
| 310 |
1/1✓ Branch 99 → 100 taken 2 times.
|
2 | emit changed(); |
| 311 | 2 | } | |
| 312 | |||
| 313 | 352 | QString RecipientChipModel::copyText(const QList<int> &rows) const { | |
| 314 | 352 | QStringList out; | |
| 315 |
2/2✓ Branch 30 → 4 taken 352 times.
✓ Branch 30 → 31 taken 352 times.
|
704 | for (const int row : rows) { |
| 316 |
6/6✓ Branch 5 → 6 taken 325 times.
✓ Branch 5 → 8 taken 27 times.
✓ Branch 7 → 8 taken 31 times.
✓ Branch 7 → 9 taken 294 times.
✓ Branch 10 → 11 taken 58 times.
✓ Branch 10 → 12 taken 294 times.
|
352 | if (row < 0 || row >= m_chips.size()) continue; |
| 317 | 294 | const Chip &chip = m_chips.at(row); | |
| 318 |
3/3✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 293 times.
✓ Branch 16 → 28 taken 1 time.
|
294 | if (chip.state == QLatin1String("group")) out.append(chip.members); |
| 319 |
3/3✓ Branch 19 → 20 taken 27 times.
✓ Branch 19 → 21 taken 266 times.
✓ Branch 20 → 28 taken 27 times.
|
293 | else if (chip.state == QLatin1String("invalid")) out.append(chip.raw); |
| 320 |
4/4✓ Branch 22 → 23 taken 144 times.
✓ Branch 22 → 24 taken 122 times.
✓ Branch 24 → 25 taken 122 times.
✓ Branch 25 → 26 taken 266 times.
|
266 | else out.append(chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address)); |
| 321 | } | ||
| 322 |
1/1✓ Branch 34 → 35 taken 352 times.
|
704 | return out.join(QStringLiteral(", ")); |
| 323 | 352 | } | |
| 324 | |||
| 325 | ✗ | void RecipientChipModel::clear() { | |
| 326 | ✗ | beginResetModel(); | |
| 327 | ✗ | m_chips.clear(); | |
| 328 | ✗ | endResetModel(); | |
| 329 | ✗ | emit changed(); | |
| 330 | ✗ | } | |
| 331 |