A self-contained chat widget. More...
#include <SimpleChatWidget.h>
Inherits Wt::WContainerWidget.
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_ |
SimpleChatServer & | server_ |
Wt::WApplication * | app_ |
Wt::JSlot | clearInput_ |
Wt::WString | user_ |
Wt::WLineEdit * | userNameEdit_ |
Wt::WText * | statusMsg_ |
Wt::WContainerWidget * | messages_ |
Wt::WContainerWidget * | messageEditArea_ |
Wt::WTextArea * | messageEdit_ |
Wt::WPushButton * | sendButton_ |
Wt::WContainerWidget * | userList_ |
boost::signals::connection | eventConnection_ |
Wt::WSound | messageReceived_ |
A self-contained chat widget.
Definition at line 33 of file SimpleChatWidget.h.
typedef std::map<Wt::WString, bool> SimpleChatWidget::UserMap [private] |
Definition at line 55 of file SimpleChatWidget.h.
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 }
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 }
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::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 }
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 }
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 00187 // default function 00188 messageEdit_->enterPressed().setPreventDefault(true); 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::updateUser | ( | ) | [private] |
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 }
Wt::WApplication* SimpleChatWidget::app_ [private] |
Definition at line 59 of file SimpleChatWidget.h.
Wt::JSlot SimpleChatWidget::clearInput_ [private] |
Definition at line 61 of file SimpleChatWidget.h.
boost::signals::connection SimpleChatWidget::eventConnection_ [private] |
Definition at line 74 of file SimpleChatWidget.h.
Wt::WTextArea* SimpleChatWidget::messageEdit_ [private] |
Definition at line 70 of file SimpleChatWidget.h.
Definition at line 69 of file SimpleChatWidget.h.
Wt::WSound SimpleChatWidget::messageReceived_ [private] |
Definition at line 76 of file SimpleChatWidget.h.
Wt::WContainerWidget* SimpleChatWidget::messages_ [private] |
Definition at line 68 of file SimpleChatWidget.h.
Wt::WPushButton* SimpleChatWidget::sendButton_ [private] |
Definition at line 71 of file SimpleChatWidget.h.
SimpleChatServer& SimpleChatWidget::server_ [private] |
Definition at line 58 of file SimpleChatWidget.h.
Wt::WText* SimpleChatWidget::statusMsg_ [private] |
Definition at line 66 of file SimpleChatWidget.h.
Wt::WString SimpleChatWidget::user_ [private] |
Definition at line 63 of file SimpleChatWidget.h.
Wt::WContainerWidget* SimpleChatWidget::userList_ [private] |
Definition at line 72 of file SimpleChatWidget.h.
Wt::WLineEdit* SimpleChatWidget::userNameEdit_ [private] |
Definition at line 65 of file SimpleChatWidget.h.
UserMap SimpleChatWidget::users_ [private] |
Definition at line 56 of file SimpleChatWidget.h.