Wt examples: composer/Composer.C Source File

Wt examples 4.10.4

Loading...
Searching...
No Matches
Composer.C
Go to the documentation of this file.
1/*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6#include <iostream>
7
8#include "AddresseeEdit.h"
9#include "AttachmentEdit.h"
10#include "Composer.h"
11#include "ContactSuggestions.h"
12#include "Label.h"
13#include "Option.h"
14#include "OptionList.h"
15
16#include <Wt/WContainerWidget.h>
17#include <Wt/WImage.h>
18#include <Wt/WLineEdit.h>
19#include <Wt/WLocalDateTime.h>
20#include <Wt/WPushButton.h>
21#include <Wt/WText.h>
22#include <Wt/WTable.h>
23#include <Wt/WTableCell.h>
24#include <Wt/WStringUtil.h>
25#include <algorithm>
26
27Composer::Composer()
28 : WCompositeWidget(),
29 saving_(false),
30 sending_(false)
31{
32 std::unique_ptr<WContainerWidget> layout
33 = std::make_unique<WContainerWidget>();
34 layout_ = layout.get();
35 setImplementation(std::move(layout));
36
37 createUi();
38}
39
40 void Composer::setTo(const std::vector<Contact>& to)
41{
42 toEdit_->setAddressees(to);
43}
44
45 void Composer::setSubject(const WString& subject)
46{
47 subject_->setText(subject);
48}
49
50 void Composer::setMessage(const WString& message)
51{
52 message_->setText(message);
53}
54
55std::vector<Contact> Composer::to() const
56{
57 return toEdit_->addressees();
58}
59
60std::vector<Contact> Composer::cc() const
61{
62 return ccEdit_->addressees();
63}
64
65std::vector<Contact> Composer::bcc() const
66{
67 return bccEdit_->addressees();
68}
69
70 void Composer::setAddressBook(const std::vector<Contact>& contacts)
71{
72 contactSuggestions_->setAddressBook(contacts);
73}
74
75 const WString& Composer::subject() const
76{
77 return subject_->text();
78}
79
80std::vector<Attachment> Composer::attachments() const
81{
82 std::vector<Attachment> attachments;
83
84 for (unsigned i = 0; i < attachments_.size() - 1; ++i) {
85 std::vector<Attachment> toadd = attachments_[i]->attachments();
86
87 attachments.insert(attachments.end(), toadd.begin(), toadd.end());
88 }
89
90 return attachments;
91}
92
93 const WString& Composer::message() const
94{
95 return message_->text();
96}
97
98 void Composer::createUi()
99{
100 setStyleClass("darker");
101
102 // horizontal layout container, used for top and bottom buttons.
103 WContainerWidget *horiz;
104
105 /*
106 * Top buttons
107 */
108 horiz = layout_->addWidget(std::make_unique<WContainerWidget>());
109 horiz->setPadding(5);
110 topSendButton_ = horiz->addWidget(std::make_unique<WPushButton>(tr("msg.send")));
111 topSendButton_->setStyleClass("default"); // default action
112 topSaveNowButton_ = horiz->addWidget(std::make_unique<WPushButton>(tr("msg.savenow")));
113 topDiscardButton_ = horiz->addWidget(std::make_unique<WPushButton>(tr("msg.discard")));
114
115 // Text widget which shows status messages, next to the top buttons.
116 statusMsg_ = horiz->addWidget(std::make_unique<WText>());
117 statusMsg_->setMargin(15, Side::Left);
118
119 /*
120 * To, Cc, Bcc, Subject, Attachments
121 *
122 * They are organized in a two-column table: left column for
123 * labels, and right column for the edit.
124 */
125 edits_ = layout_->addWidget(std::make_unique<WTable>());
126 edits_->setStyleClass("lighter");
127 edits_->resize(WLength(100, LengthUnit::Percentage), WLength::Auto);
128 edits_->elementAt(0, 0)->resize(WLength(1, LengthUnit::Percentage),
129 WLength::Auto);
130
131 /*
132 * To, Cc, Bcc
133 */
134 toEdit_ = edits_->elementAt(0,1)->addWidget(std::make_unique<AddresseeEdit>(tr("msg.to"), edits_->elementAt(0, 0)));
135 // add some space above To:
136 edits_->elementAt(0, 1)->setMargin(5, Side::Top);
137 ccEdit_ = edits_->elementAt(1,1)->addWidget(std::make_unique<AddresseeEdit>(tr("msg.cc"), edits_->elementAt(1, 0)));
138 bccEdit_ = edits_->elementAt(2,1)->addWidget(std::make_unique<AddresseeEdit>(tr("msg.bcc"), edits_->elementAt(2, 0)));
139
140 ccEdit_->hide();
141 bccEdit_->hide();
142
143 /*
144 * Addressbook suggestions popup
145 */
146 contactSuggestions_ = layout_->addChild(std::make_unique<ContactSuggestions>());
147
148 contactSuggestions_->forEdit(toEdit_);
149 contactSuggestions_->forEdit(ccEdit_);
150 contactSuggestions_->forEdit(bccEdit_);
151
152 /*
153 * We use an OptionList widget to show the expand options for
154 * ccEdit_ and bccEdit_ nicely next to each other, separated
155 * by pipe characters.
156 */
157 options_ = edits_->elementAt(3, 1)->addWidget(std::make_unique<OptionList>());
158 std::unique_ptr<Option> addcc(new Option(tr("msg.addcc")));
159 addcc_ = addcc.get();
160 std::unique_ptr<Option> addbcc(new Option(tr("msg.addbcc")));
161 addbcc_ = addbcc.get();
162
163 options_->add(std::move(addcc));
164 options_->add(std::move(addbcc));
165
166 /*
167 * Subject
168 */
169 edits_->elementAt(4, 0)->addWidget(std::make_unique<Label>(tr("msg.subject"), edits_->elementAt(4, 0)));
170 subject_ = edits_->elementAt(4, 1)->addWidget(std::make_unique<WLineEdit>());
171 subject_->resize(WLength(99, LengthUnit::Percentage), WLength::Auto);
172
173 /*
174 * Attachments
175 */
176 edits_->elementAt(5, 0)->addWidget(std::make_unique<WImage>("icons/paperclip.png"));
177 edits_->elementAt(5, 0)->setContentAlignment(AlignmentFlag::Right | AlignmentFlag::Top);
178 edits_->elementAt(5, 0)->setPadding(3);
179
180 // Attachment edits: we always have the next attachmentedit ready
181 // but hidden. This improves the response time, since the show()
182 // and hide() slots are stateless.
183 AttachmentEdit *attachmentEdit = edits_->elementAt(5, 1)->addWidget(std::make_unique<AttachmentEdit>(this));
184 attachments_.push_back(attachmentEdit);
185 attachments_.back()->hide();
186
187 /*
188 * Two options for attaching files. The first does not say 'another'.
189 */
190 attachFile_ = edits_->elementAt(5, 1)->addWidget(std::make_unique<Option>(tr("msg.attachfile")));
191 attachOtherFile_ = edits_->elementAt(5, 1)->addWidget(std::make_unique<Option>(tr("msg.attachanother")));
192 attachOtherFile_->hide();
193
194 /*
195 * Message
196 */
197 message_ = layout_->addWidget(std::make_unique<WTextArea>());
198 message_->setColumns(80);
199 message_->setRows(10); // should be 20, but let's keep it smaller
200 message_->setMargin(10);
201
202 /*
203 * Bottom buttons
204 */
205 horiz = layout_->addWidget(std::make_unique<WContainerWidget>());
206 horiz->setPadding(5);
207 botSendButton_ = horiz->addWidget(std::make_unique<WPushButton>(tr("msg.send")));
208 botSendButton_->setStyleClass("default");
209 botSaveNowButton_ = horiz->addWidget(std::make_unique<WPushButton>(tr("msg.savenow")));
210 botDiscardButton_ = horiz->addWidget(std::make_unique<WPushButton>(tr("msg.discard")));
211
212 /*
213 * Button events.
214 */
215 topSendButton_->clicked().connect(this, &Composer::sendIt);
216 botSendButton_->clicked().connect(this, &Composer::sendIt);
217 topSaveNowButton_->clicked().connect(this, &Composer::saveNow);
218 botSaveNowButton_->clicked().connect(this, &Composer::saveNow);
219 topDiscardButton_->clicked().connect(this, &Composer::discardIt);
220 botDiscardButton_->clicked().connect(this, &Composer::discardIt);
221
222 /*
223 * Option events to show the cc or Bcc edit.
224 *
225 * Clicking on the option should both show the corresponding edit, and
226 * hide the option itself.
227 */
228 addcc_->item()->clicked().connect(ccEdit_, &WWidget::show);
229 addcc_->item()->clicked().connect(addcc_, &WWidget::hide);
230 addcc_->item()->clicked().connect(options_, &OptionList::update);
231 addcc_->item()->clicked().connect(ccEdit_, &WWidget::setFocus);
232
233 addbcc_->item()->clicked().connect(bccEdit_, &WWidget::show);
234 addbcc_->item()->clicked().connect(addbcc_, &WWidget::hide);
235 addbcc_->item()->clicked().connect(options_, &OptionList::update);
236 addbcc_->item()->clicked().connect(bccEdit_, &WWidget::setFocus);
237
238 /*
239 * Option event to attach the first attachment.
240 *
241 * We show the first attachment, and call attachMore() to prepare the
242 * next attachment edit that will be hidden.
243 *
244 * In addition, we need to show the 'attach More' option, and hide the
245 * 'attach' option.
246 */
247 attachFile_->item()->clicked().connect(attachments_.back(), &WWidget::show);
248 attachFile_->item()->clicked().connect(attachOtherFile_, &WWidget::show);
249 attachFile_->item()->clicked().connect(attachFile_, &WWidget::hide);
250 attachFile_->item()->clicked().connect(this, &Composer::attachMore);
251 attachOtherFile_->item()->clicked().connect(this, &Composer::attachMore);
252}
253
254 void Composer::attachMore()
255{
256 /*
257 * Create and append the next AttachmentEdit, that will be hidden.
258 */
259 std::unique_ptr<AttachmentEdit> edit
260 = std::make_unique<AttachmentEdit>(this);
261 AttachmentEdit *editPtr = edit.get();
262 edits_->elementAt(5, 1)->insertBefore(std::move(edit), attachOtherFile_);
263 attachments_.push_back(editPtr);
264 attachments_.back()->hide();
265
266 // Connect the attachOtherFile_ option to show this attachment.
267 attachOtherFile_->item()->clicked()
268 .connect(attachments_.back(), &WWidget::show);
269}
270
271 void Composer::removeAttachment(AttachmentEdit *attachment)
272{
273 /*
274 * Remove the given attachment from the attachments list.
275 */
276 std::vector<AttachmentEdit *>::iterator i
277 = std::find(attachments_.begin(), attachments_.end(), attachment);
278
279 if (i != attachments_.end()) {
280 attachments_.erase(i);
281 attachment->removeFromParent();
282
283 if (attachments_.size() == 1) {
284 /*
285 * This was the last visible attachment, thus, we should switch
286 * the option control again.
287 */
288 attachOtherFile_->hide();
289 attachFile_->show();
290 attachFile_->item()->clicked()
291 .connect(attachments_.back(), &WWidget::show);
292 }
293 }
294}
295
296 void Composer::sendIt()
297{
298 if (!sending_) {
299 sending_ = true;
300
301 /*
302 * First save -- this will check for the sending_ state
303 * signal if successfull.
304 */
305 saveNow();
306 }
307}
308
309 void Composer::saveNow()
310{
311 if (!saving_) {
312 saving_ = true;
313
314 /*
315 * Check if any attachments still need to be uploaded.
316 * This may be the case when fileupload change events could not
317 * be caught (for example in Konqueror).
318 */
319 attachmentsPending_ = 0;
320
321 for (unsigned i = 0; i < attachments_.size() - 1; ++i) {
322 if (attachments_[i]->uploadNow()) {
323 ++attachmentsPending_;
324
325 // this will trigger attachmentDone() when done, see
326 // the AttachmentEdit constructor.
327 }
328 }
329
330 std::cerr << "Attachments pending: " << attachmentsPending_ << std::endl;
331 if (attachmentsPending_)
332 setStatus(tr("msg.uploading"), "status");
333 else
334 saved();
335 }
336}
337
338 void Composer::attachmentDone()
339{
340 if (saving_) {
341 --attachmentsPending_;
342 std::cerr << "Attachments still: " << attachmentsPending_ << std::endl;
343
344 if (attachmentsPending_ == 0)
345 saved();
346 }
347}
348
349 void Composer::setStatus(const WString& text, const WString& style)
350{
351 statusMsg_->setText(text);
352 statusMsg_->setStyleClass(style);
353}
354
355 void Composer::saved()
356{
357 /*
358 * All attachments have been processed.
359 */
360
361 bool attachmentsFailed = false;
362 for (unsigned i = 0; i < attachments_.size() - 1; ++i)
363 if (attachments_[i]->uploadFailed()) {
364 attachmentsFailed = true;
365 break;
366 }
367
368 if (attachmentsFailed) {
369 setStatus(tr("msg.attachment.failed"), "error");
370 } else {
371 setStatus(tr("msg.ok"), "status");
372 WString timeStr = Wt::WLocalDateTime::currentDateTime().toString("HH:mm");
373 statusMsg_->setText(Wt::utf8("Draft saved at {1}").arg(timeStr));
374
375 if (sending_) {
376 send.emit();
377 return;
378 }
379 }
380
381 saving_ = false;
382 sending_ = false;
383}
384
385 void Composer::discardIt()
386{
387 discard.emit();
388}
AddresseeEdit.h
AttachmentEdit.h
Composer.h
ContactSuggestions.h
Label.h
OptionList.h
Option.h
AddresseeEdit::addressees
std::vector< Contact > addressees() const
Get a list of addressees.
Definition AddresseeEdit.C:74
AddresseeEdit::setAddressees
void setAddressees(const std::vector< Contact > &contacts)
Set a list of addressees.
Definition AddresseeEdit.C:27
AttachmentEdit
An edit field for an email attachment.
Definition AttachmentEdit.h:38
Composer::botSaveNowButton_
WPushButton * botSaveNowButton_
Definition Composer.h:103
Composer::sendIt
void sendIt()
Slot attached to the Send button.
Definition Composer.C:296
Composer::statusMsg_
WText * statusMsg_
Definition Composer.h:104
Composer::attachOtherFile_
Option * attachOtherFile_
Option for attaching another file.
Definition Composer.h:131
Composer::setMessage
void setMessage(const WString &message)
Set the message.
Definition Composer.C:50
Composer::saved
void saved()
All attachments have been processed, determine the result of saving the message.
Definition Composer.C:355
Composer::message_
WTextArea * message_
WTextArea for the main message.
Definition Composer.h:137
Composer::topSaveNowButton_
WPushButton * topSaveNowButton_
Definition Composer.h:102
Composer::send
Wt::Signal send
The message is ready to be sent...
Definition Composer.h:93
Composer::bcc
std::vector< Contact > bcc() const
Get the Bc: contacts.
Definition Composer.C:65
Composer::discard
Wt::Signal discard
The message must be discarded.
Definition Composer.h:97
Composer::message
const WString & message() const
Get the message.
Definition Composer.C:93
Composer::topSendButton_
WPushButton * topSendButton_
Definition Composer.h:102
Composer::setStatus
void setStatus(const WString &text, const WString &style)
Set the status, and apply the given style.
Definition Composer.C:349
Composer::toEdit_
AddresseeEdit * toEdit_
To: Addressees edit.
Definition Composer.h:109
Composer::discardIt
void discardIt()
Slot attached to the Discard button.
Definition Composer.C:385
Composer::to
std::vector< Contact > to() const
Get the To: contacts.
Definition Composer.C:55
Composer::attachMore
void attachMore()
Add an attachment edit.
Definition Composer.C:254
Composer::saveNow
void saveNow()
Slot attached to the Save now button.
Definition Composer.C:309
Composer::botSendButton_
WPushButton * botSendButton_
Definition Composer.h:103
Composer::saving_
bool saving_
state when waiting asyncrhonously for attachments to be uploaded
Definition Composer.h:140
Composer::subject_
WLineEdit * subject_
The subject line edit.
Definition Composer.h:119
Composer::topDiscardButton_
WPushButton * topDiscardButton_
Definition Composer.h:102
Composer::attachmentsPending_
int attachmentsPending_
number of attachments waiting to be uploaded during saving
Definition Composer.h:143
Composer::setTo
void setTo(const std::vector< Contact > &to)
Set message To: contacts.
Definition Composer.C:40
Composer::contactSuggestions_
ContactSuggestions * contactSuggestions_
The suggestions popup for the addressee edits.
Definition Composer.h:116
Composer::Composer
Composer()
Construct a new Composer.
Definition Composer.C:27
Composer::attachFile_
Option * attachFile_
Option for attaching a file.
Definition Composer.h:129
Composer::botDiscardButton_
WPushButton * botDiscardButton_
Definition Composer.h:103
Composer::attachments_
std::vector< AttachmentEdit * > attachments_
Array which holds all the attachments, including one extra invisible one.
Definition Composer.h:134
Composer::edits_
WTable * edits_
Definition Composer.h:106
Composer::addbcc_
Option * addbcc_
Option for editing Bcc:
Definition Composer.h:127
Composer::ccEdit_
AddresseeEdit * ccEdit_
Cc: Addressees edit.
Definition Composer.h:111
Composer::sending_
bool sending_
Definition Composer.h:140
Composer::attachmentDone
void attachmentDone()
Slotcalled when an attachment has been uploaded.
Definition Composer.C:338
Composer::layout_
WContainerWidget * layout_
Definition Composer.h:100
Composer::setSubject
void setSubject(const WString &subject)
Set subject.
Definition Composer.C:45
Composer::removeAttachment
void removeAttachment(AttachmentEdit *attachment)
Remove the given attachment edit.
Definition Composer.C:271
Composer::subject
const WString & subject() const
Get the subject.
Definition Composer.C:75
Composer::addcc_
Option * addcc_
Option for editing Cc:
Definition Composer.h:125
Composer::setAddressBook
void setAddressBook(const std::vector< Contact > &addressBook)
Set the address book, for autocomplete suggestions.
Definition Composer.C:70
Composer::cc
std::vector< Contact > cc() const
Get the Cc: contacts.
Definition Composer.C:60
Composer::createUi
void createUi()
Definition Composer.C:98
Composer::bccEdit_
AddresseeEdit * bccEdit_
Bcc: Addressees edit.
Definition Composer.h:113
Composer::options_
OptionList * options_
OptionsList for editing Cc or Bcc.
Definition Composer.h:122
Composer::attachments
std::vector< Attachment > attachments() const
Get the list of attachments.
Definition Composer.C:80
ContactSuggestions::setAddressBook
void setAddressBook(const std::vector< Contact > &contacts)
Set the address book.
Definition ContactSuggestions.C:31
OptionList::update
void update()
Updates the stateless implementations after an Option has been hidden or shown.
Definition OptionList.C:31
OptionList::add
void add(std::unique_ptr< Option > option)
Add an Option to the list.
Definition OptionList.C:18
Option
A clickable option.
Definition Option.h:32
Option::item
WInteractWidget * item()
Returns the clickable part.
Definition Option.h:44