Wt examples  4.10.4
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 
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 {
43 }
44 
45 void Composer::setSubject(const WString& subject)
46 {
48 }
49 
50 void Composer::setMessage(const WString& message)
51 {
53 }
54 
55 std::vector<Contact> Composer::to() const
56 {
57  return toEdit_->addressees();
58 }
59 
60 std::vector<Contact> Composer::cc() const
61 {
62  return ccEdit_->addressees();
63 }
64 
65 std::vector<Contact> Composer::bcc() const
66 {
67  return bccEdit_->addressees();
68 }
69 
70 void Composer::setAddressBook(const std::vector<Contact>& contacts)
71 {
73 }
74 
75 const WString& Composer::subject() const
76 {
77  return subject_->text();
78 }
79 
80 std::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 
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 
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")));
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  */
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);
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);
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);
252 }
253 
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.
268  .connect(attachments_.back(), &WWidget::show);
269 }
270 
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  */
289  attachFile_->show();
290  attachFile_->item()->clicked()
291  .connect(attachments_.back(), &WWidget::show);
292  }
293  }
294 }
295 
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 
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  */
320 
321  for (unsigned i = 0; i < attachments_.size() - 1; ++i) {
322  if (attachments_[i]->uploadNow()) {
324 
325  // this will trigger attachmentDone() when done, see
326  // the AttachmentEdit constructor.
327  }
328  }
329 
330  std::cerr << "Attachments pending: " << attachmentsPending_ << std::endl;
332  setStatus(tr("msg.uploading"), "status");
333  else
334  saved();
335  }
336 }
337 
339 {
340  if (saving_) {
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 
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");
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 
386 {
387  discard.emit();
388 }
std::vector< Contact > addressees() const
Get a list of addressees.
Definition: AddresseeEdit.C:74
void setAddressees(const std::vector< Contact > &contacts)
Set a list of addressees.
Definition: AddresseeEdit.C:27
An edit field for an email attachment.
WPushButton * botSaveNowButton_
Definition: Composer.h:103
void sendIt()
Slot attached to the Send button.
Definition: Composer.C:296
WText * statusMsg_
Definition: Composer.h:104
Option * attachOtherFile_
Option for attaching another file.
Definition: Composer.h:131
void setMessage(const WString &message)
Set the message.
Definition: Composer.C:50
void saved()
All attachments have been processed, determine the result of saving the message.
Definition: Composer.C:355
WTextArea * message_
WTextArea for the main message.
Definition: Composer.h:137
WPushButton * topSaveNowButton_
Definition: Composer.h:102
Wt::Signal send
The message is ready to be sent...
Definition: Composer.h:93
std::vector< Contact > bcc() const
Get the Bc: contacts.
Definition: Composer.C:65
Wt::Signal discard
The message must be discarded.
Definition: Composer.h:97
const WString & message() const
Get the message.
Definition: Composer.C:93
WPushButton * topSendButton_
Definition: Composer.h:102
void setStatus(const WString &text, const WString &style)
Set the status, and apply the given style.
Definition: Composer.C:349
AddresseeEdit * toEdit_
To: Addressees edit.
Definition: Composer.h:109
void discardIt()
Slot attached to the Discard button.
Definition: Composer.C:385
std::vector< Contact > to() const
Get the To: contacts.
Definition: Composer.C:55
void attachMore()
Add an attachment edit.
Definition: Composer.C:254
void saveNow()
Slot attached to the Save now button.
Definition: Composer.C:309
WPushButton * botSendButton_
Definition: Composer.h:103
bool saving_
state when waiting asyncrhonously for attachments to be uploaded
Definition: Composer.h:140
WLineEdit * subject_
The subject line edit.
Definition: Composer.h:119
WPushButton * topDiscardButton_
Definition: Composer.h:102
int attachmentsPending_
number of attachments waiting to be uploaded during saving
Definition: Composer.h:143
void setTo(const std::vector< Contact > &to)
Set message To: contacts.
Definition: Composer.C:40
ContactSuggestions * contactSuggestions_
The suggestions popup for the addressee edits.
Definition: Composer.h:116
Composer()
Construct a new Composer.
Definition: Composer.C:27
Option * attachFile_
Option for attaching a file.
Definition: Composer.h:129
WPushButton * botDiscardButton_
Definition: Composer.h:103
std::vector< AttachmentEdit * > attachments_
Array which holds all the attachments, including one extra invisible one.
Definition: Composer.h:134
WTable * edits_
Definition: Composer.h:106
Option * addbcc_
Option for editing Bcc:
Definition: Composer.h:127
AddresseeEdit * ccEdit_
Cc: Addressees edit.
Definition: Composer.h:111
bool sending_
Definition: Composer.h:140
void attachmentDone()
Slotcalled when an attachment has been uploaded.
Definition: Composer.C:338
WContainerWidget * layout_
Definition: Composer.h:100
void setSubject(const WString &subject)
Set subject.
Definition: Composer.C:45
void removeAttachment(AttachmentEdit *attachment)
Remove the given attachment edit.
Definition: Composer.C:271
const WString & subject() const
Get the subject.
Definition: Composer.C:75
Option * addcc_
Option for editing Cc:
Definition: Composer.h:125
void setAddressBook(const std::vector< Contact > &addressBook)
Set the address book, for autocomplete suggestions.
Definition: Composer.C:70
std::vector< Contact > cc() const
Get the Cc: contacts.
Definition: Composer.C:60
void createUi()
Definition: Composer.C:98
AddresseeEdit * bccEdit_
Bcc: Addressees edit.
Definition: Composer.h:113
OptionList * options_
OptionsList for editing Cc or Bcc.
Definition: Composer.h:122
std::vector< Attachment > attachments() const
Get the list of attachments.
Definition: Composer.C:80
void setAddressBook(const std::vector< Contact > &contacts)
Set the address book.
void update()
Updates the stateless implementations after an Option has been hidden or shown.
Definition: OptionList.C:31
void add(std::unique_ptr< Option > option)
Add an Option to the list.
Definition: OptionList.C:18
A clickable option.
Definition: Option.h:32
WInteractWidget * item()
Returns the clickable part.
Definition: Option.h:44
Wt::Signals::connection connect(F function)
void emit(A... args) const
void setImplementation(std::unique_ptr< WWidget > widget)
virtual void setStyleClass(const WString &styleClass) override
void setContentAlignment(WFlags< AlignmentFlag > contentAlignment)
void setPadding(const WLength &padding, WFlags< Side > sides=AllSides)
virtual void addWidget(std::unique_ptr< WWidget > widget)
virtual void insertBefore(std::unique_ptr< WWidget > widget, WWidget *before)
EventSignal< WMouseEvent > & clicked()
const WString & text() const
virtual void setText(const WString &text)
static WLocalDateTime currentDateTime(const WLocale &locale=WLocale::currentLocale())
WString toString() const
void addChild(std::unique_ptr< WObject > child)
void forEdit(WFormWidget *edit, WFlags< PopupTrigger > popupTriggers=PopupTrigger::Editing)
WTableCell * elementAt(int row, int column)
virtual void setText(const WString &text)
void setRows(int rows)
const WString & text() const
void setColumns(int cols)
bool setText(const WString &text)
virtual void setMargin(const WLength &margin, WFlags< Side > sides=AllSides) override
virtual void resize(const WLength &width, const WLength &height) override
virtual void setStyleClass(const WString &styleClass) override
std::unique_ptr< WWidget > removeFromParent()
static WString tr(const char *key)