Wt examples: /home/koen/project/wt/public-git/wt/examples/composer/Composer.C Source File

Wt examples 3.3.0

- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

/home/koen/project/wt/public-git/wt/examples/composer/Composer.C
Go to the documentation of this file.

    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 }