GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/progs/manaplus/actions/chat.cpp Lines: 1 328 0.3 %
Date: 2021-03-17 Branches: 0 389 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2012-2019  The ManaPlus Developers
4
 *  Copyright (C) 2019-2021  Andrei Karas
5
 *
6
 *  This file is part of The ManaPlus Client.
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include "actions/chat.h"
23
24
#include "configuration.h"
25
26
#include "actions/actiondef.h"
27
28
#include "being/localplayer.h"
29
30
#include "gui/sdlinput.h"
31
32
#include "gui/windows/chatwindow.h"
33
34
#include "listeners/inputactionreplaylistener.h"
35
36
#include "net/charserverhandler.h"
37
#include "net/chathandler.h"
38
#include "net/clanhandler.h"
39
#include "net/guildhandler.h"
40
#include "net/net.h"
41
#include "net/partyhandler.h"
42
43
#ifdef TMWA_SUPPORT
44
#include "net/tmwa/guildmanager.h"
45
#endif  // TMWA_SUPPORT
46
47
#include "resources/iteminfo.h"
48
49
#include "resources/db/itemdb.h"
50
51
#include "utils/booleanoptions.h"
52
#include "utils/chatutils.h"
53
#include "utils/parameters.h"
54
55
#include "utils/translation/podict.h"
56
57
#include "debug.h"
58
59
const int DEFAULT_CHAT_WINDOW_SCROLL = 7;
60
61
namespace Actions
62
{
63
64
static void outString(ChatTab *const tab,
65
                      const std::string &str,
66
                      const std::string &def)
67
{
68
    if (tab == nullptr)
69
    {
70
        if (chatHandler != nullptr)
71
            chatHandler->talk(def);
72
        return;
73
    }
74
75
    switch (tab->getType())
76
    {
77
        case ChatTabType::CLAN:
78
        {
79
            if (clanHandler != nullptr)
80
                clanHandler->chat(str);
81
            break;
82
        }
83
        case ChatTabType::PARTY:
84
        {
85
            if (partyHandler != nullptr)
86
                partyHandler->chat(str);
87
            break;
88
        }
89
        case ChatTabType::GUILD:
90
        {
91
            if ((guildHandler == nullptr) || (localPlayer == nullptr))
92
                return;
93
            const Guild *const guild = localPlayer->getGuild();
94
            if (guild != nullptr)
95
            {
96
#ifdef TMWA_SUPPORT
97
                if (guild->getServerGuild())
98
                {
99
                    if (Net::getNetworkType() == ServerType::TMWATHENA)
100
                        return;
101
                    guildHandler->chat(str);
102
                }
103
                else if (guildManager != nullptr)
104
                {
105
                    guildManager->chat(str);
106
                }
107
#else  // TMWA_SUPPORT
108
109
                if (guild->getServerGuild())
110
                    guildHandler->chat(str);
111
#endif  // TMWA_SUPPORT
112
            }
113
            break;
114
        }
115
        case ChatTabType::CHANNEL:
116
        case ChatTabType::GM:
117
        case ChatTabType::TRADE:
118
            tab->chatInput(str);
119
            break;
120
        default:
121
        case ChatTabType::UNKNOWN:
122
        case ChatTabType::INPUT:
123
        case ChatTabType::WHISPER:
124
        case ChatTabType::DEBUG:
125
        case ChatTabType::BATTLE:
126
        case ChatTabType::LANG:
127
            if (chatHandler != nullptr)
128
                chatHandler->talk(str);
129
            break;
130
    }
131
}
132
133
impHandler0(toggleChat)
134
{
135
    return chatWindow != nullptr ? chatWindow->requestChatFocus() : false;
136
}
137
138
impHandler0(prevChatTab)
139
{
140
    if (chatWindow != nullptr)
141
    {
142
        chatWindow->prevTab();
143
        return true;
144
    }
145
    return false;
146
}
147
148
impHandler0(nextChatTab)
149
{
150
    if (chatWindow != nullptr)
151
    {
152
        chatWindow->nextTab();
153
        return true;
154
    }
155
    return false;
156
}
157
158
impHandler0(closeChatTab)
159
{
160
    if (chatWindow != nullptr)
161
    {
162
        chatWindow->closeTab();
163
        return true;
164
    }
165
    return false;
166
}
167
168
impHandler0(closeAllChatTabs)
169
{
170
    if (chatWindow != nullptr)
171
    {
172
        chatWindow->removeAllWhispers();
173
        chatWindow->saveState();
174
        return true;
175
    }
176
    return false;
177
}
178
179
impHandler0(ignoreAllWhispers)
180
{
181
    if (chatWindow != nullptr)
182
    {
183
        chatWindow->ignoreAllWhispers();
184
        chatWindow->saveState();
185
        return true;
186
    }
187
    return false;
188
}
189
190
impHandler0(scrollChatUp)
191
{
192
    if ((chatWindow != nullptr) && chatWindow->isWindowVisible())
193
    {
194
        chatWindow->scroll(-DEFAULT_CHAT_WINDOW_SCROLL);
195
        return true;
196
    }
197
    return false;
198
}
199
200
impHandler0(scrollChatDown)
201
{
202
    if ((chatWindow != nullptr) && chatWindow->isWindowVisible())
203
    {
204
        chatWindow->scroll(DEFAULT_CHAT_WINDOW_SCROLL);
205
        return true;
206
    }
207
    return false;
208
}
209
210
static bool splitWhisper(const std::string &args,
211
                         std::string &recvnick,
212
                         std::string &message)
213
{
214
    if (args.substr(0, 1) == "\"")
215
    {
216
        const size_t pos = args.find('"', 1);
217
        if (pos != std::string::npos)
218
        {
219
            recvnick = args.substr(1, pos - 1);
220
            if (pos + 2 < args.length())
221
                message = args.substr(pos + 2, args.length());
222
        }
223
    }
224
    else
225
    {
226
        const size_t pos = args.find(' ');
227
        if (pos != std::string::npos)
228
        {
229
            recvnick = args.substr(0, pos);
230
            if (pos + 1 < args.length())
231
                message = args.substr(pos + 1, args.length());
232
        }
233
        else
234
        {
235
            recvnick = std::string(args);
236
            message.clear();
237
        }
238
    }
239
240
    trim(message);
241
242
    if (message.length() > 0)
243
    {
244
        std::string playerName = localPlayer->getName();
245
        std::string tempNick = recvnick;
246
247
        toLower(playerName);
248
        toLower(tempNick);
249
250
        if (tempNick == playerName || args.empty())
251
            return false;
252
253
        return true;
254
    }
255
    return false;
256
}
257
258
impHandler(msg)
259
{
260
    std::string recvnick;
261
    std::string message;
262
263
    if (splitWhisper(event.args, recvnick, message))
264
    {
265
        if (chatWindow == nullptr)
266
            return false;
267
        ChatTab *const tab = chatWindow->addChatTab(recvnick, false, true);
268
        if (tab != nullptr)
269
        {
270
            chatWindow->saveState();
271
            tab->chatInput(message);
272
        }
273
    }
274
    else
275
    {
276
        if (event.tab != nullptr)
277
        {
278
            event.tab->chatLog(
279
                // TRANSLATORS: whisper send
280
                _("Cannot send empty whisper or channel message!"),
281
                ChatMsgType::BY_SERVER,
282
                IgnoreRecord_false,
283
                TryRemoveColors_true);
284
        }
285
    }
286
    return true;
287
}
288
289
impHandler(msgText)
290
{
291
    if (chatWindow == nullptr)
292
        return false;
293
294
    if (config.getBoolValue("whispertab"))
295
    {
296
        chatWindow->localChatInput("/q " + event.args);
297
    }
298
    else
299
    {
300
        chatWindow->addInputText(
301
            std::string("/w \"").append(event.args).append("\" "),
302
            true);
303
    }
304
    return true;
305
}
306
307
impHandler(msg2)
308
{
309
    std::string recvnick;
310
    std::string message;
311
312
    if (chatHandler != nullptr &&
313
        splitWhisper(event.args, recvnick, message))
314
    {
315
        chatHandler->privateMessage(recvnick, message);
316
    }
317
    return true;
318
}
319
320
impHandler(query)
321
{
322
    const std::string &args = event.args;
323
    if (chatWindow != nullptr)
324
    {
325
        if (chatWindow->addChatTab(args, true, true) != nullptr)
326
        {
327
            chatWindow->saveState();
328
            return true;
329
        }
330
    }
331
332
    if (event.tab != nullptr)
333
    {
334
        // TRANSLATORS: new whisper or channel query
335
        event.tab->chatLog(strprintf(_("Cannot create a whisper tab "
336
            "\"%s\"! It probably already exists."),
337
            args.c_str()),
338
            ChatMsgType::BY_SERVER,
339
            IgnoreRecord_false,
340
            TryRemoveColors_true);
341
    }
342
    return true;
343
}
344
345
impHandler0(clearChatTab)
346
{
347
    if (chatWindow != nullptr)
348
    {
349
        chatWindow->clearTab();
350
        return true;
351
    }
352
    return false;
353
}
354
355
impHandler(createParty)
356
{
357
    if (partyHandler == nullptr)
358
        return false;
359
360
    if (event.args.empty())
361
    {
362
        // TRANSLATORS: dialog header
363
        inputActionReplayListener.openDialog(_("Create party"),
364
            "",
365
            InputAction::CREATE_PARTY);
366
    }
367
    else
368
    {
369
        partyHandler->create(event.args);
370
    }
371
    return true;
372
}
373
374
impHandler(createGuild)
375
{
376
    if ((guildHandler == nullptr) ||
377
        Net::getNetworkType() == ServerType::TMWATHENA)
378
    {
379
        return false;
380
    }
381
382
    if (event.args.empty())
383
    {
384
        // TRANSLATORS: dialog header
385
        inputActionReplayListener.openDialog(_("Create guild"),
386
            "",
387
            InputAction::CREATE_GUILD);
388
    }
389
    else
390
    {
391
        guildHandler->create(event.args);
392
    }
393
    return true;
394
}
395
396
impHandler(party)
397
{
398
    if (!event.args.empty())
399
    {
400
        if (partyHandler != nullptr)
401
            partyHandler->invite(event.args);
402
    }
403
    else
404
    {
405
        if (event.tab != nullptr)
406
        {
407
            // TRANSLATORS: party invite message
408
            event.tab->chatLog(_("Please specify a name."),
409
                ChatMsgType::BY_SERVER,
410
                IgnoreRecord_false,
411
                TryRemoveColors_true);
412
        }
413
    }
414
    return true;
415
}
416
417
impHandler(guild)
418
{
419
    if ((guildHandler == nullptr) || (localPlayer == nullptr))
420
        return false;
421
422
    const std::string args = event.args;
423
    if (!args.empty())
424
    {
425
        const Guild *const guild = localPlayer->getGuild();
426
        if (guild != nullptr)
427
        {
428
#ifdef TMWA_SUPPORT
429
            if (guild->getServerGuild())
430
                guildHandler->invite(args);
431
            else if (guildManager != nullptr)
432
                GuildManager::invite(args);
433
#else  // TMWA_SUPPORT
434
435
            guildHandler->invite(args);
436
#endif  // TMWA_SUPPORT
437
        }
438
    }
439
    else
440
    {
441
        if (event.tab != nullptr)
442
        {
443
            // TRANSLATORS: guild invite message
444
            event.tab->chatLog(_("Please specify a name."),
445
                ChatMsgType::BY_SERVER,
446
                IgnoreRecord_false,
447
                TryRemoveColors_true);
448
        }
449
        else if (localChatTab != nullptr)
450
        {
451
            // TRANSLATORS: guild invite message
452
            localChatTab->chatLog(_("Please specify a name."),
453
                ChatMsgType::BY_SERVER,
454
                IgnoreRecord_false,
455
                TryRemoveColors_true);
456
        }
457
    }
458
    return true;
459
}
460
461
impHandler(me)
462
{
463
    outString(event.tab, textToMe(event.args), event.args);
464
    return true;
465
}
466
467
impHandler(toggle)
468
{
469
    if (event.args.empty())
470
    {
471
        if ((chatWindow != nullptr) && (event.tab != nullptr))
472
        {
473
            event.tab->chatLog(chatWindow->getReturnTogglesChat() ?
474
                // TRANSLATORS: message from toggle chat command
475
                _("Return toggles chat.") : _("Message closes chat."),
476
                ChatMsgType::BY_SERVER,
477
                IgnoreRecord_false,
478
                TryRemoveColors_true);
479
        }
480
        return true;
481
    }
482
483
    switch (parseBoolean(event.args))
484
    {
485
        case 1:
486
            if (event.tab != nullptr)
487
            {
488
                // TRANSLATORS: message from toggle chat command
489
                event.tab->chatLog(_("Return now toggles chat."),
490
                    ChatMsgType::BY_SERVER,
491
                    IgnoreRecord_false,
492
                    TryRemoveColors_true);
493
            }
494
            if (chatWindow != nullptr)
495
                chatWindow->setReturnTogglesChat(true);
496
            return true;
497
        case 0:
498
            if (event.tab != nullptr)
499
            {
500
                // TRANSLATORS: message from toggle chat command
501
                event.tab->chatLog(_("Message now closes chat."),
502
                    ChatMsgType::BY_SERVER,
503
                    IgnoreRecord_false,
504
                    TryRemoveColors_true);
505
            }
506
            if (chatWindow != nullptr)
507
                chatWindow->setReturnTogglesChat(false);
508
            return true;
509
        case -1:
510
            if (event.tab != nullptr)
511
            {
512
                event.tab->chatLog(strprintf(BOOLEAN_OPTIONS, "toggle"),
513
                    ChatMsgType::BY_SERVER,
514
                    IgnoreRecord_false,
515
                    TryRemoveColors_true);
516
            }
517
            return true;
518
        default:
519
            return true;
520
    }
521
}
522
523
impHandler(kickParty)
524
{
525
    if (!event.args.empty())
526
    {
527
        if (partyHandler != nullptr)
528
            partyHandler->kick(event.args);
529
    }
530
    else
531
    {
532
        if (event.tab != nullptr)
533
        {
534
            // TRANSLATORS: party kick message
535
            event.tab->chatLog(_("Please specify a name."),
536
                ChatMsgType::BY_SERVER,
537
                IgnoreRecord_false,
538
                TryRemoveColors_true);
539
        }
540
    }
541
    return true;
542
}
543
544
impHandler(kickGuild)
545
{
546
    if (!event.args.empty())
547
    {
548
        if (localPlayer != nullptr)
549
        {
550
            const Guild *const guild = localPlayer->getGuild();
551
            if (guild != nullptr)
552
            {
553
                if (guild->getServerGuild())
554
                {
555
                    if (guildHandler != nullptr)
556
                        guildHandler->kick(guild->getMember(event.args), "");
557
                }
558
#ifdef TMWA_SUPPORT
559
                else if (guildManager != nullptr)
560
                {
561
                    GuildManager::kick(event.args);
562
                }
563
#endif  // TMWA_SUPPORT
564
            }
565
        }
566
    }
567
    else
568
    {
569
        if (event.tab != nullptr)
570
        {
571
            // TRANSLATORS: party kick message
572
            event.tab->chatLog(_("Please specify a name."),
573
                ChatMsgType::BY_SERVER,
574
                IgnoreRecord_false,
575
                TryRemoveColors_true);
576
        }
577
    }
578
    return true;
579
}
580
581
impHandler(addText)
582
{
583
    if (chatWindow != nullptr)
584
        chatWindow->addInputText(event.args, true);
585
    return true;
586
}
587
588
impHandler0(clearChat)
589
{
590
    if (chatWindow != nullptr)
591
        chatWindow->clearTab();
592
    return true;
593
}
594
595
impHandler0(chatGeneralTab)
596
{
597
    if (chatWindow != nullptr)
598
        chatWindow->selectTabByType(ChatTabType::INPUT);
599
    return true;
600
}
601
602
impHandler0(chatDebugTab)
603
{
604
    if (chatWindow != nullptr)
605
        chatWindow->selectTabByType(ChatTabType::DEBUG);
606
    return true;
607
}
608
609
impHandler0(chatBattleTab)
610
{
611
    if (chatWindow != nullptr)
612
        chatWindow->selectTabByType(ChatTabType::BATTLE);
613
    return true;
614
}
615
616
impHandler0(chatTradeTab)
617
{
618
    if (chatWindow != nullptr)
619
        chatWindow->selectTabByType(ChatTabType::TRADE);
620
    return true;
621
}
622
623
impHandler0(chatLangTab)
624
{
625
    if (chatWindow != nullptr)
626
        chatWindow->selectTabByType(ChatTabType::LANG);
627
    return true;
628
}
629
630
impHandler0(chatGmTab)
631
{
632
    if (chatWindow != nullptr)
633
        chatWindow->selectTabByType(ChatTabType::GM);
634
    return true;
635
}
636
637
impHandler0(chatPartyTab)
638
{
639
    if (chatWindow != nullptr)
640
        chatWindow->selectTabByType(ChatTabType::PARTY);
641
    return true;
642
}
643
644
impHandler0(chatGuildTab)
645
{
646
    if (chatWindow != nullptr)
647
        chatWindow->selectTabByType(ChatTabType::GUILD);
648
    return true;
649
}
650
651
impHandler(hat)
652
{
653
    if ((localPlayer == nullptr) || (charServerHandler == nullptr))
654
        return false;
655
656
    const int sprite = localPlayer->getSpriteID(
657
        charServerHandler->hatSprite());
658
    std::string str;
659
    if (sprite == 0)
660
    {
661
        // TRANSLATORS: equipped hat chat message
662
        str = _("no hat equipped.");
663
    }
664
    else
665
    {
666
        const ItemInfo &info = ItemDB::get(sprite);
667
        // TRANSLATORS: equipped hat chat message
668
        str = strprintf(_("equipped hat %s."),
669
            info.getName().c_str());
670
    }
671
    outString(event.tab, str, str);
672
    return true;
673
}
674
675
impHandler(chatClipboard)
676
{
677
    int x = 0;
678
    int y = 0;
679
680
    if ((chatWindow != nullptr) && parse2Int(event.args, x, y))
681
    {
682
        chatWindow->copyToClipboard(x, y);
683
        return true;
684
    }
685
    return false;
686
}
687
688
impHandler(guildNotice)
689
{
690
    if (localPlayer == nullptr)
691
        return false;
692
    const std::string args = event.args;
693
    if (args.empty())
694
    {
695
        // TRANSLATORS: dialog header
696
        inputActionReplayListener.openDialog(_("Guild notice"),
697
            "",
698
            InputAction::GUILD_NOTICE);
699
        return true;
700
    }
701
702
    std::string str2;
703
    if (args.size() > 60)
704
        str2 = args.substr(60);
705
    const Guild *const guild = localPlayer->getGuild();
706
    if (guild != nullptr)
707
    {
708
        guildHandler->changeNotice(guild->getId(),
709
            args.substr(0, 60),
710
            str2);
711
    }
712
    return true;
713
}
714
715
impHandler(translate)
716
{
717
    if (reverseDictionary == nullptr ||
718
        localPlayer == nullptr ||
719
        event.args.empty())
720
    {
721
        return false;
722
    }
723
724
    ChatTab *const tab = event.tab;
725
    if (tab == nullptr)
726
        return false;
727
728
    std::string srcStr = event.args;
729
    std::string enStr;
730
    toLower(srcStr);
731
    if (localPlayer->getLanguageId() > 0)
732
    {
733
        if (reverseDictionary->haveStr(srcStr))
734
            enStr = reverseDictionary->getStr(srcStr);
735
        else if (dictionary->haveStr(srcStr))
736
            enStr = srcStr;
737
    }
738
    else
739
    {
740
        if (dictionary->haveStr(srcStr))
741
            enStr = srcStr;
742
    }
743
744
    if (enStr.empty())
745
    {
746
        tab->chatLog(
747
            // TRANSLATORS: translation error message
748
            strprintf(_("No translation found for string: %s"),
749
            srcStr.c_str()),
750
            ChatMsgType::BY_SERVER,
751
            IgnoreRecord_false,
752
            TryRemoveColors_true);
753
        return true;
754
    }
755
756
    tab->chatInput(enStr);
757
    return true;
758
}
759
760
impHandler(sendGuiKey)
761
{
762
    if (guiInput == nullptr)
763
        return false;
764
765
    const std::string args = event.args;
766
    if (args.empty())
767
        return false;
768
    StringVect pars;
769
    if (!splitParameters(pars, args, " ,", '\"'))
770
        return false;
771
    const int sz = CAST_S32(pars.size());
772
    if (sz < 1)
773
        return false;
774
775
    int keyValue = atoi(pars[0].c_str());
776
    if (keyValue == 0 &&
777
        pars[0].size() == 1)
778
    {
779
        keyValue = CAST_S32(pars[0][0]);
780
    }
781
    if (sz == 2)
782
    {
783
        const InputActionT actionId = InputManager::getActionByConfigField(
784
            pars[1]);
785
        guiInput->simulateKey(keyValue, actionId);
786
    }
787
    else
788
    {
789
        guiInput->simulateKey(keyValue, InputAction::NO_VALUE);
790
    }
791
    return true;
792
}
793
794
impHandler(sendMouseKey)
795
{
796
    if (guiInput == nullptr)
797
        return false;
798
    const std::string args = event.args;
799
    if (args.empty())
800
        return false;
801
    StringVect pars;
802
    if (!splitParameters(pars, args, " ,", '\"'))
803
        return false;
804
    const int sz = CAST_S32(pars.size());
805
    if (sz != 3)
806
        return false;
807
808
    const int x = atoi(pars[0].c_str());
809
    const int y = atoi(pars[1].c_str());
810
    const int key1 = CAST_S32(MouseButton::LEFT);
811
    const int key2 = CAST_S32(MouseButton::MIDDLE);
812
    const int key = atoi(pars[2].c_str());
813
    if (key < key1 || key > key2)
814
        return false;
815
    guiInput->simulateMouseClick(x,
816
        y,
817
        static_cast<MouseButtonT>(key));
818
    return true;
819
}
820
821
impHandler(sendChars)
822
{
823
    if (guiInput == nullptr)
824
        return false;
825
826
    const std::string args = event.args;
827
    if (args.empty())
828
        return false;
829
830
    const size_t sz = args.size();
831
    for (size_t f = 0; f < sz; f ++)
832
    {
833
        guiInput->simulateKey(CAST_S32(args[f]),
834
            InputAction::NO_VALUE);
835
    }
836
837
    return true;
838
}
839
840
2
}  // namespace Actions