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