1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2010  The Mana Developers
4  *  Copyright (C) 2011-2019  The ManaPlus Developers
5  *  Copyright (C) 2019-2021  Andrei Karas
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/windows/socialwindow.h"
24 
25 #include "gui/popups/popupmenu.h"
26 
27 #include "gui/windows/setupwindow.h"
28 
29 #include "gui/widgets/button.h"
30 #include "gui/widgets/label.h"
31 #include "gui/widgets/tabbedarea.h"
32 
33 #include "gui/widgets/tabs/socialattacktab.h"
34 #include "gui/widgets/tabs/socialfriendstab.h"
35 #include "gui/widgets/tabs/socialguildtab.h"
36 #include "gui/widgets/tabs/socialnavigationtab.h"
37 #include "gui/widgets/tabs/socialpartytab.h"
38 #include "gui/widgets/tabs/socialpickuptab.h"
39 #include "gui/widgets/tabs/socialplayerstab.h"
40 
41 #ifdef TMWA_SUPPORT
42 #include "gui/widgets/tabs/socialguildtab2.h"
43 
44 #include "net/tmwa/guildmanager.h"
45 #endif  // TMWA_SUPPORT
46 
47 #include "debug.h"
48 
49 SocialWindow *socialWindow = nullptr;
50 
SocialWindow()51 SocialWindow::SocialWindow() :
52     // TRANSLATORS: social window name
53     Window(_("Social"), Modal_false, nullptr, "social.xml"),
54     ActionListener(),
55     PlayerRelationsListener(),
56     mGuildInvited(0),
57     mGuildAcceptDialog(nullptr),
58     mGuildCreateDialog(nullptr),
59     mPartyInviter(),
60     mGuilds(),
61     mParties(),
62     mPartyAcceptDialog(nullptr),
63     mAttackFilter(nullptr),
64     mPickupFilter(nullptr),
65     // TRANSLATORS: here P is title for visible players tab in social window
66     mPlayers(new SocialPlayersTab(this, _("P"),
67         fromBool(getOptionBool("showtabbackground", false), Opaque))),
68     mNavigation(new SocialNavigationTab(this,
69         fromBool(getOptionBool("showtabbackground", false), Opaque))),
70     // TRANSLATORS: here F is title for friends tab in social window
71     mFriends(new SocialFriendsTab(this, _("F"),
72         fromBool(getOptionBool("showtabbackground", false), Opaque))),
73     // TRANSLATORS: social window button
74     mMenuButton(new Button(this, _("Menu"), "menu", BUTTON_SKIN, this)),
75     mCountLabel(new Label(this, "1000 / 1000")),
76     mTabs(CREATEWIDGETR(TabbedArea, this)),
77     mMap(nullptr),
78     mLastUpdateTime(0),
79     mPartyId(0),
80     mNeedUpdate(false),
81     mProcessedPortals(false)
82 {
83 }
84 
postInit()85 void SocialWindow::postInit()
86 {
87     Window::postInit();
88     setWindowName("Social");
89     setVisible(Visible_false);
90     setSaveVisible(true);
91     setResizable(true);
92     setSaveVisible(true);
93     setCloseButton(true);
94     setStickyButtonLock(true);
95 
96     setMinWidth(120);
97     setMinHeight(55);
98     setDefaultSize(590, 200, 180, 300);
99     if (setupWindow != nullptr)
100         setupWindow->registerWindowForReset(this);
101 
102     place(0, 0, mMenuButton, 1, 1);
103     place(0, 1, mCountLabel, 1, 1);
104     place(0, 2, mTabs, 2, 4);
105 
106     widgetResized(Event(nullptr));
107 
108     loadWindowState();
109 
110     mTabs->addTab(mPlayers, mPlayers->mScroll);
111     mTabs->addTab(mFriends, mFriends->mScroll);
112     mTabs->addTab(mNavigation, mNavigation->mScroll);
113     mTabs->setSelectable(false);
114     mTabs->getTabContainer()->setSelectable(false);
115     mTabs->getWidgetContainer()->setSelectable(false);
116 
117     if (config.getBoolValue("enableAttackFilter"))
118     {
119         mAttackFilter = new SocialAttackTab(this,
120             fromBool(getOptionBool("showtabbackground", false), Opaque));
121         mTabs->addTab(mAttackFilter, mAttackFilter->mScroll);
122     }
123     else
124     {
125         mAttackFilter = nullptr;
126     }
127 
128     if (config.getBoolValue("enablePickupFilter"))
129     {
130         mPickupFilter = new SocialPickupTab(this,
131             fromBool(getOptionBool("showtabbackground", false), Opaque));
132         mTabs->addTab(mPickupFilter, mPickupFilter->mScroll);
133     }
134     else
135     {
136         mPickupFilter = nullptr;
137     }
138 
139     if ((localPlayer != nullptr) && (localPlayer->getParty() != nullptr))
140         addTab(localPlayer->getParty());
141 
142     if ((localPlayer != nullptr) && (localPlayer->getGuild() != nullptr))
143         addTab(localPlayer->getGuild());
144 
145     enableVisibleSound(true);
146     updateButtons();
147     playerRelations.addListener(this);
148 }
149 
~SocialWindow()150 SocialWindow::~SocialWindow()
151 {
152     playerRelations.removeListener(this);
153     if (mGuildAcceptDialog != nullptr)
154     {
155         mGuildAcceptDialog->close();
156         mGuildAcceptDialog->scheduleDelete();
157         mGuildAcceptDialog = nullptr;
158 
159         mGuildInvited = 0;
160     }
161 
162     if (mPartyAcceptDialog != nullptr)
163     {
164         mPartyAcceptDialog->close();
165         mPartyAcceptDialog->scheduleDelete();
166         mPartyAcceptDialog = nullptr;
167 
168         mPartyInviter.clear();
169     }
170     delete2(mPlayers)
171     delete2(mNavigation)
172     delete2(mAttackFilter)
173     delete2(mPickupFilter)
174     delete2(mFriends)
175     FOR_EACH (GuildMap::iterator, it, mGuilds)
176     {
177         delete (*it).second;
178     }
179     mGuilds.clear();
180     FOR_EACH (PartyMap::iterator, it, mParties)
181     {
182         delete (*it).second;
183     }
184     mParties.clear();
185 }
186 
addTab(Guild * const guild)187 bool SocialWindow::addTab(Guild *const guild)
188 {
189     if (guild == nullptr)
190         return false;
191 
192     if (mGuilds.find(guild) != mGuilds.end())
193         return false;
194 
195     SocialTab *tab = nullptr;
196     if (guild->getServerGuild())
197     {
198         tab = new SocialGuildTab(this, guild,
199             fromBool(getOptionBool("showtabbackground", false), Opaque));
200     }
201 #ifdef TMWA_SUPPORT
202     else
203     {
204         tab = new SocialGuildTab2(this, guild,
205             fromBool(getOptionBool("showtabbackground", false), Opaque));
206     }
207 #endif  // TMWA_SUPPORT
208 
209     mGuilds[guild] = tab;
210     mTabs->addTab(tab, tab->mScroll);
211 
212     updateButtons();
213 
214     return true;
215 }
216 
removeTab(Guild * const guild)217 bool SocialWindow::removeTab(Guild *const guild)
218 {
219     const GuildMap::iterator it = mGuilds.find(guild);
220     if (it == mGuilds.end())
221         return false;
222 
223     mTabs->removeTab(it->second);
224     delete it->second;
225     mGuilds.erase(it);
226 
227     updateButtons();
228 
229     return true;
230 }
231 
addTab(Party * const party)232 bool SocialWindow::addTab(Party *const party)
233 {
234     if (party == nullptr)
235         return false;
236 
237     if (mParties.find(party) != mParties.end())
238         return false;
239 
240     SocialPartyTab *const tab = new SocialPartyTab(this, party,
241         fromBool(getOptionBool("showtabbackground", false), Opaque));
242     mParties[party] = tab;
243 
244     mTabs->addTab(tab, tab->mScroll);
245 
246     updateButtons();
247 
248     return true;
249 }
250 
removeTab(Party * const party)251 bool SocialWindow::removeTab(Party *const party)
252 {
253     const PartyMap::iterator it = mParties.find(party);
254     if (it == mParties.end())
255         return false;
256 
257     mTabs->removeTab(it->second);
258     delete it->second;
259     mParties.erase(it);
260 
261     updateButtons();
262 
263     return true;
264 }
265 
action(const ActionEvent & event)266 void SocialWindow::action(const ActionEvent &event)
267 {
268     const std::string &eventId = event.getId();
269 
270     if (event.getSource() == mPartyAcceptDialog)
271     {
272         if (eventId == "yes")
273         {
274             if (localChatTab != nullptr)
275             {
276                 localChatTab->chatLog(
277                     // TRANSLATORS: chat message
278                     strprintf(_("Accepted party invite from %s."),
279                     mPartyInviter.c_str()),
280                     ChatMsgType::BY_SERVER,
281                     IgnoreRecord_false,
282                     TryRemoveColors_true);
283             }
284             partyHandler->inviteResponse(mPartyId, true);
285         }
286         else if (eventId == "no")
287         {
288             if (localChatTab != nullptr)
289             {
290                 localChatTab->chatLog(
291                     // TRANSLATORS: chat message
292                     strprintf(_("Rejected party invite from %s."),
293                     mPartyInviter.c_str()),
294                     ChatMsgType::BY_SERVER,
295                     IgnoreRecord_false,
296                     TryRemoveColors_true);
297             }
298             partyHandler->inviteResponse(mPartyId, false);
299         }
300 
301         mPartyInviter.clear();
302         mPartyAcceptDialog = nullptr;
303     }
304     else if (event.getSource() == mGuildAcceptDialog)
305     {
306         if (eventId == "yes")
307         {
308             if (localChatTab != nullptr)
309             {
310                 localChatTab->chatLog(
311                     // TRANSLATORS: chat message
312                     strprintf(_("Accepted guild invite from %s."),
313                     mPartyInviter.c_str()),
314                     ChatMsgType::BY_SERVER,
315                     IgnoreRecord_false,
316                     TryRemoveColors_true);
317             }
318 #ifdef TMWA_SUPPORT
319             if (guildManager == nullptr || !GuildManager::getEnableGuildBot())
320                 guildHandler->inviteResponse(mGuildInvited, true);
321             else
322                 GuildManager::inviteResponse(true);
323 #else  // TMWA_SUPPORT
324 
325             guildHandler->inviteResponse(mGuildInvited, true);
326 #endif  // TMWA_SUPPORT
327         }
328         else if (eventId == "no")
329         {
330             if (localChatTab != nullptr)
331             {
332                 localChatTab->chatLog(
333                     // TRANSLATORS: chat message
334                     strprintf(_("Rejected guild invite from %s."),
335                     mPartyInviter.c_str()),
336                     ChatMsgType::BY_SERVER,
337                     IgnoreRecord_false,
338                     TryRemoveColors_true);
339             }
340 #ifdef TMWA_SUPPORT
341             if (guildManager == nullptr || !GuildManager::getEnableGuildBot())
342                 guildHandler->inviteResponse(mGuildInvited, false);
343             else
344                 GuildManager::inviteResponse(false);
345 #else  // TMWA_SUPPORT
346 
347             guildHandler->inviteResponse(mGuildInvited, false);
348 #endif  // TMWA_SUPPORT
349         }
350 
351         mGuildInvited = 0;
352         mGuildAcceptDialog = nullptr;
353     }
354     else if (eventId == "party")
355     {
356         popupMenu->showPartyPopup();
357     }
358     else if (eventId == "guild")
359     {
360         popupMenu->showGuildPopup();
361     }
362     else if (eventId == "attack")
363     {
364         popupMenu->showAttackPopup();
365     }
366     else if (eventId == "friends")
367     {
368         popupMenu->showFriendsPopup();
369     }
370     else if (eventId == "navigation")
371     {
372         popupMenu->showNavigationPopup();
373     }
374     else if (eventId == "pickup")
375     {
376         popupMenu->showPickupPopup();
377     }
378     else if (eventId == "players")
379     {
380         popupMenu->showPlayersPopup();
381     }
382 }
383 
showGuildInvite(const std::string & restrict guildName,const int guildId,const std::string & restrict inviterName)384 void SocialWindow::showGuildInvite(const std::string &restrict guildName,
385                                    const int guildId,
386                                    const std::string &restrict inviterName)
387 {
388     // check there isnt already an invite showing
389     if (mGuildInvited != 0)
390     {
391         if (localChatTab != nullptr)
392         {
393             // TRANSLATORS: chat message
394             localChatTab->chatLog(_("Received guild request, but one already "
395                 "exists."),
396                 ChatMsgType::BY_SERVER,
397                 IgnoreRecord_false,
398                 TryRemoveColors_true);
399         }
400         return;
401     }
402 
403     const std::string msg = strprintf(
404         // TRANSLATORS: chat message
405         _("%s has invited you to join the guild %s."),
406         inviterName.c_str(), guildName.c_str());
407 
408     if (localChatTab != nullptr)
409     {
410         localChatTab->chatLog(msg,
411             ChatMsgType::BY_SERVER,
412             IgnoreRecord_false,
413             TryRemoveColors_true);
414     }
415 
416     CREATEWIDGETV(mGuildAcceptDialog, ConfirmDialog,
417         // TRANSLATORS: guild invite message
418         _("Accept Guild Invite"),
419         msg,
420         SOUND_REQUEST,
421         false,
422         Modal_false,
423         this);
424     mGuildAcceptDialog->addActionListener(this);
425     mGuildInvited = guildId;
426 }
427 
showPartyInvite(const std::string & restrict partyName,const std::string & restrict inviter,const int partyId)428 void SocialWindow::showPartyInvite(const std::string &restrict partyName,
429                                    const std::string &restrict inviter,
430                                    const int partyId)
431 {
432     // check there isnt already an invite showing
433     if (!mPartyInviter.empty())
434     {
435         if (localChatTab != nullptr)
436         {
437             // TRANSLATORS: chat message
438             localChatTab->chatLog(_("Received party request, but one already "
439                 "exists."),
440                 ChatMsgType::BY_SERVER,
441                 IgnoreRecord_false,
442                 TryRemoveColors_true);
443         }
444         return;
445     }
446 
447     std::string msg;
448     if (inviter.empty())
449     {
450         if (partyName.empty())
451         {
452             // TRANSLATORS: party invite message
453             msg = _("You have been invited you to join a party.");
454         }
455         else
456         {
457             // TRANSLATORS: party invite message
458             msg = strprintf(_("You have been invited to join the %s party."),
459                 partyName.c_str());
460         }
461     }
462     else
463     {
464         if (partyName.empty())
465         {
466             // TRANSLATORS: party invite message
467             msg = strprintf(_("%s has invited you to join their party."),
468                 inviter.c_str());
469         }
470         else
471         {
472             // TRANSLATORS: party invite message
473             msg = strprintf(_("%s has invited you to join the %s party."),
474                 inviter.c_str(), partyName.c_str());
475         }
476     }
477 
478     if (localChatTab != nullptr)
479     {
480         localChatTab->chatLog(msg,
481             ChatMsgType::BY_SERVER,
482             IgnoreRecord_false,
483             TryRemoveColors_true);
484     }
485 
486     // show invite
487     CREATEWIDGETV(mPartyAcceptDialog, ConfirmDialog,
488         // TRANSLATORS: party invite message
489         _("Accept Party Invite"),
490         msg,
491         SOUND_REQUEST,
492         false,
493         Modal_false,
494         this);
495     mPartyAcceptDialog->addActionListener(this);
496     mPartyInviter = inviter;
497     mPartyId = partyId;
498 }
499 
updateActiveList()500 void SocialWindow::updateActiveList()
501 {
502     mNeedUpdate = true;
503 }
504 
slowLogic()505 void SocialWindow::slowLogic()
506 {
507     BLOCK_START("SocialWindow::slowLogic")
508     const time_t nowTime = cur_time;
509     if (mNeedUpdate && nowTime - mLastUpdateTime > 1)
510     {
511         mPlayers->updateList();
512         mFriends->updateList();
513         mNeedUpdate = false;
514         mLastUpdateTime = nowTime;
515     }
516     else if (nowTime - mLastUpdateTime > 5)
517     {
518         mPlayers->updateList();
519         mNeedUpdate = false;
520         mLastUpdateTime = nowTime;
521     }
522     BLOCK_END("SocialWindow::slowLogic")
523 }
524 
updateAvatar(const std::string & name)525 void SocialWindow::updateAvatar(const std::string &name)
526 {
527     mPlayers->updateAvatar(name);
528 }
529 
resetDamage(const std::string & name)530 void SocialWindow::resetDamage(const std::string &name)
531 {
532     mPlayers->resetDamage(name);
533 }
534 
updateButtons()535 void SocialWindow::updateButtons()
536 {
537 //    if (!mTabs)
538 //        return;
539 
540 //    const bool hasTabs = mTabs->getNumberOfTabs() > 0;
541 }
542 
updatePortals()543 void SocialWindow::updatePortals()
544 {
545     if (mNavigation != nullptr)
546         mNavigation->updateList();
547 }
548 
updatePortalNames()549 void SocialWindow::updatePortalNames()
550 {
551     if (mNavigation != nullptr)
552         static_cast<SocialNavigationTab*>(mNavigation)->updateNames();
553 }
554 
selectPortal(const unsigned num)555 void SocialWindow::selectPortal(const unsigned num)
556 {
557     if (mNavigation != nullptr)
558         mNavigation->selectIndex(num);
559 }
560 
getPortalIndex(const int x,const int y)561 int SocialWindow::getPortalIndex(const int x, const int y)
562 {
563     if (mNavigation != nullptr)
564     {
565         return static_cast<SocialNavigationTab*>(
566             mNavigation)->getPortalIndex(x, y);
567     }
568     return -1;
569 }
570 
addPortal(const int x,const int y)571 void SocialWindow::addPortal(const int x, const int y)
572 {
573     if (mNavigation != nullptr)
574         static_cast<SocialNavigationTab*>(mNavigation)->addPortal(x, y);
575 }
576 
removePortal(const int x,const int y)577 void SocialWindow::removePortal(const int x, const int y)
578 {
579     if (mNavigation != nullptr)
580         static_cast<SocialNavigationTab*>(mNavigation)->removePortal(x, y);
581 }
582 
nextTab()583 void SocialWindow::nextTab()
584 {
585     if (mTabs != nullptr)
586         mTabs->selectNextTab();
587 }
588 
prevTab()589 void SocialWindow::prevTab()
590 {
591     if (mTabs != nullptr)
592         mTabs->selectPrevTab();
593 }
594 
updateAttackFilter()595 void SocialWindow::updateAttackFilter()
596 {
597     if (mAttackFilter != nullptr)
598         mAttackFilter->updateList();
599 }
600 
updatePickupFilter()601 void SocialWindow::updatePickupFilter()
602 {
603     if (mPickupFilter != nullptr)
604         mPickupFilter->updateList();
605 }
606 
updateParty()607 void SocialWindow::updateParty()
608 {
609     if (localPlayer == nullptr)
610         return;
611 
612     Party *const party = localPlayer->getParty();
613     if (party != nullptr)
614     {
615         const PartyMap::iterator it = mParties.find(party);
616         if (it != mParties.end())
617         {
618             SocialTab *const tab = (*it).second;
619             tab->buildCounter(0, 0);
620         }
621     }
622 }
623 
widgetResized(const Event & event)624 void SocialWindow::widgetResized(const Event &event)
625 {
626     Window::widgetResized(event);
627     if (mTabs != nullptr)
628         mTabs->adjustSize();
629 }
630 
updateCounter(const SocialTab * const tab,const std::string & counter)631 void SocialWindow::updateCounter(const SocialTab *const tab,
632                                  const std::string &counter)
633 {
634     if (mTabs->getSelectedTab() == tab)
635     {
636         mCountLabel->setCaption(counter);
637         mCountLabel->adjustSize();
638     }
639 }
640 
updateMenu(const SocialTab * const tab,const std::string & menu)641 void SocialWindow::updateMenu(const SocialTab *const tab,
642                               const std::string &menu)
643 {
644     if (mTabs->getSelectedTab() == tab)
645         mMenuButton->setActionEventId(menu);
646 }
647 
updateGuildCounter(const int online,const int total)648 void SocialWindow::updateGuildCounter(const int online, const int total)
649 {
650     if (localPlayer == nullptr)
651         return;
652 
653     Guild *const guild = localPlayer->getGuild();
654     if (guild != nullptr)
655     {
656         const GuildMap::iterator it = mGuilds.find(guild);
657         if (it != mGuilds.end())
658         {
659             SocialTab *const tab = (*it).second;
660             tab->buildCounter(online, total);
661         }
662     }
663 }
664 
updatedPlayer(const std::string & name A_UNUSED)665 void SocialWindow::updatedPlayer(const std::string &name A_UNUSED)
666 {
667     mNeedUpdate = true;
668 }
669 
updateAll()670 void SocialWindow::updateAll()
671 {
672     mNeedUpdate = true;
673 }
674 
675 #ifdef USE_PROFILER
logicChildren()676 void SocialWindow::logicChildren()
677 {
678     BLOCK_START("SocialWindow::logicChildren")
679     BasicContainer::logicChildren();
680     BLOCK_END("SocialWindow::logicChildren")
681 }
682 #endif  // USE_PROFILER
683