Wt examples  3.3.0
Public Member Functions | Protected Member Functions | Private Types | Private Member Functions | Private Attributes | List of all members
SimpleChatWidget Class Reference

A self-contained chat widget. More...

#include <SimpleChatWidget.h>

Inheritance diagram for SimpleChatWidget:
Inheritance graph
[legend]

Public Member Functions

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

Protected Member Functions

virtual void createLayout (Wt::WWidget *messages, Wt::WWidget *userList, Wt::WWidget *messageEdit, Wt::WWidget *sendButton, Wt::WWidget *logoutButton)
 
virtual void updateUsers ()
 
virtual void newMessage ()
 
virtual void render (Wt::WFlags< Wt::RenderFlag > flags)
 
bool loggedIn () const
 

Private Types

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

Private Member Functions

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

Private Attributes

UserMap users_
 
SimpleChatServerserver_
 
bool loggedIn_
 
Wt::JSlot clearInput_
 
Wt::WString user_
 
Wt::WLineEdituserNameEdit_
 
Wt::WTextstatusMsg_
 
Wt::WContainerWidgetmessages_
 
Wt::WContainerWidgetmessageEditArea_
 
Wt::WTextAreamessageEdit_
 
Wt::WPushButtonsendButton_
 
Wt::WContainerWidgetuserList_
 
Wt::WSoundmessageReceived_
 

Detailed Description

A self-contained chat widget.

Definition at line 34 of file SimpleChatWidget.h.

Member Typedef Documentation

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

Definition at line 82 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.

28  : WContainerWidget(parent),
29  server_(server),
30  loggedIn_(false),
31  userList_(0),
33 {
35  letLogin();
36 }
SimpleChatWidget::~SimpleChatWidget ( )

Delete a chat widget.

Definition at line 38 of file SimpleChatWidget.C.

39 {
40  delete messageReceived_;
41  logout();
42  disconnect();
43 }

Member Function Documentation

void SimpleChatWidget::connect ( )

Definition at line 45 of file SimpleChatWidget.C.

46 {
47  if (server_.connect
48  (this, boost::bind(&SimpleChatWidget::processChatEvent, this, _1)))
50 }
void SimpleChatWidget::createLayout ( Wt::WWidget messages,
Wt::WWidget userList,
Wt::WWidget messageEdit,
Wt::WWidget sendButton,
Wt::WWidget logoutButton 
)
protectedvirtual

Reimplemented in PopupChatWidget.

Definition at line 108 of file SimpleChatWidget.C.

111 {
112  /*
113  * Create a vertical layout, which will hold 3 rows,
114  * organized like this:
115  *
116  * WVBoxLayout
117  * --------------------------------------------
118  * | nested WHBoxLayout (vertical stretch=1) |
119  * | | |
120  * | messages | userList |
121  * | (horizontal stretch=1) | |
122  * | | |
123  * --------------------------------------------
124  * | message edit area |
125  * --------------------------------------------
126  * | WHBoxLayout |
127  * | send | logout |
128  * --------------------------------------------
129  */
130  WVBoxLayout *vLayout = new WVBoxLayout();
131 
132  // Create a horizontal layout for the messages | userslist.
133  WHBoxLayout *hLayout = new WHBoxLayout();
134 
135  // Add widget to horizontal layout with stretch = 1
136  hLayout->addWidget(messages, 1);
137  messages->setStyleClass("chat-msgs");
138 
139  // Add another widget to horizontal layout with stretch = 0
140  hLayout->addWidget(userList);
141  userList->setStyleClass("chat-users");
142 
143  hLayout->setResizable(0, true);
144 
145  // Add nested layout to vertical layout with stretch = 1
146  vLayout->addLayout(hLayout, 1);
147 
148  // Add widget to vertical layout with stretch = 0
149  vLayout->addWidget(messageEdit);
150  messageEdit->setStyleClass("chat-noedit");
151 
152  // Create a horizontal layout for the buttons.
153  hLayout = new WHBoxLayout();
154 
155  // Add button to horizontal layout with stretch = 0
156  hLayout->addWidget(sendButton);
157 
158  // Add button to horizontal layout with stretch = 0
159  hLayout->addWidget(logoutButton);
160 
161  // Add nested layout to vertical layout with stretch = 0
162  vLayout->addLayout(hLayout, 0, AlignLeft);
163 
164  setLayout(vLayout);
165 }
void SimpleChatWidget::disconnect ( )

Definition at line 52 of file SimpleChatWidget.C.

53 {
54  if (server_.disconnect(this))
56 }
void SimpleChatWidget::letLogin ( )

Show a simple login screen.

Definition at line 58 of file SimpleChatWidget.C.

59 {
60  disconnect();
61 
62  clear();
63 
64  WVBoxLayout *vLayout = new WVBoxLayout();
65  setLayout(vLayout);
66 
67  WHBoxLayout *hLayout = new WHBoxLayout();
68  vLayout->addLayout(hLayout, 0, AlignTop | AlignLeft);
69 
70  hLayout->addWidget(new WLabel("User name:"), 0, AlignMiddle);
71  hLayout->addWidget(userNameEdit_ = new WLineEdit(user_), 0, AlignMiddle);
72  userNameEdit_->setFocus();
73 
74  WPushButton *b = new WPushButton("Login");
75  hLayout->addWidget(b, 0, AlignMiddle);
76 
77  b->clicked().connect(this, &SimpleChatWidget::login);
78  userNameEdit_->enterPressed().connect(this, &SimpleChatWidget::login);
79 
80  vLayout->addWidget(statusMsg_ = new WText());
81  statusMsg_->setTextFormat(PlainText);
82 }
bool SimpleChatWidget::loggedIn ( ) const
protected

Definition at line 167 of file SimpleChatWidget.C.

168 {
169  return loggedIn_;
170 }
void SimpleChatWidget::login ( )
private

Definition at line 84 of file SimpleChatWidget.C.

85 {
86  if (!loggedIn()) {
87  WString name = userNameEdit_->text();
88 
89  if (!messageReceived_)
90  messageReceived_ = new WSound("sounds/message_received.mp3");
91 
92  if (!startChat(name))
93  statusMsg_->setText("Sorry, name '" + escapeText(name) +
94  "' is already taken.");
95  }
96 }
void SimpleChatWidget::logout ( )

Definition at line 98 of file SimpleChatWidget.C.

99 {
100  if (loggedIn()) {
101  loggedIn_ = false;
103 
104  letLogin();
105  }
106 }
void SimpleChatWidget::newMessage ( )
protectedvirtual

Reimplemented in PopupChatWidget.

Definition at line 310 of file SimpleChatWidget.C.

311 { }
void SimpleChatWidget::processChatEvent ( const ChatEvent event)
private

Definition at line 319 of file SimpleChatWidget.C.

320 {
321  WApplication *app = WApplication::instance();
322 
323  /*
324  * This is where the "server-push" happens. The chat server posts to this
325  * event from other sessions, see SimpleChatServer::postChatEvent()
326  */
327 
328  /*
329  * Format and append the line to the conversation.
330  *
331  * This is also the step where the automatic XSS filtering will kick in:
332  * - if another user tried to pass on some JavaScript, it is filtered away.
333  * - if another user did not provide valid XHTML, the text is automatically
334  * interpreted as PlainText
335  */
336 
337  /*
338  * If it is not a plain message, also update the user list.
339  */
340  if (event.type() != ChatEvent::Message) {
341  if (event.type() == ChatEvent::Rename && event.user() == user_)
342  user_ = event.data();
343 
344  updateUsers();
345  }
346 
347  newMessage();
348 
349  /*
350  * Anything else doesn't matter if we are not logged in.
351  */
352  if (!loggedIn()) {
353  app->triggerUpdate();
354  return;
355  }
356 
357  bool display = event.type() != ChatEvent::Message
358  || !userList_
359  || (users_.find(event.user()) != users_.end() && users_[event.user()]);
360 
361  if (display) {
362  WText *w = new WText(messages_);
363 
364  /*
365  * If it fails, it is because the content wasn't valid XHTML
366  */
367  if (!w->setText(event.formattedHTML(user_, XHTMLText))) {
368  w->setText(event.formattedHTML(user_, PlainText));
369  w->setTextFormat(XHTMLText);
370  }
371 
372  w->setInline(false);
373  w->setStyleClass("chat-msg");
374 
375  /*
376  * Leave no more than 100 messages in the back-log
377  */
378  if (messages_->count() > 100)
379  delete messages_->children()[0];
380 
381  /*
382  * Little javascript trick to make sure we scroll along with new content
383  */
384  app->doJavaScript(messages_->jsRef() + ".scrollTop += "
385  + messages_->jsRef() + ".scrollHeight;");
386 
387  /* If this message belongs to another user, play a received sound */
388  if (event.user() != user_ && messageReceived_)
390  }
391 
392  /*
393  * This is the server push action: we propagate the updated UI to the client,
394  * (when the event was triggered by another user)
395  */
396  app->triggerUpdate();
397 }
void SimpleChatWidget::render ( Wt::WFlags< Wt::RenderFlag flags)
protectedvirtual

Definition at line 172 of file SimpleChatWidget.C.

173 {
174  if (flags & RenderFull) {
175  if (loggedIn()) {
176  /* Handle a page refresh correctly */
177  messageEdit_->setText(WString::Empty);
178  doJavaScript("setTimeout(function() { "
179  + messages_->jsRef() + ".scrollTop += "
180  + messages_->jsRef() + ".scrollHeight;}, 0);");
181  }
182  }
183 
184  WContainerWidget::render(flags);
185 }
void SimpleChatWidget::send ( )
private

Definition at line 274 of file SimpleChatWidget.C.

275 {
276  if (!messageEdit_->text().empty())
278 }
SimpleChatServer& SimpleChatWidget::server ( )
inline

Definition at line 62 of file SimpleChatWidget.h.

62 { return server_; }
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 187 of file SimpleChatWidget.C.

188 {
189  /*
190  * When logging in, we pass our processChatEvent method as the function that
191  * is used to indicate a new chat event for this user.
192  */
193  if (server_.login(user)) {
194  loggedIn_ = true;
195  connect();
196 
197  user_ = user;
198 
199  clear();
200  userNameEdit_ = 0;
201 
202  messages_ = new WContainerWidget();
203  userList_ = new WContainerWidget();
204  messageEdit_ = new WTextArea();
205  messageEdit_->setRows(2);
206  messageEdit_->setFocus();
207 
208  // Display scroll bars if contents overflows
209  messages_->setOverflow(WContainerWidget::OverflowAuto);
210  userList_->setOverflow(WContainerWidget::OverflowAuto);
211 
212  sendButton_ = new WPushButton("Send");
213  WPushButton *logoutButton = new WPushButton("Logout");
214 
216 
217  /*
218  * Connect event handlers:
219  * - click on button
220  * - enter in text area
221  *
222  * We will clear the input field using a small custom client-side
223  * JavaScript invocation.
224  */
225 
226  // Create a JavaScript 'slot' (JSlot). The JavaScript slot always takes
227  // 2 arguments: the originator of the event (in our case the
228  // button or text area), and the JavaScript event object.
230  ("function(o, e) { setTimeout(function() {"
231  "" + messageEdit_->jsRef() + ".value='';"
232  "}, 0); }");
233 
234  // Bind the C++ and JavaScript event handlers.
235  sendButton_->clicked().connect(this, &SimpleChatWidget::send);
236  messageEdit_->enterPressed().connect(this, &SimpleChatWidget::send);
237  sendButton_->clicked().connect(clearInput_);
238  messageEdit_->enterPressed().connect(clearInput_);
239  sendButton_->clicked().connect(messageEdit_, &WLineEdit::setFocus);
240  messageEdit_->enterPressed().connect(messageEdit_, &WLineEdit::setFocus);
241 
242  // Prevent the enter from generating a new line, which is its default
243  // action
244  messageEdit_->enterPressed().preventDefaultAction();
245 
246  logoutButton->clicked().connect(this, &SimpleChatWidget::logout);
247 
248  WText *msg = new WText
249  ("<div><span class='chat-info'>You are joining as "
250  + escapeText(user_) + ".</span></div>",
251  messages_);
252  msg->setStyleClass("chat-msg");
253 
254  if (!userList_->parent()) {
255  delete userList_;
256  userList_ = 0;
257  }
258 
259  if (!sendButton_->parent()) {
260  delete sendButton_;
261  sendButton_ = 0;
262  }
263 
264  if (!logoutButton->parent())
265  delete logoutButton;
266 
267  updateUsers();
268 
269  return true;
270  } else
271  return false;
272 }
void SimpleChatWidget::updateUser ( )
private

Definition at line 313 of file SimpleChatWidget.C.

314 {
315  WCheckBox *b = dynamic_cast<WCheckBox *>(sender());
316  users_[b->text()] = b->isChecked();
317 }
void SimpleChatWidget::updateUsers ( )
protectedvirtual

Reimplemented in PopupChatWidget.

Definition at line 280 of file SimpleChatWidget.C.

281 {
282  if (userList_) {
283  userList_->clear();
284 
286 
287  UserMap oldUsers = users_;
288  users_.clear();
289 
290  for (SimpleChatServer::UserSet::iterator i = users.begin();
291  i != users.end(); ++i) {
292  WCheckBox *w = new WCheckBox(escapeText(*i), userList_);
293  w->setInline(false);
294 
295  UserMap::const_iterator j = oldUsers.find(*i);
296  if (j != oldUsers.end())
297  w->setChecked(j->second);
298  else
299  w->setChecked(true);
300 
301  users_[*i] = w->isChecked();
302  w->changed().connect(this, &SimpleChatWidget::updateUser);
303 
304  if (*i == user_)
305  w->setStyleClass("chat-self");
306  }
307  }
308 }
int SimpleChatWidget::userCount ( )
inline

Definition at line 64 of file SimpleChatWidget.h.

64 { return users_.size(); }
const Wt::WString& SimpleChatWidget::userName ( ) const
inline

Definition at line 66 of file SimpleChatWidget.h.

66 { return user_; }

Member Data Documentation

Wt::JSlot SimpleChatWidget::clearInput_
private

Definition at line 88 of file SimpleChatWidget.h.

bool SimpleChatWidget::loggedIn_
private

Definition at line 86 of file SimpleChatWidget.h.

Wt::WTextArea* SimpleChatWidget::messageEdit_
private

Definition at line 97 of file SimpleChatWidget.h.

Wt::WContainerWidget* SimpleChatWidget::messageEditArea_
private

Definition at line 96 of file SimpleChatWidget.h.

Wt::WSound* SimpleChatWidget::messageReceived_
private

Definition at line 101 of file SimpleChatWidget.h.

Wt::WContainerWidget* SimpleChatWidget::messages_
private

Definition at line 95 of file SimpleChatWidget.h.

Wt::WPushButton* SimpleChatWidget::sendButton_
private

Definition at line 98 of file SimpleChatWidget.h.

SimpleChatServer& SimpleChatWidget::server_
private

Definition at line 85 of file SimpleChatWidget.h.

Wt::WText* SimpleChatWidget::statusMsg_
private

Definition at line 93 of file SimpleChatWidget.h.

Wt::WString SimpleChatWidget::user_
private

Definition at line 90 of file SimpleChatWidget.h.

Wt::WContainerWidget* SimpleChatWidget::userList_
private

Definition at line 99 of file SimpleChatWidget.h.

Wt::WLineEdit* SimpleChatWidget::userNameEdit_
private

Definition at line 92 of file SimpleChatWidget.h.

UserMap SimpleChatWidget::users_
private

Definition at line 83 of file SimpleChatWidget.h.


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

Generated on Fri May 31 2013 for the C++ Web Toolkit (Wt) by doxygen 1.8.3.1