#include <SimpleChatWidget.h>
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 | |
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_ |
Definition at line 33 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 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 }
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] |
SimpleChatServer& SimpleChatWidget::server_ [private] |
Definition at line 55 of file SimpleChatWidget.h.
Wt::WApplication* SimpleChatWidget::app_ [private] |
Definition at line 56 of file SimpleChatWidget.h.
Wt::JSlot SimpleChatWidget::clearInput_ [private] |
Definition at line 58 of file SimpleChatWidget.h.
Wt::WString SimpleChatWidget::user_ [private] |
Definition at line 60 of file SimpleChatWidget.h.
Wt::WLineEdit* SimpleChatWidget::userNameEdit_ [private] |
Definition at line 62 of file SimpleChatWidget.h.
Wt::WText* SimpleChatWidget::statusMsg_ [private] |
Definition at line 63 of file SimpleChatWidget.h.
Wt::WContainerWidget* SimpleChatWidget::messages_ [private] |
Definition at line 65 of file SimpleChatWidget.h.
Definition at line 66 of file SimpleChatWidget.h.
Wt::WTextArea* SimpleChatWidget::messageEdit_ [private] |
Definition at line 67 of file SimpleChatWidget.h.
Wt::WPushButton* SimpleChatWidget::sendButton_ [private] |
Definition at line 68 of file SimpleChatWidget.h.
Wt::WContainerWidget* SimpleChatWidget::userList_ [private] |
Definition at line 69 of file SimpleChatWidget.h.
boost::signals::connection SimpleChatWidget::eventConnection_ [private] |
Definition at line 71 of file SimpleChatWidget.h.
Wt::WSound SimpleChatWidget::messageReceived_ [private] |
Definition at line 73 of file SimpleChatWidget.h.