presentation/RecipientChipModel.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QAbstractListModel> | ||
| 4 | #include <QStringList> | ||
| 5 | #include <QVariantMap> | ||
| 6 | #include <functional> | ||
| 7 | |||
| 8 | // Sprint 108 S108.5.1 (D-13): the recipients of one field (To, Cc or Bcc) | ||
| 9 | // as chips. Text becomes chips when it parses as addresses | ||
| 10 | // (ComposeRecipients — display names, quoting and IDN as the send path reads | ||
| 11 | // them); what does not parse stays visible as an *invalid* chip that blocks | ||
| 12 | // *Send* in the check bar. A group from the completion is one chip that | ||
| 13 | // expands into its members. The field's text form ("Name <a@b>, …") is the | ||
| 14 | // interface to drafts and the message builder. | ||
| 15 | class RecipientChipModel : public QAbstractListModel { | ||
| 16 | 435 | Q_OBJECT | |
| 17 | Q_PROPERTY(int count READ count NOTIFY changed) | ||
| 18 | Q_PROPERTY(bool hasInvalid READ hasInvalid NOTIFY changed) | ||
| 19 | Q_PROPERTY(QString text READ text NOTIFY changed) | ||
| 20 | Q_PROPERTY(QString fieldName READ fieldName CONSTANT) | ||
| 21 | |||
| 22 | public: | ||
| 23 | enum Roles { | ||
| 24 | DisplayRole = Qt::UserRole + 1, // name, else the address | ||
| 25 | NameRole, | ||
| 26 | AddressRole, | ||
| 27 | StateRole, // "known", "new", "invalid", "group" | ||
| 28 | InitialsRole, | ||
| 29 | ColorIndexRole, | ||
| 30 | MemberCountRole, | ||
| 31 | TooltipRole, | ||
| 32 | AccessibleRole, | ||
| 33 | }; | ||
| 34 | struct Chip { | ||
| 35 | QString name; | ||
| 36 | QString address; | ||
| 37 | QString state; // known / new / invalid / group | ||
| 38 | QStringList members; // group: the members' mailboxes | ||
| 39 | QString raw; // invalid: the text as typed | ||
| 40 | }; | ||
| 41 | |||
| 42 | explicit RecipientChipModel(const QString &fieldName, QObject *parent = nullptr); | ||
| 43 | |||
| 44 | int rowCount(const QModelIndex &parent = {}) const override; | ||
| 45 | QVariant data(const QModelIndex &index, int role) const override; | ||
| 46 | QHash<int, QByteArray> roleNames() const override; | ||
| 47 | |||
| 48 | ✗ | QString fieldName() const { return m_fieldName; } | |
| 49 | 5719 | int count() const { return int(m_chips.size()); } | |
| 50 | bool hasInvalid() const; | ||
| 51 | // "Name <address>, …" (groups as their members). | ||
| 52 | QString text() const; | ||
| 53 | // Mailboxes for the message headers and the envelope addresses. | ||
| 54 | QStringList headers() const; | ||
| 55 | QStringList addresses() const; | ||
| 56 | QStringList invalidTexts() const; | ||
| 57 | // Lower-case addresses already in the field (dedupe across fields). | ||
| 58 | bool containsAddress(const QString &address) const; | ||
| 59 | |||
| 60 | // A contact address is "known" (quiet chip), else "new" (with a + to add). | ||
| 61 | 580 | void setKnownLookup(std::function<bool(const QString &address)> lookup) { m_known = std::move(lookup); } | |
| 62 | |||
| 63 | // Replaces every chip with the parsed text (prefill, restore). | ||
| 64 | Q_INVOKABLE void setText(const QString &text); | ||
| 65 | // What a draft stores: every chip as typed, invalid ones included (they | ||
| 66 | // are never sent, but never lost either). | ||
| 67 | QString draftText() const; | ||
| 68 | // Commits typed text: every part that parses becomes a chip; with | ||
| 69 | // `force` (Enter, Tab, focus leaves) the rest becomes invalid chips, | ||
| 70 | // otherwise it is returned to stay in the input. | ||
| 71 | Q_INVOKABLE QString commit(const QString &text, bool force); | ||
| 72 | // D-13: "," and ";" commit only when the typed text is a complete address. | ||
| 73 | Q_INVOKABLE bool commitsOnSeparator(const QString &text) const; | ||
| 74 | // A completion row (QuickContactModel::completions): a person or a group. | ||
| 75 | Q_INVOKABLE void addCompletion(const QVariantMap &row); | ||
| 76 | Q_INVOKABLE void addMailbox(const QString &name, const QString &address); | ||
| 77 | Q_INVOKABLE void remove(int row); | ||
| 78 | // Removes the chip and returns its text for the input (editing a chip). | ||
| 79 | Q_INVOKABLE QString take(int row); | ||
| 80 | Q_INVOKABLE void expandGroup(int row); | ||
| 81 | Q_INVOKABLE QVariantMap chipAt(int row) const; | ||
| 82 | // Moves a chip in from another field (drag, "Move to Cc"). | ||
| 83 | Q_INVOKABLE void insertChip(int row, const QVariantMap &chip); | ||
| 84 | Q_INVOKABLE QString copyText(const QList<int> &rows) const; | ||
| 85 | Q_INVOKABLE void clear(); | ||
| 86 | |||
| 87 | signals: | ||
| 88 | void changed(); | ||
| 89 | // A recipient was already there: the view says so instead of a duplicate. | ||
| 90 | void duplicateSkipped(const QString &address); | ||
| 91 | |||
| 92 | private: | ||
| 93 | QString m_fieldName; | ||
| 94 | QList<Chip> m_chips; | ||
| 95 | std::function<bool(const QString &)> m_known; | ||
| 96 | Chip chipFor(const QString &mailbox) const; | ||
| 97 | void append(const Chip &chip); | ||
| 98 | }; | ||
| 99 |