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

Generated on Fri Jul 25 17:05:59 2008 for Wt by doxygen 1.5.3