Wt examples  3.3.6
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::WString & userName () 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 changeName (const Wt::WString &name)
 
void send ()
 
void updateUser ()
 
void processChatEvent (const ChatEvent &event)
 

Private Attributes

UserMap users_
 
SimpleChatServerserver_
 
bool loggedIn_
 
Wt::JSlot clearInput_
 
Wt::WString user_
 
Wt::WLineEdit * userNameEdit_
 
Wt::WText * statusMsg_
 
Wt::WContainerWidget * messages_
 
Wt::WTextArea * messageEdit_
 
Wt::WPushButton * sendButton_
 
Wt::WContainerWidget * userList_
 
Wt::WSound * messageReceived_
 

Detailed Description

A self-contained chat widget.

Definition at line 34 of file SimpleChatWidget.h.

Member Typedef Documentation

§ UserMap

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

Definition at line 82 of file SimpleChatWidget.h.

Constructor & Destructor Documentation

§ SimpleChatWidget()

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

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

Definition at line 28 of file SimpleChatWidget.C.

30  : WContainerWidget(parent),
31  server_(server),
32  loggedIn_(false),
33  userList_(0),
35 {
37  letLogin();
38 }
SimpleChatServer & server_
Wt::WContainerWidget * userList_
Wt::WString suggestGuest()
Get a suggestion for a guest user name.
void letLogin()
Show a simple login screen.
Wt::WSound * messageReceived_

§ ~SimpleChatWidget()

SimpleChatWidget::~SimpleChatWidget ( )

Delete a chat widget.

Definition at line 40 of file SimpleChatWidget.C.

41 {
42  delete messageReceived_;
43  logout();
44 }
Wt::WSound * messageReceived_

Member Function Documentation

§ changeName()

void SimpleChatWidget::changeName ( const Wt::WString &  name)
private

Definition at line 305 of file SimpleChatWidget.C.

306 {
307  if (!name.empty()) {
308  if (server_.changeName(user_, name))
309  user_ = name;
310  }
311 }
SimpleChatServer & server_
bool changeName(const Wt::WString &user, const Wt::WString &newUser)
Changes the name.

§ connect()

void SimpleChatWidget::connect ( )

Definition at line 46 of file SimpleChatWidget.C.

47 {
48  if (server_.connect
49  (this, boost::bind(&SimpleChatWidget::processChatEvent, this, _1)))
50  Wt::WApplication::instance()->enableUpdates(true);
51 }
SimpleChatServer & server_
bool connect(Client *client, const ChatEventCallback &handleEvent)
Connects to the chat server.
void processChatEvent(const ChatEvent &event)

§ createLayout()

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 }

§ disconnect()

void SimpleChatWidget::disconnect ( )

Definition at line 53 of file SimpleChatWidget.C.

54 {
55  if (server_.disconnect(this))
56  Wt::WApplication::instance()->enableUpdates(false);
57 }
SimpleChatServer & server_
bool disconnect(Client *client)
Disconnect from the chat server.

§ letLogin()

void SimpleChatWidget::letLogin ( )

Show a simple login screen.

Definition at line 59 of file SimpleChatWidget.C.

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

§ loggedIn()

bool SimpleChatWidget::loggedIn ( ) const
protected

Definition at line 167 of file SimpleChatWidget.C.

168 {
169  return loggedIn_;
170 }

§ login()

void SimpleChatWidget::login ( )
private

Definition at line 83 of file SimpleChatWidget.C.

84 {
85  if (!loggedIn()) {
86  WString name = userNameEdit_->text();
87 
88  if (!messageReceived_)
89  messageReceived_ = new WSound("sounds/message_received.mp3");
90 
91  if (!startChat(name))
92  statusMsg_->setText("Sorry, name '" + escapeText(name) +
93  "' is already taken.");
94  }
95 }
Wt::WText * statusMsg_
bool startChat(const Wt::WString &user)
Start a chat for the given user.
Wt::WLineEdit * userNameEdit_
bool loggedIn() const
Wt::WSound * messageReceived_

§ logout()

void SimpleChatWidget::logout ( )

Definition at line 97 of file SimpleChatWidget.C.

98 {
99  if (loggedIn()) {
100  loggedIn_ = false;
102  disconnect();
103 
104  letLogin();
105  }
106 }
SimpleChatServer & server_
void logout(const Wt::WString &user)
Logout from the server.
void letLogin()
Show a simple login screen.
bool loggedIn() const

§ newMessage()

void SimpleChatWidget::newMessage ( )
protectedvirtual

Reimplemented in PopupChatWidget.

Definition at line 349 of file SimpleChatWidget.C.

350 { }

§ processChatEvent()

void SimpleChatWidget::processChatEvent ( const ChatEvent event)
private

Definition at line 358 of file SimpleChatWidget.C.

359 {
360  WApplication *app = WApplication::instance();
361 
362  /*
363  * This is where the "server-push" happens. The chat server posts to this
364  * event from other sessions, see SimpleChatServer::postChatEvent()
365  */
366 
367  /*
368  * Format and append the line to the conversation.
369  *
370  * This is also the step where the automatic XSS filtering will kick in:
371  * - if another user tried to pass on some JavaScript, it is filtered away.
372  * - if another user did not provide valid XHTML, the text is automatically
373  * interpreted as PlainText
374  */
375 
376  /*
377  * If it is not a plain message, also update the user list.
378  */
379  if (event.type() != ChatEvent::Message) {
380  if (event.type() == ChatEvent::Rename && event.user() == user_)
381  user_ = event.data();
382 
383  updateUsers();
384  }
385 
386  /*
387  * This is the server call: we (schedule to) propagate the updated UI to
388  * the client.
389  *
390  * This schedules an update and returns immediately
391  */
392  app->triggerUpdate();
393 
394  newMessage();
395 
396  /*
397  * Anything else doesn't matter if we are not logged in.
398  */
399  if (!loggedIn())
400  return;
401 
402  bool display = event.type() != ChatEvent::Message
403  || !userList_
404  || (users_.find(event.user()) != users_.end() && users_[event.user()]);
405 
406  if (display) {
407  WText *w = new WText(messages_);
408 
409  /*
410  * If it fails, it is because the content wasn't valid XHTML
411  */
412  if (!w->setText(event.formattedHTML(user_, XHTMLText))) {
413  w->setText(event.formattedHTML(user_, PlainText));
414  w->setTextFormat(XHTMLText);
415  }
416 
417  w->setInline(false);
418  w->setStyleClass("chat-msg");
419 
420  /*
421  * Leave no more than 100 messages in the back-log
422  */
423  if (messages_->count() > 100)
424  delete messages_->children()[0];
425 
426  /*
427  * Little javascript trick to make sure we scroll along with new content
428  */
429  app->doJavaScript(messages_->jsRef() + ".scrollTop += "
430  + messages_->jsRef() + ".scrollHeight;");
431 
432  /* If this message belongs to another user, play a received sound */
433  if (event.user() != user_ && messageReceived_)
434  messageReceived_->play();
435  }
436 }
Type type() const
Get the event type.
Wt::WContainerWidget * messages_
Wt::WContainerWidget * userList_
const Wt::WString & data() const
Get the extra data for this event.
virtual void updateUsers()
const Wt::WString formattedHTML(const Wt::WString &user, Wt::TextFormat format) const
Get the message formatted as HTML, rendered for the given user.
const Wt::WString & user() const
Get the user who caused the event.
bool loggedIn() const
Wt::WSound * messageReceived_
virtual void newMessage()

§ render()

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 }
Wt::WContainerWidget * messages_
Wt::WTextArea * messageEdit_
bool loggedIn() const

§ send()

void SimpleChatWidget::send ( )
private

Definition at line 313 of file SimpleChatWidget.C.

314 {
315  if (!messageEdit_->text().empty())
317 }
SimpleChatServer & server_
Wt::WTextArea * messageEdit_
void sendMessage(const Wt::WString &user, const Wt::WString &message)
Send a message on behalve of a user.

§ server()

SimpleChatServer& SimpleChatWidget::server ( )
inline

Definition at line 62 of file SimpleChatWidget.h.

62 { return server_; }
SimpleChatServer & server_

§ startChat()

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  /*
219  * Connect event handlers:
220  * - click on button
221  * - enter in text area
222  *
223  * We will clear the input field using a small custom client-side
224  * JavaScript invocation.
225  */
226 
227  // Create a JavaScript 'slot' (JSlot). The JavaScript slot always takes
228  // 2 arguments: the originator of the event (in our case the
229  // button or text area), and the JavaScript event object.
230  clearInput_.setJavaScript
231  ("function(o, e) { setTimeout(function() {"
232  "" + messageEdit_->jsRef() + ".value='';"
233  "}, 0); }");
234 
235  /*
236  * Set the connection monitor
237  *
238  * The connection monitor is a javascript monitor that will
239  * nootify the given object by calling the onChange method to
240  * inform of connection change (use of websockets, connection
241  * online/offline) Here we just disable the TextEdit when we are
242  * offline and enable it once we're back online
243  */
244  WApplication::instance()->setConnectionMonitor(
245  "window.monitor={ "
246  "'onChange':function(type, newV) {"
247  "var connected = window.monitor.status.connectionStatus != 0;"
248  "if(connected) {"
249  + messageEdit_->jsRef() + ".disabled=false;"
250  + messageEdit_->jsRef() + ".placeholder='';"
251  "} else { "
252  + messageEdit_->jsRef() + ".disabled=true;"
253  + messageEdit_->jsRef() + ".placeholder='connection lost';"
254  "}"
255  "}"
256  "}"
257  );
258 
259  // Bind the C++ and JavaScript event handlers.
260  sendButton_->clicked().connect(this, &SimpleChatWidget::send);
261  messageEdit_->enterPressed().connect(this, &SimpleChatWidget::send);
262  sendButton_->clicked().connect(clearInput_);
263  messageEdit_->enterPressed().connect(clearInput_);
264  sendButton_->clicked().connect((WWidget *)messageEdit_,
265  &WWidget::setFocus);
266  messageEdit_->enterPressed().connect((WWidget *)messageEdit_,
267  &WWidget::setFocus);
268 
269  // Prevent the enter from generating a new line, which is its default
270  // action
271  messageEdit_->enterPressed().preventDefaultAction();
272 
273  logoutButton->clicked().connect(this, &SimpleChatWidget::logout);
274 
275  WInPlaceEdit *nameEdit = new WInPlaceEdit();
276  nameEdit->addStyleClass("name-edit");
277  nameEdit->setButtonsEnabled(false);
278  nameEdit->setText(user_);
279  nameEdit->valueChanged().connect(this, &SimpleChatWidget::changeName);
280 
281  WTemplate *joinMsg = new WTemplate(tr("join-msg.template"), messages_);
282  joinMsg->bindWidget("name", nameEdit);
283  joinMsg->setStyleClass("chat-msg");
284 
285  if (!userList_->parent()) {
286  delete userList_;
287  userList_ = 0;
288  }
289 
290  if (!sendButton_->parent()) {
291  delete sendButton_;
292  sendButton_ = 0;
293  }
294 
295  if (!logoutButton->parent())
296  delete logoutButton;
297 
298  updateUsers();
299 
300  return true;
301  } else
302  return false;
303 }
Wt::WContainerWidget * messages_
SimpleChatServer & server_
Wt::WContainerWidget * userList_
Wt::WTextArea * messageEdit_
Wt::WPushButton * sendButton_
virtual void createLayout(Wt::WWidget *messages, Wt::WWidget *userList, Wt::WWidget *messageEdit, Wt::WWidget *sendButton, Wt::WWidget *logoutButton)
void changeName(const Wt::WString &name)
virtual void updateUsers()
Wt::WLineEdit * userNameEdit_
bool login(const Wt::WString &user)
Try to login with given user name.

§ updateUser()

void SimpleChatWidget::updateUser ( )
private

Definition at line 352 of file SimpleChatWidget.C.

353 {
354  WCheckBox *b = dynamic_cast<WCheckBox *>(sender());
355  users_[b->text()] = b->isChecked();
356 }

§ updateUsers()

void SimpleChatWidget::updateUsers ( )
protectedvirtual

Reimplemented in PopupChatWidget.

Definition at line 319 of file SimpleChatWidget.C.

320 {
321  if (userList_) {
322  userList_->clear();
323 
325 
326  UserMap oldUsers = users_;
327  users_.clear();
328 
329  for (SimpleChatServer::UserSet::iterator i = users.begin();
330  i != users.end(); ++i) {
331  WCheckBox *w = new WCheckBox(escapeText(*i), userList_);
332  w->setInline(false);
333 
334  UserMap::const_iterator j = oldUsers.find(*i);
335  if (j != oldUsers.end())
336  w->setChecked(j->second);
337  else
338  w->setChecked(true);
339 
340  users_[*i] = w->isChecked();
341  w->changed().connect(this, &SimpleChatWidget::updateUser);
342 
343  if (*i == user_)
344  w->setStyleClass("chat-self");
345  }
346  }
347 }
SimpleChatServer & server_
Wt::WContainerWidget * userList_
std::map< Wt::WString, bool > UserMap
UserSet users()
Get the users currently logged in.
std::set< Wt::WString > UserSet
Typedef for a collection of user names.

§ userCount()

int SimpleChatWidget::userCount ( )
inline

Definition at line 64 of file SimpleChatWidget.h.

64 { return users_.size(); }

§ userName()

const Wt::WString& SimpleChatWidget::userName ( ) const
inline

Definition at line 66 of file SimpleChatWidget.h.

66 { return user_; }

Member Data Documentation

§ clearInput_

Wt::JSlot SimpleChatWidget::clearInput_
private

Definition at line 88 of file SimpleChatWidget.h.

§ loggedIn_

bool SimpleChatWidget::loggedIn_
private

Definition at line 86 of file SimpleChatWidget.h.

§ messageEdit_

Wt::WTextArea* SimpleChatWidget::messageEdit_
private

Definition at line 96 of file SimpleChatWidget.h.

§ messageReceived_

Wt::WSound* SimpleChatWidget::messageReceived_
private

Definition at line 100 of file SimpleChatWidget.h.

§ messages_

Wt::WContainerWidget* SimpleChatWidget::messages_
private

Definition at line 95 of file SimpleChatWidget.h.

§ sendButton_

Wt::WPushButton* SimpleChatWidget::sendButton_
private

Definition at line 97 of file SimpleChatWidget.h.

§ server_

SimpleChatServer& SimpleChatWidget::server_
private

Definition at line 85 of file SimpleChatWidget.h.

§ statusMsg_

Wt::WText* SimpleChatWidget::statusMsg_
private

Definition at line 93 of file SimpleChatWidget.h.

§ user_

Wt::WString SimpleChatWidget::user_
private

Definition at line 90 of file SimpleChatWidget.h.

§ userList_

Wt::WContainerWidget* SimpleChatWidget::userList_
private

Definition at line 98 of file SimpleChatWidget.h.

§ userNameEdit_

Wt::WLineEdit* SimpleChatWidget::userNameEdit_
private

Definition at line 92 of file SimpleChatWidget.h.

§ users_

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 Thu Jan 12 2017 for the C++ Web Toolkit (Wt) by doxygen 1.8.12