GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/tabs/chat/chattab.cpp Lines: 26 241 10.8 %
Date: 2018-07-14 Branches: 20 444 4.5 %

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-2018  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
1
ChatTab::ChatTab(const Widget2 *const widget,
67
                 const std::string &name,
68
                 const std::string &channel,
69
                 const std::string &logName,
70
1
                 const ChatTabTypeT &type) :
71
    Tab(widget),
72
    mTextOutput(new BrowserBox(this, Opaque_true,
73

1
       "browserbox.xml")),
74
    mScrollArea(new ScrollArea(this,
75

2
        mTextOutput, Opaque_false, std::string())),
76
    mChannelName(channel),
77
    mLogName(logName),
78
1
    mType(type),
79
    mAllowHightlight(true),
80
    mRemoveNames(false),
81
    mNoAway(false),
82
10
    mShowOnline(false)
83
{
84
1
    setCaption(name);
85
86
2
    mTextOutput->setOpaque(Opaque_false);
87

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

3
}