Wt examples 3.1.10
/build/buildd/witty-3.1.10/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 }

Generated on Wed Jul 27 2011 for the C++ Web Toolkit (Wt) by doxygen 1.7.4