| Wt examples
    3.3.0
    | 
A self-contained chat widget. More...
#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 | connect () | 
| void | disconnect () | 
| void | letLogin () | 
| Show a simple login screen. | |
| bool | startChat (const Wt::WString &user) | 
| Start a chat for the given user. | |
| void | logout () | 
| SimpleChatServer & | server () | 
| 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 | send () | 
| void | updateUser () | 
| void | processChatEvent (const ChatEvent &event) | 
| Private Attributes | |
| UserMap | users_ | 
| SimpleChatServer & | server_ | 
| bool | loggedIn_ | 
| 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_ | 
| Wt::WSound * | messageReceived_ | 
A self-contained chat widget.
Definition at line 34 of file SimpleChatWidget.h.
| typedef std::map<Wt::WString, bool> SimpleChatWidget::UserMap  [private] | 
Definition at line 82 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.
: WContainerWidget(parent), server_(server), loggedIn_(false), userList_(0), messageReceived_(0) { user_ = server_.suggestGuest(); letLogin(); }
| SimpleChatWidget::~SimpleChatWidget | ( | ) | 
Delete a chat widget.
Definition at line 38 of file SimpleChatWidget.C.
{
  delete messageReceived_;
  logout();
  disconnect();
}
| void SimpleChatWidget::connect | ( | ) | 
Definition at line 45 of file SimpleChatWidget.C.
{
  if (server_.connect
      (this, boost::bind(&SimpleChatWidget::processChatEvent, this, _1)))
    Wt::WApplication::instance()->enableUpdates(true);
}
| void SimpleChatWidget::createLayout | ( | Wt::WWidget * | messages, | 
| Wt::WWidget * | userList, | ||
| Wt::WWidget * | messageEdit, | ||
| Wt::WWidget * | sendButton, | ||
| Wt::WWidget * | logoutButton | ||
| ) |  [protected, virtual] | 
Reimplemented in PopupChatWidget.
Definition at line 108 of file SimpleChatWidget.C.
{
  /*
   * Create a vertical layout, which will hold 3 rows,
   * organized like this:
   *
   * WVBoxLayout
   * --------------------------------------------
   * | nested WHBoxLayout (vertical stretch=1)  |
   * |                              |           |
   * |  messages                    | userList  |
   * |   (horizontal stretch=1)     |           |
   * |                              |           |
   * --------------------------------------------
   * | message edit area                        |
   * --------------------------------------------
   * | WHBoxLayout                              |
   * | send | logout                            |
   * --------------------------------------------
   */
  WVBoxLayout *vLayout = new WVBoxLayout();
  // Create a horizontal layout for the messages | userslist.
  WHBoxLayout *hLayout = new WHBoxLayout();
  // Add widget to horizontal layout with stretch = 1
  hLayout->addWidget(messages, 1);
  messages->setStyleClass("chat-msgs");
    // Add another widget to horizontal layout with stretch = 0
  hLayout->addWidget(userList);
  userList->setStyleClass("chat-users");
  hLayout->setResizable(0, true);
  // Add nested layout to vertical layout with stretch = 1
  vLayout->addLayout(hLayout, 1);
  // Add widget to vertical layout with stretch = 0
  vLayout->addWidget(messageEdit);
  messageEdit->setStyleClass("chat-noedit");
  // Create a horizontal layout for the buttons.
  hLayout = new WHBoxLayout();
  // Add button to horizontal layout with stretch = 0
  hLayout->addWidget(sendButton);
  // Add button to horizontal layout with stretch = 0
  hLayout->addWidget(logoutButton);
  // Add nested layout to vertical layout with stretch = 0
  vLayout->addLayout(hLayout, 0, AlignLeft);
  setLayout(vLayout);
}
| void SimpleChatWidget::disconnect | ( | ) | 
Definition at line 52 of file SimpleChatWidget.C.
{
  if (server_.disconnect(this))
    Wt::WApplication::instance()->enableUpdates(false);
}
| void SimpleChatWidget::letLogin | ( | ) | 
Show a simple login screen.
Definition at line 58 of file SimpleChatWidget.C.
{
  disconnect();
  clear();
  WVBoxLayout *vLayout = new WVBoxLayout();
  setLayout(vLayout);
  WHBoxLayout *hLayout = new WHBoxLayout();
  vLayout->addLayout(hLayout, 0, AlignTop | AlignLeft);
  hLayout->addWidget(new WLabel("User name:"), 0, AlignMiddle);
  hLayout->addWidget(userNameEdit_ = new WLineEdit(user_), 0, AlignMiddle);
  userNameEdit_->setFocus();
  WPushButton *b = new WPushButton("Login");
  hLayout->addWidget(b, 0, AlignMiddle);
  b->clicked().connect(this, &SimpleChatWidget::login);
  userNameEdit_->enterPressed().connect(this, &SimpleChatWidget::login);
  vLayout->addWidget(statusMsg_ = new WText());
  statusMsg_->setTextFormat(PlainText);
}
| bool SimpleChatWidget::loggedIn | ( | ) | const  [protected] | 
Definition at line 167 of file SimpleChatWidget.C.
{
  return loggedIn_;
}
| void SimpleChatWidget::login | ( | ) |  [private] | 
Definition at line 84 of file SimpleChatWidget.C.
{
  if (!loggedIn()) {
    WString name = userNameEdit_->text();
    if (!messageReceived_)
      messageReceived_ = new WSound("sounds/message_received.mp3");
    if (!startChat(name))
      statusMsg_->setText("Sorry, name '" + escapeText(name) +
                          "' is already taken.");
  }
}
| void SimpleChatWidget::logout | ( | ) | 
| void SimpleChatWidget::newMessage | ( | ) |  [protected, virtual] | 
| void SimpleChatWidget::processChatEvent | ( | const ChatEvent & | event | ) |  [private] | 
Definition at line 319 of file SimpleChatWidget.C.
{
  WApplication *app = WApplication::instance();
  /*
   * This is where the "server-push" happens. The chat server posts to this
   * event from other sessions, see SimpleChatServer::postChatEvent()
   */
  /*
   * Format and append the line to the conversation.
   *
   * This is also the step where the automatic XSS filtering will kick in:
   * - if another user tried to pass on some JavaScript, it is filtered away.
   * - if another user did not provide valid XHTML, the text is automatically
   *   interpreted as PlainText
   */
  /*
   * If it is not a plain message, also update the user list.
   */
  if (event.type() != ChatEvent::Message) {
    if (event.type() == ChatEvent::Rename && event.user() == user_)
      user_ = event.data();
    updateUsers();
  }
  newMessage();
  /*
   * Anything else doesn't matter if we are not logged in.
   */
  if (!loggedIn()) {
    app->triggerUpdate();
    return;
  }
  bool display = event.type() != ChatEvent::Message
    || !userList_
    || (users_.find(event.user()) != users_.end() && users_[event.user()]);
  if (display) {
    WText *w = new WText(messages_);
    /*
     * If it fails, it is because the content wasn't valid XHTML
     */
    if (!w->setText(event.formattedHTML(user_, XHTMLText))) {
      w->setText(event.formattedHTML(user_, PlainText));
      w->setTextFormat(XHTMLText);
    }
    w->setInline(false);
    w->setStyleClass("chat-msg");
    /*
     * Leave no more than 100 messages in the back-log
     */
    if (messages_->count() > 100)
      delete messages_->children()[0];
    /*
     * Little javascript trick to make sure we scroll along with new content
     */
    app->doJavaScript(messages_->jsRef() + ".scrollTop += "
                       + messages_->jsRef() + ".scrollHeight;");
    /* If this message belongs to another user, play a received sound */
    if (event.user() != user_ && messageReceived_)
      messageReceived_->play();
  }
  /*
   * This is the server push action: we propagate the updated UI to the client,
   * (when the event was triggered by another user)
   */
  app->triggerUpdate();
}
| void SimpleChatWidget::render | ( | Wt::WFlags< Wt::RenderFlag > | flags | ) |  [protected, virtual] | 
Definition at line 172 of file SimpleChatWidget.C.
{
  if (flags & RenderFull) {
    if (loggedIn()) {
      /* Handle a page refresh correctly */
      messageEdit_->setText(WString::Empty);
      doJavaScript("setTimeout(function() { "
                   + messages_->jsRef() + ".scrollTop += "
                   + messages_->jsRef() + ".scrollHeight;}, 0);");
    }
  }
  WContainerWidget::render(flags);
}
| void SimpleChatWidget::send | ( | ) |  [private] | 
Definition at line 274 of file SimpleChatWidget.C.
{
  if (!messageEdit_->text().empty())
    server_.sendMessage(user_, messageEdit_->text());
}
| SimpleChatServer& SimpleChatWidget::server | ( | ) |  [inline] | 
Definition at line 62 of file SimpleChatWidget.h.
{ 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.
{
  /*
   * When logging in, we pass our processChatEvent method as the function that
   * is used to indicate a new chat event for this user.
   */
  if (server_.login(user)) {
    loggedIn_ = true;
    connect();
    user_ = user;    
    clear();
    userNameEdit_ = 0;
    messages_ = new WContainerWidget();
    userList_ = new WContainerWidget();
    messageEdit_ = new WTextArea();
    messageEdit_->setRows(2);
    messageEdit_->setFocus();
    // Display scroll bars if contents overflows
    messages_->setOverflow(WContainerWidget::OverflowAuto);
    userList_->setOverflow(WContainerWidget::OverflowAuto);
    sendButton_ = new WPushButton("Send");
    WPushButton *logoutButton = new WPushButton("Logout");
    createLayout(messages_, userList_, messageEdit_, sendButton_, logoutButton);
    /*
     * Connect event handlers:
     *  - click on button
     *  - enter in text area
     *
     * We will clear the input field using a small custom client-side
     * JavaScript invocation.
     */
    // Create a JavaScript 'slot' (JSlot). The JavaScript slot always takes
    // 2 arguments: the originator of the event (in our case the
    // button or text area), and the JavaScript event object.
    clearInput_.setJavaScript
      ("function(o, e) { setTimeout(function() {"
       "" + messageEdit_->jsRef() + ".value='';"
       "}, 0); }");
    // Bind the C++ and JavaScript event handlers.
    sendButton_->clicked().connect(this, &SimpleChatWidget::send);
    messageEdit_->enterPressed().connect(this, &SimpleChatWidget::send);
    sendButton_->clicked().connect(clearInput_);
    messageEdit_->enterPressed().connect(clearInput_);
    sendButton_->clicked().connect(messageEdit_, &WLineEdit::setFocus);
    messageEdit_->enterPressed().connect(messageEdit_, &WLineEdit::setFocus);
    // Prevent the enter from generating a new line, which is its default
    // action
    messageEdit_->enterPressed().preventDefaultAction();
    logoutButton->clicked().connect(this, &SimpleChatWidget::logout);
    WText *msg = new WText
      ("<div><span class='chat-info'>You are joining as "
       + escapeText(user_) + ".</span></div>",
       messages_);
    msg->setStyleClass("chat-msg");
    if (!userList_->parent()) {
      delete userList_;
      userList_ = 0;
    }
    if (!sendButton_->parent()) {
      delete sendButton_;
      sendButton_ = 0;
    }
    if (!logoutButton->parent())
      delete logoutButton;
    updateUsers();
    
    return true;
  } else
    return false;
}
| void SimpleChatWidget::updateUser | ( | ) |  [private] | 
| void SimpleChatWidget::updateUsers | ( | ) |  [protected, virtual] | 
Reimplemented in PopupChatWidget.
Definition at line 280 of file SimpleChatWidget.C.
{
  if (userList_) {
    userList_->clear();
    SimpleChatServer::UserSet users = server_.users();
    UserMap oldUsers = users_;
    users_.clear();
    for (SimpleChatServer::UserSet::iterator i = users.begin();
         i != users.end(); ++i) {
      WCheckBox *w = new WCheckBox(escapeText(*i), userList_);
      w->setInline(false);
      UserMap::const_iterator j = oldUsers.find(*i);
      if (j != oldUsers.end())
        w->setChecked(j->second);
      else
        w->setChecked(true);
      users_[*i] = w->isChecked();
      w->changed().connect(this, &SimpleChatWidget::updateUser);
      if (*i == user_)
        w->setStyleClass("chat-self");
    }
  }
}
| int SimpleChatWidget::userCount | ( | ) |  [inline] | 
Definition at line 64 of file SimpleChatWidget.h.
{ return users_.size(); }
| const Wt::WString& SimpleChatWidget::userName | ( | ) | const  [inline] | 
Definition at line 66 of file SimpleChatWidget.h.
{ return user_; }
| 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.
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.
 1.7.5.1
 1.7.5.1