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