/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 <WContainerWidget>
00017 #include <WImage>
00018 #include <WLineEdit>
00019 #include <WPushButton>
00020 #include <WText>
00021 #include <WTable>
00022 #include <WTableCell>
00023 #include <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_->clicked.connect(SLOT(ccEdit_, WWidget::show));
00223   addcc_->clicked.connect(SLOT(addcc_, WWidget::hide));
00224   addcc_->clicked.connect(SLOT(options_, OptionList::update));
00225 
00226   addbcc_->clicked.connect(SLOT(bccEdit_, WWidget::show));
00227   addbcc_->clicked.connect(SLOT(addbcc_, WWidget::hide));
00228   addbcc_->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_->clicked.connect(SLOT(attachments_.back(), WWidget::show));
00240   attachFile_->clicked.connect(SLOT(attachOtherFile_, WWidget::show));
00241   attachFile_->clicked.connect(SLOT(attachFile_, WWidget::hide));
00242   attachFile_->clicked.connect(SLOT(this, Composer::attachMore));
00243   attachOtherFile_->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_->clicked.connect(SLOT(attachments_.back(), WWidget::show));
00258 }
00259 
00260 void Composer::removeAttachment(AttachmentEdit *attachment)
00261 {
00262   /*
00263    * Remove the given attachment from the attachments list.
00264    */
00265   std::vector<AttachmentEdit *>::iterator i
00266     = std::find(attachments_.begin(), attachments_.end(), attachment);
00267 
00268   if (i != attachments_.end()) {
00269     attachments_.erase(i);
00270     delete attachment;
00271 
00272     if (attachments_.size() == 1) {
00273       /*
00274        * This was the last visible attachment, thus, we should switch
00275        * the option control again.
00276        */
00277       attachOtherFile_->hide();
00278       attachFile_->show();
00279       attachFile_->clicked.connect(SLOT(attachments_.back(), WWidget::show));
00280     }
00281   }
00282 }
00283 
00284 void Composer::sendIt()
00285 {
00286   if (!sending_) {
00287     sending_ = true;
00288 
00289     /*
00290      * First save -- this will check for the sending_ state
00291      * signal if successfull.
00292      */
00293     saveNow();
00294   }
00295 }
00296 
00297 void Composer::saveNow()
00298 {
00299   if (!saving_) {
00300     saving_ = true;
00301 
00302     /*
00303      * Check if any attachments still need to be uploaded.
00304      * This may be the case when fileupload change events could not
00305      * be caught (for example in Konqueror).
00306      */
00307     attachmentsPending_ = 0;
00308 
00309     for (unsigned i = 0; i < attachments_.size() - 1; ++i) {
00310       if (attachments_[i]->uploadNow()) {
00311         ++attachmentsPending_;
00312 
00313         // this will trigger attachmentDone() when done, see
00314         // the AttachmentEdit constructor.
00315       }
00316     }
00317 
00318     std::cerr << "Attachments pending: " << attachmentsPending_ << std::endl;
00319     if (attachmentsPending_)
00320       setStatus(tr("msg.uploading"), "status");
00321     else
00322       saved();
00323   }
00324 }
00325 
00326 void Composer::attachmentDone()
00327 {
00328   if (saving_) {
00329     --attachmentsPending_;
00330     std::cerr << "Attachments still: " << attachmentsPending_ << std::endl;
00331 
00332     if (attachmentsPending_ == 0)
00333       saved();
00334   }
00335 }
00336 
00337 void Composer::setStatus(const WString& text, const WString& style)
00338 {
00339   statusMsg_->setText(text);
00340   statusMsg_->setStyleClass(style);
00341 }
00342 
00343 void Composer::saved()
00344 {
00345   /*
00346    * All attachments have been processed.
00347    */
00348 
00349   bool attachmentsFailed = false;
00350   for (unsigned i = 0; i < attachments_.size() - 1; ++i)
00351     if (attachments_[i]->uploadFailed()) {
00352       attachmentsFailed = true;
00353       break;
00354     }
00355 
00356   if (attachmentsFailed) {
00357     setStatus(tr("msg.attachment.failed"), "error");
00358   } else {
00359 #ifndef WIN32
00360     time_t t = time(0);
00361     struct tm td;
00362     gmtime_r(&t, &td);
00363     char buffer[100];
00364     strftime(buffer, 100, "%H:%M", &td);
00365 #else
00366     char buffer[] = "server"; // Should fix this; for now just make sense
00367 #endif
00368     setStatus(tr("msg.ok"), "status");
00369     statusMsg_->setText(std::string("Draft saved at ") + buffer);
00370 
00371     if (sending_) {
00372       send.emit();
00373       return;
00374     }
00375   }
00376 
00377   saving_ = false;
00378   sending_ = false;
00379 }
00380 
00381 void Composer::discardIt()
00382 { 
00383   discard.emit();
00384 }

Generated on Mon Apr 14 15:15:04 2008 for Wt by doxygen 1.5.3