Wt examples
3.2.1
Main Page
Modules
Namespaces
Classes
Files
File List
File Members
build
witty-zvyU2z
witty-3.2.1
examples
simplechat
SimpleChatWidget.C
Go to the documentation of this file.
1
/*
2
* Copyright (C) 2008 Emweb bvba, Heverlee, Belgium.
3
*
4
* See the LICENSE file for terms of use.
5
*/
6
7
#include "
SimpleChatWidget.h
"
8
#include "
SimpleChatServer.h
"
9
10
#include <Wt/WApplication>
11
#include <Wt/WContainerWidget>
12
#include <Wt/WEnvironment>
13
#include <Wt/WHBoxLayout>
14
#include <Wt/WVBoxLayout>
15
#include <Wt/WLabel>
16
#include <Wt/WLineEdit>
17
#include <Wt/WText>
18
#include <Wt/WTextArea>
19
#include <Wt/WPushButton>
20
#include <Wt/WCheckBox>
21
22
#include <iostream>
23
24
using namespace
Wt;
25
26
SimpleChatWidget::SimpleChatWidget
(
SimpleChatServer
& server,
27
Wt::WContainerWidget
*parent)
28
:
WContainerWidget
(parent),
29
server_(server),
30
loggedIn_(false),
31
userList_(0),
32
messageReceived_(0)
33
{
34
user_
=
server_
.
suggestGuest
();
35
letLogin
();
36
}
37
38
SimpleChatWidget::~SimpleChatWidget
()
39
{
40
delete
messageReceived_
;
41
logout
();
42
disconnect
();
43
}
44
45
void
SimpleChatWidget::connect
()
46
{
47
if
(
server_
.
connect
48
(
this
, boost::bind(&
SimpleChatWidget::processChatEvent
,
this
, _1)))
49
Wt::WApplication::instance
()->
enableUpdates
(
true
);
50
}
51
52
void
SimpleChatWidget::disconnect
()
53
{
54
if
(
server_
.
disconnect
(
this
))
55
Wt::WApplication::instance
()->
enableUpdates
(
false
);
56
}
57
58
void
SimpleChatWidget::letLogin
()
59
{
60
disconnect
();
61
62
clear
();
63
64
WVBoxLayout
*vLayout =
new
WVBoxLayout
();
65
setLayout
(vLayout,
AlignLeft
|
AlignTop
);
66
67
WHBoxLayout
*hLayout =
new
WHBoxLayout
();
68
vLayout->
addLayout
(hLayout);
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
}
83
84
void
SimpleChatWidget::login
()
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
}
97
98
void
SimpleChatWidget::logout
()
99
{
100
if
(
loggedIn
()) {
101
loggedIn_
=
false
;
102
server_
.
logout
(
user_
);
103
104
letLogin
();
105
}
106
}
107
108
void
SimpleChatWidget::createLayout
(
WWidget
*messages,
WWidget
*userList,
109
WWidget
*messageEdit,
110
WWidget
*sendButton,
WWidget
*logoutButton)
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
|
AlignTop
);
163
164
setLayout
(vLayout);
165
}
166
167
bool
SimpleChatWidget::loggedIn
()
const
168
{
169
return
loggedIn_
;
170
}
171
172
void
SimpleChatWidget::render
(WFlags<RenderFlag> flags)
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
}
186
187
bool
SimpleChatWidget::startChat
(
const
WString
& user)
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
215
createLayout
(
messages_
,
userList_
,
messageEdit_
,
sendButton_
, logoutButton);
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.
229
clearInput_
.
setJavaScript
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
}
273
274
void
SimpleChatWidget::send
()
275
{
276
if
(!
messageEdit_
->
text
().
empty
())
277
server_
.
sendMessage
(
user_
,
messageEdit_
->
text
());
278
}
279
280
void
SimpleChatWidget::updateUsers
()
281
{
282
if
(
userList_
) {
283
userList_
->
clear
();
284
285
SimpleChatServer::UserSet
users =
server_
.
users
();
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
}
309
310
void
SimpleChatWidget::newMessage
()
311
{ }
312
313
void
SimpleChatWidget::updateUser
()
314
{
315
WCheckBox
*b =
dynamic_cast<
WCheckBox
*
>
(
sender
());
316
users_
[b->
text
()] = b->
isChecked
();
317
}
318
319
void
SimpleChatWidget::processChatEvent
(
const
ChatEvent
& event)
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_
)
389
messageReceived_
->
play
();
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
}
Generated on Wed Aug 27 2014 for
the C++ Web Toolkit (Wt)
by
1.8.1.2