Wt examples
3.2.2
|
00001 /* 00002 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium. 00003 * 00004 * See the LICENSE file for terms of use. 00005 */ 00006 #include <iostream> 00007 00008 #include "AddresseeEdit.h" 00009 #include "AttachmentEdit.h" 00010 #include "Composer.h" 00011 #include "ContactSuggestions.h" 00012 #include "Label.h" 00013 #include "Option.h" 00014 #include "OptionList.h" 00015 00016 #include <Wt/WContainerWidget> 00017 #include <Wt/WImage> 00018 #include <Wt/WLineEdit> 00019 #include <Wt/WPushButton> 00020 #include <Wt/WText> 00021 #include <Wt/WTable> 00022 #include <Wt/WTableCell> 00023 #include <Wt/WStringUtil> 00024 00025 Composer::Composer(WContainerWidget *parent) 00026 : WCompositeWidget(parent), 00027 saving_(false), 00028 sending_(false) 00029 { 00030 setImplementation(layout_ = new WContainerWidget()); 00031 00032 createUi(); 00033 } 00034 00035 void Composer::setTo(const std::vector<Contact>& to) 00036 { 00037 toEdit_->setAddressees(to); 00038 } 00039 00040 void Composer::setSubject(const WString& subject) 00041 { 00042 subject_->setText(subject); 00043 } 00044 00045 void Composer::setMessage(const WString& message) 00046 { 00047 message_->setText(message); 00048 } 00049 00050 std::vector<Contact> Composer::to() const 00051 { 00052 return toEdit_->addressees(); 00053 } 00054 00055 std::vector<Contact> Composer::cc() const 00056 { 00057 return ccEdit_->addressees(); 00058 } 00059 00060 std::vector<Contact> Composer::bcc() const 00061 { 00062 return bccEdit_->addressees(); 00063 } 00064 00065 void Composer::setAddressBook(const std::vector<Contact>& contacts) 00066 { 00067 contactSuggestions_->setAddressBook(contacts); 00068 } 00069 00070 const WString& Composer::subject() const 00071 { 00072 return subject_->text(); 00073 } 00074 00075 std::vector<Attachment> Composer::attachments() const 00076 { 00077 std::vector<Attachment> attachments; 00078 00079 for (unsigned i = 0; i < attachments_.size() - 1; ++i) { 00080 std::vector<Attachment> toadd = attachments_[i]->attachments(); 00081 00082 attachments.insert(attachments.end(), toadd.begin(), toadd.end()); 00083 } 00084 00085 return attachments; 00086 } 00087 00088 const WString& Composer::message() const 00089 { 00090 return message_->text(); 00091 } 00092 00093 void Composer::createUi() 00094 { 00095 setStyleClass("darker"); 00096 00097 // horizontal layout container, used for top and bottom buttons. 00098 WContainerWidget *horiz; 00099 00100 /* 00101 * Top buttons 00102 */ 00103 horiz = new WContainerWidget(layout_); 00104 horiz->setPadding(5); 00105 topSendButton_ = new WPushButton(tr("msg.send"), horiz); 00106 topSendButton_->setStyleClass("default"); // default action 00107 topSaveNowButton_ = new WPushButton(tr("msg.savenow"), horiz); 00108 topDiscardButton_ = new WPushButton(tr("msg.discard"), horiz); 00109 00110 // Text widget which shows status messages, next to the top buttons. 00111 statusMsg_ = new WText(horiz); 00112 statusMsg_->setMargin(15, Left); 00113 00114 /* 00115 * To, Cc, Bcc, Subject, Attachments 00116 * 00117 * They are organized in a two-column table: left column for 00118 * labels, and right column for the edit. 00119 */ 00120 edits_ = new WTable(layout_); 00121 edits_->setStyleClass("lighter"); 00122 edits_->resize(WLength(100, WLength::Percentage), WLength::Auto); 00123 edits_->elementAt(0, 0)->resize(WLength(1, WLength::Percentage), 00124 WLength::Auto); 00125 00126 /* 00127 * To, Cc, Bcc 00128 */ 00129 toEdit_ = new AddresseeEdit(tr("msg.to"), edits_->elementAt(0, 1), 00130 edits_->elementAt(0, 0)); 00131 // add some space above To: 00132 edits_->elementAt(0, 1)->setMargin(5, Top); 00133 ccEdit_ = new AddresseeEdit(tr("msg.cc"), edits_->elementAt(1, 1), 00134 edits_->elementAt(1, 0)); 00135 bccEdit_ = new AddresseeEdit(tr("msg.bcc"), edits_->elementAt(2, 1), 00136 edits_->elementAt(2, 0)); 00137 00138 ccEdit_->hide(); 00139 bccEdit_->hide(); 00140 00141 /* 00142 * Addressbook suggestions popup 00143 */ 00144 contactSuggestions_ = new ContactSuggestions(layout_); 00145 00146 contactSuggestions_->forEdit(toEdit_); 00147 contactSuggestions_->forEdit(ccEdit_); 00148 contactSuggestions_->forEdit(bccEdit_); 00149 00150 /* 00151 * We use an OptionList widget to show the expand options for 00152 * ccEdit_ and bccEdit_ nicely next to each other, separated 00153 * by pipe characters. 00154 */ 00155 options_ = new OptionList(edits_->elementAt(3, 1)); 00156 00157 options_->add(addcc_ = new Option(tr("msg.addcc"))); 00158 options_->add(addbcc_ = new Option(tr("msg.addbcc"))); 00159 00160 /* 00161 * Subject 00162 */ 00163 new Label(tr("msg.subject"), edits_->elementAt(4, 0)); 00164 subject_ = new WLineEdit(edits_->elementAt(4, 1)); 00165 subject_->resize(WLength(99, WLength::Percentage), WLength::Auto); 00166 00167 /* 00168 * Attachments 00169 */ 00170 new WImage("icons/paperclip.png", edits_->elementAt(5, 0)); 00171 edits_->elementAt(5, 0)->setContentAlignment(AlignRight | AlignTop); 00172 edits_->elementAt(5, 0)->setPadding(3); 00173 00174 // Attachment edits: we always have the next attachmentedit ready 00175 // but hidden. This improves the response time, since the show() 00176 // and hide() slots are stateless. 00177 attachments_.push_back(new AttachmentEdit(this, edits_->elementAt(5, 1))); 00178 attachments_.back()->hide(); 00179 00180 /* 00181 * Two options for attaching files. The first does not say 'another'. 00182 */ 00183 attachFile_ = new Option(tr("msg.attachfile"), 00184 edits_->elementAt(5, 1)); 00185 attachOtherFile_ = new Option(tr("msg.attachanother"), 00186 edits_->elementAt(5, 1)); 00187 attachOtherFile_->hide(); 00188 00189 /* 00190 * Message 00191 */ 00192 message_ = new WTextArea(layout_); 00193 message_->setColumns(80); 00194 message_->setRows(10); // should be 20, but let's keep it smaller 00195 message_->setMargin(10); 00196 00197 /* 00198 * Bottom buttons 00199 */ 00200 horiz = new WContainerWidget(layout_); 00201 horiz->setPadding(5); 00202 botSendButton_ = new WPushButton(tr("msg.send"), horiz); 00203 botSendButton_->setStyleClass("default"); 00204 botSaveNowButton_ = new WPushButton(tr("msg.savenow"), horiz); 00205 botDiscardButton_ = new WPushButton(tr("msg.discard"), horiz); 00206 00207 /* 00208 * Button events. 00209 */ 00210 topSendButton_->clicked().connect(this, &Composer::sendIt); 00211 botSendButton_->clicked().connect(this, &Composer::sendIt); 00212 topSaveNowButton_->clicked().connect(this, &Composer::saveNow); 00213 botSaveNowButton_->clicked().connect(this, &Composer::saveNow); 00214 topDiscardButton_->clicked().connect(this, &Composer::discardIt); 00215 botDiscardButton_->clicked().connect(this, &Composer::discardIt); 00216 00217 /* 00218 * Option events to show the cc or Bcc edit. 00219 * 00220 * Clicking on the option should both show the corresponding edit, and 00221 * hide the option itself. 00222 */ 00223 addcc_->item()->clicked().connect(ccEdit_, &WWidget::show); 00224 addcc_->item()->clicked().connect(addcc_, &WWidget::hide); 00225 addcc_->item()->clicked().connect(options_, &OptionList::update); 00226 addcc_->item()->clicked().connect(ccEdit_, &WFormWidget::setFocus); 00227 00228 addbcc_->item()->clicked().connect(bccEdit_, &WWidget::show); 00229 addbcc_->item()->clicked().connect(addbcc_, &WWidget::hide); 00230 addbcc_->item()->clicked().connect(options_, &OptionList::update); 00231 addbcc_->item()->clicked().connect(bccEdit_, &WFormWidget::setFocus); 00232 00233 /* 00234 * Option event to attach the first attachment. 00235 * 00236 * We show the first attachment, and call attachMore() to prepare the 00237 * next attachment edit that will be hidden. 00238 * 00239 * In addition, we need to show the 'attach More' option, and hide the 00240 * 'attach' option. 00241 */ 00242 attachFile_->item()->clicked().connect(attachments_.back(), &WWidget::show); 00243 attachFile_->item()->clicked().connect(attachOtherFile_, &WWidget::show); 00244 attachFile_->item()->clicked().connect(attachFile_, &WWidget::hide); 00245 attachFile_->item()->clicked().connect(this, &Composer::attachMore); 00246 attachOtherFile_->item()->clicked().connect(this, &Composer::attachMore); 00247 } 00248 00249 void Composer::attachMore() 00250 { 00251 /* 00252 * Create and append the next AttachmentEdit, that will be hidden. 00253 */ 00254 AttachmentEdit *edit = new AttachmentEdit(this); 00255 edits_->elementAt(5, 1)->insertBefore(edit, attachOtherFile_); 00256 attachments_.push_back(edit); 00257 attachments_.back()->hide(); 00258 00259 // Connect the attachOtherFile_ option to show this attachment. 00260 attachOtherFile_->item()->clicked() 00261 .connect(attachments_.back(), &WWidget::show); 00262 } 00263 00264 void Composer::removeAttachment(AttachmentEdit *attachment) 00265 { 00266 /* 00267 * Remove the given attachment from the attachments list. 00268 */ 00269 std::vector<AttachmentEdit *>::iterator i 00270 = std::find(attachments_.begin(), attachments_.end(), attachment); 00271 00272 if (i != attachments_.end()) { 00273 attachments_.erase(i); 00274 delete attachment; 00275 00276 if (attachments_.size() == 1) { 00277 /* 00278 * This was the last visible attachment, thus, we should switch 00279 * the option control again. 00280 */ 00281 attachOtherFile_->hide(); 00282 attachFile_->show(); 00283 attachFile_->item()->clicked() 00284 .connect(attachments_.back(), &WWidget::show); 00285 } 00286 } 00287 } 00288 00289 void Composer::sendIt() 00290 { 00291 if (!sending_) { 00292 sending_ = true; 00293 00294 /* 00295 * First save -- this will check for the sending_ state 00296 * signal if successfull. 00297 */ 00298 saveNow(); 00299 } 00300 } 00301 00302 void Composer::saveNow() 00303 { 00304 if (!saving_) { 00305 saving_ = true; 00306 00307 /* 00308 * Check if any attachments still need to be uploaded. 00309 * This may be the case when fileupload change events could not 00310 * be caught (for example in Konqueror). 00311 */ 00312 attachmentsPending_ = 0; 00313 00314 for (unsigned i = 0; i < attachments_.size() - 1; ++i) { 00315 if (attachments_[i]->uploadNow()) { 00316 ++attachmentsPending_; 00317 00318 // this will trigger attachmentDone() when done, see 00319 // the AttachmentEdit constructor. 00320 } 00321 } 00322 00323 std::cerr << "Attachments pending: " << attachmentsPending_ << std::endl; 00324 if (attachmentsPending_) 00325 setStatus(tr("msg.uploading"), "status"); 00326 else 00327 saved(); 00328 } 00329 } 00330 00331 void Composer::attachmentDone() 00332 { 00333 if (saving_) { 00334 --attachmentsPending_; 00335 std::cerr << "Attachments still: " << attachmentsPending_ << std::endl; 00336 00337 if (attachmentsPending_ == 0) 00338 saved(); 00339 } 00340 } 00341 00342 void Composer::setStatus(const WString& text, const WString& style) 00343 { 00344 statusMsg_->setText(text); 00345 statusMsg_->setStyleClass(style); 00346 } 00347 00348 void Composer::saved() 00349 { 00350 /* 00351 * All attachments have been processed. 00352 */ 00353 00354 bool attachmentsFailed = false; 00355 for (unsigned i = 0; i < attachments_.size() - 1; ++i) 00356 if (attachments_[i]->uploadFailed()) { 00357 attachmentsFailed = true; 00358 break; 00359 } 00360 00361 if (attachmentsFailed) { 00362 setStatus(tr("msg.attachment.failed"), "error"); 00363 } else { 00364 #ifndef WIN32 00365 time_t t = time(0); 00366 struct tm td; 00367 gmtime_r(&t, &td); 00368 char buffer[100]; 00369 strftime(buffer, 100, "%H:%M", &td); 00370 #else 00371 char buffer[] = "server"; // Should fix this; for now just make sense 00372 #endif 00373 setStatus(tr("msg.ok"), "status"); 00374 statusMsg_->setText(std::string("Draft saved at ") + buffer); 00375 00376 if (sending_) { 00377 send.emit(); 00378 return; 00379 } 00380 } 00381 00382 saving_ = false; 00383 sending_ = false; 00384 } 00385 00386 void Composer::discardIt() 00387 { 00388 discard.emit(); 00389 }