diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-11-17 12:04:41 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-11-17 17:57:36 +0100 |
commit | 56d0411b0a8bce305220adbf12f6b859abf9f431 (patch) | |
tree | ec1e6aeb0bd1b73b6c59d48eb3fa9d623a69a0b4 /framework/domain | |
parent | c633f7c5fce50db60035792526b6c7e84b51149f (diff) | |
download | kube-56d0411b0a8bce305220adbf12f6b859abf9f431.tar.gz kube-56d0411b0a8bce305220adbf12f6b859abf9f431.zip |
AutocompleteLineEdit
Diffstat (limited to 'framework/domain')
-rw-r--r-- | framework/domain/CMakeLists.txt | 1 | ||||
-rw-r--r-- | framework/domain/composercontroller.cpp | 106 | ||||
-rw-r--r-- | framework/domain/composercontroller.h | 8 | ||||
-rw-r--r-- | framework/domain/recepientautocompletionmodel.cpp | 110 | ||||
-rw-r--r-- | framework/domain/recepientautocompletionmodel.h | 55 |
5 files changed, 259 insertions, 21 deletions
diff --git a/framework/domain/CMakeLists.txt b/framework/domain/CMakeLists.txt index 094eac04..24fdb49f 100644 --- a/framework/domain/CMakeLists.txt +++ b/framework/domain/CMakeLists.txt | |||
@@ -18,6 +18,7 @@ set(mailplugin_SRCS | |||
18 | accountsmodel.cpp | 18 | accountsmodel.cpp |
19 | outboxmodel.cpp | 19 | outboxmodel.cpp |
20 | identitiesmodel.cpp | 20 | identitiesmodel.cpp |
21 | recepientautocompletionmodel.cpp | ||
21 | settings/accountsettings.cpp | 22 | settings/accountsettings.cpp |
22 | ) | 23 | ) |
23 | find_package(KF5 REQUIRED COMPONENTS Package) | 24 | find_package(KF5 REQUIRED COMPONENTS Package) |
diff --git a/framework/domain/composercontroller.cpp b/framework/domain/composercontroller.cpp index b2ad7ecc..7fd2593d 100644 --- a/framework/domain/composercontroller.cpp +++ b/framework/domain/composercontroller.cpp | |||
@@ -25,14 +25,20 @@ | |||
25 | #include <KMime/Message> | 25 | #include <KMime/Message> |
26 | #include <KCodecs/KEmailAddress> | 26 | #include <KCodecs/KEmailAddress> |
27 | #include <QVariant> | 27 | #include <QVariant> |
28 | #include <QSortFilterProxyModel> | ||
29 | #include <QList> | ||
28 | #include <QDebug> | 30 | #include <QDebug> |
29 | #include <QQmlEngine> | 31 | #include <QQmlEngine> |
30 | #include <sink/store.h> | 32 | #include <sink/store.h> |
33 | #include <sink/log.h> | ||
31 | 34 | ||
32 | #include "accountsmodel.h" | 35 | #include "accountsmodel.h" |
33 | #include "identitiesmodel.h" | 36 | #include "identitiesmodel.h" |
37 | #include "recepientautocompletionmodel.h" | ||
34 | #include "mailtemplates.h" | 38 | #include "mailtemplates.h" |
35 | 39 | ||
40 | SINK_DEBUG_AREA("composercontroller"); | ||
41 | |||
36 | ComposerController::ComposerController(QObject *parent) : QObject(parent) | 42 | ComposerController::ComposerController(QObject *parent) : QObject(parent) |
37 | { | 43 | { |
38 | } | 44 | } |
@@ -102,6 +108,18 @@ void ComposerController::setBody(const QString &body) | |||
102 | } | 108 | } |
103 | } | 109 | } |
104 | 110 | ||
111 | QString ComposerController::recepientSearchString() const | ||
112 | { | ||
113 | return QString(); | ||
114 | } | ||
115 | |||
116 | void ComposerController::setRecepientSearchString(const QString &s) | ||
117 | { | ||
118 | if (auto model = static_cast<RecipientAutocompletionModel*>(recepientAutocompletionModel())) { | ||
119 | model->setFilter(s); | ||
120 | } | ||
121 | } | ||
122 | |||
105 | QAbstractItemModel *ComposerController::identityModel() const | 123 | QAbstractItemModel *ComposerController::identityModel() const |
106 | { | 124 | { |
107 | static auto model = new IdentitiesModel(); | 125 | static auto model = new IdentitiesModel(); |
@@ -109,6 +127,13 @@ QAbstractItemModel *ComposerController::identityModel() const | |||
109 | return model; | 127 | return model; |
110 | } | 128 | } |
111 | 129 | ||
130 | QAbstractItemModel *ComposerController::recepientAutocompletionModel() const | ||
131 | { | ||
132 | static auto model = new RecipientAutocompletionModel(); | ||
133 | QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership); | ||
134 | return model; | ||
135 | } | ||
136 | |||
112 | QStringList ComposerController::attachemts() const | 137 | QStringList ComposerController::attachemts() const |
113 | { | 138 | { |
114 | return m_attachments; | 139 | return m_attachments; |
@@ -153,43 +178,82 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | |||
153 | }).exec(); | 178 | }).exec(); |
154 | } | 179 | } |
155 | 180 | ||
156 | KMime::Message::Ptr ComposerController::assembleMessage() | 181 | void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName) |
157 | { | 182 | { |
158 | auto mail = m_msg.value<KMime::Message::Ptr>(); | 183 | if (auto model = static_cast<RecipientAutocompletionModel*>(recepientAutocompletionModel())) { |
159 | if (!mail) { | 184 | model->addEntry(addrSpec, displayName); |
160 | mail = KMime::Message::Ptr::create(); | ||
161 | } | 185 | } |
162 | for (const auto &to : KEmailAddress::splitAddressList(m_to)) { | 186 | } |
187 | |||
188 | void applyAddresses(const QString &list, std::function<void(const QByteArray &, const QByteArray &)> callback) | ||
189 | { | ||
190 | for (const auto &to : KEmailAddress::splitAddressList(list)) { | ||
163 | QByteArray displayName; | 191 | QByteArray displayName; |
164 | QByteArray addrSpec; | 192 | QByteArray addrSpec; |
165 | QByteArray comment; | 193 | QByteArray comment; |
166 | KEmailAddress::splitAddress(to.toUtf8(), displayName, addrSpec, comment); | 194 | KEmailAddress::splitAddress(to.toUtf8(), displayName, addrSpec, comment); |
195 | callback(addrSpec, displayName); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | bool ComposerController::identityIsSet() const | ||
200 | { | ||
201 | return (identityModel()->rowCount() > 0) && (m_currentAccountIndex >= 0); | ||
202 | } | ||
203 | |||
204 | KMime::Message::Ptr ComposerController::assembleMessage() | ||
205 | { | ||
206 | auto mail = m_msg.value<KMime::Message::Ptr>(); | ||
207 | if (!mail) { | ||
208 | mail = KMime::Message::Ptr::create(); | ||
209 | } | ||
210 | applyAddresses(m_to, [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
167 | mail->to(true)->addAddress(addrSpec, displayName); | 211 | mail->to(true)->addAddress(addrSpec, displayName); |
212 | recordForAutocompletion(addrSpec, displayName); | ||
213 | }); | ||
214 | applyAddresses(m_cc, [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
215 | mail->cc(true)->addAddress(addrSpec, displayName); | ||
216 | recordForAutocompletion(addrSpec, displayName); | ||
217 | }); | ||
218 | applyAddresses(m_bcc, [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
219 | mail->bcc(true)->addAddress(addrSpec, displayName); | ||
220 | recordForAutocompletion(addrSpec, displayName); | ||
221 | }); | ||
222 | if (!identityIsSet()) { | ||
223 | SinkWarning() << "We don't have an identity to send the mail with."; | ||
224 | } else { | ||
225 | auto currentIndex = identityModel()->index(m_currentAccountIndex, 0); | ||
226 | KMime::Types::Mailbox mb; | ||
227 | mb.setName(currentIndex.data(IdentitiesModel::Username).toString()); | ||
228 | mb.setAddress(currentIndex.data(IdentitiesModel::Address).toString().toUtf8()); | ||
229 | mail->from(true)->addAddress(mb); | ||
230 | mail->subject(true)->fromUnicodeString(m_subject, "utf-8"); | ||
231 | mail->setBody(m_body.toUtf8()); | ||
232 | mail->assemble(); | ||
233 | return mail; | ||
168 | } | 234 | } |
169 | auto currentIndex = identityModel()->index(m_currentAccountIndex, 0); | 235 | return KMime::Message::Ptr(); |
170 | KMime::Types::Mailbox mb; | ||
171 | mb.setName(currentIndex.data(IdentitiesModel::Username).toString()); | ||
172 | mb.setAddress(currentIndex.data(IdentitiesModel::Address).toString().toUtf8()); | ||
173 | mail->from(true)->addAddress(mb); | ||
174 | mail->subject(true)->fromUnicodeString(m_subject, "utf-8"); | ||
175 | mail->setBody(m_body.toUtf8()); | ||
176 | mail->assemble(); | ||
177 | return mail; | ||
178 | } | 236 | } |
179 | 237 | ||
180 | void ComposerController::send() | 238 | void ComposerController::send() |
181 | { | 239 | { |
182 | auto mail = assembleMessage(); | 240 | auto mail = assembleMessage(); |
183 | auto currentAccountId = identityModel()->index(m_currentAccountIndex, 0).data(IdentitiesModel::AccountId).toByteArray(); | ||
184 | 241 | ||
185 | Kube::Context context; | 242 | //TODO deactivate action if we don't have the identiy set |
186 | context.setProperty("message", QVariant::fromValue(mail)); | 243 | if (!identityIsSet()) { |
187 | context.setProperty("accountId", QVariant::fromValue(currentAccountId)); | 244 | SinkWarning() << "We don't have an identity to send the mail with."; |
245 | } else { | ||
246 | auto currentAccountId = identityModel()->index(m_currentAccountIndex, 0).data(IdentitiesModel::AccountId).toByteArray(); | ||
188 | 247 | ||
189 | qDebug() << "Current account " << currentAccountId; | 248 | Kube::Context context; |
249 | context.setProperty("message", QVariant::fromValue(mail)); | ||
250 | context.setProperty("accountId", QVariant::fromValue(currentAccountId)); | ||
190 | 251 | ||
191 | Kube::Action("org.kde.kube.actions.sendmail", context).execute(); | 252 | qDebug() << "Current account " << currentAccountId; |
192 | clear(); | 253 | |
254 | Kube::Action("org.kde.kube.actions.sendmail", context).execute(); | ||
255 | clear(); | ||
256 | } | ||
193 | } | 257 | } |
194 | 258 | ||
195 | void ComposerController::saveAsDraft() | 259 | void ComposerController::saveAsDraft() |
diff --git a/framework/domain/composercontroller.h b/framework/domain/composercontroller.h index 8390c639..aa2ae0d7 100644 --- a/framework/domain/composercontroller.h +++ b/framework/domain/composercontroller.h | |||
@@ -38,6 +38,8 @@ class ComposerController : public QObject | |||
38 | Q_PROPERTY (QString bcc READ bcc WRITE setBcc NOTIFY bccChanged) | 38 | Q_PROPERTY (QString bcc READ bcc WRITE setBcc NOTIFY bccChanged) |
39 | Q_PROPERTY (QString subject READ subject WRITE setSubject NOTIFY subjectChanged) | 39 | Q_PROPERTY (QString subject READ subject WRITE setSubject NOTIFY subjectChanged) |
40 | Q_PROPERTY (QString body READ body WRITE setBody NOTIFY bodyChanged) | 40 | Q_PROPERTY (QString body READ body WRITE setBody NOTIFY bodyChanged) |
41 | Q_PROPERTY (QString recepientSearchString READ recepientSearchString WRITE setRecepientSearchString) | ||
42 | Q_PROPERTY (QAbstractItemModel* recepientAutocompletionModel READ recepientAutocompletionModel CONSTANT) | ||
41 | Q_PROPERTY (QAbstractItemModel* identityModel READ identityModel CONSTANT) | 43 | Q_PROPERTY (QAbstractItemModel* identityModel READ identityModel CONSTANT) |
42 | Q_PROPERTY (int currentIdentityIndex MEMBER m_currentAccountIndex) | 44 | Q_PROPERTY (int currentIdentityIndex MEMBER m_currentAccountIndex) |
43 | Q_PROPERTY (QStringList attachments READ attachemts NOTIFY attachmentsChanged) | 45 | Q_PROPERTY (QStringList attachments READ attachemts NOTIFY attachmentsChanged) |
@@ -60,7 +62,11 @@ public: | |||
60 | QString body() const; | 62 | QString body() const; |
61 | void setBody(const QString &body); | 63 | void setBody(const QString &body); |
62 | 64 | ||
65 | QString recepientSearchString() const; | ||
66 | void setRecepientSearchString(const QString &body); | ||
67 | |||
63 | QAbstractItemModel *identityModel() const; | 68 | QAbstractItemModel *identityModel() const; |
69 | QAbstractItemModel *recepientAutocompletionModel() const; | ||
64 | 70 | ||
65 | QStringList attachemts() const; | 71 | QStringList attachemts() const; |
66 | Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); | 72 | Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); |
@@ -81,6 +87,8 @@ public slots: | |||
81 | void addAttachment(const QUrl &fileUrl); | 87 | void addAttachment(const QUrl &fileUrl); |
82 | 88 | ||
83 | private: | 89 | private: |
90 | bool identityIsSet() const; | ||
91 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); | ||
84 | void setMessage(const QSharedPointer<KMime::Message> &msg); | 92 | void setMessage(const QSharedPointer<KMime::Message> &msg); |
85 | QSharedPointer<KMime::Message> assembleMessage(); | 93 | QSharedPointer<KMime::Message> assembleMessage(); |
86 | QString m_to; | 94 | QString m_to; |
diff --git a/framework/domain/recepientautocompletionmodel.cpp b/framework/domain/recepientautocompletionmodel.cpp new file mode 100644 index 00000000..4e5fed95 --- /dev/null +++ b/framework/domain/recepientautocompletionmodel.cpp | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | #include "recepientautocompletionmodel.h" | ||
20 | |||
21 | #include <QStandardItemModel> | ||
22 | #include <QSettings> | ||
23 | #include <QStandardPaths> | ||
24 | #include <QSet> | ||
25 | #include <QDebug> | ||
26 | #include <QTimer> | ||
27 | |||
28 | RecipientAutocompletionModel::RecipientAutocompletionModel(QObject *parent) | ||
29 | : QSortFilterProxyModel(), | ||
30 | mSourceModel(new QStandardItemModel), | ||
31 | mTimer(new QTimer) | ||
32 | { | ||
33 | setSourceModel(mSourceModel.data()); | ||
34 | setDynamicSortFilter(true); | ||
35 | setFilterCaseSensitivity(Qt::CaseInsensitive); | ||
36 | mTimer->setSingleShot(true); | ||
37 | QObject::connect(mTimer.data(), &QTimer::timeout, this, &RecipientAutocompletionModel::save); | ||
38 | |||
39 | load(); | ||
40 | } | ||
41 | |||
42 | RecipientAutocompletionModel::~RecipientAutocompletionModel() | ||
43 | { | ||
44 | save(); | ||
45 | } | ||
46 | |||
47 | static QString getPath() | ||
48 | { | ||
49 | return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kube/recepientautocompletion.ini"; | ||
50 | } | ||
51 | |||
52 | void RecipientAutocompletionModel::save() | ||
53 | { | ||
54 | QSet<QString> list; | ||
55 | for (int row = 0; row < mSourceModel->rowCount(); row++) { | ||
56 | list << mSourceModel->item(row)->data(Text).toString(); | ||
57 | } | ||
58 | |||
59 | qWarning() << "Path " << getPath(); | ||
60 | QSettings settings(getPath(), QSettings::IniFormat); | ||
61 | settings.setValue("list", QStringList{list.toList()}); | ||
62 | } | ||
63 | |||
64 | void RecipientAutocompletionModel::load() | ||
65 | { | ||
66 | qWarning() << "Path " << getPath(); | ||
67 | QSettings settings(getPath(), QSettings::IniFormat); | ||
68 | auto list = settings.value("list").toStringList(); | ||
69 | auto add = [] (const QString &n) { | ||
70 | auto item = new QStandardItem{n}; | ||
71 | item->setData(n, Text); | ||
72 | return item; | ||
73 | }; | ||
74 | for (const auto &entry : list) { | ||
75 | mSourceModel->appendRow(add(entry)); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | QHash< int, QByteArray > RecipientAutocompletionModel::roleNames() const | ||
80 | { | ||
81 | QHash<int, QByteArray> roles; | ||
82 | roles[Text] = "text"; | ||
83 | roles[Color] = "color"; | ||
84 | return roles; | ||
85 | } | ||
86 | |||
87 | void RecipientAutocompletionModel::addEntry(const QByteArray &address, const QByteArray &name) | ||
88 | { | ||
89 | auto add = [] (const QString &n) { | ||
90 | auto item = new QStandardItem{n}; | ||
91 | item->setData(n, Text); | ||
92 | return item; | ||
93 | }; | ||
94 | auto formattedName = [&] () { | ||
95 | if (name.isEmpty()) { | ||
96 | return QString(address); | ||
97 | } | ||
98 | return QString("%1 <%2>").arg(QString(address), QString(name)); | ||
99 | }(); | ||
100 | auto matches = mSourceModel->findItems(formattedName); | ||
101 | if (matches.isEmpty()) { | ||
102 | mSourceModel->appendRow(add(formattedName)); | ||
103 | mTimer->start(100); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | void RecipientAutocompletionModel::setFilter(const QString &filter) | ||
108 | { | ||
109 | setFilterWildcard("*" + filter +"*"); | ||
110 | } | ||
diff --git a/framework/domain/recepientautocompletionmodel.h b/framework/domain/recepientautocompletionmodel.h new file mode 100644 index 00000000..7e89e513 --- /dev/null +++ b/framework/domain/recepientautocompletionmodel.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | |||
20 | #pragma once | ||
21 | |||
22 | #include <QSortFilterProxyModel> | ||
23 | #include <QScopedPointer> | ||
24 | |||
25 | class QStandardItemModel; | ||
26 | class QTimer; | ||
27 | |||
28 | class RecipientAutocompletionModel : public QSortFilterProxyModel | ||
29 | { | ||
30 | Q_OBJECT | ||
31 | |||
32 | public: | ||
33 | RecipientAutocompletionModel(QObject *parent = Q_NULLPTR); | ||
34 | ~RecipientAutocompletionModel(); | ||
35 | |||
36 | enum Roles { | ||
37 | Text = Qt::UserRole + 1, | ||
38 | Color | ||
39 | }; | ||
40 | Q_ENUMS(Roles) | ||
41 | |||
42 | QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE; | ||
43 | |||
44 | void addEntry(const QByteArray &address, const QByteArray &name); | ||
45 | void setFilter(const QString &); | ||
46 | |||
47 | private slots: | ||
48 | void save(); | ||
49 | |||
50 | private: | ||
51 | void load(); | ||
52 | |||
53 | QScopedPointer<QStandardItemModel> mSourceModel; | ||
54 | QScopedPointer<QTimer> mTimer; | ||
55 | }; | ||