GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/tabs/chat/chattab.cpp Lines: 25 244 10.2 %
Date: 2017-11-29 Branches: 21 424 5.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2008-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "gui/widgets/tabs/chat/chattab.h"
24
25
#include "chatlogger.h"
26
#include "configuration.h"
27
#include "settings.h"
28
#include "soundmanager.h"
29
30
#include "being/localplayer.h"
31
32
#include "const/sound.h"
33
34
#include "gui/chatlog.h"
35
#include "gui/windowmanager.h"
36
37
#include "gui/windows/chatwindow.h"
38
#include "gui/windows/helpwindow.h"
39
40
#include "gui/widgets/scrollarea.h"
41
#include "gui/widgets/itemlinkhandler.h"
42
#include "gui/widgets/tabbedarea.h"
43
44
#include "input/inputmanager.h"
45
46
#include "net/chathandler.h"
47
#include "net/net.h"
48
49
#include "utils/chatutils.h"
50
#include "utils/delete2.h"
51
#include "utils/gettext.h"
52
53
#ifdef WIN32
54
#include <sys/time.h>
55
#endif  // WIN32
56
57
#include <sstream>
58
59
#include "debug.h"
60
61
ChatTab *localChatTab = nullptr;
62
ChatTab *debugChatTab = nullptr;
63
64
static const unsigned int MAX_WORD_SIZE = 50;
65
66
2
ChatTab::ChatTab(const Widget2 *const widget,
67
                 const std::string &name,
68
                 const std::string &channel,
69
                 const std::string &logName,
70
2
                 const ChatTabTypeT &type) :
71
    Tab(widget),
72
    mTextOutput(new BrowserBox(this, Opaque_true,
73

2
       "browserbox.xml")),
74

2
    mScrollArea(new ScrollArea(this, mTextOutput, Opaque_false)),
75
    mChannelName(channel),
76
    mLogName(logName),
77
2
    mType(type),
78
    mAllowHightlight(true),
79
    mRemoveNames(false),
80
    mNoAway(false),
81

24
    mShowOnline(false)
82
{
83
2
    setCaption(name);
84
85
4
    mTextOutput->setOpaque(Opaque_false);
86

10
    mTextOutput->setMaxRow(config.getIntValue("ChatLogLength"));
87
2
    if (chatWindow != nullptr)
88
2
        mTextOutput->setLinkHandler(chatWindow->mItemLinkHandler);
89
4
    mTextOutput->setAlwaysUpdate(false);
90
91
2
    mScrollArea->setScrollPolicy(ScrollArea::SHOW_NEVER,
92
        ScrollArea::SHOW_ALWAYS);
93
2
    mScrollArea->setScrollAmount(0, 1);
94
95
2
    if (chatWindow != nullptr)
96
2
        chatWindow->addTab(this);
97
2
    mTextOutput->updateSize(true);
98
2
}
99
100
10
ChatTab::~ChatTab()
101
{
102
2
    if (chatWindow != nullptr)
103
2
        chatWindow->removeTab(this);
104
105
2
    delete2(mTextOutput);
106
2
    delete2(mScrollArea);
107
4
}
108
109
void ChatTab::chatLog(std::string line,
110
                      ChatMsgTypeT own,
111
                      const IgnoreRecord ignoreRecord,
112
                      const TryRemoveColors tryRemoveColors)
113
{
114
    // Trim whitespace
115
    trim(line);
116
117
    if (line.empty())
118
        return;
119
120
    if (tryRemoveColors == TryRemoveColors_true &&
121
        own == ChatMsgType::BY_OTHER &&
122
        config.getBoolValue("removeColors"))
123
    {
124
        line = removeColors(line);
125
        if (line.empty())
126
            return;
127
    }
128
129
    const unsigned lineLim = config.getIntValue("chatMaxCharLimit");
130
    if (lineLim > 0 && line.length() > lineLim)
131
        line = line.substr(0, lineLim);
132
133
    if (line.empty())
134
        return;
135
136
    CHATLOG tmp;
137
    tmp.own = own;
138
    tmp.nick.clear();
139
    tmp.text = line;
140
141
    const size_t pos = line.find(" : ");
142
    if (pos != std::string::npos)
143
    {
144
        if (line.length() <= pos + 3)
145
            return;
146
147
        tmp.nick = line.substr(0, pos);
148
        tmp.text = line.substr(pos + 3);
149
    }
150
    else
151
    {
152
        // Fix the owner of welcome message.
153
        if (line.length() > 7 && line.substr(0, 7) == "Welcome")
154
            own = ChatMsgType::BY_SERVER;
155
    }
156
157
    // *implements actions in a backwards compatible way*
158
    if ((own == ChatMsgType::BY_PLAYER || own == ChatMsgType::BY_OTHER) &&
159
        tmp.text.at(0) == '*' &&
160
        tmp.text.at(tmp.text.length()-1) == '*')
161
    {
162
        tmp.text[0] = ' ';
163
        tmp.text.erase(tmp.text.length() - 1);
164
        own = ChatMsgType::ACT_IS;
165
    }
166
167
    std::string lineColor("##C");
168
    switch (own)
169
    {
170
        case ChatMsgType::BY_GM:
171
            if (tmp.nick.empty())
172
            {
173
                // TRANSLATORS: chat message
174
                tmp.nick = std::string(_("Global announcement:")).append(" ");
175
                lineColor = "##G";
176
            }
177
            else
178
            {
179
                // TRANSLATORS: chat message
180
                tmp.nick = strprintf(_("Global announcement from %s:"),
181
                                     tmp.nick.c_str()).append(" ");
182
                lineColor = "##g";  // Equiv. to BrowserBox::RED
183
            }
184
            break;
185
        case ChatMsgType::BY_PLAYER:
186
            tmp.nick.append(": ");
187
            lineColor = "##Y";
188
            break;
189
        case ChatMsgType::BY_OTHER:
190
        case ChatMsgType::BY_UNKNOWN:
191
            tmp.nick.append(": ");
192
            lineColor = "##C";
193
            break;
194
        case ChatMsgType::BY_SERVER:
195
            // TRANSLATORS: chat message
196
            tmp.nick.clear();
197
            tmp.text = line;
198
            lineColor = "##S";
199
            break;
200
        case ChatMsgType::BY_CHANNEL:
201
            tmp.nick.clear();
202
            lineColor = "##2";  // Equiv. to BrowserBox::GREEN
203
            break;
204
        case ChatMsgType::ACT_WHISPER:
205
            // TRANSLATORS: chat message
206
            tmp.nick = strprintf(_("%s whispers: %s"), tmp.nick.c_str(), "");
207
            lineColor = "##W";
208
            break;
209
        case ChatMsgType::ACT_IS:
210
            lineColor = "##I";
211
            break;
212
        case ChatMsgType::BY_LOGGER:
213
            tmp.nick.clear();
214
            tmp.text = line;
215
            lineColor = "##L";
216
            break;
217
        default:
218
            break;
219
    }
220
221
    if (tmp.nick == ": ")
222
    {
223
        tmp.nick.clear();
224
        lineColor = "##S";
225
    }
226
227
    // if configured, move magic messages log to debug chat tab
228
    if (Net::getNetworkType() == ServerType::TMWATHENA
229
        && (localChatTab != nullptr) && this == localChatTab
230
        && ((config.getBoolValue("showMagicInDebug")
231
        && own == ChatMsgType::BY_PLAYER
232
        && tmp.text.length() > 1
233
        && tmp.text.at(0) == '#'
234
        && tmp.text.at(1) != '#')
235
        || (config.getBoolValue("serverMsgInDebug")
236
        && (own == ChatMsgType::BY_SERVER
237
        || tmp.nick.empty()))))
238
    {
239
        if (debugChatTab != nullptr)
240
            debugChatTab->chatLog(line, own, ignoreRecord, tryRemoveColors);
241
        return;
242
    }
243
244
    // Get the current system time
245
    time_t t;
246
    time(&t);
247
248
    if (config.getBoolValue("useLocalTime"))
249
    {
250
        const tm *const timeInfo = localtime(&t);
251
        if (timeInfo != nullptr)
252
        {
253
            line = strprintf("%s[%02d:%02d] %s%s", lineColor.c_str(),
254
                timeInfo->tm_hour, timeInfo->tm_min, tmp.nick.c_str(),
255
                tmp.text.c_str());
256
        }
257
        else
258
        {
259
            line = strprintf("%s %s%s", lineColor.c_str(),
260
                tmp.nick.c_str(), tmp.text.c_str());
261
        }
262
    }
263
    else
264
    {
265
        // Format the time string properly
266
        std::stringstream timeStr;
267
        timeStr << "[" << ((((t / 60) / 60) % 24 < 10) ? "0" : "")
268
            << CAST_S32(((t / 60) / 60) % 24)
269
            << ":" << (((t / 60) % 60 < 10) ? "0" : "")
270
            << CAST_S32((t / 60) % 60)
271
            << "] ";
272
        line = std::string(lineColor).append(timeStr.str()).append(
273
            tmp.nick).append(tmp.text);
274
    }
275
276
    if (config.getBoolValue("enableChatLog"))
277
        saveToLogFile(line);
278
279
    mTextOutput->setMaxRow(config.getIntValue("chatMaxLinesLimit"));
280
281
    // We look if the Vertical Scroll Bar is set at the max before
282
    // adding a row, otherwise the max will always be a row higher
283
    // at comparison.
284
    if (mScrollArea->getVerticalScrollAmount() + 2 >=
285
        mScrollArea->getVerticalMaxScroll())
286
    {
287
        addRow(line);
288
        mScrollArea->setVerticalScrollAmount(
289
            mScrollArea->getVerticalMaxScroll());
290
    }
291
    else
292
    {
293
        addRow(line);
294
    }
295
296
    if ((chatWindow != nullptr) && this == localChatTab)
297
        chatWindow->addToAwayLog(line);
298
299
    mScrollArea->logic();
300
    if (own != ChatMsgType::BY_PLAYER)
301
    {
302
        if (own == ChatMsgType::BY_SERVER &&
303
            (getType() == ChatTabType::PARTY ||
304
            getType() == ChatTabType::CHANNEL ||
305
            getType() == ChatTabType::GUILD))
306
        {
307
            return;
308
        }
309
310
        const TabbedArea *const tabArea = getTabbedArea();
311
        if (tabArea == nullptr)
312
            return;
313
314
        const bool notFocused = WindowManager::getIsMinimized() ||
315
            (!settings.mouseFocused &&
316
            settings.inputFocused == KeyboardFocus::Unfocused);
317
318
        if (this != tabArea->getSelectedTab() || notFocused)
319
        {
320
            if (getFlash() == 0)
321
            {
322
                if (chatWindow != nullptr &&
323
                    chatWindow->findHighlight(tmp.text))
324
                {
325
                    setFlash(2);
326
                    soundManager.playGuiSound(SOUND_HIGHLIGHT);
327
                }
328
                else
329
                {
330
                    setFlash(1);
331
                }
332
            }
333
            else if (getFlash() == 2)
334
            {
335
                if (chatWindow != nullptr &&
336
                    chatWindow->findHighlight(tmp.text))
337
                {
338
                    soundManager.playGuiSound(SOUND_HIGHLIGHT);
339
                }
340
            }
341
        }
342
343
        if ((getAllowHighlight() ||
344
            own == ChatMsgType::BY_GM) &&
345
            (this != tabArea->getSelectedTab() ||
346
            notFocused))
347
        {
348
            if (own == ChatMsgType::BY_GM)
349
            {
350
                if (chatWindow != nullptr)
351
                    chatWindow->unHideWindow();
352
                soundManager.playGuiSound(SOUND_GLOBAL);
353
            }
354
            else if (own != ChatMsgType::BY_SERVER)
355
            {
356
                if (chatWindow != nullptr)
357
                    chatWindow->unHideWindow();
358
                playNewMessageSound();
359
            }
360
            WindowManager::newChatMessage();
361
        }
362
    }
363
}
364
365
void ChatTab::chatLog(const std::string &nick, std::string msg)
366
{
367
    if (localPlayer == nullptr)
368
        return;
369
370
    const ChatMsgTypeT byWho = (nick == localPlayer->getName()
371
        ? ChatMsgType::BY_PLAYER : ChatMsgType::BY_OTHER);
372
    if (byWho == ChatMsgType::BY_OTHER && config.getBoolValue("removeColors"))
373
        msg = removeColors(msg);
374
    chatLog(std::string(nick).append(" : ").append(msg),
375
        byWho,
376
        IgnoreRecord_false,
377
        TryRemoveColors_false);
378
}
379
380
void ChatTab::chatInput(const std::string &message)
381
{
382
    std::string msg = message;
383
    trim(msg);
384
385
    if (msg.empty())
386
        return;
387
388
    replaceItemLinks(msg);
389
    replaceVars(msg);
390
391
    switch (msg[0])
392
    {
393
        case '/':
394
            handleCommandStr(std::string(msg, 1));
395
            break;
396
        case '?':
397
            if (msg.size() > 1 &&
398
                msg[1] != '!' &&
399
                msg[1] != '?' &&
400
                msg[1] != '.' &&
401
                msg[1] != ' ' &&
402
                msg[1] != ',')
403
            {
404
                handleHelp(std::string(msg, 1));
405
            }
406
            else
407
            {
408
                handleInput(msg);
409
            }
410
            break;
411
        default:
412
            handleInput(msg);
413
            break;
414
    }
415
}
416
417
void ChatTab::scroll(const int amount)
418
{
419
    const int range = mScrollArea->getHeight() / 8 * amount;
420
    Rect scr;
421
    scr.y = mScrollArea->getVerticalScrollAmount() + range;
422
    scr.height = abs(range);
423
    mTextOutput->showPart(scr);
424
}
425
426
void ChatTab::clearText()
427
{
428
    mTextOutput->clearRows();
429
}
430
431
void ChatTab::handleInput(const std::string &msg)
432
{
433
    if (chatHandler)
434
    {
435
        chatHandler->talk(ChatWindow::doReplace(msg),
436
            mChannelName);
437
    }
438
}
439
440
void ChatTab::handleCommandStr(const std::string &msg)
441
{
442
    const size_t pos = msg.find(' ');
443
    const std::string type(msg, 0, pos);
444
    std::string args(msg, pos == std::string::npos ? msg.size() : pos + 1);
445
446
    args = trim(args);
447
    if (!handleCommand(type, args))
448
        inputManager.executeChatCommand(type, args, this);
449
}
450
451
void ChatTab::handleHelp(const std::string &msg)
452
{
453
    if (helpWindow != nullptr)
454
    {
455
        helpWindow->search(msg);
456
        helpWindow->requestMoveToTop();
457
    }
458
}
459
460
bool ChatTab::handleCommands(const std::string &type, const std::string &args)
461
{
462
    // need split to commands and call each
463
464
    return handleCommand(type, args);
465
}
466
467
void ChatTab::saveToLogFile(const std::string &msg) const
468
{
469
    if (chatLogger != nullptr)
470
    {
471
        if (getType() == ChatTabType::INPUT)
472
        {
473
            chatLogger->log(msg);
474
        }
475
        else if (getType() == ChatTabType::DEBUG)
476
        {
477
            if (config.getBoolValue("enableDebugLog"))
478
                chatLogger->log("#Debug", msg);
479
        }
480
        else if (!mLogName.empty())
481
        {
482
            chatLogger->log(mLogName, msg);
483
        }
484
    }
485
}
486
487
void ChatTab::addRow(std::string &line)
488
{
489
    if (line.find("[@@http") == std::string::npos)
490
    {
491
        size_t idx = 0;
492
        for (size_t f = 0; f < line.length(); f++)
493
        {
494
            if (line.at(f) == ' ')
495
            {
496
                idx = f;
497
            }
498
            else if (f - idx > MAX_WORD_SIZE)
499
            {
500
                line.insert(f, " ");
501
                idx = f;
502
            }
503
        }
504
    }
505
    mTextOutput->addRow(line);
506
}
507
508
void ChatTab::loadFromLogFile(const std::string &name)
509
{
510
    if (chatLogger != nullptr)
511
    {
512
        std::list<std::string> list;
513
        chatLogger->loadLast(name, list, 5);
514
        std::list<std::string>::const_iterator i = list.begin();
515
        while (i != list.end())
516
        {
517
            std::string line("##o" + *i);
518
            addRow(line);
519
            ++i;
520
        }
521
    }
522
}
523
524
void ChatTab::addNewRow(std::string &line)
525
{
526
    if (mScrollArea->getVerticalScrollAmount() >=
527
        mScrollArea->getVerticalMaxScroll())
528
    {
529
        addRow(line);
530
        mScrollArea->setVerticalScrollAmount(
531
            mScrollArea->getVerticalMaxScroll());
532
    }
533
    else
534
    {
535
        addRow(line);
536
    }
537
    mScrollArea->logic();
538
}
539
540
void ChatTab::playNewMessageSound() const
541
{
542
    soundManager.playGuiSound(SOUND_WHISPER);
543
}
544
545
void ChatTab::showOnline(const std::string &nick,
546
                         const Online online)
547
{
548
    if (!mShowOnline)
549
        return;
550
551
    if (online == Online_true)
552
    {
553
        // TRANSLATORS: chat message
554
        chatLog(strprintf(_("%s is now Online."), nick.c_str()),
555
            ChatMsgType::BY_SERVER);
556
    }
557
    else
558
    {
559
        // TRANSLATORS: chat message
560
        chatLog(strprintf(_("%s is now Offline."), nick.c_str()),
561
            ChatMsgType::BY_SERVER);
562
    }
563

6
}