1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2004-2009  The Mana World Development Team
4  *  Copyright (C) 2009-2010  The Mana Developers
5  *  Copyright (C) 2011-2019  The ManaPlus Developers
6  *  Copyright (C) 2019-2021  Andrei Karas
7  *
8  *  This file is part of The ManaPlus Client.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "gui/popups/popupmenu.h"
25 
26 #include "actormanager.h"
27 #include "configuration.h"
28 #include "party.h"
29 #include "settings.h"
30 
31 #include "being/flooritem.h"
32 #include "being/localplayer.h"
33 #include "being/playerinfo.h"
34 #include "being/playerrelation.h"
35 #include "being/playerrelations.h"
36 
37 #include "const/spells.h"
38 
39 #include "enums/resources/map/mapitemtype.h"
40 
41 #include "input/inputmanager.h"
42 
43 #include "gui/buttontext.h"
44 #include "gui/gui.h"
45 #include "gui/popupmanager.h"
46 #include "gui/viewport.h"
47 #include "gui/windowmenu.h"
48 
49 #include "gui/windows/chatwindow.h"
50 #include "gui/windows/inventorywindow.h"
51 #include "gui/windows/minimap.h"
52 #include "gui/windows/ministatuswindow.h"
53 #include "gui/windows/npcdialog.h"
54 #include "gui/windows/outfitwindow.h"
55 #include "gui/windows/socialwindow.h"
56 #include "gui/windows/textcommandeditor.h"
57 #include "gui/windows/textdialog.h"
58 #include "gui/windows/tradewindow.h"
59 
60 #include "gui/widgets/button.h"
61 #include "gui/widgets/createwidget.h"
62 #include "gui/widgets/progressbar.h"
63 #include "gui/widgets/scrollarea.h"
64 #include "gui/widgets/staticbrowserbox.h"
65 #include "gui/widgets/textfield.h"
66 
67 #include "gui/widgets/tabs/chat/whispertab.h"
68 
69 #include "net/adminhandler.h"
70 #include "net/chathandler.h"
71 #include "net/guildhandler.h"
72 #include "net/npchandler.h"
73 #include "net/net.h"
74 
75 #ifdef TMWA_SUPPORT
76 #include "net/tmwa/guildmanager.h"
77 #endif  // TMWA_SUPPORT
78 
79 #include "resources/chatobject.h"
80 #include "resources/groupinfo.h"
81 #include "resources/iteminfo.h"
82 #include "resources/itemmenuitem.h"
83 
84 #include "resources/db/groupdb.h"
85 #include "resources/db/npcdb.h"
86 
87 #include "resources/item/item.h"
88 
89 #include "resources/map/map.h"
90 #include "resources/map/mapitem.h"
91 #include "resources/map/speciallayer.h"
92 
93 #include "resources/skill/skillinfo.h"
94 
95 #include "utils/checkutils.h"
96 #include "utils/foreach.h"
97 #include "utils/gettext.h"
98 
99 #include "debug.h"
100 
101 std::string tradePartnerName;
102 
103 PopupMenu *popupMenu = nullptr;
104 
PopupMenu()105 PopupMenu::PopupMenu() :
106     Popup("PopupMenu", "popupmenu.xml"),
107     mBrowserBox(new StaticBrowserBox(this, Opaque_true,
108         "popupbrowserbox.xml")),
109     mScrollArea(nullptr),
110     mBeingId(BeingId_zero),
111     mFloorItemId(BeingId_zero),
112     mItemId(0),
113     mItemIndex(-1),
114     mItemColor(ItemColor_one),
115     mMapItem(nullptr),
116     mTab(nullptr),
117     mSpell(nullptr),
118     mCallerWindow(nullptr),
119     mRenameListener(),
120     mPlayerListener(),
121     mDialog(nullptr),
122     mButton(nullptr),
123     mGroup(nullptr),
124     mName(),
125     mExtName(),
126     mTextField(nullptr),
127     mType(ActorType::Unknown),
128     mSubType(BeingTypeId_zero),
129     mX(0),
130     mY(0),
131     mAllowCleanMenu(true)
132 {
133     mBrowserBox->setOpaque(Opaque_false);
134     mBrowserBox->setLinkHandler(this);
135     mRenameListener.setMapItem(nullptr);
136     mRenameListener.setDialog(nullptr);
137     mPlayerListener.setNick("");
138     mPlayerListener.setDialog(nullptr);
139     mPlayerListener.setType(ActorType::Unknown);
140     mScrollArea = new ScrollArea(this,
141         mBrowserBox, Opaque_false, std::string());
142     mScrollArea->setVerticalScrollPolicy(ScrollArea::SHOW_AUTO);
143     addMouseListener(this);
144 }
145 
postInit()146 void PopupMenu::postInit()
147 {
148     Popup::postInit();
149     add(mScrollArea);
150 }
151 
initPopup()152 void PopupMenu::initPopup()
153 {
154     if (localPlayer == nullptr)
155         return;
156     const int groupId = localPlayer->getGroupId();
157     mGroup = GroupDb::getGroup(groupId);
158 }
159 
isAllowCommand(const ServerCommandTypeT command)160 bool PopupMenu::isAllowCommand(const ServerCommandTypeT command)
161 {
162     if (mGroup == nullptr)
163         return false;
164 #ifdef TMWA_SUPPORT
165     // allow any commands for legacy if group > 0
166     if (Net::getNetworkType() == ServerType::TMWATHENA &&
167         localPlayer != nullptr &&
168         localPlayer->isGM())
169     {
170         return true;
171     }
172 #endif
173     if (mGroup->mPermissions[CAST_SIZE(ServerPermissionType::all_commands)] ==
174         Enable_true)
175     {
176         return true;
177     }
178     const ServerCommandEnable::Type enabled =
179         mGroup->mCommands[CAST_SIZE(command)];
180     return (enabled & ServerCommandEnable::Self) != 0;
181 }
182 
isAllowOtherCommand(const ServerCommandTypeT command)183 bool PopupMenu::isAllowOtherCommand(const ServerCommandTypeT command)
184 {
185     if (mGroup == nullptr ||
186         localPlayer == nullptr)
187         return false;
188 #ifdef TMWA_SUPPORT
189     // allow any commands for legacy if group > 0
190     if (Net::getNetworkType() == ServerType::TMWATHENA &&
191         localPlayer->isGM())
192     {
193         return true;
194     }
195 #endif
196     const ServerCommandEnable::Type enabled =
197         mGroup->mCommands[CAST_SIZE(command)];
198     if (mName == localPlayer->getName())
199         return (enabled & ServerCommandEnable::Self) != 0;
200     return (enabled & ServerCommandEnable::Other) != 0;
201 }
202 
showPopup(const int x,const int y,const Being * const being)203 void PopupMenu::showPopup(const int x, const int y, const Being *const being)
204 {
205     if (being == nullptr ||
206         localPlayer == nullptr ||
207         actorManager == nullptr)
208     {
209         return;
210     }
211 
212     initPopup();
213     mBeingId = being->getId();
214     mName = being->getName();
215     mExtName = being->getExtName();
216     mType = being->getType();
217     mSubType = being->getSubType();
218     mBrowserBox->clearRows();
219     mX = x;
220     mY = y;
221 
222     const std::string &name = mName;
223     if (being->getType() != ActorType::SkillUnit)
224     {
225         mBrowserBox->addRow(name + being->getGenderSignWithSpace(),
226             false);
227     }
228 
229     switch (being->getType())
230     {
231         case ActorType::Player:
232         {
233             // TRANSLATORS: popup menu item
234             // TRANSLATORS: trade with player
235             mBrowserBox->addRow("/trade 'NAME'", _("Trade"));
236             // TRANSLATORS: popup menu item
237             // TRANSLATORS: trade attack player
238             mBrowserBox->addRow("/attack 'NAME'", _("Attack"));
239             // TRANSLATORS: popup menu item
240             // TRANSLATORS: send whisper to player
241             mBrowserBox->addRow("/whispertext 'NAME'", _("Whisper"));
242             addMailCommands();
243             addGmCommands();
244             mBrowserBox->addSeparator("##3---");
245 
246 #ifdef TMWA_SUPPORT
247             if (Net::getNetworkType() == ServerType::TMWATHENA)
248             {
249                 // TRANSLATORS: popup menu item
250                 // TRANSLATORS: heal player
251                 mBrowserBox->addRow("/heal :'BEINGID'", _("Heal"));
252                 mBrowserBox->addSeparator("##3---");
253             }
254 #endif  // TMWA_SUPPORT
255 
256             addPlayerRelation(name);
257             mBrowserBox->addSeparator("##3---");
258 
259             addFollow();
260             addPartyName(being->getPartyName());
261 
262             const Guild *const guild1 = being->getGuild();
263             const Guild *const guild2 = localPlayer->getGuild();
264             if (guild2 != nullptr)
265             {
266                 if (guild1 != nullptr)
267                 {
268                     if (guild1->getId() == guild2->getId())
269                     {
270                         mBrowserBox->addRow("/kickguild 'NAME'",
271                             // TRANSLATORS: popup menu item
272                             // TRANSLATORS: kick player from guild
273                             _("Kick from guild"));
274                         if (guild2->getServerGuild())
275                         {
276                             mBrowserBox->addRow(strprintf(
277                                 "@@guild-pos|%s >@@",
278                                 // TRANSLATORS: popup menu item
279                                 // TRANSLATORS: change player position in guild
280                                 _("Change pos in guild")),
281                                 false);
282                         }
283                     }
284                 }
285                 else if (guild2->getMember(mName) != nullptr)
286                 {
287                     mBrowserBox->addRow("/kickguild 'NAME'",
288                         // TRANSLATORS: popup menu item
289                         // TRANSLATORS: kick player from guild
290                         _("Kick from guild"));
291                     if (guild2->getServerGuild())
292                     {
293                         mBrowserBox->addRow(strprintf(
294                             "@@guild-pos|%s >@@",
295                             // TRANSLATORS: popup menu item
296                             // TRANSLATORS: change player position in guild
297                             _("Change pos in guild")),
298                             false);
299                     }
300                 }
301                 else
302                 {
303 #ifdef TMWA_SUPPORT
304                     if (guild2->getServerGuild() ||
305                         (guildManager != nullptr && guildManager->havePower()))
306 #endif  // TMWA_SUPPORT
307                     {
308                         mBrowserBox->addRow("/guild 'NAME'",
309                             // TRANSLATORS: popup menu item
310                             // TRANSLATORS: invite player to guild
311                             _("Invite to guild"));
312                     }
313                 }
314             }
315 
316             // TRANSLATORS: popup menu item
317             // TRANSLATORS: set player invisible for self by id
318             mBrowserBox->addRow("/nuke 'NAME'", _("Nuke"));
319             // TRANSLATORS: popup menu item
320             // TRANSLATORS: move to player location
321             mBrowserBox->addRow("/navigateto 'NAME'", _("Move"));
322             addPlayerMisc();
323             addBuySell(being);
324             addChat(being);
325             break;
326         }
327 
328         case ActorType::Npc:
329             if (!addBeingMenu())
330             {
331                 // TRANSLATORS: popup menu item
332                 // TRANSLATORS: talk with npc
333                 mBrowserBox->addRow("/talk :'BEINGID'", _("Talk"));
334 #ifdef TMWA_SUPPORT
335                 if (Net::getNetworkType() != ServerType::TMWATHENA)
336 #endif
337                 {
338                     mBrowserBox->addRow("/whispertext NPC:'NAME'",
339                         // TRANSLATORS: popup menu item
340                         // TRANSLATORS: whisper to npc
341                         _("Whisper"));
342                 }
343                 // TRANSLATORS: popup menu item
344                 // TRANSLATORS: buy from npc
345                 mBrowserBox->addRow("/buy 'NAME'", _("Buy"));
346                 // TRANSLATORS: popup menu item
347                 // TRANSLATORS: sell to npc
348                 mBrowserBox->addRow("/sell 'NAME'", _("Sell"));
349             }
350 
351             addGmCommands();
352             mBrowserBox->addSeparator("##3---");
353             // TRANSLATORS: popup menu item
354             // TRANSLATORS: move to npc location
355             mBrowserBox->addRow("/navigateto 'NAME'", _("Move"));
356             // TRANSLATORS: popup menu item
357             // TRANSLATORS: add comment to npc
358             mBrowserBox->addRow("addcomment", _("Add comment"));
359             addChat(being);
360             break;
361 
362         case ActorType::Monster:
363         {
364             // Monsters can be attacked
365             // TRANSLATORS: popup menu item
366             // TRANSLATORS: attack monster
367             mBrowserBox->addRow("/attack :'BEINGID'", _("Attack"));
368             addCatchPetCommands();
369             addGmCommands();
370             mBrowserBox->addSeparator("##3---");
371 
372             if (config.getBoolValue("enableAttackFilter"))
373             {
374                 mBrowserBox->addSeparator("##3---");
375                 if (actorManager->isInAttackList(name)
376                     || actorManager->isInIgnoreAttackList(name)
377                     || actorManager->isInPriorityAttackList(name))
378                 {
379                     mBrowserBox->addRow("/removeattack 'NAME'",
380                         // TRANSLATORS: remove monster from attack list
381                         // TRANSLATORS: popup menu item
382                         _("Remove from attack list"));
383                 }
384                 else
385                 {
386                     mBrowserBox->addRow("/addpriorityattack 'NAME'",
387                         // TRANSLATORS: popup menu item
388                         // TRANSLATORS: add monster to priotiry attack list
389                         _("Add to priority attack list"));
390                     mBrowserBox->addRow("/addattack 'NAME'",
391                         // TRANSLATORS: popup menu item
392                         // TRANSLATORS: add monster to attack list
393                         _("Add to attack list"));
394                     mBrowserBox->addRow("/addignoreattack 'NAME'",
395                         // TRANSLATORS: popup menu item
396                         // TRANSLATORS: add monster to ignore list
397                         _("Add to ignore list"));
398                 }
399             }
400             break;
401         }
402 
403         case ActorType::Mercenary:
404             // TRANSLATORS: popup menu item
405             // TRANSLATORS: Mercenary move to master
406             mBrowserBox->addRow("/mercenarytomaster", _("Move to master"));
407             addGmCommands();
408             mBrowserBox->addSeparator("##3---");
409             // TRANSLATORS: popup menu item
410             // TRANSLATORS: fire mercenary
411             mBrowserBox->addRow("/firemercenary", _("Fire"));
412             mBrowserBox->addSeparator("##3---");
413             break;
414 
415         case ActorType::Homunculus:
416         {
417             const HomunculusInfo *const info = PlayerInfo::getHomunculus();
418             if (info != nullptr)
419             {
420                 mBrowserBox->addRow("/homunculustomaster",
421                     // TRANSLATORS: popup menu item
422                     // TRANSLATORS: homunculus move to master
423                     _("Move to master"));
424                 // TRANSLATORS: popup menu item
425                 // TRANSLATORS: feed homunculus
426                 mBrowserBox->addRow("/homunculusfeed", _("Feed"));
427                 mBrowserBox->addSeparator("##3---");
428                 // TRANSLATORS: popup menu item
429                 // TRANSLATORS: pet rename item
430                 mBrowserBox->addRow("/sethomunname", _("Rename"));
431                 mBrowserBox->addSeparator("##3---");
432                 // TRANSLATORS: popup menu item
433                 // TRANSLATORS: delete homunculus
434                 mBrowserBox->addRow("/homunculusfire", _("Kill"));
435                 mBrowserBox->addSeparator("##3---");
436             }
437             addGmCommands();
438             break;
439         }
440         case ActorType::Pet:
441             if (being->getOwner() == localPlayer)
442             {
443                 // TRANSLATORS: popup menu item
444                 // TRANSLATORS: feed pet
445                 mBrowserBox->addRow("/petfeed", _("Feed"));
446                 // TRANSLATORS: popup menu item
447                 // TRANSLATORS: pet drop loot
448                 mBrowserBox->addRow("/petdroploot", _("Drop loot"));
449                 // TRANSLATORS: popup menu item
450                 // TRANSLATORS: pet unequip item
451                 mBrowserBox->addRow("/petunequip", _("Unequip"));
452                 addGmCommands();
453                 mBrowserBox->addSeparator("##3---");
454                 // TRANSLATORS: popup menu item
455                 // TRANSLATORS: pet rename item
456                 mBrowserBox->addRow("/setpetname", _("Rename"));
457                 mBrowserBox->addSeparator("##3---");
458                 const BeingInfo *const info = being->getInfo();
459                 std::string msg;
460                 if (info != nullptr)
461                     msg = info->getString(0);
462                 if (!msg.empty())
463                 {
464                     mBrowserBox->addRow("/petreturnegg", msg.c_str());
465                 }
466                 else
467                 {
468                     mBrowserBox->addRow("/petreturnegg",
469                         // TRANSLATORS: popup menu item
470                         // TRANSLATORS: pet return to egg
471                         _("Return to egg"));
472                 }
473                 mBrowserBox->addSeparator("##3---");
474             }
475             else
476             {
477                 addCatchPetCommands();
478             }
479             break;
480         case ActorType::SkillUnit:
481         {
482             const BeingId id = being->getCreatorId();
483             std::string creatorName;
484             Being *const creator = actorManager->findBeing(id);
485             if (creator != nullptr)
486                 creatorName = creator->getName();
487             else
488                 creatorName = actorManager->getSeenPlayerById(id);
489 
490             if (creatorName.empty())
491                 creatorName = strprintf("?%d", CAST_S32(id));
492 
493             mBrowserBox->addRow(strprintf("%s, %d (%s)",
494                 name.c_str(),
495                 CAST_S32(being->getSubType()),
496                 creatorName.c_str()),
497                 false);
498             break;
499         }
500         case ActorType::Avatar:
501         case ActorType::Unknown:
502         case ActorType::FloorItem:
503         case ActorType::Portal:
504         case ActorType::Elemental:
505         default:
506             break;
507     }
508     // TRANSLATORS: popup menu item
509     // TRANSLATORS: add being name to chat
510     mBrowserBox->addRow("/addtext 'NAME'", _("Add name to chat"));
511     mBrowserBox->addSeparator("##3---");
512 
513     // TRANSLATORS: popup menu item
514     // TRANSLATORS: close menu
515     mBrowserBox->addRow("cancel", _("Cancel"));
516 
517     showPopup(x, y);
518 }
519 
addBeingMenu()520 bool PopupMenu::addBeingMenu()
521 {
522     Being *being = actorManager->findBeing(mBeingId);
523     if (being == nullptr)
524         return false;
525 
526     BeingInfo *const info = NPCDB::get(fromInt(
527         being->getSubType(), BeingTypeId));
528     if (info == nullptr)
529         return false;
530 
531     const STD_VECTOR<BeingMenuItem> &menu = info->getMenu();
532     FOR_EACH (STD_VECTOR<BeingMenuItem>::const_iterator, it, menu)
533     {
534         const BeingMenuItem &item = *it;
535         mBrowserBox->addRow("/" + item.command, item.name.c_str());
536     }
537     return true;
538 }
539 
setMousePos()540 void PopupMenu::setMousePos()
541 {
542     if (viewport != nullptr)
543     {
544         mX = viewport->mMouseX;
545         mY = viewport->mMouseY;
546     }
547     else
548     {
549         Gui::getMouseState(mX, mY);
550     }
551 }
552 
setMousePos2()553 void PopupMenu::setMousePos2()
554 {
555     if (mX == 0 && mY == 0)
556     {
557         if (viewport != nullptr)
558         {
559             mX = viewport->mMouseX;
560             mY = viewport->mMouseY;
561         }
562         else
563         {
564             Gui::getMouseState(mX, mY);
565         }
566     }
567 }
568 
showPopup(const int x,const int y,const STD_VECTOR<ActorSprite * > & beings)569 void PopupMenu::showPopup(const int x, const int y,
570                           const STD_VECTOR<ActorSprite*> &beings)
571 {
572     initPopup();
573     mX = x;
574     mY = y;
575     mBrowserBox->clearRows();
576     // TRANSLATORS: popup menu header
577     mBrowserBox->addRow(_("Players"), false);
578     FOR_EACH (STD_VECTOR<ActorSprite*>::const_iterator, it, beings)
579     {
580         const Being *const being = dynamic_cast<const Being*>(*it);
581         const ActorSprite *const actor = *it;
582         if ((being != nullptr) && !being->getName().empty())
583         {
584             mBrowserBox->addRow(strprintf("@@player_%u|%s >@@",
585                 CAST_U32(being->getId()), (being->getName()
586                 + being->getGenderSignWithSpace()).c_str()),
587                 false);
588         }
589         else if (actor->getType() == ActorType::FloorItem)
590         {
591             const FloorItem *const floorItem
592                 = static_cast<const FloorItem*>(actor);
593             mBrowserBox->addRow(strprintf("@@flooritem_%u|%s >@@",
594                 CAST_U32(actor->getId()),
595                 floorItem->getName().c_str()),
596                 false);
597         }
598     }
599     mBrowserBox->addSeparator("##3---");
600     // TRANSLATORS: popup menu item
601     // TRANSLATORS: close menu
602     mBrowserBox->addRow("cancel", _("Cancel"));
603     showPopup(x, y);
604 }
605 
showPlayerPopup(const std::string & nick)606 void PopupMenu::showPlayerPopup(const std::string &nick)
607 {
608     if (nick.empty() || (localPlayer == nullptr))
609         return;
610 
611     initPopup();
612     setMousePos();
613     mName = nick;
614     mExtName = nick;
615     mBeingId = BeingId_zero;
616     mType = ActorType::Player;
617     mSubType = BeingTypeId_zero;
618     mBrowserBox->clearRows();
619 
620     const std::string &name = mName;
621 
622     mBrowserBox->addRow(name, false);
623 
624     // TRANSLATORS: popup menu item
625     // TRANSLATORS: send whisper to player
626     mBrowserBox->addRow("/whispertext 'NAME'", _("Whisper"));
627     addMailCommands();
628     addGmCommands();
629     mBrowserBox->addSeparator("##3---");
630 
631     addPlayerRelation(name);
632     mBrowserBox->addSeparator("##3---");
633 
634     addFollow();
635     // TRANSLATORS: popup menu item
636     // TRANSLATORS: add comment to player
637     mBrowserBox->addRow("addcomment", _("Add comment"));
638 
639     if (localPlayer->isInParty())
640     {
641         const Party *const party = localPlayer->getParty();
642         if (party != nullptr)
643         {
644             const PartyMember *const member = party->getMember(mName);
645             if (member != nullptr)
646             {
647                 // TRANSLATORS: popup menu item
648                 // TRANSLATORS: kick player from party
649                 mBrowserBox->addRow("/kickparty 'NAME'", _("Kick from party"));
650                 mBrowserBox->addSeparator("##3---");
651                 const PartyMember *const o = party->getMember(
652                     localPlayer->getName());
653                 showAdoptCommands();
654                 if ((o != nullptr) && member->getMap() == o->getMap())
655                 {
656                     // TRANSLATORS: popup menu item
657                     // TRANSLATORS: move to player position
658                     mBrowserBox->addRow("/navigate 'X' 'Y'", _("Move"));
659                 }
660             }
661         }
662     }
663 
664     const Guild *const guild2 = localPlayer->getGuild();
665     if (guild2 != nullptr)
666     {
667         if (guild2->getMember(mName) != nullptr)
668         {
669 #ifdef TMWA_SUPPORT
670             if (guild2->getServerGuild() || (guildManager != nullptr
671                 && guildManager->havePower()))
672 #endif  // TMWA_SUPPORT
673             {
674                 mBrowserBox->addRow("/kickguild 'NAME'",
675                     // TRANSLATORS: popup menu item
676                     // TRANSLATORS: kick player from guild
677                     _("Kick from guild"));
678             }
679             if (guild2->getServerGuild())
680             {
681                 mBrowserBox->addRow(strprintf(
682                     // TRANSLATORS: popup menu item
683                     // TRANSLATORS: change player position in guild
684                     "@@guild-pos|%s >@@", _("Change pos in guild")),
685                     false);
686             }
687         }
688         else
689         {
690 #ifdef TMWA_SUPPORT
691             if (guild2->getServerGuild() || (guildManager != nullptr
692                 && guildManager->havePower()))
693 #endif  // TMWA_SUPPORT
694             {
695                 // TRANSLATORS: popup menu item
696                 // TRANSLATORS: invite player to guild
697                 mBrowserBox->addRow("/guild 'NAME'", _("Invite to guild"));
698             }
699         }
700     }
701 
702     addBuySellDefault();
703     // TRANSLATORS: popup menu item
704     // TRANSLATORS: add player name to chat
705     mBrowserBox->addRow("/addtext 'NAME'", _("Add name to chat"));
706     mBrowserBox->addSeparator("##3---");
707     // TRANSLATORS: popup menu item
708     // TRANSLATORS: close menu
709     mBrowserBox->addRow("cancel", _("Cancel"));
710 
711     showPopup(mX, mY);
712 }
713 
showPopup(const int x,const int y,const FloorItem * const floorItem)714 void PopupMenu::showPopup(const int x, const int y,
715                           const FloorItem *const floorItem)
716 {
717     if (floorItem == nullptr)
718         return;
719 
720     initPopup();
721     mX = x;
722     mY = y;
723     mFloorItemId = floorItem->getId();
724     mItemId = floorItem->getItemId();
725     mType = ActorType::FloorItem;
726     mSubType = BeingTypeId_zero;
727     for (int f = 0; f < maxCards; f ++)
728         mItemCards[f] = floorItem->getCard(f);
729     mBrowserBox->clearRows();
730     const std::string name = floorItem->getName();
731     mName = name;
732     mExtName = name;
733 
734     mBrowserBox->addRow(name, false);
735 
736     if (config.getBoolValue("enablePickupFilter"))
737     {
738         if (actorManager->isInPickupList(name)
739             || (actorManager->isInPickupList("")
740             && !actorManager->isInIgnorePickupList(name)))
741         {
742             // TRANSLATORS: popup menu item
743             // TRANSLATORS: pickup item from ground
744             mBrowserBox->addRow("/pickup 'FLOORID'", _("Pick up"));
745             mBrowserBox->addSeparator("##3---");
746         }
747         addPickupFilter(name);
748     }
749     else
750     {
751         // TRANSLATORS: popup menu item
752         // TRANSLATORS: pickup item from ground
753         mBrowserBox->addRow("/pickup 'FLOORID'", _("Pick up"));
754     }
755     addProtection();
756     // TRANSLATORS: popup menu item
757     // TRANSLATORS: add item name to chat
758     mBrowserBox->addRow("/addchat 'FLOORID''CARDS'", _("Add to chat"));
759     mBrowserBox->addSeparator("##3---");
760     addGmCommands();
761 
762     // TRANSLATORS: popup menu item
763     // TRANSLATORS: close menu
764     mBrowserBox->addRow("cancel", _("Cancel"));
765 
766     showPopup(mX, mY);
767 }
768 
showPopup(const int x,const int y,MapItem * const mapItem)769 void PopupMenu::showPopup(const int x, const int y, MapItem *const mapItem)
770 {
771     if (mapItem == nullptr)
772         return;
773 
774     initPopup();
775     mMapItem = mapItem;
776     mX = x;
777     mY = y;
778 
779     mBrowserBox->clearRows();
780 
781     // TRANSLATORS: popup menu header
782     mBrowserBox->addRow(_("Map Item"), false);
783     // TRANSLATORS: popup menu item
784     // TRANSLATORS: rename map item
785     mBrowserBox->addRow("rename map", _("Rename"));
786     // TRANSLATORS: popup menu item
787     // TRANSLATORS: remove map item
788     mBrowserBox->addRow("remove map", _("Remove"));
789 
790     if (isAllowCommand(ServerCommandType::slide))
791     {
792         mBrowserBox->addSeparator("##3---");
793         // TRANSLATORS: popup menu item
794         // TRANSLATORS: warp to map item
795         mBrowserBox->addRow("/slide 'MAPX' 'MAPY'", _("Warp"));
796     }
797     mBrowserBox->addSeparator("##3---");
798     // TRANSLATORS: popup menu item
799     // TRANSLATORS: close menu
800     mBrowserBox->addRow("cancel", _("Cancel"));
801 
802     showPopup(x, y);
803 }
804 
showMapPopup(const int x,const int y,const int x2,const int y2,const bool isMinimap)805 void PopupMenu::showMapPopup(const int x, const int y,
806                              const int x2, const int y2,
807                              const bool isMinimap)
808 {
809     initPopup();
810     mX = x2;
811     mY = y2;
812 
813     if (isMinimap)
814         mCallerWindow = minimap;
815 
816     mBrowserBox->clearRows();
817 
818     // TRANSLATORS: popup menu header
819     mBrowserBox->addRow(_("Map Item"), false);
820 
821     if (isAllowCommand(ServerCommandType::slide))
822     {
823         // TRANSLATORS: popup menu item
824         // TRANSLATORS: warp to map item
825         mBrowserBox->addRow("/slide 'MAPX' 'MAPY'", _("Warp"));
826     }
827     // TRANSLATORS: popup menu item
828     // TRANSLATORS: move to map item
829     mBrowserBox->addRow("/navigate 'X' 'Y'", _("Move"));
830     // TRANSLATORS: popup menu item
831     // TRANSLATORS: move camera to map item
832     mBrowserBox->addRow("/movecamera 'X' 'Y'", _("Move camera"));
833     if (settings.cameraMode != 0)
834     {
835         // TRANSLATORS: popup menu item
836         // TRANSLATORS: restore camera to default view
837         mBrowserBox->addRow("/restorecamera", _("Restore camera"));
838     }
839     mBrowserBox->addSeparator("##3---");
840     if (isMinimap)
841     {
842         // TRANSLATORS: popup menu item
843         mBrowserBox->addRow("window close", _("Close"));
844     }
845     mBrowserBox->addSeparator("##3---");
846     // TRANSLATORS: popup menu item
847     // TRANSLATORS: close menu
848     mBrowserBox->addRow("cancel", _("Cancel"));
849 
850     showPopup(x, y);
851 }
852 
showOutfitsWindowPopup(const int x,const int y)853 void PopupMenu::showOutfitsWindowPopup(const int x, const int y)
854 {
855     initPopup();
856     mX = x;
857     mY = y;
858     mCallerWindow = outfitWindow;
859 
860     mBrowserBox->clearRows();
861 
862     // TRANSLATORS: popup menu header
863     mBrowserBox->addRow(_("Outfits"), false);
864     // TRANSLATORS: popup menu item
865     // TRANSLATORS: copy selected outfit to chat input
866     mBrowserBox->addRow("/outfittochat", _("Add to chat"));
867     mBrowserBox->addSeparator("##3---");
868     // TRANSLATORS: popup menu item
869     // TRANSLATORS: copy equipment from player to outfit
870     mBrowserBox->addRow("/itemstooutfit", _("Copy from player"));
871     mBrowserBox->addSeparator("##3---");
872     // TRANSLATORS: popup menu item
873     // TRANSLATORS: clear selected outfit
874     mBrowserBox->addRow("/clearoutfit", _("Clear outfit"));
875     mBrowserBox->addSeparator("##3---");
876 
877     addWindowMenu(outfitWindow);
878     mBrowserBox->addSeparator("##3---");
879 
880     // TRANSLATORS: popup menu item
881     // TRANSLATORS: close menu
882     mBrowserBox->addRow("cancel", _("Cancel"));
883 
884     showPopup(x, y);
885 }
886 
showSpellPopup(const int x,const int y,TextCommand * const cmd)887 void PopupMenu::showSpellPopup(const int x, const int y,
888                                TextCommand *const cmd)
889 {
890     if (cmd == nullptr)
891         return;
892 
893     initPopup();
894     mBrowserBox->clearRows();
895 
896     mSpell = cmd;
897     mX = x;
898     mY = y;
899 
900     // TRANSLATORS: popup menu header
901     mBrowserBox->addRow(_("Spells"), false);
902     // TRANSLATORS: popup menu item
903     // TRANSLATORS: edit selected spell
904     mBrowserBox->addRow("edit spell", _("Edit spell"));
905     mBrowserBox->addSeparator("##3---");
906     // TRANSLATORS: popup menu item
907     // TRANSLATORS: close menu
908     mBrowserBox->addRow("cancel", _("Cancel"));
909 
910     showPopup(x, y);
911 }
912 
showChatPopup(const int x,const int y,ChatTab * const tab)913 void PopupMenu::showChatPopup(const int x, const int y, ChatTab *const tab)
914 {
915     if (tab == nullptr ||
916         actorManager == nullptr ||
917         localPlayer == nullptr)
918     {
919         return;
920     }
921 
922     initPopup();
923     mTab = tab;
924     mX = x;
925     mY = y;
926     mCallerWindow = chatWindow;
927 
928     mBrowserBox->clearRows();
929 
930     const ChatTabTypeT &type = tab->getType();
931     if (type == ChatTabType::WHISPER || type == ChatTabType::CHANNEL)
932     {
933         // TRANSLATORS: popup menu item
934         // TRANSLATORS: close chat tab
935         mBrowserBox->addRow("/close", _("Close"));
936     }
937 
938     // TRANSLATORS: popup menu item
939     // TRANSLATORS: remove all text from chat tab
940     mBrowserBox->addRow("/chatclear", _("Clear"));
941     mBrowserBox->addSeparator("##3---");
942 
943     if (tab->getAllowHighlight())
944     {
945         // TRANSLATORS: popup menu item
946         // TRANSLATORS: disable chat tab highlight
947         mBrowserBox->addRow("/disablehighlight", _("Disable highlight"));
948     }
949     else
950     {
951         // TRANSLATORS: popup menu item
952         // TRANSLATORS: enable chat tab highlight
953         mBrowserBox->addRow("/enablehighlight", _("Enable highlight"));
954     }
955     if (tab->getRemoveNames())
956     {
957         // TRANSLATORS: popup menu item
958         // TRANSLATORS: do not remove player names from chat tab
959         mBrowserBox->addRow("/dontremovename", _("Don't remove name"));
960     }
961     else
962     {
963         // TRANSLATORS: popup menu item
964         // TRANSLATORS: remove player names from chat tab
965         mBrowserBox->addRow("/removename", _("Remove name"));
966     }
967     if (tab->getNoAway())
968     {
969         // TRANSLATORS: popup menu item
970         // TRANSLATORS: enable away messages in chat tab
971         mBrowserBox->addRow("/enableaway", _("Enable away"));
972     }
973     else
974     {
975         // TRANSLATORS: popup menu item
976         // TRANSLATORS: disable away messages in chat tab
977         mBrowserBox->addRow("/disableaway", _("Disable away"));
978     }
979     mBrowserBox->addSeparator("##3---");
980     if (type == ChatTabType::PARTY)
981     {
982         // TRANSLATORS: popup menu item
983         // TRANSLATORS: enable away messages in chat tab
984         mBrowserBox->addRow("/leaveparty", _("Leave"));
985         mBrowserBox->addSeparator("##3---");
986     }
987     // TRANSLATORS: popup menu item
988     // TRANSLATORS: copy selected text to clipboard
989     mBrowserBox->addRow("/chatclipboard 'X' 'Y'", _("Copy to clipboard"));
990     mBrowserBox->addSeparator("##3---");
991 
992     if (type == ChatTabType::WHISPER)
993     {
994         const WhisperTab *const wTab = static_cast<WhisperTab*>(tab);
995         const std::string &name = wTab->getNick();
996 
997         const Being* const being  = actorManager->findBeingByName(
998             name, ActorType::Player);
999 
1000         addGmCommands();
1001         if (being != nullptr)
1002         {
1003             mBeingId = being->getId();
1004             mName = being->getName();
1005             mExtName = being->getExtName();
1006             mType = being->getType();
1007             mSubType = being->getSubType();
1008 
1009             // TRANSLATORS: popup menu item
1010             // TRANSLATORS: trade with player
1011             mBrowserBox->addRow("/trade 'NAME'", _("Trade"));
1012             // TRANSLATORS: popup menu item
1013             // TRANSLATORS: attack player
1014             mBrowserBox->addRow("/attack 'NAME'", _("Attack"));
1015             addMailCommands();
1016             mBrowserBox->addSeparator("##3---");
1017             // TRANSLATORS: popup menu item
1018             // TRANSLATORS: heal player
1019             mBrowserBox->addRow("/heal :'BEINGID'", _("Heal"));
1020             mBrowserBox->addSeparator("##3---");
1021             addPlayerRelation(name);
1022             mBrowserBox->addSeparator("##3---");
1023             addFollow();
1024             // TRANSLATORS: popup menu item
1025             // TRANSLATORS: move to player position
1026             mBrowserBox->addRow("/navigateto 'NAME'", _("Move"));
1027             addPlayerMisc();
1028             addBuySell(being);
1029             mBrowserBox->addSeparator("##3---");
1030             addParty(wTab->getNick());
1031             const Guild *const guild1 = being->getGuild();
1032             const Guild *const guild2 = localPlayer->getGuild();
1033             if (guild2 != nullptr)
1034             {
1035                 if (guild1 != nullptr)
1036                 {
1037                     if (guild1->getId() == guild2->getId())
1038                     {
1039 #ifdef TMWA_SUPPORT
1040                         if (guild2->getServerGuild() ||
1041                             (guildManager != nullptr &&
1042                             guildManager->havePower()))
1043 #endif  // TMWA_SUPPORT
1044                         {
1045                             mBrowserBox->addRow("/kickguild 'NAME'",
1046                                 // TRANSLATORS: popup menu item
1047                                 // TRANSLATORS: kick player from guild
1048                                 _("Kick from guild"));
1049                         }
1050                         if (guild2->getServerGuild())
1051                         {
1052                             mBrowserBox->addRow(strprintf("@@guild-pos|%s >@@",
1053                                 // TRANSLATORS: popup menu item
1054                                 // TRANSLATORS: change player position in guild
1055                                  _("Change pos in guild")),
1056                                 false);
1057                         }
1058                     }
1059                 }
1060                 else
1061                 {
1062 #ifdef TMWA_SUPPORT
1063                     if (guild2->getServerGuild() || (guildManager != nullptr
1064                         && guildManager->havePower()))
1065 #endif  // TMWA_SUPPORT
1066                     {
1067                         mBrowserBox->addRow("/guild 'NAME'",
1068                             // TRANSLATORS: popup menu item
1069                             // TRANSLATORS: invite player to guild
1070                             _("Invite to guild"));
1071                     }
1072                 }
1073             }
1074         }
1075         else
1076         {
1077             mBeingId = BeingId_zero;
1078             mName = name;
1079             mExtName = name;
1080             mType = ActorType::Player;
1081             mSubType = BeingTypeId_zero;
1082             addPlayerRelation(name);
1083             mBrowserBox->addSeparator("##3---");
1084             addMailCommands();
1085             addFollow();
1086 
1087             if (localPlayer->isInParty())
1088             {
1089                 const Party *const party = localPlayer->getParty();
1090                 if (party != nullptr)
1091                 {
1092                     const PartyMember *const m = party->getMember(mName);
1093                     if (m != nullptr)
1094                     {
1095                         // TRANSLATORS: popup menu item
1096                         // TRANSLATORS: move to player location
1097                         mBrowserBox->addRow("/navigateto 'NAME'", _("Move"));
1098                     }
1099                 }
1100             }
1101             addPlayerMisc();
1102             addBuySellDefault();
1103             if (Net::getNetworkType() != ServerType::TMWATHENA)
1104                 addParty(wTab->getNick());
1105             mBrowserBox->addSeparator("##3---");
1106         }
1107     }
1108 
1109     addWindowMenu(chatWindow);
1110     // TRANSLATORS: popup menu item
1111     // TRANSLATORS: close menu
1112     mBrowserBox->addRow("cancel", _("Cancel"));
1113 
1114     showPopup(x, y);
1115 }
1116 
showChangePos(const int x,const int y)1117 void PopupMenu::showChangePos(const int x, const int y)
1118 {
1119     initPopup();
1120     mBrowserBox->clearRows();
1121     // TRANSLATORS: popup menu header
1122     mBrowserBox->addRow(_("Change guild position"), false);
1123 
1124     if (localPlayer == nullptr)
1125         return;
1126 
1127     mX = x;
1128     mY = y;
1129     const Guild *const guild = localPlayer->getGuild();
1130     if (guild != nullptr)
1131     {
1132         const PositionsMap &map = guild->getPositions();
1133         FOR_EACH (PositionsMap::const_iterator, itr, map)
1134         {
1135             mBrowserBox->addRow(strprintf("@@guild-pos-%u|%s@@",
1136                 itr->first, itr->second.c_str()),
1137                 false);
1138         }
1139         // TRANSLATORS: popup menu item
1140         // TRANSLATORS: close menu
1141         mBrowserBox->addRow("cancel", _("Cancel"));
1142 
1143         showPopup(x, y);
1144     }
1145     else
1146     {
1147         mBeingId = BeingId_zero;
1148         mFloorItemId = BeingId_zero;
1149         mItemIndex = -1;
1150         mItemId = 0;
1151         for (int f = 0; f < maxCards; f ++)
1152             mItemCards[f] = 0;
1153         mMapItem = nullptr;
1154         mName.clear();
1155         mExtName.clear();
1156         mType = ActorType::Unknown;
1157         mSubType = BeingTypeId_zero;
1158         mX = 0;
1159         mY = 0;
1160         setVisible(Visible_false);
1161     }
1162 }
1163 
showWindowPopup(Window * const window)1164 void PopupMenu::showWindowPopup(Window *const window)
1165 {
1166     if (window == nullptr)
1167         return;
1168 
1169     initPopup();
1170     setMousePos();
1171     mCallerWindow = window;
1172     mBrowserBox->clearRows();
1173     // TRANSLATORS: popup menu header
1174     mBrowserBox->addRow(_("window"), false);
1175 
1176     addWindowMenu(window);
1177 
1178     // TRANSLATORS: popup menu item
1179     // TRANSLATORS: close menu
1180     mBrowserBox->addRow("cancel", _("Cancel"));
1181 
1182     showPopup(mX, mY);
1183 }
1184 
addWindowMenu(const Window * const window)1185 void PopupMenu::addWindowMenu(const Window *const window)
1186 {
1187     if (window == nullptr)
1188         return;
1189 
1190     initPopup();
1191     if (window->getAlowClose())
1192     {
1193         // TRANSLATORS: popup menu item
1194         // TRANSLATORS: close window
1195         mBrowserBox->addRow("window close", _("Close"));
1196     }
1197 
1198     if (window->isStickyButtonLock())
1199     {
1200         if (window->isSticky())
1201         {
1202             // TRANSLATORS: popup menu item
1203             // TRANSLATORS: unlock window
1204             mBrowserBox->addRow("window unlock", _("Unlock"));
1205         }
1206         else
1207         {
1208             // TRANSLATORS: popup menu item
1209             // TRANSLATORS: lock window
1210             mBrowserBox->addRow("window lock", _("Lock"));
1211         }
1212     }
1213 }
1214 
showEmoteType()1215 void PopupMenu::showEmoteType()
1216 {
1217     initPopup();
1218     setMousePos();
1219 
1220     mBrowserBox->clearRows();
1221     // TRANSLATORS: popup menu header
1222     mBrowserBox->addRow(_("Show emotes for:"), false);
1223 
1224     // TRANSLATORS: popup menu item
1225     // TRANSLATORS: show emotes for player
1226     mBrowserBox->addRow("/setemotetype player", _("Player"));
1227 
1228     // TRANSLATORS: popup menu item
1229     // TRANSLATORS: show emotes for pet
1230     mBrowserBox->addRow("/setemotetype pet", _("Pet"));
1231 
1232     if (Net::getNetworkType() == ServerType::EVOL2)
1233     {
1234         // TRANSLATORS: popup menu item
1235         // TRANSLATORS: show emotes for homuncules
1236         mBrowserBox->addRow("/setemotetype homun", _("Homunculus"));
1237 
1238         // TRANSLATORS: popup menu item
1239         // TRANSLATORS: show emotes for mercenary
1240         mBrowserBox->addRow("/setemotetype merc", _("Mercenary"));
1241     }
1242     mBrowserBox->addSeparator("##3---");
1243 
1244     // TRANSLATORS: popup menu item
1245     // TRANSLATORS: close menu
1246     mBrowserBox->addRow("cancel", _("Cancel"));
1247 
1248     showPopup(mX, mY);
1249 }
1250 
handleLink(const std::string & link,MouseEvent * event A_UNUSED)1251 void PopupMenu::handleLink(const std::string &link,
1252                            MouseEvent *event A_UNUSED)
1253 {
1254     Being *being = nullptr;
1255     if (actorManager != nullptr)
1256         being = actorManager->findBeing(mBeingId);
1257 
1258     mAllowCleanMenu = true;
1259 
1260     if (link == "remove map" && (mMapItem != nullptr))
1261     {
1262         if (viewport != nullptr)
1263         {
1264             const Map *const map = viewport->getMap();
1265             if (map != nullptr)
1266             {
1267                 SpecialLayer *const specialLayer = map->getSpecialLayer();
1268                 if (specialLayer != nullptr)
1269                 {
1270                     const bool isHome = (mMapItem->getType()
1271                         == CAST_S32(MapItemType::HOME));
1272                     const int x = static_cast<int>(mMapItem->getX());
1273                     const int y = static_cast<int>(mMapItem->getY());
1274                     specialLayer->setTile(x, y,
1275                         CAST_S32(MapItemType::EMPTY));
1276                     specialLayer->updateCache();
1277                     if (socialWindow != nullptr)
1278                         socialWindow->removePortal(x, y);
1279                     if (isHome && (localPlayer != nullptr))
1280                     {
1281                         localPlayer->removeHome();
1282                         localPlayer->saveHomes();
1283                     }
1284                 }
1285             }
1286         }
1287     }
1288     else if (link == "rename map" && (mMapItem != nullptr))
1289     {
1290         mRenameListener.setMapItem(mMapItem);
1291         CREATEWIDGETV(mDialog, TextDialog,
1292             // TRANSLATORS: dialog caption
1293             // TRANSLATORS: number of chars in string should be near original
1294             _("Rename map sign          "),
1295             // TRANSLATORS: label
1296             // TRANSLATORS: number of chars in string should be near original
1297             _("Name:                    "),
1298             nullptr,
1299             false);
1300         mRenameListener.setDialog(mDialog);
1301         mDialog->setText(mMapItem->getComment());
1302         mDialog->setActionEventId("ok");
1303         mDialog->addActionListener(&mRenameListener);
1304     }
1305     else if (link == "edit spell" && (mSpell != nullptr))
1306     {
1307         CREATEWIDGET(TextCommandEditor, mSpell);
1308     }
1309     else if (link == "addcomment" && !mName.empty())
1310     {
1311         TextDialog *const dialog = CREATEWIDGETR(TextDialog,
1312             // TRANSLATORS: dialog caption
1313             // TRANSLATORS: number of chars in string should be near original
1314             _("Player comment            "),
1315             // TRANSLATORS: label
1316             // TRANSLATORS: number of chars in string should be near original
1317             _("Comment:                      "),
1318             nullptr,
1319             false);
1320         mPlayerListener.setDialog(dialog);
1321         mPlayerListener.setNick(mName);
1322         mPlayerListener.setType(mType);
1323 
1324         if (being != nullptr)
1325         {
1326             being->updateComment();
1327             dialog->setText(being->getComment());
1328         }
1329         else
1330         {
1331             dialog->setText(Being::loadComment(mName,
1332                 static_cast<ActorTypeT>(mType)));
1333         }
1334         dialog->setActionEventId("ok");
1335         dialog->addActionListener(&mPlayerListener);
1336     }
1337     else if (link == "guild-pos" && !mName.empty())
1338     {
1339         showChangePos(getX(), getY());
1340         return;
1341     }
1342     else if (link == "clipboard copy")
1343     {
1344         if (mTextField != nullptr)
1345             mTextField->handleCopy();
1346     }
1347     else if (link == "clipboard paste")
1348     {
1349         if (mTextField != nullptr)
1350             mTextField->handlePaste();
1351     }
1352     else if (link == "gm" && !mName.empty())
1353     {
1354         showGMPopup(mName);
1355         return;
1356     }
1357     else if (link == "mute" && !mName.empty())
1358     {
1359         showMuteCommands();
1360         return;
1361     }
1362     else if (link == "show" && !mName.empty())
1363     {
1364         showPlayerMenu();
1365         return;
1366     }
1367     else if (link == "window close" && (mCallerWindow != nullptr))
1368     {
1369         if (Widget::widgetExists(mCallerWindow))
1370             mCallerWindow->close();
1371     }
1372     else if (link == "window unlock" && (mCallerWindow != nullptr))
1373     {
1374         if (Widget::widgetExists(mCallerWindow))
1375             mCallerWindow->setSticky(false);
1376     }
1377     else if (link == "window lock" && (mCallerWindow != nullptr))
1378     {
1379         if (Widget::widgetExists(mCallerWindow))
1380             mCallerWindow->setSticky(true);
1381     }
1382     else if (link == "join chat" && (being != nullptr))
1383     {
1384         const ChatObject *const chat = being->getChat();
1385         if (chat != nullptr)
1386             chatHandler->joinChat(chat, "");
1387     }
1388     else if (link == "craftmenu")
1389     {
1390         showCraftPopup();
1391         return;
1392     }
1393     else if (link.compare(0, 10, "guild-pos-") == 0)
1394     {
1395         if (localPlayer != nullptr)
1396         {
1397             const Guild *const guild = localPlayer->getGuild();
1398             if (guild != nullptr)
1399             {
1400                 const int num = atoi(link.substr(10).c_str());
1401                 guildHandler->changeMemberPostion(
1402                     guild->getMember(mName), num);
1403             }
1404         }
1405     }
1406     else if (link.compare(0, 7, "player_") == 0)
1407     {
1408         if (actorManager != nullptr)
1409         {
1410             mBeingId = fromInt(atoi(link.substr(7).c_str()), BeingId);
1411             being = actorManager->findBeing(mBeingId);
1412             if (being != nullptr)
1413             {
1414                 showPopup(getX(), getY(), being);
1415                 return;
1416             }
1417         }
1418     }
1419     else if (link.compare(0, 10, "flooritem_") == 0)
1420     {
1421         if (actorManager != nullptr)
1422         {
1423             const BeingId id = fromInt(atoi(
1424                 link.substr(10).c_str()), BeingId);
1425             if (id != BeingId_zero)
1426             {
1427                 const FloorItem *const item = actorManager->findItem(id);
1428                 if (item != nullptr)
1429                 {
1430                     mFloorItemId = item->getId();
1431                     mItemId = item->getItemId();
1432                     for (int f = 0; f < maxCards; f ++)
1433                         mItemCards[f] = item->getCard(f);
1434                     showPopup(getX(), getY(), item);
1435                     return;
1436                 }
1437             }
1438         }
1439     }
1440     else if (link.compare(0, 12, "hide button_") == 0)
1441     {
1442         if (windowMenu != nullptr)
1443             windowMenu->showButton(link.substr(12), Visible_false);
1444     }
1445     else if (link.compare(0, 12, "show button_") == 0)
1446     {
1447         if (windowMenu != nullptr)
1448             windowMenu->showButton(link.substr(12), Visible_true);
1449     }
1450     else if (link.compare(0, 9, "hide bar_") == 0)
1451     {
1452         if (miniStatusWindow != nullptr)
1453             miniStatusWindow->showBar(link.substr(9), Visible_false);
1454     }
1455     else if (link.compare(0, 9, "show bar_") == 0)
1456     {
1457         if (miniStatusWindow != nullptr)
1458             miniStatusWindow->showBar(link.substr(9), Visible_true);
1459     }
1460     else if (link.compare(0, 12, "show window_") == 0)
1461     {
1462         const int id = atoi(link.substr(12).c_str());
1463         if (id >= 0)
1464             inputManager.executeAction(static_cast<InputActionT>(id));
1465     }
1466     else if (link.compare(0, 6, "mute_+") == 0)
1467     {
1468         if (being != nullptr)
1469         {
1470             const int time = atoi(link.substr(6).c_str());
1471             adminHandler->mute(being, 1, time);
1472         }
1473     }
1474     else if (link.compare(0, 6, "mute_-") == 0)
1475     {
1476         if (being != nullptr)
1477         {
1478             const int time = atoi(link.substr(6).c_str());
1479             adminHandler->mute(being, 0, time);
1480         }
1481     }
1482     else if (!link.empty() && link[0] == '/')
1483     {
1484         std::string cmd = link.substr(1);
1485         replaceAll(cmd, "'NAME'", mName);
1486         replaceAll(cmd, "'ENAME'", escapeString(mName));
1487         replaceAll(cmd, "'EXTNAME'", mExtName);
1488         replaceAll(cmd, "'EEXTNAME'", escapeString(mExtName));
1489         replaceAll(cmd, "'X'", toString(mX));
1490         replaceAll(cmd, "'Y'", toString(mY));
1491         replaceAll(cmd, "'BEINGID'", toString(toInt(mBeingId, int)));
1492         replaceAll(cmd, "'FLOORID'", toString(toInt(mFloorItemId, int)));
1493         replaceAll(cmd, "'ITEMID'", toString(mItemId));
1494         replaceAll(cmd, "'ITEMCOLOR'", toString(toInt(mItemColor, int)));
1495         replaceAll(cmd, "'BEINGTYPEID'", toString(CAST_S32(mType)));
1496         replaceAll(cmd, "'BEINGSUBTYPEID'", toString(CAST_S32(mSubType)));
1497         replaceAll(cmd, "'PLAYER'", localPlayer->getName());
1498         replaceAll(cmd, "'EPLAYER'", escapeString(localPlayer->getName()));
1499         replaceAll(cmd, "'PLAYERX'", toString(localPlayer->getTileX()));
1500         replaceAll(cmd, "'PLAYERY'", toString(localPlayer->getTileY()));
1501         if (mItemIndex >= 0)
1502             replaceAll(cmd, "'INVINDEX'", toString(mItemIndex));
1503         else
1504             replaceAll(cmd, "'INVINDEX'", "0");
1505         if (mMapItem != nullptr)
1506         {
1507             replaceAll(cmd, "'MAPX'", toString(mMapItem->getX()));
1508             replaceAll(cmd, "'MAPY'", toString(mMapItem->getY()));
1509         }
1510         else
1511         {
1512             replaceAll(cmd, "'MAPX'", toString(mX));
1513             replaceAll(cmd, "'MAPY'", toString(mY));
1514         }
1515 
1516         std::string cards;
1517         for (int f = 0; f < maxCards; f ++)
1518         {
1519             const int id = mItemCards[f];
1520             if (id != 0)
1521             {
1522                 cards.append(",");
1523                 cards.append(toString(id));
1524             }
1525         }
1526         replaceAll(cmd, "'CARDS'", cards);
1527         replaceAll(cmd, "'ECARDS'", escapeString(cards));
1528         if (actorManager != nullptr)
1529         {
1530             if (being == nullptr)
1531             {
1532                 being = actorManager->findBeingByName(mName,
1533                     ActorType::Player);
1534             }
1535         }
1536         if (being != nullptr)
1537             replaceAll(cmd, "'PARTY'", being->getPartyName());
1538         else
1539             replaceAll(cmd, "'PARTY'", "");
1540 
1541         const size_t pos = cmd.find(' ');
1542         const std::string type(cmd, 0, pos);
1543         std::string args(cmd, pos == std::string::npos ? cmd.size() : pos + 1);
1544         args = trim(args);
1545         inputManager.executeChatCommand(type, args, mTab);
1546     }
1547     // Unknown actions
1548     else if (link != "cancel")
1549     {
1550         reportAlways("PopupMenu: Warning, unknown action '%s'", link.c_str())
1551     }
1552 
1553     if (!mAllowCleanMenu)
1554         return;
1555 
1556     setVisible(Visible_false);
1557 
1558     mBeingId = BeingId_zero;
1559     mFloorItemId = BeingId_zero;
1560     mItemId = 0;
1561     mItemIndex = -1;
1562     for (int f = 0; f < maxCards; f ++)
1563         mItemCards[f] = 0;
1564     mItemColor = ItemColor_one;
1565     mMapItem = nullptr;
1566     mTab = nullptr;
1567     mSpell = nullptr;
1568     mCallerWindow = nullptr;
1569     mDialog = nullptr;
1570     mButton = nullptr;
1571     mName.clear();
1572     mExtName.clear();
1573     mTextField = nullptr;
1574     mType = ActorType::Unknown;
1575     mSubType = BeingTypeId_zero;
1576     mX = 0;
1577     mY = 0;
1578 }
1579 
showPopup(Window * const parent,const int x,const int y,const Item * const item,const InventoryTypeT type)1580 void PopupMenu::showPopup(Window *const parent,
1581                           const int x, const int y,
1582                           const Item *const item,
1583                           const InventoryTypeT type)
1584 {
1585     if (item == nullptr)
1586         return;
1587 
1588     initPopup();
1589     mItemId = item->getId();
1590     mItemIndex = item->getInvIndex();
1591     for (int f = 0; f < maxCards; f ++)
1592         mItemCards[f] = item->getCard(f);
1593     mItemColor = item->getColor();
1594     mCallerWindow = parent;
1595     mX = x;
1596     mY = y;
1597     mName.clear();
1598     mExtName.clear();
1599     mBrowserBox->clearRows();
1600 
1601     const int cnt = item->getQuantity();
1602     const bool isProtected = PlayerInfo::isItemProtected(mItemId);
1603 
1604     switch (type)
1605     {
1606         case InventoryType::Inventory:
1607             if (tradeWindow != nullptr &&
1608                 tradeWindow->isWindowVisible() &&
1609                 !isProtected)
1610             {
1611                 // TRANSLATORS: popup menu item
1612                 // TRANSLATORS: add item to trade
1613                 mBrowserBox->addRow("/addtrade 'INVINDEX'", _("Add to trade"));
1614                 if (cnt > 1)
1615                 {
1616                     if (cnt > 10)
1617                     {
1618                         mBrowserBox->addRow("/addtrade 'INVINDEX' 10",
1619                             // TRANSLATORS: popup menu item
1620                             // TRANSLATORS: add 10 item amount to trade
1621                             _("Add to trade 10"));
1622                     }
1623                     mBrowserBox->addRow("/addtrade 'INVINDEX' /",
1624                         // TRANSLATORS: popup menu item
1625                         // TRANSLATORS: add half item amount to trade
1626                         _("Add to trade half"));
1627                     mBrowserBox->addRow("/addtrade 'INVINDEX' -1",
1628                         // TRANSLATORS: popup menu item
1629                         // TRANSLATORS: add all amount except one item to trade
1630                         _("Add to trade all-1"));
1631                     mBrowserBox->addRow("/addtrade 'INVINDEX' all",
1632                         // TRANSLATORS: popup menu item
1633                         // TRANSLATORS: add all amount item to trade
1634                         _("Add to trade all"));
1635                 }
1636                 mBrowserBox->addSeparator("##3---");
1637             }
1638             if (InventoryWindow::isStorageActive())
1639             {
1640                 // TRANSLATORS: popup menu item
1641                 // TRANSLATORS: add item to storage
1642                 mBrowserBox->addRow("/invtostorage 'INVINDEX'", _("Store"));
1643                 if (cnt > 1)
1644                 {
1645                     if (cnt > 10)
1646                     {
1647                         mBrowserBox->addRow("/invtostorage 'INVINDEX' 10",
1648                             // TRANSLATORS: popup menu item
1649                             // TRANSLATORS: add 10 item amount to storage
1650                             _("Store 10"));
1651                     }
1652                     mBrowserBox->addRow("/invtostorage 'INVINDEX' /",
1653                         // TRANSLATORS: popup menu item
1654                         // TRANSLATORS: add half item amount to storage
1655                         _("Store half"));
1656                     mBrowserBox->addRow("/invtostorage 'INVINDEX' -1",
1657                         // TRANSLATORS: popup menu item
1658                         // TRANSLATORS: add all except one item to storage
1659                         _("Store all-1"));
1660                     mBrowserBox->addRow("/invtostorage 'INVINDEX' all",
1661                         // TRANSLATORS: popup menu item
1662                         // TRANSLATORS: add all item amount to storage
1663                         _("Store all"));
1664                 }
1665                 mBrowserBox->addSeparator("##3---");
1666             }
1667             if (npcHandler != nullptr)
1668             {
1669                 NpcDialog *const dialog = npcHandler->getCurrentNpcDialog();
1670                 if ((dialog != nullptr) &&
1671                     dialog->getInputState() == NpcInputState::ITEM_CRAFT)
1672                 {
1673                     mBrowserBox->addRow("craftmenu",
1674                         // TRANSLATORS: popup menu item
1675                         // TRANSLATORS: sub menu for craft
1676                         _("Move to craft..."));
1677                 }
1678             }
1679             addItemMenu(item, InventoryType::Inventory);
1680             addDrop(item, isProtected);
1681             break;
1682 
1683         case InventoryType::Storage:
1684             // TRANSLATORS: popup menu item
1685             // TRANSLATORS: get item from storage
1686             mBrowserBox->addRow("/storagetoinv 'INVINDEX'", _("Retrieve"));
1687             if (cnt > 1)
1688             {
1689                 if (cnt > 10)
1690                 {
1691                     mBrowserBox->addRow("/storagetoinv 'INVINDEX' 10",
1692                         // TRANSLATORS: popup menu item
1693                         // TRANSLATORS: get 10 item amount from storage
1694                         _("Retrieve 10"));
1695                 }
1696                 mBrowserBox->addRow("/storagetoinv 'INVINDEX' /",
1697                     // TRANSLATORS: popup menu item
1698                     // TRANSLATORS: get half item amount from storage
1699                     _("Retrieve half"));
1700                 mBrowserBox->addRow("/storagetoinv 'INVINDEX' -1",
1701                     // TRANSLATORS: popup menu item
1702                     // TRANSLATORS: get all except one item amount from storage
1703                     _("Retrieve all-1"));
1704                 mBrowserBox->addRow("/storagetoinv 'INVINDEX' all",
1705                     // TRANSLATORS: popup menu item
1706                     // TRANSLATORS: get all item amount from storage
1707                     _("Retrieve all"));
1708                 mBrowserBox->addSeparator("##3---");
1709             }
1710             addItemMenu(item, InventoryType::Storage);
1711             break;
1712         case InventoryType::Cart:
1713             addItemMenu(item, InventoryType::Cart);
1714             break;
1715 
1716         case InventoryType::Trade:
1717         case InventoryType::Npc:
1718         case InventoryType::Vending:
1719         case InventoryType::MailEdit:
1720         case InventoryType::MailView:
1721         case InventoryType::Craft:
1722         case InventoryType::TypeEnd:
1723         default:
1724             break;
1725     }
1726 
1727 
1728     addProtection();
1729     if (config.getBoolValue("enablePickupFilter"))
1730     {
1731         mName = item->getName();
1732         mExtName = mName;
1733         mBrowserBox->addSeparator("##3---");
1734         addPickupFilter(mName);
1735     }
1736     addGmCommands();
1737     // TRANSLATORS: popup menu item
1738     // TRANSLATORS: add item name to chat
1739     mBrowserBox->addRow("/addchat 'ITEMID''CARDS'", _("Add to chat"));
1740     mBrowserBox->addSeparator("##3---");
1741     // TRANSLATORS: popup menu item
1742     // TRANSLATORS: close menu
1743     mBrowserBox->addRow("cancel", _("Cancel"));
1744 
1745     showPopup(x, y);
1746 }
1747 
showItemPopup(const int x,const int y,const int itemId,const ItemColor color)1748 void PopupMenu::showItemPopup(const int x, const int y,
1749                               const int itemId,
1750                               const ItemColor color)
1751 {
1752     const Inventory *const inv = PlayerInfo::getInventory();
1753     if (inv == nullptr)
1754         return;
1755 
1756     Item *const item = inv->findItem(itemId, color);
1757     if (item != nullptr)
1758     {
1759         showItemPopup(x, y, item);
1760     }
1761     else
1762     {
1763         initPopup();
1764         mItemId = itemId;
1765         mItemIndex = -1;
1766         mItemColor = color;
1767         for (int f = 0; f < maxCards; f ++)
1768             mItemCards[f] = 0;
1769         mX = x;
1770         mY = y;
1771         mBrowserBox->clearRows();
1772 
1773         if (!PlayerInfo::isItemProtected(mItemId))
1774         {
1775             // TRANSLATORS: popup menu item
1776             // TRANSLATORS: use item
1777             mBrowserBox->addRow("/use 'ITEMID'", _("Use"));
1778         }
1779         addProtection();
1780         addGmCommands();
1781         mBrowserBox->addSeparator("##3---");
1782         // TRANSLATORS: popup menu item
1783         // TRANSLATORS: close menu
1784         mBrowserBox->addRow("cancel", _("Cancel"));
1785 
1786         showPopup(x, y);
1787     }
1788 }
1789 
showItemPopup(const int x,const int y,const Item * const item)1790 void PopupMenu::showItemPopup(const int x, const int y,
1791                               const Item *const item)
1792 {
1793     initPopup();
1794     mX = x;
1795     mY = y;
1796     if (item != nullptr)
1797     {
1798         mItemId = item->getId();
1799         mItemColor = item->getColor();
1800         mItemIndex = item->getInvIndex();
1801         for (int f = 0; f < maxCards; f ++)
1802             mItemCards[f] = item->getCard(f);
1803     }
1804     else
1805     {
1806         mItemId = 0;
1807         mItemColor = ItemColor_one;
1808         mItemIndex = -1;
1809         for (int f = 0; f < maxCards; f ++)
1810             mItemCards[f] = 0;
1811     }
1812     mName.clear();
1813     mExtName.clear();
1814     mBrowserBox->clearRows();
1815 
1816     if (item != nullptr)
1817     {
1818         const bool isProtected = PlayerInfo::isItemProtected(mItemId);
1819         addUse(item);
1820         addDrop(item, isProtected);
1821         if (InventoryWindow::isStorageActive())
1822         {
1823             // TRANSLATORS: popup menu item
1824             // TRANSLATORS: add item to storage
1825             mBrowserBox->addRow("/invtostorage 'INVINDEX'", _("Store"));
1826         }
1827         // TRANSLATORS: popup menu item
1828         // TRANSLATORS: add item name to chat
1829         mBrowserBox->addRow("/addchat 'ITEMID''CARDS'", _("Add to chat"));
1830 
1831         if (config.getBoolValue("enablePickupFilter"))
1832         {
1833             mName = item->getName();
1834             mExtName = mName;
1835             mBrowserBox->addSeparator("##3---");
1836             addPickupFilter(mName);
1837         }
1838     }
1839     addProtection();
1840     addGmCommands();
1841     mBrowserBox->addSeparator("##3---");
1842     // TRANSLATORS: popup menu item
1843     // TRANSLATORS: close menu
1844     mBrowserBox->addRow("cancel", _("Cancel"));
1845 
1846     showPopup(x, y);
1847 }
1848 
showDropPopup(const int x,const int y,const Item * const item)1849 void PopupMenu::showDropPopup(const int x,
1850                               const int y,
1851                               const Item *const item)
1852 {
1853     initPopup();
1854     mX = x;
1855     mY = y;
1856     mName.clear();
1857     mExtName.clear();
1858     mBrowserBox->clearRows();
1859 
1860     if (item != nullptr)
1861     {
1862         mItemId = item->getId();
1863         mItemColor = item->getColor();
1864         mItemIndex = item->getInvIndex();
1865         for (int f = 0; f < maxCards; f ++)
1866             mItemCards[f] = item->getCard(f);
1867         const bool isProtected = PlayerInfo::isItemProtected(mItemId);
1868         addUse(item);
1869         addDrop(item, isProtected);
1870         if (InventoryWindow::isStorageActive())
1871         {
1872             // TRANSLATORS: popup menu item
1873             // TRANSLATORS: add item to storage
1874             mBrowserBox->addRow("/invtostorage 'INVINDEX'", _("Store"));
1875         }
1876         addProtection();
1877         // TRANSLATORS: popup menu item
1878         // TRANSLATORS: add item name to chat
1879         mBrowserBox->addRow("/addchat 'ITEMID''CARDS'", _("Add to chat"));
1880         if (config.getBoolValue("enablePickupFilter"))
1881         {
1882             mName = item->getName();
1883             mExtName = mName;
1884             mBrowserBox->addSeparator("##3---");
1885             addPickupFilter(mName);
1886         }
1887     }
1888     addGmCommands();
1889     mBrowserBox->addSeparator("##3---");
1890     // TRANSLATORS: popup menu item
1891     mBrowserBox->addRow("/cleardrops", _("Clear drop window"));
1892     mBrowserBox->addSeparator("##3---");
1893     // TRANSLATORS: popup menu item
1894     // TRANSLATORS: close menu
1895     mBrowserBox->addRow("cancel", _("Cancel"));
1896 
1897     showPopup(x, y);
1898 }
1899 
showPopup(const int x,const int y,Button * const button)1900 void PopupMenu::showPopup(const int x, const int y, Button *const button)
1901 {
1902     if ((button == nullptr) || (windowMenu == nullptr))
1903         return;
1904 
1905     initPopup();
1906     mButton = button;
1907     mX = x;
1908     mY = y;
1909 
1910     mBrowserBox->clearRows();
1911     STD_VECTOR<Button *> names = windowMenu->getButtons();
1912     for (STD_VECTOR<Button *>::const_iterator it = names.begin(),
1913          it_end = names.end(); it != it_end; ++ it)
1914     {
1915         const Button *const btn = *it;
1916         if ((btn == nullptr) || btn->getActionEventId() == "SET")
1917             continue;
1918 
1919         if (btn->mVisible == Visible_true)
1920         {
1921             mBrowserBox->addRow(strprintf("@@hide button_%s|%s %s (%s)@@",
1922                 // TRANSLATORS: popup menu item
1923                 btn->getActionEventId().c_str(), _("Hide"),
1924                 btn->getDescription().c_str(), btn->getCaption().c_str()),
1925                 false);
1926         }
1927         else
1928         {
1929             mBrowserBox->addRow(strprintf("@@show button_%s|%s %s (%s)@@",
1930                 // TRANSLATORS: popup menu item
1931                 btn->getActionEventId().c_str(), _("Show"),
1932                 btn->getDescription().c_str(), btn->getCaption().c_str()),
1933                 false);
1934         }
1935     }
1936     mBrowserBox->addSeparator("##3---");
1937     // TRANSLATORS: popup menu item
1938     // TRANSLATORS: close menu
1939     mBrowserBox->addRow("cancel", _("Cancel"));
1940 
1941     showPopup(x, y);
1942 }
1943 
showPopup(const int x,const int y,const ProgressBar * const b)1944 void PopupMenu::showPopup(const int x, const int y, const ProgressBar *const b)
1945 {
1946     if ((b == nullptr) || (miniStatusWindow == nullptr))
1947         return;
1948 
1949     initPopup();
1950     mName = b->text();
1951     mExtName = mName;
1952     mX = x;
1953     mY = y;
1954 
1955     mBrowserBox->clearRows();
1956     STD_VECTOR <ProgressBar*> bars = miniStatusWindow->getBars();
1957     ProgressBar *onlyBar = nullptr;
1958     int cnt = 0;
1959 
1960     // search for alone visible bar
1961     for (STD_VECTOR <ProgressBar*>::const_iterator it = bars.begin(),
1962          it_end = bars.end(); it != it_end; ++it)
1963     {
1964         ProgressBar *const bar = *it;
1965         if (bar == nullptr)
1966             continue;
1967 
1968         if (bar->mVisible == Visible_true)
1969         {
1970             cnt ++;
1971             onlyBar = bar;
1972         }
1973     }
1974     if (cnt > 1)
1975         onlyBar = nullptr;
1976 
1977     for (STD_VECTOR <ProgressBar*>::const_iterator it = bars.begin(),
1978          it_end = bars.end(); it != it_end; ++it)
1979     {
1980         ProgressBar *const bar = *it;
1981         if ((bar == nullptr) || bar == onlyBar)
1982             continue;
1983 
1984         if (bar->mVisible == Visible_true)
1985         {
1986             mBrowserBox->addRow(strprintf("@@hide bar_%s|%s %s@@",
1987                 // TRANSLATORS: popup menu item
1988                 bar->getActionEventId().c_str(), _("Hide"),
1989                 bar->getId().c_str()),
1990                 false);
1991         }
1992         else
1993         {
1994             mBrowserBox->addRow(strprintf("@@show bar_%s|%s %s@@",
1995                 // TRANSLATORS: popup menu item
1996                 bar->getActionEventId().c_str(), _("Show"),
1997                 bar->getId().c_str()),
1998                 false);
1999         }
2000     }
2001 
2002     mBrowserBox->addSeparator("##3---");
2003     // TRANSLATORS: popup menu item
2004     mBrowserBox->addRow("/yellowbar", _("Open yellow bar settings"));
2005     // TRANSLATORS: popup menu item
2006     mBrowserBox->addRow("/resetmodifiers", _("Reset yellow bar"));
2007     mBrowserBox->addSeparator("##3---");
2008     // TRANSLATORS: popup menu item
2009     // TRANSLATORS: copy status to chat
2010     mBrowserBox->addRow("/bartochat 'NAME'", _("Copy to chat"));
2011     // TRANSLATORS: popup menu item
2012     // TRANSLATORS: close menu
2013     mBrowserBox->addRow("cancel", _("Cancel"));
2014 
2015     showPopup(x, y);
2016 }
2017 
showAttackMonsterPopup(const int x,const int y,const std::string & name,const int type)2018 void PopupMenu::showAttackMonsterPopup(const int x, const int y,
2019                                        const std::string &name,
2020                                        const int type)
2021 {
2022     if ((localPlayer == nullptr) || (actorManager == nullptr))
2023         return;
2024 
2025     initPopup();
2026     mName = name;
2027     mExtName = name;
2028     mType = ActorType::Monster;
2029     mSubType = BeingTypeId_zero;
2030     mX = x;
2031     mY = y;
2032 
2033     mBrowserBox->clearRows();
2034 
2035     if (name.empty())
2036     {
2037         // TRANSLATORS: popup menu header
2038         mBrowserBox->addRow(_("(default)"), false);
2039     }
2040     else
2041     {
2042         mBrowserBox->addRow(name, false);
2043     }
2044     switch (type)
2045     {
2046         case MapItemType::ATTACK:
2047         {
2048             const int idx = actorManager->getAttackMobIndex(name);
2049             const int size = actorManager->getAttackMobsSize();
2050             if (idx > 0)
2051             {
2052                 // TRANSLATORS: popup menu item
2053                 // TRANSLATORS: move attack target up
2054                 mBrowserBox->addRow("/moveattackup 'NAME'", _("Move up"));
2055             }
2056             if (idx + 1 < size)
2057             {
2058                 // TRANSLATORS: popup menu item
2059                 // TRANSLATORS: move attack target down
2060                 mBrowserBox->addRow("/moveattackdown 'NAME'", _("Move down"));
2061             }
2062             break;
2063         }
2064         case MapItemType::PRIORITY:
2065         {
2066             const int idx = actorManager->
2067                 getPriorityAttackMobIndex(name);
2068             const int size = actorManager->getPriorityAttackMobsSize();
2069             if (idx > 0)
2070             {
2071                 mBrowserBox->addRow("/movepriorityattackup 'NAME'",
2072                     // TRANSLATORS: popup menu item
2073                     // TRANSLATORS: move attack target up
2074                     _("Move up"));
2075             }
2076             if (idx + 1 < size)
2077             {
2078                 mBrowserBox->addRow("/movepriorityattackdown 'NAME'",
2079                     // TRANSLATORS: popup menu item
2080                     // TRANSLATORS: move attack target down
2081                     _("Move down"));
2082             }
2083             break;
2084         }
2085         case MapItemType::IGNORE_:
2086         default:
2087             break;
2088     }
2089 
2090     // TRANSLATORS: popup menu item
2091     // TRANSLATORS: remove attack target
2092     mBrowserBox->addRow("/removeattack 'NAME'", _("Remove"));
2093     mBrowserBox->addSeparator("##3---");
2094     // TRANSLATORS: popup menu item
2095     // TRANSLATORS: close menu
2096     mBrowserBox->addRow("cancel", _("Cancel"));
2097 
2098     showPopup(x, y);
2099 }
2100 
showPickupItemPopup(const int x,const int y,const std::string & name)2101 void PopupMenu::showPickupItemPopup(const int x, const int y,
2102                                     const std::string &name)
2103 {
2104     if ((localPlayer == nullptr) || (actorManager == nullptr))
2105         return;
2106 
2107     initPopup();
2108     mName = name;
2109     mExtName = name;
2110     mType = ActorType::FloorItem;
2111     mSubType = BeingTypeId_zero;
2112     mX = x;
2113     mY = y;
2114 
2115     mBrowserBox->clearRows();
2116 
2117     if (name.empty())
2118     {
2119         // TRANSLATORS: popup menu header
2120         mBrowserBox->addRow(_("(default)"), false);
2121     }
2122     else
2123     {
2124         mBrowserBox->addRow(name, false);
2125     }
2126 
2127     // TRANSLATORS: popup menu item
2128     // TRANSLATORS: remove item from pickup filter
2129     mBrowserBox->addRow("/removepickup 'NAME'", _("Remove"));
2130     mBrowserBox->addSeparator("##3---");
2131     // TRANSLATORS: popup menu item
2132     // TRANSLATORS: close menu
2133     mBrowserBox->addRow("cancel", _("Cancel"));
2134 
2135     showPopup(x, y);
2136 }
2137 
showUndressPopup(const int x,const int y,const Being * const being,const Item * const item)2138 void PopupMenu::showUndressPopup(const int x, const int y,
2139                                  const Being *const being,
2140                                  const Item *const item)
2141 {
2142     if ((being == nullptr) || (item == nullptr))
2143         return;
2144 
2145     initPopup();
2146     mBeingId = being->getId();
2147     mItemId = item->getId();
2148     mItemColor = item->getColor();
2149     mItemIndex = item->getInvIndex();
2150     for (int f = 0; f < maxCards; f ++)
2151         mItemCards[f] = item->getCard(f);
2152     mX = x;
2153     mY = y;
2154 
2155     mBrowserBox->clearRows();
2156 
2157     // TRANSLATORS: popup menu item
2158     // TRANSLATORS: undress item from player
2159     mBrowserBox->addRow("/undress :'BEINGID' 'ITEMID'", _("Undress"));
2160     // TRANSLATORS: popup menu item
2161     // TRANSLATORS: add item name to chat
2162     mBrowserBox->addRow("/addchat 'ITEMID''CARDS'", _("Add to chat"));
2163     addGmCommands();
2164     mBrowserBox->addSeparator("##3---");
2165     // TRANSLATORS: popup menu item
2166     // TRANSLATORS: close menu
2167     mBrowserBox->addRow("cancel", _("Cancel"));
2168 
2169     showPopup(x, y);
2170 }
2171 
showTextFieldPopup(TextField * const input)2172 void PopupMenu::showTextFieldPopup(TextField *const input)
2173 {
2174     initPopup();
2175     setMousePos();
2176     mTextField = input;
2177 
2178     mBrowserBox->clearRows();
2179 
2180     // TRANSLATORS: popup menu item
2181     // TRANSLATORS: copy text to clipboard
2182     mBrowserBox->addRow("clipboard copy", _("Copy"));
2183     // TRANSLATORS: popup menu item
2184     // TRANSLATORS: paste text from clipboard
2185     mBrowserBox->addRow("clipboard paste", _("Paste"));
2186     mBrowserBox->addSeparator("##3---");
2187     // TRANSLATORS: popup menu item
2188     // TRANSLATORS: close menu
2189     mBrowserBox->addRow("cancel", _("Cancel"));
2190 
2191     showPopup(mX, mY);
2192 }
2193 
showLinkPopup(const std::string & link)2194 void PopupMenu::showLinkPopup(const std::string &link)
2195 {
2196     initPopup();
2197     setMousePos();
2198     mName = link;
2199     mExtName = link;
2200 
2201     mBrowserBox->clearRows();
2202 
2203     // TRANSLATORS: popup menu item
2204     // TRANSLATORS: open link in browser
2205     mBrowserBox->addRow("/open 'NAME'", _("Open link"));
2206     // TRANSLATORS: popup menu item
2207     // TRANSLATORS: copy link to clipboard
2208     mBrowserBox->addRow("/clipboardcopy 'NAME'", _("Copy to clipboard"));
2209     mBrowserBox->addSeparator("##3---");
2210     // TRANSLATORS: popup menu item
2211     // TRANSLATORS: close menu
2212     mBrowserBox->addRow("cancel", _("Cancel"));
2213 
2214     showPopup(mX, mY);
2215 }
2216 
showWindowsPopup()2217 void PopupMenu::showWindowsPopup()
2218 {
2219     initPopup();
2220     setMousePos();
2221     mBrowserBox->clearRows();
2222     const STD_VECTOR<ButtonText*> &names = windowMenu->getButtonTexts();
2223     // TRANSLATORS: popup menu header
2224     mBrowserBox->addRow(_("Show window"), false);
2225 
2226     FOR_EACH (STD_VECTOR<ButtonText*>::const_iterator, it, names)
2227     {
2228         const ButtonText *const btn = *it;
2229         if (btn == nullptr)
2230             continue;
2231 
2232         mBrowserBox->addRow(strprintf("show window_%d",
2233             CAST_S32(btn->key)),
2234             btn->text.c_str());
2235     }
2236     mBrowserBox->addSeparator("##3---");
2237     // TRANSLATORS: popup menu item
2238     // TRANSLATORS: close menu
2239     mBrowserBox->addRow("cancel", _("Cancel"));
2240 
2241     showPopup(mX, mY);
2242 }
2243 
showNpcDialogPopup(const BeingId npcId,const int x,const int y)2244 void PopupMenu::showNpcDialogPopup(const BeingId npcId,
2245                                    const int x, const int y)
2246 {
2247     initPopup();
2248     mBeingId = npcId;
2249     mX = x;
2250     mY = y;
2251     mBrowserBox->clearRows();
2252     // TRANSLATORS: popup menu item
2253     // TRANSLATORS: copy npc text to clipboard
2254     mBrowserBox->addRow("/npcclipboard 'X' 'Y'", _("Copy to clipboard"));
2255     mBrowserBox->addSeparator("##3---");
2256     // TRANSLATORS: popup menu item
2257     // TRANSLATORS: close menu
2258     mBrowserBox->addRow("cancel", _("Cancel"));
2259 
2260     showPopup(x, y);
2261 }
2262 
showSkillPopup(const SkillInfo * const info)2263 void PopupMenu::showSkillPopup(const SkillInfo *const info)
2264 {
2265     if (info == nullptr)
2266         return;
2267 
2268     initPopup();
2269     setMousePos();
2270 
2271     // using mItemId as skill id
2272     mItemId = info->id;
2273     // using mItemIndex as skill level
2274     mItemIndex = info->level;
2275     mBrowserBox->clearRows();
2276 
2277     // TRANSLATORS: popup menu header
2278     mBrowserBox->addRow(_("Skill"), false);
2279     mBrowserBox->addRow("/addskillshortcut 'ITEMID'",
2280         // TRANSLATORS: popup menu item
2281         // TRANSLATORS: add skill to shortcurs tab
2282         _("Add skill shortcut"));
2283     mBrowserBox->addSeparator("##3---");
2284     mBrowserBox->addRow("/showskilllevels 'ITEMID'",
2285         // TRANSLATORS: popup menu item
2286         // TRANSLATORS: set skill level
2287         _("Skill level..."));
2288     mBrowserBox->addRow("/showskilltypes 'ITEMID'",
2289         // TRANSLATORS: popup menu item
2290         // TRANSLATORS: set skill cast type
2291         _("Skill cast type..."));
2292     mBrowserBox->addRow("/showskilloffsetx 'ITEMID'",
2293         // TRANSLATORS: popup menu item
2294         // TRANSLATORS: set skill cast offset by x
2295         _("Skill offset by x..."));
2296     mBrowserBox->addRow("/showskilloffsety 'ITEMID'",
2297         // TRANSLATORS: popup menu item
2298         // TRANSLATORS: set skill cast offset by y
2299         _("Skill offset by y..."));
2300     // TRANSLATORS: popup menu item
2301     // TRANSLATORS: close menu
2302     mBrowserBox->addRow("cancel", _("Cancel"));
2303 
2304     showPopup(mX, mY);
2305 }
2306 
showSkillOffsetPopup(const SkillInfo * const info,const bool isOffsetX)2307 void PopupMenu::showSkillOffsetPopup(const SkillInfo *const info,
2308                                      const bool isOffsetX)
2309 {
2310     if (info == nullptr)
2311         return;
2312 
2313     initPopup();
2314     setMousePos2();
2315 
2316     // using mItemId as skill id
2317     mItemId = info->id;
2318     // using mItemIndex as skill level
2319     mItemIndex = info->level;
2320     mBrowserBox->clearRows();
2321     char letter = ' ';
2322 
2323     if (isOffsetX)
2324     {
2325         // TRANSLATORS: popup menu header
2326         mBrowserBox->addRow(_("Skill cast offset by x"), false);
2327         letter = 'x';
2328     }
2329     else
2330     {
2331         // TRANSLATORS: popup menu header
2332         mBrowserBox->addRow(_("Skill cast offset by y"), false);
2333         letter = 'y';
2334     }
2335     for (int f = -9; f <= 9; f ++)
2336     {
2337         const std::string command = strprintf(
2338             "/setskilloffset%c 'ITEMID' %d", letter, f);
2339         if (f == 0)
2340         {
2341             mBrowserBox->addRow(command,
2342                 strprintf("%d", f).c_str());
2343         }
2344         else
2345         {
2346             mBrowserBox->addRow(command,
2347                 strprintf("%+d", f).c_str());
2348         }
2349     }
2350     // TRANSLATORS: popup menu item
2351     // TRANSLATORS: close menu
2352     mBrowserBox->addRow("cancel", _("Cancel"));
2353     showPopup(mX, mY);
2354 }
2355 
showSkillLevelPopup(const SkillInfo * const info)2356 void PopupMenu::showSkillLevelPopup(const SkillInfo *const info)
2357 {
2358     if (info == nullptr)
2359         return;
2360 
2361     initPopup();
2362     setMousePos2();
2363 
2364     // using mItemId as skill id
2365     mItemId = info->id;
2366     // using mItemIndex as skill level
2367     mItemIndex = info->level;
2368 
2369     for (int f = 0; f < maxCards; f ++)
2370         mItemCards[f] = 0;
2371     mBrowserBox->clearRows();
2372     // TRANSLATORS: popup menu item
2373     // TRANSLATORS: skill level header
2374     mBrowserBox->addRow(_("Select skill level"), false);
2375     for (int f = 1; f <= mItemIndex; f ++)
2376     {
2377         mBrowserBox->addRow(strprintf("/selectskilllevel %d %d", mItemId, f),
2378             // TRANSLATORS: popup menu item
2379             // TRANSLATORS: skill level
2380             strprintf(_("Level: %d"), f).c_str());
2381     }
2382     mBrowserBox->addRow(strprintf("/selectskilllevel %d 0", mItemId),
2383         // TRANSLATORS: popup menu item
2384         // TRANSLATORS: skill level
2385         _("Max level"));
2386     mBrowserBox->addSeparator("##3---");
2387     // TRANSLATORS: popup menu item
2388     // TRANSLATORS: close menu
2389     mBrowserBox->addRow("cancel", _("Cancel"));
2390 
2391     showPopup(mX, mY);
2392 }
2393 
showSkillTypePopup(const SkillInfo * const info)2394 void PopupMenu::showSkillTypePopup(const SkillInfo *const info)
2395 {
2396     if (info == nullptr)
2397         return;
2398 
2399     initPopup();
2400     setMousePos2();
2401 
2402     // using mItemId as skill id
2403     mItemId = info->id;
2404     // using mItemIndex as skill level
2405     mItemIndex = info->level;
2406 
2407     for (int f = 0; f < maxCards; f ++)
2408         mItemCards[f] = 0;
2409     mBrowserBox->clearRows();
2410 
2411     // TRANSLATORS: popup menu item
2412     // TRANSLATORS: skill cast type header
2413     mBrowserBox->addRow(_("Select skill cast type"), false);
2414     mBrowserBox->addRow(strprintf("/selectskilltype %d 0", mItemId),
2415         // TRANSLATORS: popup menu item
2416         // TRANSLATORS: skill cast type
2417         _("Default"));
2418     mBrowserBox->addRow(strprintf("/selectskilltype %d 1", mItemId),
2419         // TRANSLATORS: popup menu item
2420         // TRANSLATORS: skill cast type
2421         _("Target"));
2422     mBrowserBox->addRow(strprintf("/selectskilltype %d 2", mItemId),
2423         // TRANSLATORS: popup menu item
2424         // TRANSLATORS: skill cast type
2425         _("Mouse position"));
2426     mBrowserBox->addRow(strprintf("/selectskilltype %d 3", mItemId),
2427         // TRANSLATORS: popup menu item
2428         // TRANSLATORS: skill cast type
2429         _("Self"));
2430     mBrowserBox->addSeparator("##3---");
2431     // TRANSLATORS: popup menu item
2432     // TRANSLATORS: close menu
2433     mBrowserBox->addRow("cancel", _("Cancel"));
2434 
2435     showPopup(mX, mY);
2436 }
2437 
showPopup(int x,int y)2438 void PopupMenu::showPopup(int x, int y)
2439 {
2440     PopupManager::hideItemPopup();
2441     const int pad2 = 2 * mPadding;
2442     mBrowserBox->setPosition(mPadding, mPadding);
2443     mScrollArea->setPosition(mPadding, mPadding);
2444     mBrowserBox->updateHeight();
2445     // add padding to initial size before draw browserbox
2446     int height = mBrowserBox->getHeight();
2447     if (height + pad2 >= mainGraphics->getHeight())
2448     {
2449         height = mainGraphics->getHeight() - pad2;
2450         mBrowserBox->setWidth(mBrowserBox->getWidth() + 5);
2451         mScrollArea->setWidth(mBrowserBox->getWidth() + pad2 + 10);
2452         setContentSize(mBrowserBox->getWidth() + pad2 + 20,
2453             height + pad2);
2454     }
2455     else
2456     {
2457         mBrowserBox->setWidth(mBrowserBox->getWidth());
2458         mScrollArea->setWidth(mBrowserBox->getWidth() + pad2);
2459         setContentSize(mBrowserBox->getWidth() + pad2,
2460             height + pad2);
2461     }
2462     if (mainGraphics->mWidth < (x + getWidth() + 5))
2463         x = mainGraphics->mWidth - getWidth();
2464     if (mainGraphics->mHeight < (y + getHeight() + 5))
2465         y = mainGraphics->mHeight - getHeight();
2466     mScrollArea->setHeight(height);
2467     setPosition(x, y);
2468     setVisible(Visible_true);
2469     requestMoveToTop();
2470     mAllowCleanMenu = false;
2471 }
2472 
addNormalRelations()2473 void PopupMenu::addNormalRelations()
2474 {
2475     // TRANSLATORS: popup menu item
2476     // TRANSLATORS: add player to disregarded list
2477     mBrowserBox->addRow("/disregard 'NAME'", _("Disregard"));
2478     // TRANSLATORS: popup menu item
2479     // TRANSLATORS: add player to ignore list
2480     mBrowserBox->addRow("/ignore 'NAME'", _("Ignore"));
2481     // TRANSLATORS: popup menu item
2482     // TRANSLATORS: add player to black list
2483     mBrowserBox->addRow("/blacklist 'NAME'", _("Black list"));
2484     // TRANSLATORS: popup menu item
2485     // TRANSLATORS: add player to enemy list
2486     mBrowserBox->addRow("/enemy 'NAME'", _("Set as enemy"));
2487     // TRANSLATORS: popup menu item
2488     // TRANSLATORS: add player to erased list
2489     mBrowserBox->addRow("/erase 'NAME'", _("Erase"));
2490 }
2491 
addPlayerRelation(const std::string & name)2492 void PopupMenu::addPlayerRelation(const std::string &name)
2493 {
2494     switch (playerRelations.getRelation(name))
2495     {
2496         case Relation::NEUTRAL:
2497             // TRANSLATORS: popup menu item
2498             // TRANSLATORS: add player to friends list
2499             mBrowserBox->addRow("/friend 'NAME'", _("Be friend"));
2500             addNormalRelations();
2501             break;
2502 
2503         case Relation::FRIEND:
2504             addNormalRelations();
2505             break;
2506 
2507         case Relation::BLACKLISTED:
2508             // TRANSLATORS: popup menu item
2509             // TRANSLATORS: remove player from ignore list
2510             mBrowserBox->addRow("/unignore 'NAME'", _("Unignore"));
2511             // TRANSLATORS: popup menu item
2512             // TRANSLATORS: add player to disregarded list
2513             mBrowserBox->addRow("/disregard 'NAME'", _("Disregard"));
2514             // TRANSLATORS: popup menu item
2515             // TRANSLATORS: add player to ignore list
2516             mBrowserBox->addRow("/ignore 'NAME'", _("Ignore"));
2517             // TRANSLATORS: popup menu item
2518             // TRANSLATORS: add player to enemy list
2519             mBrowserBox->addRow("/enemy 'NAME'", _("Set as enemy"));
2520             // TRANSLATORS: popup menu item
2521             // TRANSLATORS: add player to erased list
2522             mBrowserBox->addRow("/erase 'NAME'", _("Erase"));
2523             break;
2524 
2525         case Relation::DISREGARDED:
2526             // TRANSLATORS: popup menu item
2527             // TRANSLATORS: remove player from ignore list
2528             mBrowserBox->addRow("/unignore 'NAME'", _("Unignore"));
2529             // TRANSLATORS: popup menu item
2530             // TRANSLATORS: add player to completle ignore list
2531             mBrowserBox->addRow("/ignore 'NAME'", _("Completely ignore"));
2532             // TRANSLATORS: popup menu item
2533             // TRANSLATORS: add player to erased list
2534             mBrowserBox->addRow("/erase 'NAME'", _("Erase"));
2535             break;
2536 
2537         case Relation::IGNORED:
2538             // TRANSLATORS: popup menu item
2539             // TRANSLATORS: remove player from ignore list
2540             mBrowserBox->addRow("/unignore 'NAME'", _("Unignore"));
2541             // TRANSLATORS: popup menu item
2542             // TRANSLATORS: add player to erased list
2543             mBrowserBox->addRow("/erase 'NAME'", _("Erase"));
2544             break;
2545 
2546         case Relation::ENEMY2:
2547             // TRANSLATORS: popup menu item
2548             // TRANSLATORS: remove player from ignore list
2549             mBrowserBox->addRow("/unignore 'NAME'", _("Unignore"));
2550             // TRANSLATORS: popup menu item
2551             // TRANSLATORS: add player to disregarded list
2552             mBrowserBox->addRow("/disregard 'NAME'", _("Disregard"));
2553             // TRANSLATORS: popup menu item
2554             // TRANSLATORS: add player to ignore list
2555             mBrowserBox->addRow("/ignore 'NAME'", _("Ignore"));
2556             // TRANSLATORS: popup menu item
2557             // TRANSLATORS: add player to black list
2558             mBrowserBox->addRow("/blacklist 'NAME'", _("Black list"));
2559             // TRANSLATORS: popup menu item
2560             // TRANSLATORS: add player to erased list
2561             mBrowserBox->addRow("/erase 'NAME'", _("Erase"));
2562             break;
2563 
2564         case Relation::ERASED:
2565             // TRANSLATORS: popup menu item
2566             // TRANSLATORS: remove player from ignore list
2567             mBrowserBox->addRow("/unignore 'NAME'", _("Unignore"));
2568             // TRANSLATORS: popup menu item
2569             // TRANSLATORS: add player to disregarded list
2570             mBrowserBox->addRow("/disregard 'NAME'", _("Disregard"));
2571             // TRANSLATORS: popup menu item
2572             // TRANSLATORS: add player to ignore list
2573             mBrowserBox->addRow("/ignore 'NAME'", _("Completely ignore"));
2574             break;
2575 
2576         default:
2577             break;
2578     }
2579 }
2580 
addFollow()2581 void PopupMenu::addFollow()
2582 {
2583     if (features.getBoolValue("allowFollow"))
2584     {
2585         // TRANSLATORS: popup menu item
2586         mBrowserBox->addRow("/follow 'NAME'", _("Follow"));
2587     }
2588     // TRANSLATORS: popup menu item
2589     // TRANSLATORS: imitate player
2590     mBrowserBox->addRow("/imitation 'NAME'", _("Imitate"));
2591 }
2592 
addBuySell(const Being * const being)2593 void PopupMenu::addBuySell(const Being *const being)
2594 {
2595     if ((playerRelations.getDefault() & PlayerRelation::TRADE) != 0U)
2596     {
2597         mBrowserBox->addSeparator("##3---");
2598         const bool haveVending =
2599             (Net::getNetworkType() != ServerType::TMWATHENA);
2600         if (being->isSellShopEnabled())
2601         {
2602             // TRANSLATORS: popup menu item
2603             // TRANSLATORS: buy item
2604             mBrowserBox->addRow("/buy 'NAME'", _("Buy"));
2605         }
2606         else if (!haveVending)
2607         {
2608             // TRANSLATORS: popup menu item
2609             // TRANSLATORS: buy item
2610             mBrowserBox->addRow("/buy 'NAME'", _("Buy (?)"));
2611         }
2612         if (being->isBuyShopEnabled())
2613         {
2614             // TRANSLATORS: popup menu item
2615             // TRANSLATORS: sell item
2616             mBrowserBox->addRow("/sell 'NAME'", _("Sell"));
2617         }
2618         else if (!haveVending)
2619         {
2620             // TRANSLATORS: popup menu item
2621             // TRANSLATORS: sell item
2622             mBrowserBox->addRow("/sell 'NAME'", _("Sell (?)"));
2623         }
2624     }
2625 }
2626 
addBuySellDefault()2627 void PopupMenu::addBuySellDefault()
2628 {
2629     if ((playerRelations.getDefault() & PlayerRelation::TRADE) != 0U)
2630     {
2631         mBrowserBox->addSeparator("##3---");
2632         // TRANSLATORS: popup menu item
2633         // TRANSLATORS: buy item
2634         mBrowserBox->addRow("/buy 'NAME'", _("Buy (?)"));
2635         // TRANSLATORS: popup menu item
2636         // TRANSLATORS: sell item
2637         mBrowserBox->addRow("/sell 'NAME'", _("Sell (?)"));
2638     }
2639 }
2640 
addPartyName(const std::string & partyName)2641 void PopupMenu::addPartyName(const std::string &partyName)
2642 {
2643     if (localPlayer->isInParty())
2644     {
2645         if (localPlayer->getParty() != nullptr)
2646         {
2647             if (localPlayer->getParty()->getName() != partyName)
2648             {
2649                 // TRANSLATORS: popup menu item
2650                 // TRANSLATORS: invite player to party
2651                 mBrowserBox->addRow("/party 'NAME'", _("Invite to party"));
2652             }
2653             else
2654             {
2655                 // TRANSLATORS: popup menu item
2656                 // TRANSLATORS: kick player from party
2657                 mBrowserBox->addRow("/kickparty 'NAME'", _("Kick from party"));
2658                 showAdoptCommands();
2659             }
2660             mBrowserBox->addSeparator("##3---");
2661         }
2662     }
2663 }
2664 
addParty(const std::string & nick)2665 void PopupMenu::addParty(const std::string &nick)
2666 {
2667     if (localPlayer->isInParty())
2668     {
2669         const Party *const party = localPlayer->getParty();
2670         if (party != nullptr)
2671         {
2672             if (!party->isMember(nick))
2673             {
2674                 // TRANSLATORS: popup menu item
2675                 // TRANSLATORS: invite player to party
2676                 mBrowserBox->addRow("/party 'NAME'", _("Invite to party"));
2677             }
2678             else
2679             {
2680                 // TRANSLATORS: popup menu item
2681                 // TRANSLATORS: kick player from party
2682                 mBrowserBox->addRow("/kickparty 'NAME'", _("Kick from party"));
2683                 showAdoptCommands();
2684             }
2685             mBrowserBox->addSeparator("##3---");
2686         }
2687     }
2688 }
2689 
addChat(const Being * const being)2690 void PopupMenu::addChat(const Being *const being)
2691 {
2692     if (being == nullptr)
2693         return;
2694     const ChatObject *const chat = being->getChat();
2695     if (chat != nullptr)
2696     {
2697         mBrowserBox->addRow("join chat",
2698             // TRANSLATORS: popup menu item
2699             // TRANSLATORS: invite player to party
2700             strprintf(_("Join chat %s"), chat->title.c_str()).c_str());
2701         mBrowserBox->addSeparator("##3---");
2702     }
2703 }
2704 
addPlayerMisc()2705 void PopupMenu::addPlayerMisc()
2706 {
2707     // TRANSLATORS: popup menu item
2708     mBrowserBox->addRow("/showitems :'BEINGID'", _("Show Items"));
2709     // TRANSLATORS: popup menu item
2710     // TRANSLATORS: undress player
2711     mBrowserBox->addRow("/undress :'BEINGID'", _("Undress"));
2712     // TRANSLATORS: popup menu item
2713     // TRANSLATORS: add comment to player
2714     mBrowserBox->addRow("addcomment", _("Add comment"));
2715 }
2716 
addPickupFilter(const std::string & name)2717 void PopupMenu::addPickupFilter(const std::string &name)
2718 {
2719     if (actorManager->isInPickupList(name)
2720         || actorManager->isInIgnorePickupList(name))
2721     {
2722         mBrowserBox->addRow("/removepickup 'NAME'",
2723             // TRANSLATORS: popup menu item
2724             // TRANSLATORS: remove item from pickup list
2725             _("Remove from pickup list"));
2726     }
2727     else
2728     {
2729         mBrowserBox->addRow("/addpickup 'NAME'",
2730             // TRANSLATORS: popup menu item
2731             // TRANSLATORS: popup menu item
2732             _("Add to pickup list"));
2733         mBrowserBox->addRow("/ignorepickup 'NAME'",
2734             // TRANSLATORS: popup menu item
2735             // TRANSLATORS: add item to pickup list
2736             _("Add to ignore list"));
2737     }
2738 }
2739 
showPopup(const int x,const int y,ListModel * const model)2740 void PopupMenu::showPopup(const int x, const int y,
2741                           ListModel *const model)
2742 {
2743     if (model == nullptr)
2744         return;
2745 
2746     mBrowserBox->clearRows();
2747     for (int f = 0, sz = model->getNumberOfElements(); f < sz; f ++)
2748     {
2749         mBrowserBox->addRow(strprintf("dropdown_%d", f),
2750             model->getElementAt(f).c_str());
2751     }
2752     // TRANSLATORS: popup menu item
2753     // TRANSLATORS: close menu
2754     mBrowserBox->addRow("cancel", _("Cancel"));
2755     showPopup(x, y);
2756 }
2757 
clear()2758 void PopupMenu::clear()
2759 {
2760     if (mDialog != nullptr)
2761     {
2762         mDialog->close();
2763         mDialog = nullptr;
2764     }
2765     mItemIndex = -1;
2766     mItemId = 0;
2767     for (int f = 0; f < maxCards; f ++)
2768         mItemCards[f] = 0;
2769     mMapItem = nullptr;
2770     mTab = nullptr;
2771     mSpell = nullptr;
2772     mCallerWindow = nullptr;
2773     mButton = nullptr;
2774     mTextField = nullptr;
2775 }
2776 
addProtection()2777 void PopupMenu::addProtection()
2778 {
2779     if (PlayerInfo::isItemProtected(mItemId))
2780     {
2781         mBrowserBox->addSeparator("##3---");
2782         // TRANSLATORS: popup menu item
2783         // TRANSLATORS: remove protection from item
2784         mBrowserBox->addRow("/unprotectitem 'ITEMID'", _("Unprotect item"));
2785     }
2786     else
2787     {
2788         if (mItemId < SPELL_MIN_ID)
2789         {
2790             mBrowserBox->addSeparator("##3---");
2791             // TRANSLATORS: popup menu item
2792             // TRANSLATORS: add protection to item
2793             mBrowserBox->addRow("/protectitem 'ITEMID'", _("Protect item"));
2794         }
2795     }
2796 }
2797 
addUse(const Item * const item)2798 void PopupMenu::addUse(const Item *const item)
2799 {
2800     const ItemInfo &info = item->getInfo();
2801     const std::string &str = (item->isEquipment() == Equipm_true
2802         && item->isEquipped() == Equipped_true)
2803         ? info.getUseButton2() : info.getUseButton();
2804 
2805     if (str.empty())
2806     {
2807         // TRANSLATORS: popup menu item
2808         mBrowserBox->addRow("/useinv 'INVINDEX'", _("Use"));
2809     }
2810     else
2811     {
2812         // TRANSLATORS: popup menu item
2813         mBrowserBox->addRow("/useinv 'INVINDEX'", str.c_str());
2814     }
2815 }
2816 
addItemMenu(const Item * const item,const InventoryTypeT type)2817 void PopupMenu::addItemMenu(const Item *const item,
2818                             const InventoryTypeT type)
2819 {
2820     const ItemInfo &info = item->getInfo();
2821     const STD_VECTOR<ItemMenuItem> *menu = nullptr;
2822     switch (type)
2823     {
2824         case InventoryType::Inventory:
2825             menu = &info.getInventoryMenuConst();
2826             break;
2827         case InventoryType::Storage:
2828             menu = &info.getStorageMenuConst();
2829             break;
2830         case InventoryType::Cart:
2831             menu = &info.getCartMenuConst();
2832             break;
2833         case InventoryType::Trade:
2834         case InventoryType::Npc:
2835         case InventoryType::Vending:
2836         case InventoryType::MailEdit:
2837         case InventoryType::MailView:
2838         case InventoryType::Craft:
2839         case InventoryType::TypeEnd:
2840         default:
2841             return;
2842     }
2843 
2844     const bool firstMode = (item->isEquipment() == Equipm_true ?
2845         (item->isEquipped() != Equipped_true) : (item->getQuantity() == 1));
2846 
2847     FOR_EACHP (STD_VECTOR<ItemMenuItem>::const_iterator, it, menu)
2848     {
2849         const ItemMenuItem &menuItem = *it;
2850         const std::string &name = firstMode ?
2851             menuItem.name1 : menuItem.name2;
2852         const std::string &command = firstMode ?
2853             menuItem.command1 : menuItem.command2;
2854         if (command.empty() ||
2855             name.empty())
2856         {
2857             continue;
2858         }
2859         mBrowserBox->addRow("/" + command, name.c_str());
2860     }
2861     mBrowserBox->addSeparator("##3---");
2862 }
2863 
addDrop(const Item * const item,const bool isProtected)2864 void PopupMenu::addDrop(const Item *const item,
2865                         const bool isProtected)
2866 {
2867     if (!isProtected)
2868     {
2869         mBrowserBox->addSeparator("##3---");
2870         if (item->getQuantity() > 1)
2871         {
2872             // TRANSLATORS: popup menu item
2873             // TRANSLATORS: drop item
2874             mBrowserBox->addRow("/dropinv 'INVINDEX'", _("Drop..."));
2875             // TRANSLATORS: popup menu item
2876             // TRANSLATORS: drop all item amount
2877             mBrowserBox->addRow("/dropinvall 'INVINDEX'", _("Drop all"));
2878         }
2879         else
2880         {
2881             // TRANSLATORS: popup menu item
2882             // TRANSLATORS: drop item
2883             mBrowserBox->addRow("/dropinv 'INVINDEX'", _("Drop"));
2884         }
2885     }
2886 }
2887 
addGmCommands()2888 void PopupMenu::addGmCommands()
2889 {
2890     if (localPlayer->isGM())
2891     {
2892         // TRANSLATORS: popup menu item
2893         // TRANSLATORS: gm commands
2894         mBrowserBox->addRow("gm", _("GM..."));
2895     }
2896 }
2897 
showPlayerMenu()2898 void PopupMenu::showPlayerMenu()
2899 {
2900     mBrowserBox->clearRows();
2901     // TRANSLATORS: popup menu header
2902     mBrowserBox->addRow(strprintf(_("Show %s"),
2903         mName.c_str()),
2904         false);
2905     if (isAllowCommand(ServerCommandType::accinfo))
2906     {
2907         // TRANSLATORS: popup menu item
2908         // TRANSLATORS: show player account info
2909         mBrowserBox->addRow("/showaccountinfo 'NAME'", _("Account info"));
2910     }
2911     if (isAllowOtherCommand(ServerCommandType::exp))
2912     {
2913         // TRANSLATORS: popup menu item
2914         // TRANSLATORS: show player level
2915         mBrowserBox->addRow("/showlevel 'NAME'", _("Level"));
2916     }
2917     if (isAllowOtherCommand(ServerCommandType::stats))
2918     {
2919         // TRANSLATORS: popup menu item
2920         // TRANSLATORS: show player stats
2921         mBrowserBox->addRow("/showstats 'NAME'", _("Stats"));
2922     }
2923     if (isAllowOtherCommand(ServerCommandType::itemlist))
2924     {
2925         // TRANSLATORS: popup menu item
2926         // TRANSLATORS: show player inventory list
2927         mBrowserBox->addRow("/showinventory 'NAME'", _("Inventory"));
2928     }
2929     if (isAllowOtherCommand(ServerCommandType::storagelist))
2930     {
2931         // TRANSLATORS: popup menu item
2932         // TRANSLATORS: show player storage list
2933         mBrowserBox->addRow("/showstorage 'NAME'", _("Storage"));
2934     }
2935     if (isAllowOtherCommand(ServerCommandType::cartlist))
2936     {
2937         // TRANSLATORS: popup menu item
2938         // TRANSLATORS: show player cart list
2939         mBrowserBox->addRow("/showcart 'NAME'", _("Cart"));
2940     }
2941     if (isAllowOtherCommand(ServerCommandType::commands))
2942     {
2943         // TRANSLATORS: popup menu item
2944         // TRANSLATORS: gm commands
2945         mBrowserBox->addRow("/gmcommands 'NAME'", _("Commands"));
2946     }
2947     if (isAllowOtherCommand(ServerCommandType::charcommands))
2948     {
2949         // TRANSLATORS: popup menu item
2950         // TRANSLATORS: gm char commands
2951         mBrowserBox->addRow("/gmcharcommands 'NAME'", _("Char commands"));
2952     }
2953     // TRANSLATORS: popup menu item
2954     // TRANSLATORS: back to gm menu
2955     mBrowserBox->addRow("gm", _("Back"));
2956     // TRANSLATORS: popup menu item
2957     // TRANSLATORS: close menu
2958     mBrowserBox->addRow("cancel", _("Cancel"));
2959     showPopup(getX(), getY());
2960 }
2961 
showPlayerGMCommands(const std::string & name)2962 void PopupMenu::showPlayerGMCommands(const std::string &name)
2963 {
2964     if (localPlayer == nullptr)
2965         return;
2966     const bool legacy = Net::getNetworkType() == ServerType::TMWATHENA;
2967     if (!legacy && isAllowCommand(ServerCommandType::where))
2968     {
2969         // TRANSLATORS: popup menu item
2970         // TRANSLATORS: find player position
2971         mBrowserBox->addRow("/locateplayer 'NAME'", _("Locate"));
2972     }
2973     if (legacy)
2974     {
2975         // TRANSLATORS: popup menu item
2976         // TRANSLATORS: check player ip
2977         mBrowserBox->addRow("/ipcheck 'NAME'", _("Check ip"));
2978     }
2979     if (isAllowCommand(ServerCommandType::jumpto))
2980     {
2981         // TRANSLATORS: popup menu item
2982         // TRANSLATORS: go to player position
2983         mBrowserBox->addRow("/gotopc 'NAME'", _("Goto"));
2984     }
2985     if (isAllowCommand(ServerCommandType::recall))
2986     {
2987         // TRANSLATORS: popup menu item
2988         // TRANSLATORS: recall player to current position
2989         mBrowserBox->addRow("/recallpc 'NAME'", _("Recall"));
2990     }
2991     if (isAllowOtherCommand(ServerCommandType::alive))
2992     {
2993         // TRANSLATORS: popup menu item
2994         // TRANSLATORS: revive player
2995         mBrowserBox->addRow("/alive 'NAME'", _("Revive"));
2996     }
2997     if (!legacy)
2998     {
2999         Being *const being = actorManager->findBeingByName(name,
3000             ActorType::Player);
3001         if (being != nullptr && being == localPlayer)
3002         {
3003             if (!being->getPartyName().empty() &&
3004                 isAllowCommand(ServerCommandType::partyrecall))
3005             {
3006                 mBrowserBox->addRow("/partyrecall 'PARTY'",
3007                     // TRANSLATORS: popup menu item
3008                     // TRANSLATORS: recall all party members to player location
3009                     _("Recall party"));
3010             }
3011             if (!being->getGuildName().empty() &&
3012                 isAllowCommand(ServerCommandType::guildrecall))
3013             {
3014                 mBrowserBox->addRow("/guildrecall 'PARTY'",
3015                     // TRANSLATORS: popup menu item
3016                     // TRANSLATORS: recall all guild members to player location
3017                     _("Recall guild"));
3018             }
3019         }
3020         if (localPlayer->isInParty())
3021         {
3022             const Party *const party = localPlayer->getParty();
3023             if (party != nullptr &&
3024                 party->isMember(name) &&
3025                 isAllowCommand(ServerCommandType::changeleader))
3026             {
3027                 mBrowserBox->addRow("/gmpartyleader 'NAME'",
3028                     // TRANSLATORS: popup menu item
3029                     // TRANSLATORS: give party leader status
3030                     _("Give party leader"));
3031             }
3032         }
3033 
3034         if (isAllowCommand(ServerCommandType::nuke))
3035         {
3036             // TRANSLATORS: popup menu item
3037             // TRANSLATORS: nuke player
3038             mBrowserBox->addRow("/gmnuke 'NAME'", _("Nuke"));
3039         }
3040         if (isAllowOtherCommand(ServerCommandType::kill))
3041         {
3042             // TRANSLATORS: popup menu item
3043             // TRANSLATORS: kill player
3044             mBrowserBox->addRow("/kill 'NAME'", _("Kill"));
3045         }
3046         if (mBeingId != BeingId_zero)
3047         {
3048             mBrowserBox->addSeparator("##3---");
3049             // TRANSLATORS: popup menu item
3050             // TRANSLATORS: show menu
3051             mBrowserBox->addRow("show", _("Show..."));
3052             // TRANSLATORS: popup menu item
3053             // TRANSLATORS: mute menu
3054             mBrowserBox->addRow("mute", _("Mute..."));
3055         }
3056     }
3057     if (legacy)
3058     {
3059         if (localPlayer->getName() == mName)
3060         {
3061             // TRANSLATORS: popup menu item
3062             // TRANSLATORS: hide player
3063             mBrowserBox->addRow("/hide", _("Hide"));
3064             mBrowserBox->addSeparator("##3---");
3065         }
3066     }
3067     else
3068     {
3069         if (isAllowOtherCommand(ServerCommandType::heal))
3070         {
3071             // TRANSLATORS: popup menu item
3072             // TRANSLATORS: heal player
3073             mBrowserBox->addRow("/gmheal 'NAME'", _("Heal"));
3074         }
3075         if (isAllowOtherCommand(ServerCommandType::monsterignore))
3076         {
3077             // TRANSLATORS: popup menu item
3078             // TRANSLATORS: enable immortal mode for player
3079             mBrowserBox->addRow("/immortal 'NAME'", _("Immortal"));
3080         }
3081         if (isAllowCommand(ServerCommandType::jail))
3082         {
3083             // TRANSLATORS: popup menu item
3084             // TRANSLATORS: send player to jail
3085             mBrowserBox->addRow("/jail 'NAME'", _("Jail"));
3086         }
3087         if (isAllowCommand(ServerCommandType::unjail))
3088         {
3089             // TRANSLATORS: popup menu item
3090             // TRANSLATORS: restore player from jail
3091             mBrowserBox->addRow("/unjail 'NAME'", _("Unjail"));
3092         }
3093         if (name == localPlayer->getName() &&
3094             isAllowCommand(ServerCommandType::hide))
3095         {
3096             // TRANSLATORS: popup menu item
3097             // TRANSLATORS: hide player
3098             mBrowserBox->addRow("/hide 'NAME'", _("Hide"));
3099         }
3100         mBrowserBox->addSeparator("##3---");
3101         if (isAllowOtherCommand(ServerCommandType::killer))
3102         {
3103             // TRANSLATORS: popup menu item
3104             // TRANSLATORS: set player as killer
3105             mBrowserBox->addRow("/killer 'NAME'", _("Killer"));
3106         }
3107         if (isAllowOtherCommand(ServerCommandType::killable))
3108         {
3109             // TRANSLATORS: popup menu item
3110             // TRANSLATORS: set player as killable
3111             mBrowserBox->addRow("/killable 'NAME'", _("Killable"));
3112         }
3113         if (isAllowOtherCommand(ServerCommandType::save))
3114         {
3115             // TRANSLATORS: popup menu item
3116             // TRANSLATORS: set player save position
3117             mBrowserBox->addRow("/savepos 'NAME'", _("Set save"));
3118         }
3119         if (isAllowOtherCommand(ServerCommandType::load))
3120         {
3121             // TRANSLATORS: popup menu item
3122             // TRANSLATORS: warp player to save position
3123             mBrowserBox->addRow("/loadpos 'NAME'", _("Warp to save"));
3124         }
3125         if (isAllowOtherCommand(ServerCommandType::jump))
3126         {
3127             // TRANSLATORS: popup menu item
3128             // TRANSLATORS: warp player to random position on same map
3129             mBrowserBox->addRow("/randomwarp 'NAME'", _("Warp to random"));
3130         }
3131         mBrowserBox->addSeparator("##3---");
3132         if (isAllowCommand(ServerCommandType::clone))
3133         {
3134             // TRANSLATORS: popup menu item
3135             // TRANSLATORS: spawn player clone
3136             mBrowserBox->addRow("/spawnclone 'NAME'", _("Spawn clone"));
3137         }
3138         if (isAllowCommand(ServerCommandType::slaveclone))
3139         {
3140             mBrowserBox->addRow("/spawnslaveclone 'NAME'",
3141                 // TRANSLATORS: popup menu item
3142                 // TRANSLATORS: spawn slave player clone
3143                 _("Spawn slave clone"));
3144         }
3145         if (isAllowCommand(ServerCommandType::evilclone))
3146         {
3147             mBrowserBox->addRow("/spawnevilclone 'NAME'",
3148                 // TRANSLATORS: popup menu item
3149                 // TRANSLATORS: spawn evil player clone
3150                 _("Spawn evil clone"));
3151         }
3152         mBrowserBox->addSeparator("##3---");
3153         if (isAllowOtherCommand(ServerCommandType::breakguild))
3154         {
3155             // TRANSLATORS: popup menu item
3156             // TRANSLATORS: break guild
3157             mBrowserBox->addRow("/gmbreakguild 'NAME'", _("Break guild"));
3158         }
3159     }
3160     if (mBeingId != BeingId_zero &&
3161         isAllowCommand(ServerCommandType::kick))
3162     {
3163         mBrowserBox->addSeparator("##3---");
3164         // TRANSLATORS: popup menu item
3165         // TRANSLATORS: kick player
3166         mBrowserBox->addRow("/kick :'BEINGID'", _("Kick"));
3167     }
3168 }
3169 
showMuteCommands()3170 void PopupMenu::showMuteCommands()
3171 {
3172     mBrowserBox->clearRows();
3173     // TRANSLATORS: popup menu header
3174     mBrowserBox->addRow(strprintf(_("Mute %s"),
3175         mName.c_str()),
3176         false);
3177     if (mBeingId != BeingId_zero &&
3178         Net::getNetworkType() != ServerType::TMWATHENA &&
3179         isAllowCommand(ServerCommandType::mute))
3180     {
3181         mBrowserBox->addRow("mute_+1",
3182             // TRANSLATORS: popup menu item
3183             // TRANSLATORS: mute player
3184             strprintf(_("Mute %d"), 1).c_str());
3185         mBrowserBox->addRow("mute_+5",
3186             // TRANSLATORS: popup menu item
3187             // TRANSLATORS: mute player
3188             strprintf(_("Mute %d"), 5).c_str());
3189         mBrowserBox->addRow("mute_+10",
3190             // TRANSLATORS: popup menu item
3191             // TRANSLATORS: mute player
3192             strprintf(_("Mute %d"), 10).c_str());
3193         mBrowserBox->addRow("mute_+15",
3194             // TRANSLATORS: popup menu item
3195             // TRANSLATORS: mute player
3196             strprintf(_("Mute %d"), 15).c_str());
3197         mBrowserBox->addRow("mute_+30",
3198             // TRANSLATORS: popup menu item
3199             // TRANSLATORS: mute player
3200             strprintf(_("Mute %d"), 30).c_str());
3201 
3202         mBrowserBox->addRow("mute_-1",
3203             // TRANSLATORS: popup menu item
3204             // TRANSLATORS: mute player
3205             strprintf(_("Unmute %d"), 1).c_str());
3206         mBrowserBox->addRow("mute_-5",
3207             // TRANSLATORS: popup menu item
3208             // TRANSLATORS: mute player
3209             strprintf(_("Unmute %d"), 5).c_str());
3210         mBrowserBox->addRow("mute_-10",
3211             // TRANSLATORS: popup menu item
3212             // TRANSLATORS: mute player
3213             strprintf(_("Unmute %d"), 10).c_str());
3214         mBrowserBox->addRow("mute_-15",
3215             // TRANSLATORS: popup menu item
3216             // TRANSLATORS: mute player
3217             strprintf(_("Unmute %d"), 15).c_str());
3218         mBrowserBox->addRow("mute_-30",
3219             // TRANSLATORS: popup menu item
3220             // TRANSLATORS: mute player
3221             strprintf(_("Unmute %d"), 30).c_str());
3222         mBrowserBox->addSeparator("##3---");
3223     }
3224     // TRANSLATORS: popup menu item
3225     // TRANSLATORS: back to gm menu
3226     mBrowserBox->addRow("gm", _("Back"));
3227     // TRANSLATORS: popup menu item
3228     // TRANSLATORS: close menu
3229     mBrowserBox->addRow("cancel", _("Cancel"));
3230     showPopup(getX(), getY());
3231 }
3232 
showNpcGMCommands()3233 void PopupMenu::showNpcGMCommands()
3234 {
3235     if (mBeingId != BeingId_zero)
3236     {
3237         if (isAllowCommand(ServerCommandType::kick))
3238         {
3239             // TRANSLATORS: popup menu item
3240             // TRANSLATORS: kick player
3241             mBrowserBox->addRow("/kick :'BEINGID'", _("Kick"));
3242         }
3243         const bool legacy = Net::getNetworkType() == ServerType::TMWATHENA;
3244         if (!legacy)
3245         {
3246             if (isAllowCommand(ServerCommandType::hidenpc))
3247             {
3248                 // TRANSLATORS: popup menu item
3249                 // TRANSLATORS: hide npc
3250                 mBrowserBox->addRow("/hidenpc 'EXTNAME'", _("Hide"));
3251             }
3252             mBrowserBox->addSeparator("##3---");
3253             if (isAllowCommand(ServerCommandType::npcmove))
3254             {
3255                 mBrowserBox->addRow("/npcmove 'EEXTNAME' 'PLAYERX' 'PLAYERY'",
3256                     // TRANSLATORS: popup menu item
3257                     // TRANSLATORS: warp npc to player location
3258                     _("Recall"));
3259             }
3260             if (isAllowCommand(ServerCommandType::disguise))
3261             {
3262                 mBrowserBox->addRow("/disguise 'BEINGSUBTYPEID'",
3263                     // TRANSLATORS: popup menu item
3264                     // TRANSLATORS: disguise to npc
3265                     _("Disguise"));
3266             }
3267             if (isAllowCommand(ServerCommandType::tonpc))
3268             {
3269                 // TRANSLATORS: popup menu item
3270                 // TRANSLATORS: warp to npc
3271                 mBrowserBox->addRow("/gotonpc 'EXTNAME'", _("Goto"));
3272             }
3273         }
3274     }
3275 }
3276 
showMonsterGMCommands()3277 void PopupMenu::showMonsterGMCommands()
3278 {
3279     if (mBeingId != BeingId_zero)
3280     {
3281         const bool legacy = Net::getNetworkType() == ServerType::TMWATHENA;
3282         if (isAllowCommand(ServerCommandType::kick))
3283         {
3284             // TRANSLATORS: popup menu item
3285             // TRANSLATORS: kick monster
3286             mBrowserBox->addRow("/kick :'BEINGID'", _("Kick"));
3287         }
3288         if (isAllowCommand(ServerCommandType::monster))
3289         {
3290             // TRANSLATORS: popup menu item
3291             // TRANSLATORS: spawn monster
3292             mBrowserBox->addRow("/spawn 'BEINGSUBTYPEID'", _("Spawn same"));
3293         }
3294         if (!legacy)
3295         {
3296             if (isAllowCommand(ServerCommandType::summon))
3297             {
3298                 mBrowserBox->addRow("/spawnslave 'BEINGSUBTYPEID'",
3299                     // TRANSLATORS: popup menu item
3300                     // TRANSLATORS: spawn slave monster
3301                     _("Spawn slave"));
3302             }
3303             if (isAllowCommand(ServerCommandType::disguise))
3304             {
3305                 mBrowserBox->addRow("/disguise 'BEINGSUBTYPEID'",
3306                     // TRANSLATORS: popup menu item
3307                     // TRANSLATORS: disguise to monster
3308                     _("Disguise"));
3309             }
3310             mBrowserBox->addSeparator("##3---");
3311             if (isAllowCommand(ServerCommandType::mobinfo))
3312             {
3313                 mBrowserBox->addRow("/monsterinfo 'BEINGSUBTYPEID'",
3314                     // TRANSLATORS: popup menu item
3315                     // TRANSLATORS: show monster information
3316                     _("Info"));
3317             }
3318             if (isAllowCommand(ServerCommandType::mobsearch))
3319             {
3320                 mBrowserBox->addRow("/mobsearch 'BEINGSUBTYPEID'",
3321                     // TRANSLATORS: popup menu item
3322                     // TRANSLATORS: show selected monster in current map
3323                     _("Search"));
3324             }
3325             if (isAllowCommand(ServerCommandType::whereis))
3326             {
3327                 mBrowserBox->addRow("/mobspawnsearch 'BEINGSUBTYPEID'",
3328                     // TRANSLATORS: popup menu item
3329                     // TRANSLATORS: show selected monster spawns in all maps
3330                     _("Search spawns"));
3331             }
3332         }
3333     }
3334 }
3335 
showFloorItemGMCommands()3336 void PopupMenu::showFloorItemGMCommands()
3337 {
3338     const bool legacy = Net::getNetworkType() == ServerType::TMWATHENA;
3339     if (legacy)
3340         return;
3341     if (isAllowCommand(ServerCommandType::iteminfo))
3342     {
3343         // TRANSLATORS: popup menu item
3344         // TRANSLATORS: show item information
3345         mBrowserBox->addRow("/iteminfo 'ITEMID'", _("Info"));
3346     }
3347     if (isAllowCommand(ServerCommandType::whodrops))
3348     {
3349         // TRANSLATORS: popup menu item
3350         // TRANSLATORS: show who drops item
3351         mBrowserBox->addRow("/whodrops 'ITEMID'", _("Who drops"));
3352     }
3353 }
3354 
showItemGMCommands()3355 void PopupMenu::showItemGMCommands()
3356 {
3357     const bool legacy = Net::getNetworkType() == ServerType::TMWATHENA;
3358     if (legacy)
3359         return;
3360     if (isAllowCommand(ServerCommandType::iteminfo))
3361     {
3362         // TRANSLATORS: popup menu item
3363         // TRANSLATORS: show item information
3364         mBrowserBox->addRow("/iteminfo 'ITEMID'", _("Info"));
3365     }
3366     if (isAllowCommand(ServerCommandType::whodrops))
3367     {
3368         // TRANSLATORS: popup menu item
3369         // TRANSLATORS: show who drops item
3370         mBrowserBox->addRow("/whodrops 'ITEMID'", _("Who drops"));
3371     }
3372     mBrowserBox->addSeparator("##3---");
3373     if (isAllowCommand(ServerCommandType::item))
3374     {
3375         // TRANSLATORS: popup menu item
3376         // TRANSLATORS: gm create item
3377         mBrowserBox->addRow("/createitem 'ITEMID' 1", _("Add 1"));
3378         // TRANSLATORS: popup menu item
3379         // TRANSLATORS: gm create item
3380         mBrowserBox->addRow("/createitem 'ITEMID' 5", _("Add 5"));
3381         // TRANSLATORS: popup menu item
3382         // TRANSLATORS: gm create item
3383         mBrowserBox->addRow("/createitem 'ITEMID' 10", _("Add 10"));
3384         // TRANSLATORS: popup menu item
3385         // TRANSLATORS: gm create item
3386         mBrowserBox->addRow("/createitem 'ITEMID' 100", _("Add 100"));
3387         // TRANSLATORS: popup menu item
3388         // TRANSLATORS: gm create item
3389         mBrowserBox->addRow("/createitem 'ITEMID' 1000", _("Add 1000"));
3390         // TRANSLATORS: popup menu item
3391         // TRANSLATORS: gm create item
3392         mBrowserBox->addRow("/createitem 'ITEMID' 10000", _("Add 10000"));
3393     }
3394 }
3395 
showGMPopup(const std::string & name)3396 void PopupMenu::showGMPopup(const std::string &name)
3397 {
3398     mBrowserBox->clearRows();
3399     // TRANSLATORS: popup menu header
3400     mBrowserBox->addRow(_("GM commands"), false);
3401     if (localPlayer->isGM())
3402     {
3403         switch (mType)
3404         {
3405             case ActorType::Player:
3406                 showPlayerGMCommands(name);
3407                 break;
3408             case ActorType::Npc:
3409                 showNpcGMCommands();
3410                 break;
3411             case ActorType::Monster:
3412                 showMonsterGMCommands();
3413                 break;
3414             case ActorType::FloorItem:
3415                 showFloorItemGMCommands();
3416                 break;
3417             case ActorType::Homunculus:
3418                 showHomunGMCommands();
3419                 break;
3420             case ActorType::Pet:
3421                 showPetGMCommands();
3422                 break;
3423             case ActorType::Mercenary:
3424                 showMercenaryGMCommands();
3425                 break;
3426             case ActorType::SkillUnit:
3427                 break;
3428             default:
3429             case ActorType::Unknown:
3430                 if (mItemId != 0)
3431                     showItemGMCommands();
3432                 break;
3433             case ActorType::Portal:
3434             case ActorType::Avatar:
3435             case ActorType::Elemental:
3436                 break;
3437         }
3438     }
3439 
3440     mBrowserBox->addSeparator("##3---");
3441     // TRANSLATORS: popup menu item
3442     // TRANSLATORS: close menu
3443     mBrowserBox->addRow("cancel", _("Cancel"));
3444 
3445     showPopup(getX(), getY());
3446 }
3447 
showHomunGMCommands()3448 void PopupMenu::showHomunGMCommands()
3449 {
3450 }
3451 
showPetGMCommands()3452 void PopupMenu::showPetGMCommands()
3453 {
3454 }
3455 
showMercenaryGMCommands()3456 void PopupMenu::showMercenaryGMCommands()
3457 {
3458 }
3459 
showCraftPopup()3460 void PopupMenu::showCraftPopup()
3461 {
3462     mBrowserBox->clearRows();
3463 
3464     for (int f = 0; f < 9; f ++)
3465     {
3466         const std::string cmd = strprintf("/craft %d", f);
3467         mBrowserBox->addRow(cmd,
3468             // TRANSLATORS: popup menu item
3469             // TRANSLATORS: move current item to craft slot
3470             strprintf(_("Move to craft %d"), f + 1).c_str());
3471     }
3472 
3473     // TRANSLATORS: popup menu item
3474     // TRANSLATORS: close menu
3475     mBrowserBox->addRow("cancel", _("Cancel"));
3476 
3477     showPopup(mX, mY);
3478 }
3479 
addMailCommands()3480 void PopupMenu::addMailCommands()
3481 {
3482     if (Net::getNetworkType() == ServerType::TMWATHENA)
3483         return;
3484 
3485     // TRANSLATORS: popup menu item
3486     // TRANSLATORS: open mail dialog
3487     mBrowserBox->addRow("/mailto 'NAME'", _("Mail to..."));
3488 }
3489 
addCatchPetCommands()3490 void PopupMenu::addCatchPetCommands()
3491 {
3492     if (Net::getNetworkType() == ServerType::TMWATHENA)
3493         return;
3494     // TRANSLATORS: popup menu item
3495     // TRANSLATORS: catch pet command
3496     mBrowserBox->addRow("/catchpet :'BEINGID'", _("Taming pet"));
3497 }
3498 
showAdoptCommands()3499 void PopupMenu::showAdoptCommands()
3500 {
3501     if (Net::getNetworkType() == ServerType::TMWATHENA)
3502         return;
3503     // TRANSLATORS: popup menu item
3504     // TRANSLATORS: adopt child command
3505     mBrowserBox->addRow("/adoptchild 'NAME'", _("Adopt child"));
3506 }
3507 
addSocialMenu()3508 void PopupMenu::addSocialMenu()
3509 {
3510     initPopup();
3511     mBrowserBox->clearRows();
3512     setMousePos();
3513     const Party *const party = localPlayer->getParty();
3514     if (party != nullptr)
3515     {
3516         // TRANSLATORS: popup menu item
3517         // TRANSLATORS: leave party
3518         mBrowserBox->addRow("/leaveparty", _("Leave party"));
3519     }
3520     else
3521     {
3522         // TRANSLATORS: popup menu item
3523         // TRANSLATORS: create party
3524         mBrowserBox->addRow("/createparty", _("Create party"));
3525     }
3526 
3527     if (Net::getNetworkType() != ServerType::TMWATHENA)
3528     {
3529         const Guild *const guild = localPlayer->getGuild();
3530         if (guild != nullptr)
3531         {
3532             // TRANSLATORS: popup menu item
3533             // TRANSLATORS: leave guild
3534             mBrowserBox->addRow("/leaveguild", _("Leave guild"));
3535         }
3536         else
3537         {
3538             // TRANSLATORS: popup menu item
3539             // TRANSLATORS: create guild
3540             mBrowserBox->addRow("/createguild", _("Create guild"));
3541         }
3542     }
3543     mBrowserBox->addSeparator("##3---");
3544 }
3545 
showPartyPopup()3546 void PopupMenu::showPartyPopup()
3547 {
3548     addSocialMenu();
3549 
3550     // TRANSLATORS: popup menu item
3551     // TRANSLATORS: close menu
3552     mBrowserBox->addRow("cancel", _("Cancel"));
3553     showPopup(mX, mY);
3554 }
3555 
showGuildPopup()3556 void PopupMenu::showGuildPopup()
3557 {
3558     addSocialMenu();
3559 
3560     // TRANSLATORS: popup menu item
3561     // TRANSLATORS: change guild notice message
3562     mBrowserBox->addRow("/guildnotice", _("Change notice"));
3563 
3564     // TRANSLATORS: popup menu item
3565     // TRANSLATORS: close menu
3566     mBrowserBox->addRow("cancel", _("Cancel"));
3567     showPopup(mX, mY);
3568 }
3569 
showAttackPopup()3570 void PopupMenu::showAttackPopup()
3571 {
3572     addSocialMenu();
3573 
3574     // TRANSLATORS: popup menu item
3575     // TRANSLATORS: close menu
3576     mBrowserBox->addRow("cancel", _("Cancel"));
3577     showPopup(mX, mY);
3578 }
3579 
showNavigationPopup()3580 void PopupMenu::showNavigationPopup()
3581 {
3582     addSocialMenu();
3583 
3584     // TRANSLATORS: popup menu item
3585     // TRANSLATORS: close menu
3586     mBrowserBox->addRow("cancel", _("Cancel"));
3587     showPopup(mX, mY);
3588 }
3589 
showPickupPopup()3590 void PopupMenu::showPickupPopup()
3591 {
3592     addSocialMenu();
3593 
3594     // TRANSLATORS: popup menu item
3595     // TRANSLATORS: close menu
3596     mBrowserBox->addRow("cancel", _("Cancel"));
3597     showPopup(mX, mY);
3598 }
3599 
showPlayersPopup()3600 void PopupMenu::showPlayersPopup()
3601 {
3602     addSocialMenu();
3603 
3604     // TRANSLATORS: popup menu item
3605     // TRANSLATORS: close menu
3606     mBrowserBox->addRow("cancel", _("Cancel"));
3607     showPopup(mX, mY);
3608 }
3609 
showFriendsPopup()3610 void PopupMenu::showFriendsPopup()
3611 {
3612     addSocialMenu();
3613 
3614     // TRANSLATORS: popup menu item
3615     // TRANSLATORS: close menu
3616     mBrowserBox->addRow("cancel", _("Cancel"));
3617     showPopup(mX, mY);
3618 }
3619 
moveUp()3620 void PopupMenu::moveUp()
3621 {
3622     mBrowserBox->moveSelectionUp();
3623 }
3624 
moveDown()3625 void PopupMenu::moveDown()
3626 {
3627     mBrowserBox->moveSelectionDown();
3628 }
3629 
select()3630 void PopupMenu::select()
3631 {
3632     mBrowserBox->selectSelection();
3633 }
3634