summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-06-12 22:11:52 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-06-12 22:11:52 +0200
commit0ef55c3bf02f8730ab821c3409c71e67bd382ecc (patch)
treebbe8478e476bccd7f2dd9f99b1796848510f40d5
parentc8372a71b128eeb4f0440736e36c372d61b5a9a2 (diff)
downloadkube-0ef55c3bf02f8730ab821c3409c71e67bd382ecc.tar.gz
kube-0ef55c3bf02f8730ab821c3409c71e67bd382ecc.zip
Working but ugly editor for recipients in composer
What remains to be done is: * proper delegates * removal of recipeients * at some point d&d of recipients * Perhaps something that encapsulates the whole list and the model in a nicer way
-rw-r--r--components/kube/contents/ui/ComposerView.qml92
-rw-r--r--framework/qml/AutocompleteLineEdit.qml28
-rw-r--r--framework/src/domain/composercontroller.cpp129
-rw-r--r--framework/src/domain/composercontroller.h22
4 files changed, 195 insertions, 76 deletions
diff --git a/components/kube/contents/ui/ComposerView.qml b/components/kube/contents/ui/ComposerView.qml
index 576a1590..49a59024 100644
--- a/components/kube/contents/ui/ComposerView.qml
+++ b/components/kube/contents/ui/ComposerView.qml
@@ -287,55 +287,59 @@ Kube.View {
287 height: parent.height 287 height: parent.height
288 288
289 ColumnLayout { 289 ColumnLayout {
290 anchors { 290 anchors {
291 top: parent.top 291 top: parent.top
292 bottom: bottomButtons.top 292 bottom: bottomButtons.top
293 left: parent.left 293 left: parent.left
294 right: parent.right 294 right: parent.right
295 margins: Kube.Units.largeSpacing 295 margins: Kube.Units.largeSpacing
296 } 296 }
297 297
298 Kube.Label { 298 Kube.Label {
299 text: "Sending Email to:" 299 text: "Sending Email to:"
300 } 300 }
301 Kube.AutocompleteLineEdit {
302 id: to
303 Layout.fillWidth: true
304 text: composerController.to
305 onTextChanged: composerController.to = text
306 model: composerController.recipientCompleter.model
307 onSearchTermChanged: composerController.recipientCompleter.searchString = searchTerm
308 }
309 301
310 Kube.Label { 302 AddresseeListEditor {
311 text: "Sending Copy to (CC):" 303 id: to
312 } 304 Layout.preferredHeight: to.implicitHeight
313 Kube.AutocompleteLineEdit { 305 Layout.fillWidth: true
314 id: cc 306 completer: composerController.recipientCompleter
315 Layout.fillWidth: true 307 model: composerController.toModel
316 text: composerController.cc 308 onAdded: composerController.addTo(text)
317 onTextChanged: composerController.cc = text 309 onRemoved: composerController.removeTo(text)
318 model: composerController.recipientCompleter.model 310 }
319 onSearchTermChanged: composerController.recipientCompleter.searchString = searchTerm
320 }
321 311
322 Kube.Label { 312 Kube.Label {
323 text: "Sending Secret Copy to (Bcc):" 313 text: "Sending Copy to (CC):"
324 } 314 }
325 Kube.AutocompleteLineEdit { 315 AddresseeListEditor {
326 id: bcc 316 id: cc
327 Layout.fillWidth: true 317 Layout.preferredHeight: cc.implicitHeight
328 text: composerController.bcc 318 Layout.fillWidth: true
329 onTextChanged: composerController.bcc = text; 319 completer: composerController.recipientCompleter
330 model: composerController.recipientCompleter.model 320 model: composerController.ccModel
331 onSearchTermChanged: composerController.recipientCompleter.searchString = searchTerm 321 onAdded: composerController.addCc(text)
332 } 322 onRemoved: composerController.removeCc(text)
323 }
333 324
334 Item { 325 Kube.Label {
335 width: parent.width 326 text: "Sending Secret Copy to (Bcc):"
336 Layout.fillHeight: true 327 }
328 AddresseeListEditor {
329 id: bcc
330 Layout.preferredHeight: bcc.implicitHeight
331 Layout.fillWidth: true
332 completer: composerController.recipientCompleter
333 model: composerController.bccModel
334 onAdded: composerController.addBcc(text)
335 onRemoved: composerController.removeBcc(text)
336 }
337
338 Item {
339 width: parent.width
340 Layout.fillHeight: true
341 }
337 } 342 }
338 }
339 343
340 344
341 Item { 345 Item {
diff --git a/framework/qml/AutocompleteLineEdit.qml b/framework/qml/AutocompleteLineEdit.qml
index dbe00421..e7518867 100644
--- a/framework/qml/AutocompleteLineEdit.qml
+++ b/framework/qml/AutocompleteLineEdit.qml
@@ -24,10 +24,13 @@ import org.kde.kirigami 1.0 as Kirigami
24import org.kube.framework 1.0 as Kube 24import org.kube.framework 1.0 as Kube
25 25
26Kube.TextField { 26Kube.TextField {
27 id: textField 27 id: root
28 28
29 property string searchTerm 29 property string searchTerm
30 property variant model 30 property variant model
31
32 selectByMouse: true
33
31 onTextChanged: { 34 onTextChanged: {
32 if (text.length >= 2) { 35 if (text.length >= 2) {
33 searchTerm = text 36 searchTerm = text
@@ -68,8 +71,11 @@ Kube.TextField {
68 } 71 }
69 72
70 function accept() { 73 function accept() {
71 textField.text = listView.currentItem.text; 74 if (listView.currentItem) {
75 root.text = listView.currentItem.text;
76 }
72 popup.close() 77 popup.close()
78 root.accepted();
73 } 79 }
74 80
75 function abort() { 81 function abort() {
@@ -79,9 +85,9 @@ Kube.TextField {
79 Controls2.Popup { 85 Controls2.Popup {
80 id: popup 86 id: popup
81 x: 0 87 x: 0
82 y: textField.y + textField.height 88 y: root.height
83 padding: 0 89 padding: 0
84 contentWidth: rect.width 90 width: root.width
85 contentHeight: rect.height 91 contentHeight: rect.height
86 92
87 Rectangle { 93 Rectangle {
@@ -91,7 +97,7 @@ Kube.TextField {
91 anchors.left: popup.left 97 anchors.left: popup.left
92 98
93 height: listView.contentHeight 99 height: listView.contentHeight
94 width: textField.width 100 width: popup.width
95 101
96 border.color: Kube.Colors.textColor 102 border.color: Kube.Colors.textColor
97 color: Kube.Colors.backgroundColor 103 color: Kube.Colors.backgroundColor
@@ -99,16 +105,16 @@ Kube.TextField {
99 radius: 5 105 radius: 5
100 ListView { 106 ListView {
101 id: listView 107 id: listView
102 height: childrenRect.height 108 height: contentHeight
103 width: parent.width 109 width: parent.width
104 interactive: true 110 interactive: true
105 model: textField.model 111 model: root.model
106 delegate: Kirigami.AbstractListItem { 112 delegate: Kirigami.AbstractListItem {
107 id: listDelegate 113 id: listDelegate
108 property string text: model.text 114 property string text: model.text
109 115
110 width: listView.width 116 width: listView.width
111 height: textField.height 117 height: root.height
112 118
113 enabled: true 119 enabled: true
114 supportsMouseEvents: true 120 supportsMouseEvents: true
@@ -128,11 +134,17 @@ Kube.TextField {
128 anchors { 134 anchors {
129 verticalCenter: parent.verticalCenter 135 verticalCenter: parent.verticalCenter
130 left: parent.left 136 left: parent.left
137 right: parent.right
131 } 138 }
132 139
133 Kube.Label{ 140 Kube.Label{
141 anchors {
142 left: parent.left
143 right: parent.right
144 }
134 text: model.text 145 text: model.text
135 color: listDelegate.checked ? Kube.Colors.highlightedTextColor : Kube.Colors.textColor 146 color: listDelegate.checked ? Kube.Colors.highlightedTextColor : Kube.Colors.textColor
147 elide: Text.ElideRight
136 } 148 }
137 } 149 }
138 } 150 }
diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp
index 332101ac..cbc43afe 100644
--- a/framework/src/domain/composercontroller.cpp
+++ b/framework/src/domain/composercontroller.cpp
@@ -27,7 +27,6 @@
27#include <QSortFilterProxyModel> 27#include <QSortFilterProxyModel>
28#include <QList> 28#include <QList>
29#include <QDebug> 29#include <QDebug>
30#include <QQmlEngine>
31#include <sink/store.h> 30#include <sink/store.h>
32#include <sink/log.h> 31#include <sink/log.h>
33 32
@@ -82,7 +81,10 @@ ComposerController::ComposerController()
82 action_send{new Kube::ControllerAction{this, &ComposerController::send}}, 81 action_send{new Kube::ControllerAction{this, &ComposerController::send}},
83 action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}}, 82 action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}},
84 mRecipientCompleter{new RecipientCompleter}, 83 mRecipientCompleter{new RecipientCompleter},
85 mIdentitySelector{new IdentitySelector{*this}} 84 mIdentitySelector{new IdentitySelector{*this}},
85 mToModel{new QStringListModel},
86 mCcModel{new QStringListModel},
87 mBccModel{new QStringListModel}
86{ 88{
87 updateSaveAsDraftAction(); 89 updateSaveAsDraftAction();
88 // mSendAction->monitorProperty<To>(); 90 // mSendAction->monitorProperty<To>();
@@ -93,10 +95,8 @@ ComposerController::ComposerController()
93 // actionDepends<ControllerAction, To, Subject>(); 95 // actionDepends<ControllerAction, To, Subject>();
94 // TODO do in constructor 96 // TODO do in constructor
95 97
96 QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction);
97 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); 98 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction);
98 QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSendAction); 99 QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSendAction);
99 QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSaveAsDraftAction);
100 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSaveAsDraftAction); 100 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSaveAsDraftAction);
101 QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSaveAsDraftAction); 101 QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSaveAsDraftAction);
102 updateSendAction(); 102 updateSendAction();
@@ -107,6 +107,72 @@ void ComposerController::clear()
107 Controller::clear(); 107 Controller::clear();
108 //Reapply account and identity from selection 108 //Reapply account and identity from selection
109 mIdentitySelector->reapplyCurrentIndex(); 109 mIdentitySelector->reapplyCurrentIndex();
110 mToModel->setStringList({});
111 mCcModel->setStringList({});
112 mBccModel->setStringList({});
113}
114
115QAbstractItemModel *ComposerController::toModel() const
116{
117 return mToModel.data();
118}
119
120void ComposerController::addTo(const QString &s)
121{
122 auto list = mToModel->stringList();
123 list.append(s);
124 mToModel->setStringList(list);
125 updateSendAction();
126}
127
128void ComposerController::removeTo(const QString &s)
129{
130 auto list = mToModel->stringList();
131 list.removeAll(s);
132 mToModel->setStringList(list);
133 updateSendAction();
134}
135
136QAbstractItemModel *ComposerController::ccModel() const
137{
138 return mCcModel.data();
139}
140
141void ComposerController::addCc(const QString &s)
142{
143 auto list = mCcModel->stringList();
144 list.append(s);
145 mCcModel->setStringList(list);
146 updateSendAction();
147}
148
149void ComposerController::removeCc(const QString &s)
150{
151 auto list = mCcModel->stringList();
152 list.removeAll(s);
153 mCcModel->setStringList(list);
154 updateSendAction();
155}
156
157QAbstractItemModel *ComposerController::bccModel() const
158{
159 return mBccModel.data();
160}
161
162void ComposerController::addBcc(const QString &s)
163{
164 auto list = mBccModel->stringList();
165 list.append(s);
166 mBccModel->setStringList(list);
167 updateSendAction();
168}
169
170void ComposerController::removeBcc(const QString &s)
171{
172 auto list = mBccModel->stringList();
173 list.removeAll(s);
174 mBccModel->setStringList(list);
175 updateSendAction();
110} 176}
111 177
112Completer *ComposerController::recipientCompleter() const 178Completer *ComposerController::recipientCompleter() const
@@ -119,10 +185,42 @@ Selector *ComposerController::identitySelector() const
119 return mIdentitySelector.data(); 185 return mIdentitySelector.data();
120} 186}
121 187
188static void applyAddresses(const QStringList &list, std::function<void(const QByteArray &, const QByteArray &)> callback)
189{
190 for (const auto &to : list) {
191 QByteArray displayName;
192 QByteArray addrSpec;
193 QByteArray comment;
194 KEmailAddress::splitAddress(to.toUtf8(), displayName, addrSpec, comment);
195 callback(addrSpec, displayName);
196 }
197}
198
199static void applyAddresses(const QString &list, std::function<void(const QByteArray &, const QByteArray &)> callback)
200{
201 applyAddresses(KEmailAddress::splitAddressList(list), callback);
202}
203
204
205static QStringList getStringListFromAddresses(const QString &s)
206{
207 QStringList list;
208 applyAddresses(s, [&](const QByteArray &addrSpec, const QByteArray &displayName) {
209 if (displayName.isEmpty()) {
210 list << QString{addrSpec};
211 } else {
212 list << QString("%1 <%2>").arg(QString{displayName}).arg(QString{addrSpec});
213 }
214 });
215 return list;
216}
217
122void ComposerController::setMessage(const KMime::Message::Ptr &msg) 218void ComposerController::setMessage(const KMime::Message::Ptr &msg)
123{ 219{
124 setTo(msg->to(true)->asUnicodeString()); 220 mToModel->setStringList(getStringListFromAddresses(msg->to(true)->asUnicodeString()));
125 setCc(msg->cc(true)->asUnicodeString()); 221 mCcModel->setStringList(getStringListFromAddresses(msg->cc(true)->asUnicodeString()));
222 mBccModel->setStringList(getStringListFromAddresses(msg->bcc(true)->asUnicodeString()));
223
126 setSubject(msg->subject(true)->asUnicodeString()); 224 setSubject(msg->subject(true)->asUnicodeString());
127 setBody(msg->body()); 225 setBody(msg->body());
128 setExistingMessage(msg); 226 setExistingMessage(msg);
@@ -165,32 +263,21 @@ void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, con
165 } 263 }
166} 264}
167 265
168void applyAddresses(const QString &list, std::function<void(const QByteArray &, const QByteArray &)> callback)
169{
170 for (const auto &to : KEmailAddress::splitAddressList(list)) {
171 QByteArray displayName;
172 QByteArray addrSpec;
173 QByteArray comment;
174 KEmailAddress::splitAddress(to.toUtf8(), displayName, addrSpec, comment);
175 callback(addrSpec, displayName);
176 }
177}
178
179KMime::Message::Ptr ComposerController::assembleMessage() 266KMime::Message::Ptr ComposerController::assembleMessage()
180{ 267{
181 auto mail = mExistingMessage; 268 auto mail = mExistingMessage;
182 if (!mail) { 269 if (!mail) {
183 mail = KMime::Message::Ptr::create(); 270 mail = KMime::Message::Ptr::create();
184 } 271 }
185 applyAddresses(getTo(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { 272 applyAddresses(mToModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) {
186 mail->to(true)->addAddress(addrSpec, displayName); 273 mail->to(true)->addAddress(addrSpec, displayName);
187 recordForAutocompletion(addrSpec, displayName); 274 recordForAutocompletion(addrSpec, displayName);
188 }); 275 });
189 applyAddresses(getCc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { 276 applyAddresses(mCcModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) {
190 mail->cc(true)->addAddress(addrSpec, displayName); 277 mail->cc(true)->addAddress(addrSpec, displayName);
191 recordForAutocompletion(addrSpec, displayName); 278 recordForAutocompletion(addrSpec, displayName);
192 }); 279 });
193 applyAddresses(getBcc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { 280 applyAddresses(mBccModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) {
194 mail->bcc(true)->addAddress(addrSpec, displayName); 281 mail->bcc(true)->addAddress(addrSpec, displayName);
195 recordForAutocompletion(addrSpec, displayName); 282 recordForAutocompletion(addrSpec, displayName);
196 }); 283 });
@@ -212,7 +299,7 @@ KMime::Message::Ptr ComposerController::assembleMessage()
212 299
213void ComposerController::updateSendAction() 300void ComposerController::updateSendAction()
214{ 301{
215 auto enabled = !getTo().isEmpty() && !getSubject().isEmpty() && !getAccountId().isEmpty(); 302 auto enabled = !mToModel->stringList().isEmpty() && !getSubject().isEmpty() && !getAccountId().isEmpty();
216 sendAction()->setEnabled(enabled); 303 sendAction()->setEnabled(enabled);
217} 304}
218 305
diff --git a/framework/src/domain/composercontroller.h b/framework/src/domain/composercontroller.h
index 92467d05..87349d0c 100644
--- a/framework/src/domain/composercontroller.h
+++ b/framework/src/domain/composercontroller.h
@@ -23,6 +23,7 @@
23#include <QObject> 23#include <QObject>
24#include <QString> 24#include <QString>
25#include <QStringList> 25#include <QStringList>
26#include <QStringListModel>
26#include <QVariant> 27#include <QVariant>
27#include <sink/applicationdomaintype.h> 28#include <sink/applicationdomaintype.h>
28#include <KMime/Message> 29#include <KMime/Message>
@@ -47,9 +48,6 @@ class ComposerController : public Kube::Controller
47 Q_OBJECT 48 Q_OBJECT
48 49
49 //Interface properties 50 //Interface properties
50 KUBE_CONTROLLER_PROPERTY(QString, To, to)
51 KUBE_CONTROLLER_PROPERTY(QString, Cc, cc)
52 KUBE_CONTROLLER_PROPERTY(QString, Bcc, bcc)
53 KUBE_CONTROLLER_PROPERTY(QString, Subject, subject) 51 KUBE_CONTROLLER_PROPERTY(QString, Subject, subject)
54 KUBE_CONTROLLER_PROPERTY(QString, Body, body) 52 KUBE_CONTROLLER_PROPERTY(QString, Body, body)
55 53
@@ -65,6 +63,10 @@ class ComposerController : public Kube::Controller
65 Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) 63 Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT)
66 //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT) 64 //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT)
67 65
66 Q_PROPERTY (QAbstractItemModel* toModel READ toModel CONSTANT)
67 Q_PROPERTY (QAbstractItemModel* ccModel READ ccModel CONSTANT)
68 Q_PROPERTY (QAbstractItemModel* bccModel READ bccModel CONSTANT)
69
68 KUBE_CONTROLLER_ACTION(send) 70 KUBE_CONTROLLER_ACTION(send)
69 KUBE_CONTROLLER_ACTION(saveAsDraft) 71 KUBE_CONTROLLER_ACTION(saveAsDraft)
70 72
@@ -76,6 +78,17 @@ public:
76 78
77 Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); 79 Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft);
78 80
81 QAbstractItemModel *toModel() const;
82 QAbstractItemModel *ccModel() const;
83 QAbstractItemModel *bccModel() const;
84
85 Q_INVOKABLE void addTo(const QString &);
86 Q_INVOKABLE void removeTo(const QString &);
87 Q_INVOKABLE void addCc(const QString &);
88 Q_INVOKABLE void removeCc(const QString &);
89 Q_INVOKABLE void addBcc(const QString &);
90 Q_INVOKABLE void removeBcc(const QString &);
91
79public slots: 92public slots:
80 virtual void clear() Q_DECL_OVERRIDE; 93 virtual void clear() Q_DECL_OVERRIDE;
81 94
@@ -90,4 +103,7 @@ private:
90 103
91 QScopedPointer<Completer> mRecipientCompleter; 104 QScopedPointer<Completer> mRecipientCompleter;
92 QScopedPointer<Selector> mIdentitySelector; 105 QScopedPointer<Selector> mIdentitySelector;
106 QScopedPointer<QStringListModel> mToModel;
107 QScopedPointer<QStringListModel> mCcModel;
108 QScopedPointer<QStringListModel> mBccModel;
93}; 109};