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 Member Functions

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

Private Attributes

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.


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 25 of file SimpleChatWidget.C.

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

SimpleChatWidget::~SimpleChatWidget (  ) 

Delete a chat widget.

Definition at line 41 of file SimpleChatWidget.C.

00042 {
00043   logout();
00044 }


Member Function Documentation

void SimpleChatWidget::letLogin (  ) 

Show a simple login screen.

Definition at line 46 of file SimpleChatWidget.C.

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

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 89 of file SimpleChatWidget.C.

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

void SimpleChatWidget::login (  )  [private]

Definition at line 71 of file SimpleChatWidget.C.

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

void SimpleChatWidget::logout (  )  [private]

Definition at line 79 of file SimpleChatWidget.C.

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

void SimpleChatWidget::send (  )  [private]

Definition at line 201 of file SimpleChatWidget.C.

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

void SimpleChatWidget::updateUsers (  )  [private]

Definition at line 212 of file SimpleChatWidget.C.

00213 {
00214   userList_->clear();
00215 
00216   SimpleChatServer::UserSet users = server_.users();
00217 
00218   WString usersStr;
00219 
00220   for (SimpleChatServer::UserSet::iterator i = users.begin();
00221        i != users.end(); ++i) {
00222     if (*i == user_)
00223       usersStr += "<div><span class='chat-self'>" + *i + "</span></div>";
00224     else
00225       usersStr += "<div>" + *i + "</div>";
00226   }
00227 
00228   userList_->addWidget(new WText(usersStr));
00229 }

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

Definition at line 231 of file SimpleChatWidget.C.

00232 {
00233   /* If this message belongs to another user, play a received sound */
00234   if (event.user() != user_) {
00235     messageReceived_.play();
00236   }
00237 
00238   /*
00239    * This is where the "server-push" happens. This method is called
00240    * when a new event or message needs to be notified to the user. In
00241    * general, it is called from another session.
00242    */
00243 
00244   /*
00245    * First, take the lock to safely manipulate the UI outside of the
00246    * normal event loop, by having exclusive access to the session.
00247    */
00248   WApplication::UpdateLock lock = app_->getUpdateLock();
00249 
00250   /*
00251    * Format and append the line to the conversation.
00252    *
00253    * This is also the step where the automatic XSS filtering will kick in:
00254    * - if another user tried to pass on some JavaScript, it is filtered away.
00255    * - if another user did not provide valid XHTML, the text is automatically
00256    *   interpreted as PlainText
00257    */
00258   WText *w = new WText(event.formattedHTML(user_), messages_);
00259   w->setInline(false);
00260   w->setStyleClass("chat-msg");
00261 
00262   /*
00263    * Leave not more than 100 messages in the back-log
00264    */
00265   if (messages_->count() > 100)
00266     delete messages_->children()[0];
00267 
00268   /*
00269    * If it is not a normal message, also update the user list.
00270    */
00271   if (event.type() != ChatEvent::Message)
00272     updateUsers();
00273 
00274   /*
00275    * Little javascript trick to make sure we scroll along with new content
00276    */
00277   app_->doJavaScript(messages_->jsRef() + ".scrollTop += "
00278                      + messages_->jsRef() + ".scrollHeight;");
00279 
00280   app_->triggerUpdate();
00281 }

void SimpleChatWidget::onEditBlur (  )  [private]

void SimpleChatWidget::onEditFocus (  )  [private]


Member Data Documentation

Definition at line 55 of file SimpleChatWidget.h.

Definition at line 56 of file SimpleChatWidget.h.

Definition at line 58 of file SimpleChatWidget.h.

Definition at line 60 of file SimpleChatWidget.h.

Definition at line 62 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 67 of file SimpleChatWidget.h.

Definition at line 68 of file SimpleChatWidget.h.

Definition at line 69 of file SimpleChatWidget.h.

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

Definition at line 71 of file SimpleChatWidget.h.

Definition at line 73 of file SimpleChatWidget.h.


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

Generated on Tue Sep 1 17:51:24 2009 for Wt by doxygen 1.5.6