SimpleChatWidget Class Reference
[Chat example]

A self-contained chat widget. More...

#include <SimpleChatWidget.h>

Inheritance diagram for SimpleChatWidget:

Inheritance graph
[legend]

List of all members.

Public Member Functions

 SimpleChatWidget (SimpleChatServer &server, Wt::WContainerWidget *parent=0)
 Create a chat widget that will connect to the given server.
 ~SimpleChatWidget ()
 Delete a chat widget.
void letLogin ()
 Show a simple login screen.
bool startChat (const Wt::WString &user)
 Start a chat for the given user.

Private Types

typedef std::map< Wt::WString,
bool > 
UserMap

Private Member Functions

void login ()
void logout ()
void send ()
void updateUsers ()
void updateUser ()
void processChatEvent (const ChatEvent &event)

Private Attributes

UserMap users_
SimpleChatServerserver_
Wt::WApplicationapp_
Wt::JSlot clearInput_
Wt::WString user_
Wt::WLineEdituserNameEdit_
Wt::WTextstatusMsg_
Wt::WContainerWidgetmessages_
Wt::WContainerWidgetmessageEditArea_
Wt::WTextAreamessageEdit_
Wt::WPushButtonsendButton_
Wt::WContainerWidgetuserList_
boost::signals::connection eventConnection_
Wt::WSound messageReceived_


Detailed Description

A self-contained chat widget.

Definition at line 33 of file SimpleChatWidget.h.


Member Typedef Documentation

typedef std::map<Wt::WString, bool> SimpleChatWidget::UserMap [private]

Definition at line 55 of file SimpleChatWidget.h.


Constructor & Destructor Documentation

SimpleChatWidget::SimpleChatWidget ( SimpleChatServer server,
Wt::WContainerWidget parent = 0 
)

Create a chat widget that will connect to the given server.

Definition at line 26 of file SimpleChatWidget.C.

00028   : WContainerWidget(parent),
00029     server_(server),
00030     app_(WApplication::instance()),
00031     messageReceived_("sounds/message_received.mp3")
00032 {
00033   user_ = server_.suggestGuest();
00034   letLogin();
00035 
00036   // this widget supports server-side updates its processChatEvent()
00037   // method is connected to a slot that is triggered from outside this
00038   // session's event loop (usually because another user enters text).
00039   app_->enableUpdates();
00040 }

SimpleChatWidget::~SimpleChatWidget (  ) 

Delete a chat widget.

Definition at line 42 of file SimpleChatWidget.C.

00043 {
00044   logout();
00045 }


Member Function Documentation

void SimpleChatWidget::letLogin (  ) 

Show a simple login screen.

Definition at line 47 of file SimpleChatWidget.C.

00048 {
00049   clear();
00050 
00051   WVBoxLayout *vLayout = new WVBoxLayout();
00052   setLayout(vLayout, AlignLeft | AlignTop);
00053 
00054   WHBoxLayout *hLayout = new WHBoxLayout();
00055   vLayout->addLayout(hLayout);
00056 
00057   hLayout->addWidget(new WLabel("User name:"), 0, AlignMiddle);
00058   hLayout->addWidget(userNameEdit_ = new WLineEdit(user_), 0, AlignMiddle);
00059   userNameEdit_->setFocus();
00060 
00061   WPushButton *b = new WPushButton("Login");
00062   hLayout->addWidget(b, 0, AlignMiddle);
00063   hLayout->addStretch(1);
00064 
00065   b->clicked().connect(SLOT(this, SimpleChatWidget::login));
00066   userNameEdit_->enterPressed().connect(SLOT(this, SimpleChatWidget::login));
00067 
00068   vLayout->addWidget(statusMsg_ = new WText());
00069   statusMsg_->setTextFormat(PlainText);
00070 }

bool SimpleChatWidget::startChat ( const Wt::WString user  ) 

Start a chat for the given user.

Returns false if the user could not login.

Definition at line 90 of file SimpleChatWidget.C.

00091 {
00092   if (server_.login(user)) {
00093     eventConnection_
00094       = server_.chatEvent().connect(SLOT(this,
00095                                        SimpleChatWidget::processChatEvent));
00096     user_ = user;    
00097 
00098     clear();
00099 
00100     /*
00101      * Create a vertical layout, which will hold 3 rows,
00102      * organized like this:
00103      *
00104      * WVBoxLayout
00105      * --------------------------------------------
00106      * | nested WHBoxLayout (vertical stretch=1)  |
00107      * |                              |           |
00108      * |  messages                    | userslist |
00109      * |   (horizontal stretch=1)     |           |
00110      * |                              |           |
00111      * --------------------------------------------
00112      * | message edit area                        |
00113      * --------------------------------------------
00114      * | WHBoxLayout                              |
00115      * | send | logout |       stretch = 1        |
00116      * --------------------------------------------
00117      */
00118     WVBoxLayout *vLayout = new WVBoxLayout();
00119 
00120     // Create a horizontal layout for the messages | userslist.
00121     WHBoxLayout *hLayout = new WHBoxLayout();
00122 
00123     // Add widget to horizontal layout with stretch = 1
00124     hLayout->addWidget(messages_ = new WContainerWidget(), 1);
00125     messages_->setStyleClass("chat-msgs");
00126     // Display scroll bars if contents overflows
00127     messages_->setOverflow(WContainerWidget::OverflowAuto);
00128 
00129     // Add another widget to hirozontal layout with stretch = 0
00130     hLayout->addWidget(userList_ = new WContainerWidget());
00131     userList_->setStyleClass("chat-users");
00132     userList_->setOverflow(WContainerWidget::OverflowAuto);
00133 
00134     hLayout->setResizable(0, true);
00135 
00136     // Add nested layout to vertical layout with stretch = 1
00137     vLayout->addLayout(hLayout, 1);
00138 
00139     // Add widget to vertical layout with stretch = 0
00140     vLayout->addWidget(messageEdit_ = new WTextArea());
00141     messageEdit_->setStyleClass("chat-noedit");
00142     messageEdit_->setRows(2);
00143     messageEdit_->setFocus();
00144 
00145     // Create a horizontal layout for the buttons.
00146     hLayout = new WHBoxLayout();
00147 
00148     // Add button to horizontal layout with stretch = 0
00149     hLayout->addWidget(sendButton_ = new WPushButton("Send"));
00150     WPushButton *b;
00151 
00152     // Add button to horizontal layout with stretch = 0
00153     hLayout->addWidget(b = new WPushButton("Logout"));
00154 
00155     // Add stretching spacer to horizontal layout
00156     hLayout->addStretch(1);
00157 
00158     // Add nested layout to vertical layout with stretch = 0
00159     vLayout->addLayout(hLayout);
00160 
00161     setLayout(vLayout);
00162 
00163     /*
00164      * Connect event handlers:
00165      *  - click on button
00166      *  - enter in text area
00167      *
00168      * We will clear the input field using a small custom client-side
00169      * JavaScript invocation.
00170      */
00171 
00172     // Create a JavaScript 'slot' (JSlot). The JavaScript slot always takes
00173     // 2 arguments: the originator of the event (in our case the
00174     // button or text area), and the JavaScript event object.
00175     clearInput_.setJavaScript
00176       ("function(o, e) {"
00177        "" + messageEdit_->jsRef() + ".value='';"
00178        "}");
00179 
00180     // Bind the C++ and JavaScript event handlers.
00181     sendButton_->clicked().connect(SLOT(this, SimpleChatWidget::send));
00182     messageEdit_->enterPressed().connect(SLOT(this, SimpleChatWidget::send));
00183     sendButton_->clicked().connect(clearInput_);
00184     messageEdit_->enterPressed().connect(clearInput_);
00185 
00186     // Prevent the enter from generating a new line, which is its default
00187     // action
00188     messageEdit_->enterPressed().preventDefaultAction();
00189 
00190     b->clicked().connect(SLOT(this, SimpleChatWidget::logout));
00191 
00192     WText *msg = new WText
00193       ("<div><span class='chat-info'>You are joining the conversation as "
00194        + user_ + "</span></div>", messages_);
00195     msg->setStyleClass("chat-msg");
00196 
00197     updateUsers();
00198     
00199     return true;
00200   } else
00201     return false;
00202 }

void SimpleChatWidget::login (  )  [private]

Definition at line 72 of file SimpleChatWidget.C.

00073 {
00074   WString name = WWebWidget::escapeText(userNameEdit_->text());
00075 
00076   if (!startChat(name))
00077     statusMsg_->setText("Sorry, name '" + name + "' is already taken.");
00078 }

void SimpleChatWidget::logout (  )  [private]

Definition at line 80 of file SimpleChatWidget.C.

00081 {
00082   if (eventConnection_.connected()) {
00083     eventConnection_.disconnect(); // do not listen for more events
00084     server_.logout(user_);
00085 
00086     letLogin();
00087   }
00088 }

void SimpleChatWidget::send (  )  [private]

Definition at line 204 of file SimpleChatWidget.C.

00205 {
00206   if (!messageEdit_->text().empty()) {
00207     server_.sendMessage(user_, messageEdit_->text());
00208     if (!WApplication::instance()->environment().ajax())
00209       messageEdit_->setText(WString::Empty);
00210   }
00211 
00212   messageEdit_->setFocus();
00213 }

void SimpleChatWidget::updateUsers (  )  [private]

Definition at line 215 of file SimpleChatWidget.C.

00216 {
00217   userList_->clear();
00218 
00219   SimpleChatServer::UserSet users = server_.users();
00220 
00221   UserMap oldUsers = users_;
00222   users_.clear();
00223 
00224   for (SimpleChatServer::UserSet::iterator i = users.begin();
00225        i != users.end(); ++i) {
00226     WCheckBox *w = new WCheckBox(*i, userList_);
00227     w->setInline(false);
00228 
00229     UserMap::const_iterator j = oldUsers.find(*i);
00230     if (j != oldUsers.end())
00231       w->setChecked(j->second);
00232     else
00233       w->setChecked(true);
00234 
00235     users_[*i] = w->isChecked();
00236     w->changed().connect(SLOT(this, SimpleChatWidget::updateUser));
00237 
00238     if (*i == user_)
00239       w->setStyleClass("chat-self");
00240   }
00241 }

void SimpleChatWidget::updateUser (  )  [private]

Definition at line 243 of file SimpleChatWidget.C.

00244 {
00245   WCheckBox *b = dynamic_cast<WCheckBox *>(sender());
00246   users_[b->text()] = b->isChecked();
00247 }

void SimpleChatWidget::processChatEvent ( const ChatEvent event  )  [private]

Definition at line 249 of file SimpleChatWidget.C.

00250 {
00251   /*
00252    * This is where the "server-push" happens. This method is called
00253    * when a new event or message needs to be notified to the user. In
00254    * general, it is called from another session.
00255    */
00256 
00257   /*
00258    * First, take the lock to safely manipulate the UI outside of the
00259    * normal event loop, by having exclusive access to the session.
00260    */
00261   WApplication::UpdateLock lock = app_->getUpdateLock();
00262 
00263   /*
00264    * Format and append the line to the conversation.
00265    *
00266    * This is also the step where the automatic XSS filtering will kick in:
00267    * - if another user tried to pass on some JavaScript, it is filtered away.
00268    * - if another user did not provide valid XHTML, the text is automatically
00269    *   interpreted as PlainText
00270    */
00271   bool needPush = false;
00272 
00273   /*
00274    * If it is not a normal message, also update the user list.
00275    */
00276   if (event.type() != ChatEvent::Message) {
00277     needPush = true;
00278     updateUsers();
00279   }
00280 
00281   bool display = event.type() != ChatEvent::Message
00282     || (users_.find(event.user()) != users_.end() && users_[event.user()]);
00283 
00284   if (display) {
00285     needPush = true;
00286 
00287     WText *w = new WText(event.formattedHTML(user_), messages_);
00288     w->setInline(false);
00289     w->setStyleClass("chat-msg");
00290 
00291     /*
00292      * Leave not more than 100 messages in the back-log
00293      */
00294     if (messages_->count() > 100)
00295       delete messages_->children()[0];
00296 
00297     /*
00298      * Little javascript trick to make sure we scroll along with new content
00299      */
00300     app_->doJavaScript(messages_->jsRef() + ".scrollTop += "
00301                        + messages_->jsRef() + ".scrollHeight;");
00302 
00303     /* If this message belongs to another user, play a received sound */
00304     if (event.user() != user_)
00305       messageReceived_.play();
00306   }
00307 
00308   if (needPush)
00309     app_->triggerUpdate();
00310 }


Member Data Documentation

Definition at line 56 of file SimpleChatWidget.h.

Definition at line 58 of file SimpleChatWidget.h.

Definition at line 59 of file SimpleChatWidget.h.

Definition at line 61 of file SimpleChatWidget.h.

Definition at line 63 of file SimpleChatWidget.h.

Definition at line 65 of file SimpleChatWidget.h.

Definition at line 66 of file SimpleChatWidget.h.

Definition at line 68 of file SimpleChatWidget.h.

Definition at line 69 of file SimpleChatWidget.h.

Definition at line 70 of file SimpleChatWidget.h.

Definition at line 71 of file SimpleChatWidget.h.

Definition at line 72 of file SimpleChatWidget.h.

boost::signals::connection SimpleChatWidget::eventConnection_ [private]

Definition at line 74 of file SimpleChatWidget.h.

Definition at line 76 of file SimpleChatWidget.h.


The documentation for this class was generated from the following files:

Generated on Thu May 20 18:14:57 2010 for Wt by doxygen 1.5.6