summaryrefslogtreecommitdiff
path: root/src/gui/popups
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2013-09-30 15:01:42 +0300
committerAndrei Karas <akaras@inbox.ru>2013-09-30 15:01:42 +0300
commit64781090e80fa93a43ce37f0705bbb1cf22ed8bf (patch)
tree6932ff7f29b4a1e1fe2f3834592587c33d7c4651 /src/gui/popups
parent25d77892d72d455f8a89372687db45aefbc61cec (diff)
downloadManaVerse-64781090e80fa93a43ce37f0705bbb1cf22ed8bf.tar.gz
ManaVerse-64781090e80fa93a43ce37f0705bbb1cf22ed8bf.tar.bz2
ManaVerse-64781090e80fa93a43ce37f0705bbb1cf22ed8bf.tar.xz
ManaVerse-64781090e80fa93a43ce37f0705bbb1cf22ed8bf.zip
move popups into popups directory.
Diffstat (limited to 'src/gui/popups')
-rw-r--r--src/gui/popups/beingpopup.cpp211
-rw-r--r--src/gui/popups/beingpopup.h65
-rw-r--r--src/gui/popups/itempopup.cpp267
-rw-r--r--src/gui/popups/itempopup.h76
-rw-r--r--src/gui/popups/popupmenu.cpp2914
-rw-r--r--src/gui/popups/popupmenu.h241
-rw-r--r--src/gui/popups/speechbubble.cpp97
-rw-r--r--src/gui/popups/speechbubble.h64
-rw-r--r--src/gui/popups/spellpopup.cpp116
-rw-r--r--src/gui/popups/spellpopup.h69
-rw-r--r--src/gui/popups/statuspopup.cpp169
-rw-r--r--src/gui/popups/statuspopup.h69
-rw-r--r--src/gui/popups/textpopup.cpp105
-rw-r--r--src/gui/popups/textpopup.h79
14 files changed, 4542 insertions, 0 deletions
diff --git a/src/gui/popups/beingpopup.cpp b/src/gui/popups/beingpopup.cpp
new file mode 100644
index 000000000..ae16d8eb2
--- /dev/null
+++ b/src/gui/popups/beingpopup.cpp
@@ -0,0 +1,211 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gui/popups/beingpopup.h"
+
+#include "being/being.h"
+#include "being/playerrelations.h"
+
+#include "gui/gui.h"
+#include "gui/sdlfont.h"
+
+#include "gui/widgets/label.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <guichan/font.hpp>
+
+#include "debug.h"
+
+BeingPopup::BeingPopup() :
+ Popup("BeingPopup", "beingpopup.xml"),
+ mBeingName(new Label(this, "A")),
+ mBeingParty(new Label(this, "A")),
+ mBeingGuild(new Label(this, "A")),
+ mBeingRank(new Label(this, "A")),
+ mBeingComment(new Label(this, "A"))
+{
+ // Being Name
+ mBeingName->setFont(boldFont);
+ mBeingName->setPosition(0, 0);
+
+ const int fontHeight = mBeingName->getHeight();
+
+ // Being's party
+ mBeingParty->setPosition(0, fontHeight);
+ // Being's party
+ mBeingGuild->setPosition(0, 2 * fontHeight);
+ mBeingRank->setPosition(0, 3 * fontHeight);
+ mBeingComment->setPosition(0, 4 * fontHeight);
+
+ mBeingParty->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+ mBeingGuild->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+ mBeingRank->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+ mBeingComment->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+
+ add(mBeingName);
+ add(mBeingParty);
+ add(mBeingGuild);
+ add(mBeingRank);
+ add(mBeingComment);
+}
+
+BeingPopup::~BeingPopup()
+{
+}
+
+void BeingPopup::show(const int x, const int y, Being *const b)
+{
+ if (!b)
+ {
+ setVisible(false);
+ return;
+ }
+
+ Label *label1 = mBeingParty;
+ Label *label2 = mBeingGuild;
+ Label *label3 = mBeingRank;
+ Label *label4 = mBeingComment;
+
+ b->updateComment();
+
+ if (b->getType() == Being::NPC && b->getComment().empty())
+ {
+ setVisible(false);
+ return;
+ }
+
+ mBeingName->setCaption(b->getName() + b->getGenderSignWithSpace());
+ if (gui)
+ {
+ if (player_relations.isGoodName(b))
+ mBeingName->setFont(boldFont);
+ else
+ mBeingName->setFont(gui->getSecureFont());
+ }
+ if (b->isAdvanced())
+ {
+ mBeingName->setForegroundColorAll(getThemeColor(
+ Theme::PLAYER_ADVANCED), getThemeColor(
+ Theme::PLAYER_ADVANCED_OUTLINE));
+ }
+ else
+ {
+ mBeingName->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+ }
+
+ mBeingName->adjustSize();
+ label1->setCaption("");
+ label2->setCaption("");
+ label3->setCaption("");
+ label4->setCaption("");
+
+ if (!(b->getPartyName().empty()))
+ {
+ // TRANSLATORS: being popup label
+ label1->setCaption(strprintf(_("Party: %s"),
+ b->getPartyName().c_str()));
+ label1->adjustSize();
+ }
+ else
+ {
+ label4 = label3;
+ label3 = label2;
+ label2 = label1;
+ label1 = nullptr;
+ }
+
+ if (!(b->getGuildName().empty()))
+ {
+ // TRANSLATORS: being popup label
+ label2->setCaption(strprintf(_("Guild: %s"),
+ b->getGuildName().c_str()));
+ label2->adjustSize();
+ }
+ else
+ {
+ label4 = label3;
+ label3 = label2;
+ label2 = nullptr;
+ }
+
+ if (b->getPvpRank() > 0)
+ {
+ // TRANSLATORS: being popup label
+ label3->setCaption(strprintf(_("Pvp rank: %u"), b->getPvpRank()));
+ label3->adjustSize();
+ }
+ else
+ {
+ label4 = label3;
+ label3 = nullptr;
+ }
+
+ if (!b->getComment().empty())
+ {
+ // TRANSLATORS: being popup label
+ label4->setCaption(strprintf(_("Comment: %s"),
+ b->getComment().c_str()));
+ label4->adjustSize();
+ }
+ else
+ {
+ label4 = nullptr;
+ }
+
+ int minWidth = mBeingName->getWidth();
+ if (label1 && label1->getWidth() > minWidth)
+ minWidth = label1->getWidth();
+ if (label2 && label2->getWidth() > minWidth)
+ minWidth = label2->getWidth();
+ if (label3 && label3->getWidth() > minWidth)
+ minWidth = label3->getWidth();
+ if (label4 && label4->getWidth() > minWidth)
+ minWidth = label4->getWidth();
+
+ const int height1 = getFont()->getHeight();
+ int height = height1;
+ if (label1)
+ height += height1;
+ if (label2)
+ height += height1;
+ if (label3)
+ height += height1;
+ if (label4)
+ height += height1;
+
+ setContentSize(minWidth, height);
+ position(x, y);
+ return;
+}
+
+#ifdef USE_PROFILER
+void BeingPopup::logic()
+{
+ logicChildren();
+}
+#endif
diff --git a/src/gui/popups/beingpopup.h b/src/gui/popups/beingpopup.h
new file mode 100644
index 000000000..3ac46d30c
--- /dev/null
+++ b/src/gui/popups/beingpopup.h
@@ -0,0 +1,65 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GUI_BEINGPOPUP_H
+#define GUI_BEINGPOPUP_H
+
+#include "gui/widgets/popup.h"
+
+class Being;
+class Label;
+
+/**
+ * A popup that displays information about a being.
+ */
+class BeingPopup final : public Popup
+{
+ public:
+ /**
+ * Constructor. Initializes the being popup.
+ */
+ BeingPopup();
+
+ A_DELETE_COPY(BeingPopup)
+
+ /**
+ * Destructor. Cleans up the being popup on deletion.
+ */
+ ~BeingPopup();
+
+ /**
+ * Sets the info to be displayed given a particular player.
+ */
+ void show(const int x, const int y, Being *const b);
+
+#ifdef USE_PROFILER
+ void logic();
+#endif
+
+ private:
+ Label *mBeingName;
+ Label *mBeingParty;
+ Label *mBeingGuild;
+ Label *mBeingRank;
+ Label *mBeingComment;
+};
+
+#endif // GUI_BEINGPOPUP_H
diff --git a/src/gui/popups/itempopup.cpp b/src/gui/popups/itempopup.cpp
new file mode 100644
index 000000000..63fe3628c
--- /dev/null
+++ b/src/gui/popups/itempopup.cpp
@@ -0,0 +1,267 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gui/popups/itempopup.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "item.h"
+#include "units.h"
+
+#include "gui/gui.h"
+#include "gui/sdlfont.h"
+
+#include "gui/widgets/icon.h"
+#include "gui/widgets/label.h"
+#include "gui/widgets/textbox.h"
+
+#include "utils/gettext.h"
+
+#include "resources/image.h"
+#include "resources/resourcemanager.h"
+
+#include <guichan/font.hpp>
+
+#include "debug.h"
+
+ItemPopup::ItemPopup() :
+ Popup("ItemPopup", "itempopup.xml"),
+ mItemName(new Label(this)),
+ mItemDesc(new TextBox(this)),
+ mItemEffect(new TextBox(this)),
+ mItemWeight(new TextBox(this)),
+ mItemType(ITEM_UNUSABLE),
+ mIcon(new Icon(this, nullptr)),
+ mLastName(),
+ mLastColor(1)
+{
+ // Item Name
+ mItemName->setFont(boldFont);
+ mItemName->setPosition(0, 0);
+
+ const int fontHeight = getFont()->getHeight();
+
+ // Item Description
+ mItemDesc->setEditable(false);
+ mItemDesc->setPosition(0, fontHeight);
+ mItemDesc->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+
+ // Item Effect
+ mItemEffect->setEditable(false);
+ mItemEffect->setPosition(0, 2 * fontHeight);
+ mItemEffect->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+
+ // Item Weight
+ mItemWeight->setEditable(false);
+ mItemWeight->setPosition(0, 3 * fontHeight);
+ mItemWeight->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+
+ add(mItemName);
+ add(mItemDesc);
+ add(mItemEffect);
+ add(mItemWeight);
+ add(mIcon);
+
+ addMouseListener(this);
+}
+
+ItemPopup::~ItemPopup()
+{
+ if (mIcon)
+ {
+ Image *const image = mIcon->getImage();
+ if (image)
+ image->decRef();
+ }
+}
+
+void ItemPopup::setItem(const Item *const item, const bool showImage)
+{
+ if (!item)
+ return;
+
+ const ItemInfo &ii = item->getInfo();
+ setItem(ii, item->getColor(), showImage, item->getId());
+ if (item->getRefine() > 0)
+ {
+ mLastName = ii.getName();
+ mLastColor = item->getColor();
+ if (serverVersion > 0)
+ {
+ mItemName->setCaption(strprintf("%s (+%d), %d", ii.getName(
+ item->getColor()).c_str(), item->getRefine(), ii.getId()));
+ }
+ else
+ {
+ mItemName->setCaption(strprintf("%s (+%d), %d",
+ ii.getName().c_str(), item->getRefine(), ii.getId()));
+ }
+ mItemName->adjustSize();
+ const unsigned minWidth = mItemName->getWidth() + 8;
+ if (static_cast<unsigned>(getWidth()) < minWidth)
+ setWidth(minWidth);
+ }
+}
+
+void ItemPopup::setItem(const ItemInfo &item, const unsigned char color,
+ const bool showImage, int id)
+{
+ if (!mIcon || (item.getName() == mLastName && color == mLastColor))
+ return;
+
+ if (id == -1)
+ id = item.getId();
+
+ int space = 0;
+
+ Image *const oldImage = mIcon->getImage();
+ if (oldImage)
+ oldImage->decRef();
+
+ if (showImage)
+ {
+ ResourceManager *const resman = ResourceManager::getInstance();
+ Image *const image = resman->getImage(combineDye2(
+ paths.getStringValue("itemIcons").append(
+ item.getDisplay().image), item.getDyeColorsString(color)));
+
+ mIcon->setImage(image);
+ if (image)
+ {
+ mIcon->setPosition(0, 0);
+ space = mIcon->getWidth();
+ }
+ }
+ else
+ {
+ mIcon->setImage(nullptr);
+ }
+
+ mItemType = item.getType();
+
+ mLastName = item.getName();
+ mLastColor = color;
+
+ if (serverVersion > 0)
+ {
+ mItemName->setCaption(strprintf("%s, %d",
+ item.getName(color).c_str(), id));
+ mItemDesc->setTextWrapped(item.getDescription(color), 196);
+ }
+ else
+ {
+ mItemName->setCaption(strprintf("%s, %d",
+ item.getName().c_str(), id));
+ mItemDesc->setTextWrapped(item.getDescription(), 196);
+ }
+
+ mItemName->adjustSize();
+ setLabelColor(mItemName, mItemType);
+ mItemName->setPosition(space, 0);
+
+ mItemEffect->setTextWrapped(item.getEffect(), 196);
+ // TRANSLATORS: popup label
+ mItemWeight->setTextWrapped(strprintf(_("Weight: %s"),
+ Units::formatWeight(item.getWeight()).c_str()), 196);
+
+ int minWidth = mItemName->getWidth() + space;
+
+ if (mItemName->getWidth() + space > minWidth)
+ minWidth = mItemName->getWidth() + space;
+ if (mItemDesc->getMinWidth() > minWidth)
+ minWidth = mItemDesc->getMinWidth();
+ if (mItemEffect->getMinWidth() > minWidth)
+ minWidth = mItemEffect->getMinWidth();
+ if (mItemWeight->getMinWidth() > minWidth)
+ minWidth = mItemWeight->getMinWidth();
+
+ const int numRowsDesc = mItemDesc->getNumberOfRows();
+ const int numRowsEffect = mItemEffect->getNumberOfRows();
+ const int numRowsWeight = mItemWeight->getNumberOfRows();
+ const int height = getFont()->getHeight();
+
+ if (item.getEffect().empty())
+ {
+ setContentSize(minWidth, (numRowsDesc + 2 + numRowsWeight) * height);
+ mItemWeight->setPosition(0, (numRowsDesc + 2) * height);
+ }
+ else
+ {
+ setContentSize(minWidth, (numRowsDesc + numRowsEffect + 2
+ + numRowsWeight) * height);
+ mItemWeight->setPosition(0, (numRowsDesc + numRowsEffect + 2)
+ * height);
+ mItemEffect->setPosition(0, (numRowsDesc + 2) * height);
+ }
+
+ mItemDesc->setPosition(0, 2 * height);
+}
+
+#define caseSetColor(name1, name2) \
+ case name1: \
+ { \
+ return label->setForegroundColorAll(getThemeColor(name2), \
+ getThemeColor(name2##_OUTLINE)); \
+ }
+void ItemPopup::setLabelColor(Label *label, const ItemType type) const
+{
+ switch (type)
+ {
+ caseSetColor(ITEM_UNUSABLE, Theme::GENERIC)
+ caseSetColor(ITEM_USABLE, Theme::USABLE)
+ caseSetColor(ITEM_EQUIPMENT_ONE_HAND_WEAPON, Theme::ONEHAND)
+ caseSetColor(ITEM_EQUIPMENT_TWO_HANDS_WEAPON, Theme::TWOHAND)
+ caseSetColor(ITEM_EQUIPMENT_TORSO, Theme::TORSO)
+ caseSetColor(ITEM_EQUIPMENT_ARMS, Theme::ARMS)
+ caseSetColor(ITEM_EQUIPMENT_HEAD, Theme::HEAD)
+ caseSetColor(ITEM_EQUIPMENT_LEGS, Theme::LEGS)
+ caseSetColor(ITEM_EQUIPMENT_SHIELD, Theme::SHIELD)
+ caseSetColor(ITEM_EQUIPMENT_RING, Theme::RING)
+ caseSetColor(ITEM_EQUIPMENT_NECKLACE, Theme::NECKLACE)
+ caseSetColor(ITEM_EQUIPMENT_FEET, Theme::FEET)
+ caseSetColor(ITEM_EQUIPMENT_AMMO, Theme::AMMO)
+ caseSetColor(ITEM_EQUIPMENT_CHARM, Theme::CHARM)
+ caseSetColor(ITEM_SPRITE_RACE, Theme::UNKNOWN_ITEM)
+ caseSetColor(ITEM_SPRITE_HAIR, Theme::UNKNOWN_ITEM)
+ default:
+ {
+ return label->setForegroundColorAll(getThemeColor(
+ Theme::UNKNOWN_ITEM), getThemeColor(
+ Theme::UNKNOWN_ITEM_OUTLINE));
+ }
+ }
+}
+#undef caseSetColor
+
+void ItemPopup::mouseMoved(gcn::MouseEvent &event)
+{
+ Popup::mouseMoved(event);
+
+ // When the mouse moved on top of the popup, hide it
+ setVisible(false);
+ mLastName.clear();
+ mLastColor = 1;
+}
diff --git a/src/gui/popups/itempopup.h b/src/gui/popups/itempopup.h
new file mode 100644
index 000000000..d0b313e26
--- /dev/null
+++ b/src/gui/popups/itempopup.h
@@ -0,0 +1,76 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GUI_ITEMPOPUP_H
+#define GUI_ITEMPOPUP_H
+
+#include "gui/widgets/popup.h"
+
+#include "resources/iteminfo.h"
+
+class Icon;
+class Label;
+class TextBox;
+
+/**
+ * A popup that displays information about an item.
+ */
+class ItemPopup final : public Popup
+{
+ public:
+ /**
+ * Constructor. Initializes the item popup.
+ */
+ ItemPopup();
+
+ A_DELETE_COPY(ItemPopup)
+
+ /**
+ * Destructor. Cleans up the item popup on deletion.
+ */
+ ~ItemPopup();
+
+ /**
+ * Sets the info to be displayed given a particular item.
+ */
+ void setItem(const ItemInfo &item, const unsigned char color,
+ const bool showImage = false, int id = -1);
+
+ void setItem(const Item *const item, const bool showImage = false);
+
+ void mouseMoved(gcn::MouseEvent &mouseEvent) override;
+
+ private:
+ Label *mItemName;
+ TextBox *mItemDesc;
+ TextBox *mItemEffect;
+ TextBox *mItemWeight;
+ ItemType mItemType;
+ Icon *mIcon;
+ std::string mLastName;
+ unsigned char mLastColor;
+
+ void setLabelColor(Label *label, const ItemType type) const;
+};
+
+#endif // GUI_ITEMPOPUP_H
diff --git a/src/gui/popups/popupmenu.cpp b/src/gui/popups/popupmenu.cpp
new file mode 100644
index 000000000..6771e7e3e
--- /dev/null
+++ b/src/gui/popups/popupmenu.cpp
@@ -0,0 +1,2914 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gui/popups/popupmenu.h"
+
+#include "actorspritemanager.h"
+#include "commandhandler.h"
+#include "configuration.h"
+#include "dropshortcut.h"
+#include "game.h"
+#include "guild.h"
+#include "guildmanager.h"
+#include "item.h"
+#include "maplayer.h"
+#include "party.h"
+#include "spellmanager.h"
+
+#include "being/localplayer.h"
+#include "being/playerinfo.h"
+#include "being/playerrelations.h"
+
+#include "input/inputmanager.h"
+
+#include "gui/windows/chatwindow.h"
+#include "gui/windows/equipmentwindow.h"
+#include "gui/windows/inventorywindow.h"
+#include "gui/windows/itemamountwindow.h"
+#include "gui/windows/ministatuswindow.h"
+#include "gui/windows/npcdialog.h"
+#include "gui/windows/outfitwindow.h"
+#include "gui/windows/skilldialog.h"
+#include "gui/windows/socialwindow.h"
+#include "gui/windows/textcommandeditor.h"
+#include "gui/windows/textdialog.h"
+#include "gui/windows/tradewindow.h"
+#include "gui/windowmenu.h"
+
+#include "gui/viewport.h"
+
+#include "gui/widgets/browserbox.h"
+#include "gui/widgets/chattab.h"
+#include "gui/widgets/progressbar.h"
+#include "gui/widgets/scrollarea.h"
+#include "gui/widgets/textfield.h"
+#include "gui/widgets/whispertab.h"
+
+#include "net/adminhandler.h"
+#include "net/beinghandler.h"
+#include "net/buysellhandler.h"
+#include "net/guildhandler.h"
+#include "net/inventoryhandler.h"
+#include "net/net.h"
+#include "net/npchandler.h"
+#include "net/partyhandler.h"
+#include "net/tradehandler.h"
+
+#include "resources/iteminfo.h"
+
+#include "utils/copynpaste.h"
+#include "utils/gettext.h"
+#include "utils/process.h"
+
+#include <guichan/listmodel.hpp>
+
+#include "debug.h"
+
+extern int serverVersion;
+
+std::string tradePartnerName;
+
+PopupMenu::PopupMenu() :
+ Popup("PopupMenu", "popupmenu.xml"),
+ mBrowserBox(new BrowserBox(this)),
+ mScrollArea(nullptr),
+ mBeingId(0),
+ mFloorItemId(0),
+ mItem(nullptr),
+ mItemId(0),
+ mItemColor(1),
+ mMapItem(nullptr),
+ mTab(nullptr),
+ mSpell(nullptr),
+ mWindow(nullptr),
+ mRenameListener(),
+ mPlayerListener(),
+ mDialog(nullptr),
+ mButton(nullptr),
+ mNick(),
+ mTextField(nullptr),
+ mType(static_cast<int>(Being::UNKNOWN)),
+ mX(0),
+ mY(0)
+{
+ mBrowserBox->setHighlightMode(BrowserBox::BACKGROUND);
+ mBrowserBox->setOpaque(false);
+ mBrowserBox->setLinkHandler(this);
+ mRenameListener.setMapItem(nullptr);
+ mRenameListener.setDialog(nullptr);
+ mPlayerListener.setNick("");
+ mPlayerListener.setDialog(nullptr);
+ mPlayerListener.setType(static_cast<int>(Being::UNKNOWN));
+ mScrollArea = new ScrollArea(mBrowserBox, false);
+ mScrollArea->setVerticalScrollPolicy(ScrollArea::SHOW_AUTO);
+ add(mScrollArea);
+}
+
+void PopupMenu::showPopup(const int x, const int y, const Being *const being)
+{
+ if (!being || !player_node || !actorSpriteManager)
+ return;
+
+ mBeingId = being->getId();
+ mNick = being->getName();
+ mType = static_cast<int>(being->getType());
+ mBrowserBox->clearRows();
+ mX = x;
+ mY = y;
+
+ const std::string &name = mNick;
+ mBrowserBox->addRow(name + being->getGenderSignWithSpace());
+
+ switch (being->getType())
+ {
+ case ActorSprite::PLAYER:
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: trade with player
+ mBrowserBox->addRow("trade", _("Trade"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: trade attack player
+ mBrowserBox->addRow("attack", _("Attack"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: send whisper to player
+ mBrowserBox->addRow("whisper", _("Whisper"));
+ addGmCommands();
+ mBrowserBox->addRow("##3---");
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: heal player
+ mBrowserBox->addRow("heal", _("Heal"));
+ mBrowserBox->addRow("##3---");
+
+ addPlayerRelation(name);
+ mBrowserBox->addRow("##3---");
+
+ addFollow();
+ addParty(being->getPartyName());
+
+ const Guild *const guild1 = being->getGuild();
+ const Guild *const guild2 = player_node->getGuild();
+ if (guild2)
+ {
+ if (guild1)
+ {
+ if (guild1->getId() == guild2->getId())
+ {
+ mBrowserBox->addRow("guild-kick",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: kick player from guild
+ _("Kick from guild"));
+ if (guild2->getServerGuild())
+ {
+ mBrowserBox->addRow(strprintf(
+ "@@guild-pos|%s >@@",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: change player position in guild
+ _("Change pos in guild")));
+ }
+ }
+ }
+ else if (guild2->getMember(mNick))
+ {
+ mBrowserBox->addRow("guild-kick",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: kick player from guild
+ _("Kick from guild"));
+ if (guild2->getServerGuild())
+ {
+ mBrowserBox->addRow(strprintf(
+ "@@guild-pos|%s >@@",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: change player position in guild
+ _("Change pos in guild")));
+ }
+ }
+ else
+ {
+ if (guild2->getServerGuild()
+ || (guildManager && guildManager->havePower()))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: invite player to guild
+ mBrowserBox->addRow("guild", _("Invite to guild"));
+ }
+ }
+ }
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: set player invisible for self by id
+ mBrowserBox->addRow("nuke", _("Nuke"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move to player location
+ mBrowserBox->addRow("move", _("Move"));
+ addPlayerMisc();
+ addBuySell(being);
+ break;
+ }
+
+ case ActorSprite::NPC:
+ // NPCs can be talked to (single option, candidate for removal
+ // unless more options would be added)
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: talk with npc
+ mBrowserBox->addRow("talk", _("Talk"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: buy from npc
+ mBrowserBox->addRow("buy", _("Buy"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: sell to npc
+ mBrowserBox->addRow("sell", _("Sell"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move to npc location
+ mBrowserBox->addRow("move", _("Move"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add comment to npc
+ mBrowserBox->addRow("addcomment", _("Add comment"));
+ break;
+
+ case ActorSprite::MONSTER:
+ {
+ // Monsters can be attacked
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: attack monster
+ mBrowserBox->addRow("attack", _("Attack"));
+
+ if (config.getBoolValue("enableAttackFilter"))
+ {
+ mBrowserBox->addRow("##3---");
+ if (actorSpriteManager->isInAttackList(name)
+ || actorSpriteManager->isInIgnoreAttackList(name)
+ || actorSpriteManager->isInPriorityAttackList(name))
+ {
+ mBrowserBox->addRow("remove attack",
+ // TRANSLATORS: remove monster from attack list
+ // TRANSLATORS: popup menu item
+ _("Remove from attack list"));
+ }
+ else
+ {
+ mBrowserBox->addRow("add attack priority",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add monster to priotiry attack list
+ _("Add to priority attack list"));
+ mBrowserBox->addRow("add attack",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add monster to attack list
+ _("Add to attack list"));
+ mBrowserBox->addRow("add attack ignore",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add monster to ignore list
+ _("Add to ignore list"));
+ }
+ }
+ break;
+ }
+
+ case ActorSprite::AVATAR:
+ case ActorSprite::UNKNOWN:
+ case ActorSprite::FLOOR_ITEM:
+ case ActorSprite::PORTAL:
+ case ActorSprite::PET:
+ default:
+ /* Other beings aren't interesting... */
+ return;
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add being name to chat
+ mBrowserBox->addRow("name", _("Add name to chat"));
+ mBrowserBox->addRow("##3---");
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showPopup(const int x, const int y,
+ std::vector<ActorSprite*> &beings)
+{
+ mX = x;
+ mY = y;
+ mBrowserBox->clearRows();
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("Players"));
+ FOR_EACH (std::vector<ActorSprite*>::const_iterator, it, beings)
+ {
+ const Being *const being = dynamic_cast<Being*>(*it);
+ const ActorSprite *const actor = *it;
+ if (being && !being->getName().empty())
+ {
+ mBrowserBox->addRow(strprintf("@@player_%u|%s >@@",
+ static_cast<unsigned>(being->getId()), (being->getName()
+ + being->getGenderSignWithSpace()).c_str()));
+ }
+ else if (actor->getType() == ActorSprite::FLOOR_ITEM)
+ {
+ const FloorItem *const floorItem
+ = static_cast<const FloorItem*>(actor);
+ mBrowserBox->addRow(strprintf("@@flooritem_%u|%s >@@",
+ static_cast<unsigned>(actor->getId()),
+ floorItem->getName().c_str()));
+ }
+ }
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+ showPopup(x, y);
+}
+
+void PopupMenu::showPlayerPopup(const int x, const int y,
+ const std::string &nick)
+{
+ if (nick.empty() || !player_node)
+ return;
+
+ mNick = nick;
+ mBeingId = 0;
+ mType = static_cast<int>(Being::PLAYER);
+ mX = x;
+ mY = y;
+ mBrowserBox->clearRows();
+
+ const std::string &name = mNick;
+
+ mBrowserBox->addRow(name);
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: send whisper to player
+ mBrowserBox->addRow("whisper", _("Whisper"));
+ addGmCommands();
+ mBrowserBox->addRow("##3---");
+
+ addPlayerRelation(name);
+ mBrowserBox->addRow("##3---");
+
+ addFollow();
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add comment to player
+ mBrowserBox->addRow("addcomment", _("Add comment"));
+
+ if (player_node->isInParty())
+ {
+ const Party *const party = player_node->getParty();
+ if (party)
+ {
+ const PartyMember *const member = party->getMember(mNick);
+ if (member)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: kick player from party
+ mBrowserBox->addRow("kick party", _("Kick from party"));
+ mBrowserBox->addRow("##3---");
+ const PartyMember *const o = party->getMember(
+ player_node->getName());
+ if (o && member->getMap() == o->getMap())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move to player position
+ mBrowserBox->addRow("move", _("Move"));
+ }
+ }
+ }
+ }
+
+ const Guild *const guild2 = player_node->getGuild();
+ if (guild2)
+ {
+ if (guild2->getMember(mNick))
+ {
+ if (guild2->getServerGuild() || (guildManager
+ && guildManager->havePower()))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: kick player from guild
+ mBrowserBox->addRow("guild-kick", _("Kick from guild"));
+ }
+ if (guild2->getServerGuild())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: change player position in guild
+ mBrowserBox->addRow(strprintf(
+ "@@guild-pos|%s >@@", _("Change pos in guild")));
+ }
+ }
+ else
+ {
+ if (guild2->getServerGuild() || (guildManager
+ && guildManager->havePower()))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: invite player to guild
+ mBrowserBox->addRow("guild", _("Invite to guild"));
+ }
+ }
+ }
+
+ addBuySellDefault();
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player name to chat
+ mBrowserBox->addRow("name", _("Add name to chat"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showPopup(const int x, const int y,
+ const FloorItem *const floorItem)
+{
+ if (!floorItem)
+ return;
+
+ mFloorItemId = floorItem->getId();
+ mX = x;
+ mY = y;
+ mType = static_cast<int>(Being::FLOOR_ITEM);
+ mBrowserBox->clearRows();
+ const std::string name = floorItem->getName();
+ mNick = name;
+
+ mBrowserBox->addRow(name);
+
+ if (config.getBoolValue("enablePickupFilter"))
+ {
+ if (actorSpriteManager->isInPickupList(name)
+ || (actorSpriteManager->isInPickupList("")
+ && !actorSpriteManager->isInIgnorePickupList(name)))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: pickup item from ground
+ mBrowserBox->addRow("pickup", _("Pick up"));
+ mBrowserBox->addRow("##3---");
+ }
+ addPickupFilter(name);
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: pickup item from ground
+ mBrowserBox->addRow("pickup", _("Pick up"));
+ }
+ addProtection();
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item name to chat
+ mBrowserBox->addRow("chat", _("Add to chat"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showPopup(const int x, const int y, MapItem *const mapItem)
+{
+ if (!mapItem)
+ return;
+
+ mMapItem = mapItem;
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("Map Item"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: rename map item
+ mBrowserBox->addRow("rename map", _("Rename"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove map item
+ mBrowserBox->addRow("remove map", _("Remove"));
+
+ if (player_node && player_node->isGM())
+ {
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: warp to map item
+ mBrowserBox->addRow("warp map", _("Warp"));
+ }
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showMapPopup(const int x, const int y,
+ const int x2, const int y2)
+{
+ mX = x2;
+ mY = y2;
+
+ mBrowserBox->clearRows();
+
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("Map Item"));
+
+ if (player_node && player_node->isGM())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: warp to map item
+ mBrowserBox->addRow("warp map", _("Warp"));
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move to map item
+ mBrowserBox->addRow("move", _("Move"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move camera to map item
+ mBrowserBox->addRow("movecamera", _("Move camera"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showOutfitsPopup(const int x, const int y)
+{
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("Outfits"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: clear selected outfit
+ mBrowserBox->addRow("clear outfit", _("Clear outfit"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showSpellPopup(const int x, const int y,
+ TextCommand *const cmd)
+{
+ if (!cmd)
+ return;
+
+ mBrowserBox->clearRows();
+
+ mSpell = cmd;
+ mX = x;
+ mY = y;
+
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("Spells"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: edit selected spell
+ mBrowserBox->addRow("edit spell", _("Edit spell"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showChatPopup(const int x, const int y, ChatTab *const tab)
+{
+ if (!tab || !actorSpriteManager || !player_node)
+ return;
+
+ mTab = tab;
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+
+ if (tab->getType() == static_cast<int>(ChatTab::TAB_WHISPER))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close chat tab
+ mBrowserBox->addRow("chat close", _("Close"));
+ }
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove all text from chat tab
+ mBrowserBox->addRow("chat clear", _("Clear"));
+ mBrowserBox->addRow("##3---");
+
+ if (tab->getAllowHighlight())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: disable chat tab highlight
+ mBrowserBox->addRow("disable highlight", _("Disable highlight"));
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: enable chat tab highlight
+ mBrowserBox->addRow("enable highlight", _("Enable highlight"));
+ }
+ if (tab->getRemoveNames())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: do not remove player names from chat tab
+ mBrowserBox->addRow("dont remove name", _("Don't remove name"));
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove player names from chat tab
+ mBrowserBox->addRow("remove name", _("Remove name"));
+ }
+ if (tab->getNoAway())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: enable away messages in chat tab
+ mBrowserBox->addRow("enable away", _("Enable away"));
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: disable away messages in chat tab
+ mBrowserBox->addRow("disable away", _("Disable away"));
+ }
+ mBrowserBox->addRow("##3---");
+ if (tab->getType() == static_cast<int>(ChatTab::TAB_PARTY))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: enable away messages in chat tab
+ mBrowserBox->addRow("leave party", _("Leave"));
+ mBrowserBox->addRow("##3---");
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: copy selected text to clipboard
+ mBrowserBox->addRow("chat clipboard", _("Copy to clipboard"));
+ mBrowserBox->addRow("##3---");
+
+ if (tab->getType() == static_cast<int>(ChatTab::TAB_WHISPER))
+ {
+ const WhisperTab *const wTab = static_cast<WhisperTab*>(tab);
+ std::string name = wTab->getNick();
+
+ const Being* const being = actorSpriteManager->findBeingByName(
+ name, Being::PLAYER);
+
+ if (being)
+ {
+ mBeingId = being->getId();
+ mNick = being->getName();
+ mType = static_cast<int>(being->getType());
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: trade with player
+ mBrowserBox->addRow("trade", _("Trade"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: attack player
+ mBrowserBox->addRow("attack", _("Attack"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: heal player
+ mBrowserBox->addRow("heal", _("Heal"));
+ mBrowserBox->addRow("##3---");
+ addPlayerRelation(name);
+ mBrowserBox->addRow("##3---");
+ addFollow();
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move to player position
+ mBrowserBox->addRow("move", _("Move"));
+ addPlayerMisc();
+ addBuySell(being);
+ mBrowserBox->addRow("##3---");
+
+ if (player_node->isInParty())
+ {
+ const Party *const party = player_node->getParty();
+ if (party)
+ {
+ if (!party->isMember(wTab->getNick()))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: invite player to party
+ mBrowserBox->addRow("party", _("Invite to party"));
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: kick player from party
+ mBrowserBox->addRow("kick party",
+ _("Kick from party"));
+ }
+ mBrowserBox->addRow("##3---");
+ }
+ }
+ const Guild *const guild1 = being->getGuild();
+ const Guild *const guild2 = player_node->getGuild();
+ if (guild2)
+ {
+ if (guild1)
+ {
+ if (guild1->getId() == guild2->getId())
+ {
+ if (guild2->getServerGuild() || (guildManager
+ && guildManager->havePower()))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: kick player from guild
+ mBrowserBox->addRow(strprintf(
+ "@@guild-kick|%s@@", _("Kick from guild")));
+ }
+ if (guild2->getServerGuild())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: change player position in guild
+ mBrowserBox->addRow(strprintf("@@guild-pos|%s >@@",
+ _("Change pos in guild")));
+ }
+ }
+ }
+ else
+ {
+ if (guild2->getServerGuild() || (guildManager
+ && guildManager->havePower()))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: invite player to guild
+ mBrowserBox->addRow("guild", _("Invite to guild"));
+ }
+ }
+ }
+ }
+ else
+ {
+ mNick = name;
+ mType = static_cast<int>(Being::PLAYER);
+ addPlayerRelation(name);
+ mBrowserBox->addRow("##3---");
+ addFollow();
+
+ if (player_node->isInParty())
+ {
+ const Party *const party = player_node->getParty();
+ if (party)
+ {
+ const PartyMember *const m = party->getMember(mNick);
+ if (m)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move to player location
+ mBrowserBox->addRow("move", _("Move"));
+ }
+ }
+ }
+ addPlayerMisc();
+ addBuySellDefault();
+ mBrowserBox->addRow("##3---");
+ }
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showChangePos(const int x, const int y)
+{
+ mBrowserBox->clearRows();
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("Change guild position"));
+
+ if (!player_node)
+ return;
+
+ mX = x;
+ mY = y;
+ const Guild *const guild = player_node->getGuild();
+ if (guild)
+ {
+ const PositionsMap map = guild->getPositions();
+ FOR_EACH (PositionsMap::const_iterator, itr, map)
+ {
+ mBrowserBox->addRow(strprintf("@@guild-pos-%u|%s@@",
+ itr->first, itr->second.c_str()));
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+ }
+ else
+ {
+ mBeingId = 0;
+ mFloorItemId = 0;
+ mItem = nullptr;
+ mMapItem = nullptr;
+ mNick.clear();
+ mType = static_cast<int>(Being::UNKNOWN);
+ mX = 0;
+ mY = 0;
+ setVisible(false);
+ }
+}
+
+void PopupMenu::handleLink(const std::string &link,
+ gcn::MouseEvent *event A_UNUSED)
+{
+ Being *being = nullptr;
+ if (actorSpriteManager)
+ being = actorSpriteManager->findBeing(mBeingId);
+
+ // Talk To action
+ if (link == "talk" && being && being->canTalk())
+ {
+ being->talkTo();
+ }
+ // Trade action
+ else if (link == "trade" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ Net::getTradeHandler()->request(being);
+ tradePartnerName = being->getName();
+ if (tradeWindow)
+ tradeWindow->clear();
+ }
+ else if (link == "buy" && being && mBeingId != 0)
+ {
+ if (being->getType() == Being::NPC)
+ Net::getNpcHandler()->buy(mBeingId);
+ else if (being->getType() == Being::PLAYER)
+ Net::getBuySellHandler()->requestSellList(being->getName());
+ }
+ else if (link == "buy" && !mNick.empty())
+ {
+ Net::getBuySellHandler()->requestSellList(mNick);
+ }
+ else if (link == "sell" && being && mBeingId != 0)
+ {
+ if (being->getType() == Being::NPC)
+ Net::getNpcHandler()->sell(mBeingId);
+ else if (being->getType() == Being::PLAYER)
+ Net::getBuySellHandler()->requestBuyList(being->getName());
+ }
+ else if (link == "sell" && !mNick.empty())
+ {
+ Net::getBuySellHandler()->requestBuyList(mNick);
+ }
+ else if (link == "attack" && being)
+ {
+ if (player_node)
+ player_node->attack(being, true);
+ }
+ else if (link == "heal" && being && being->getType() != Being::MONSTER)
+ {
+ if (actorSpriteManager)
+ actorSpriteManager->heal(being);
+ }
+ else if (link == "unignore" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ player_relations.setRelation(being->getName(),
+ PlayerRelation::NEUTRAL);
+ }
+ else if (link == "unignore" && !mNick.empty())
+ {
+ player_relations.setRelation(mNick, PlayerRelation::NEUTRAL);
+ }
+ else if (link == "ignore" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ player_relations.setRelation(being->getName(),
+ PlayerRelation::IGNORED);
+ }
+ else if (link == "ignore" && !mNick.empty())
+ {
+ player_relations.setRelation(mNick, PlayerRelation::IGNORED);
+ }
+
+ else if (link == "blacklist" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ player_relations.setRelation(being->getName(),
+ PlayerRelation::BLACKLISTED);
+ }
+ else if (link == "blacklist" && !mNick.empty())
+ {
+ player_relations.setRelation(mNick, PlayerRelation::BLACKLISTED);
+ }
+ else if (link == "enemy" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ player_relations.setRelation(being->getName(),
+ PlayerRelation::ENEMY2);
+ }
+ else if (link == "enemy" && !mNick.empty())
+ {
+ player_relations.setRelation(mNick, PlayerRelation::ENEMY2);
+ }
+ else if (link == "erase" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ player_relations.setRelation(being->getName(), PlayerRelation::ERASED);
+ being->updateName();
+ }
+ else if (link == "erase" && !mNick.empty())
+ {
+ player_relations.setRelation(mNick, PlayerRelation::ERASED);
+ }
+ else if (link == "disregard" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ player_relations.setRelation(being->getName(),
+ PlayerRelation::DISREGARDED);
+ }
+ else if (link == "disregard" && !mNick.empty())
+ {
+ player_relations.setRelation(mNick, PlayerRelation::DISREGARDED);
+ }
+ else if (link == "friend" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ player_relations.setRelation(being->getName(), PlayerRelation::FRIEND);
+ }
+ else if (link == "friend" && !mNick.empty())
+ {
+ player_relations.setRelation(mNick, PlayerRelation::FRIEND);
+ }
+ // Guild action
+ else if (link == "guild" && !mNick.empty())
+ {
+ if (player_node)
+ {
+ const Guild *const guild = player_node->getGuild();
+ if (guild)
+ {
+ if (guild->getServerGuild())
+ Net::getGuildHandler()->invite(guild->getId(), mNick);
+ else if (guildManager)
+ guildManager->invite(mNick);
+ }
+ }
+ }
+ else if (link == "nuke" && being)
+ {
+ if (actorSpriteManager)
+ {
+ actorSpriteManager->addBlock(static_cast<uint32_t>(
+ being->getId()));
+ actorSpriteManager->destroy(being);
+ }
+ }
+ // Follow Player action
+ else if (link == "follow" && !mNick.empty())
+ {
+ if (player_node)
+ player_node->setFollow(mNick);
+ }
+ else if (link == "imitation" && !mNick.empty())
+ {
+ if (player_node)
+ player_node->setImitate(mNick);
+ }
+ // Pick Up Floor Item action
+ else if ((link == "pickup") && mFloorItemId)
+ {
+ if (player_node && actorSpriteManager)
+ {
+ FloorItem *const item = actorSpriteManager->findItem(
+ mFloorItemId);
+ if (item)
+ player_node->pickUp(item);
+ }
+ }
+ else if (link == "use" && mItemId)
+ {
+ if (mItemId < SPELL_MIN_ID)
+ {
+ const Inventory *const inv = PlayerInfo::getInventory();
+ if (inv)
+ {
+ const Item *const item = inv->findItem(mItemId, mItemColor);
+ if (item)
+ {
+ if (item->isEquipment())
+ {
+ if (item->isEquipped())
+ Net::getInventoryHandler()->unequipItem(item);
+ else
+ Net::getInventoryHandler()->equipItem(item);
+ }
+ else
+ {
+ if (!PlayerInfo::isItemProtected(item->getId()))
+ Net::getInventoryHandler()->useItem(item);
+ }
+ }
+ }
+ }
+ else if (mItemId < SKILL_MIN_ID && spellManager)
+ {
+ spellManager->useItem(mItemId);
+ }
+ else if (skillDialog)
+ {
+ skillDialog->useItem(mItemId);
+ }
+ }
+ else if (link == "chat")
+ {
+ if (chatWindow)
+ {
+ if (mItem)
+ {
+ if (serverVersion > 0)
+ {
+ chatWindow->addItemText(mItem->getInfo().getName(
+ mItem->getColor()));
+ }
+ else
+ {
+ chatWindow->addItemText(mItem->getInfo().getName());
+ }
+ }
+ else if (mFloorItemId && actorSpriteManager)
+ {
+ const FloorItem *const item = actorSpriteManager->findItem(
+ mFloorItemId);
+
+ if (item)
+ {
+ if (serverVersion > 0)
+ {
+ chatWindow->addItemText(item->getInfo().getName(
+ item->getColor()));
+ }
+ else
+ {
+ chatWindow->addItemText(item->getInfo().getName());
+ }
+ }
+ }
+ }
+ }
+ else if (link == "whisper" && !mNick.empty() && chatWindow)
+ {
+ if (chatWindow)
+ {
+ if (config.getBoolValue("whispertab"))
+ {
+ chatWindow->localChatInput("/q " + mNick);
+ }
+ else
+ {
+ chatWindow->addInputText(std::string("/w \"").append(
+ mNick).append("\" "));
+ }
+ }
+ }
+ else if (link == "move" && !mNick.empty())
+ {
+ if (player_node)
+ {
+ if (being)
+ {
+ player_node->navigateTo(being->getTileX(), being->getTileY());
+ }
+ else if (player_node->isInParty())
+ {
+ const Party *const party = player_node->getParty();
+ if (party)
+ {
+ const PartyMember *const m = party->getMember(mNick);
+ const PartyMember *const o = party->getMember(
+ player_node->getName());
+ if (m && o && m->getMap() == o->getMap())
+ player_node->navigateTo(m->getX(), m->getY());
+ }
+ }
+ }
+ }
+ else if (link == "move" && (mX || mY))
+ {
+ if (player_node)
+ player_node->navigateTo(mX, mY);
+ }
+ else if (link == "movecamera" && (mX || mY))
+ {
+ if (viewport)
+ viewport->moveCameraToPosition(mX * 32, mY * 32);
+ }
+ else if (link == "split" && mItem)
+ {
+ ItemAmountWindow::showWindow(ItemAmountWindow::ItemSplit,
+ inventoryWindow, mItem);
+ }
+ else if (link == "drop" && mItem)
+ {
+ if (!PlayerInfo::isItemProtected(mItem->getId()))
+ {
+ ItemAmountWindow::showWindow(ItemAmountWindow::ItemDrop,
+ inventoryWindow, mItem);
+ }
+ }
+ else if (link == "drop all" && mItem)
+ {
+ if (!PlayerInfo::isItemProtected(mItem->getId()))
+ Net::getInventoryHandler()->dropItem(mItem, mItem->getQuantity());
+ }
+ else if (link == "store" && mItem)
+ {
+ ItemAmountWindow::showWindow(ItemAmountWindow::StoreAdd,
+ inventoryWindow, mItem);
+ }
+ else if (link == "store 10" && mItem)
+ {
+ int cnt = 10;
+ if (cnt > mItem->getQuantity())
+ cnt = mItem->getQuantity();
+ Net::getInventoryHandler()->moveItem2(Inventory::INVENTORY,
+ mItem->getInvIndex(), cnt,
+ Inventory::STORAGE);
+ }
+ else if (link == "store half" && mItem)
+ {
+ Net::getInventoryHandler()->moveItem2(Inventory::INVENTORY,
+ mItem->getInvIndex(), mItem->getQuantity() / 2,
+ Inventory::STORAGE);
+ }
+ else if (link == "store all-1" && mItem)
+ {
+ Net::getInventoryHandler()->moveItem2(Inventory::INVENTORY,
+ mItem->getInvIndex(), mItem->getQuantity() - 1,
+ Inventory::STORAGE);
+ }
+ else if (link == "store all" && mItem)
+ {
+ Net::getInventoryHandler()->moveItem2(Inventory::INVENTORY,
+ mItem->getInvIndex(), mItem->getQuantity(),
+ Inventory::STORAGE);
+ }
+ else if (link == "addtrade" && mItem)
+ {
+ if (!PlayerInfo::isItemProtected(mItem->getId()))
+ {
+ ItemAmountWindow::showWindow(ItemAmountWindow::TradeAdd,
+ tradeWindow, mItem);
+ }
+ }
+ else if (link == "addtrade 10" && mItem)
+ {
+ if (tradeWindow && !PlayerInfo::isItemProtected(mItem->getId()))
+ {
+ int cnt = 10;
+ if (cnt > mItem->getQuantity())
+ cnt = mItem->getQuantity();
+ tradeWindow->tradeItem(mItem, cnt, true);
+ }
+ }
+ else if (link == "addtrade half" && mItem)
+ {
+ if (tradeWindow && !PlayerInfo::isItemProtected(mItem->getId()))
+ tradeWindow->tradeItem(mItem, mItem->getQuantity() / 2, true);
+ }
+ else if (link == "addtrade all-1" && mItem)
+ {
+ if (tradeWindow && !PlayerInfo::isItemProtected(mItem->getId()))
+ tradeWindow->tradeItem(mItem, mItem->getQuantity() - 1, true);
+ }
+ else if (link == "addtrade all" && mItem)
+ {
+ if (tradeWindow && !PlayerInfo::isItemProtected(mItem->getId()))
+ tradeWindow->tradeItem(mItem, mItem->getQuantity(), true);
+ }
+ else if (link == "retrieve" && mItem)
+ {
+ ItemAmountWindow::showWindow(ItemAmountWindow::StoreRemove,
+ mWindow, mItem);
+ }
+ else if (link == "retrieve 10" && mItem)
+ {
+ int cnt = 10;
+ if (cnt > mItem->getQuantity())
+ cnt = mItem->getQuantity();
+ Net::getInventoryHandler()->moveItem2(Inventory::STORAGE,
+ mItem->getInvIndex(), cnt,
+ Inventory::INVENTORY);
+ }
+ else if (link == "retrieve half" && mItem)
+ {
+ Net::getInventoryHandler()->moveItem2(Inventory::STORAGE,
+ mItem->getInvIndex(), mItem->getQuantity() / 2,
+ Inventory::INVENTORY);
+ }
+ else if (link == "retrieve all-1" && mItem)
+ {
+ Net::getInventoryHandler()->moveItem2(Inventory::STORAGE,
+ mItem->getInvIndex(), mItem->getQuantity() - 1,
+ Inventory::INVENTORY);
+ }
+ else if (link == "retrieve all" && mItem)
+ {
+ Net::getInventoryHandler()->moveItem2(Inventory::STORAGE,
+ mItem->getInvIndex(), mItem->getQuantity(),
+ Inventory::INVENTORY);
+ }
+ else if (link == "protect item" && mItemId)
+ {
+ PlayerInfo::protectItem(mItemId);
+ }
+ else if (link == "unprotect item" && mItemId)
+ {
+ PlayerInfo::unprotectItem(mItemId);
+ }
+ else if (link == "party" && being &&
+ being->getType() == ActorSprite::PLAYER)
+ {
+ Net::getPartyHandler()->invite(being);
+ }
+ else if (link == "kick party" && being
+ && being->getType() == Being::PLAYER)
+ {
+ Net::getPartyHandler()->kick(being);
+ }
+ else if (link == "kick party" && !mNick.empty())
+ {
+ if (player_node && player_node->getParty())
+ {
+ const PartyMember *const member = player_node->
+ getParty()->getMember(mNick);
+ if (member)
+ Net::getPartyHandler()->kick(mNick);
+ }
+ }
+ else if (link == "name" && !mNick.empty())
+ {
+ const std::string &name = mNick;
+ if (chatWindow)
+ chatWindow->addInputText(name);
+ }
+ else if (link == "admin-kick" && being &&
+ (being->getType() == ActorSprite::PLAYER ||
+ being->getType() == ActorSprite::MONSTER))
+ {
+ Net::getAdminHandler()->kick(being->getId());
+ }
+ else if (link == "chat close" && mTab)
+ {
+ if (commandHandler)
+ commandHandler->invokeCommand("close", "", mTab);
+ }
+ else if (link == "leave party" && mTab)
+ {
+ Net::getPartyHandler()->leave();
+ }
+ else if (link == "chat clear" && mTab)
+ {
+ if (chatWindow)
+ chatWindow->clearTab();
+ }
+ else if (link == "warp map" && mMapItem)
+ {
+ if (Game::instance())
+ {
+ Net::getAdminHandler()->warp(Game::instance()->getCurrentMapName(),
+ mMapItem->getX(), mMapItem->getY());
+ }
+ }
+ else if (link == "warp map" && (mX || mY))
+ {
+ if (Game::instance())
+ {
+ Net::getAdminHandler()->warp(Game::instance()->getCurrentMapName(),
+ mX, mY);
+ }
+ }
+ else if (link == "remove map" && mMapItem)
+ {
+ if (viewport)
+ {
+ const Map *const map = viewport->getMap();
+ if (map)
+ {
+ SpecialLayer *const specialLayer = map->getSpecialLayer();
+ if (specialLayer)
+ {
+ const bool isHome = (mMapItem->getType()
+ == static_cast<int>(MapItem::HOME));
+ const int x = static_cast<const int>(mMapItem->getX());
+ const int y = static_cast<const int>(mMapItem->getY());
+ specialLayer->setTile(x, y,
+ static_cast<int>(MapItem::EMPTY));
+ if (socialWindow)
+ socialWindow->removePortal(x, y);
+ if (isHome && player_node)
+ {
+ player_node->removeHome();
+ player_node->saveHomes();
+ }
+ }
+ }
+ }
+ }
+ else if (link == "rename map" && mMapItem)
+ {
+ mRenameListener.setMapItem(mMapItem);
+ // TRANSLATORS: number of chars in string should be near original
+ mDialog = new TextDialog(_("Rename map sign "),
+ // TRANSLATORS: number of chars in string should be near original
+ _("Name: "));
+ mRenameListener.setDialog(mDialog);
+ mDialog->setText(mMapItem->getComment());
+ mDialog->setActionEventId("ok");
+ mDialog->addActionListener(&mRenameListener);
+ }
+ else if (link == "clear drops")
+ {
+ if (dropShortcut)
+ dropShortcut->clear();
+ }
+ else if (link == "edit spell" && mSpell)
+ {
+ new TextCommandEditor(mSpell);
+ }
+ else if (link == "undress" && being)
+ {
+ Net::getBeingHandler()->undress(being);
+ }
+ else if (link == "addcomment" && !mNick.empty())
+ {
+ // TRANSLATORS: number of chars in string should be near original
+ TextDialog *const dialog = new TextDialog(
+ _("Player comment "),
+ // TRANSLATORS: number of chars in string should be near original
+ _("Comment: "));
+ mPlayerListener.setDialog(dialog);
+ mPlayerListener.setNick(mNick);
+ mPlayerListener.setType(mType);
+
+ if (being)
+ {
+ being->updateComment();
+ dialog->setText(being->getComment());
+ }
+ else
+ {
+ dialog->setText(Being::loadComment(mNick, mType));
+ }
+ dialog->setActionEventId("ok");
+ dialog->addActionListener(&mPlayerListener);
+ }
+ else if (link == "guild-kick" && !mNick.empty())
+ {
+ if (player_node)
+ {
+ const Guild *const guild = player_node->getGuild();
+ if (guild)
+ {
+ if (guild->getServerGuild())
+ Net::getGuildHandler()->kick(guild->getMember(mNick), "");
+ else if (guildManager)
+ guildManager->kick(mNick);
+ }
+ }
+ }
+ else if (link == "enable highlight" && mTab)
+ {
+ if (commandHandler)
+ commandHandler->invokeCommand(COMMAND_ENABLEHIGHLIGHT, mTab);
+ }
+ else if (link == "disable highlight" && mTab)
+ {
+ if (commandHandler)
+ commandHandler->invokeCommand(COMMAND_DISABLEHIGHLIGHT, mTab);
+ }
+ else if (link == "dont remove name" && mTab)
+ {
+ if (commandHandler)
+ commandHandler->invokeCommand(COMMAND_DONTREMOVENAME, mTab);
+ }
+ else if (link == "remove name" && mTab)
+ {
+ if (commandHandler)
+ commandHandler->invokeCommand(COMMAND_REMOVENAME, mTab);
+ }
+ else if (link == "disable away" && mTab)
+ {
+ if (commandHandler)
+ commandHandler->invokeCommand(COMMAND_DISABLEAWAY, mTab);
+ }
+ else if (link == "enable away" && mTab)
+ {
+ if (commandHandler)
+ commandHandler->invokeCommand(COMMAND_ENABLEAWAY, mTab);
+ }
+ else if (link == "chat clipboard" && mTab)
+ {
+ if (chatWindow)
+ chatWindow->copyToClipboard(mX, mY);
+ }
+ else if (link == "npc clipboard" && mBeingId)
+ {
+ NpcDialog::copyToClipboard(mBeingId, mX, mY);
+ }
+ else if (link == "remove attack" && being)
+ {
+ if (actorSpriteManager && being->getType() == Being::MONSTER)
+ {
+ actorSpriteManager->removeAttackMob(being->getName());
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ else if (link == "add attack" && being)
+ {
+ if (actorSpriteManager && being->getType() == Being::MONSTER)
+ {
+ actorSpriteManager->addAttackMob(being->getName());
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ else if (link == "add attack priority" && being)
+ {
+ if (actorSpriteManager && being->getType() == Being::MONSTER)
+ {
+ actorSpriteManager->addPriorityAttackMob(being->getName());
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ else if (link == "add attack ignore" && being)
+ {
+ if (actorSpriteManager && being->getType() == Being::MONSTER)
+ {
+ actorSpriteManager->addIgnoreAttackMob(being->getName());
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ else if (link == "remove pickup" && !mNick.empty())
+ {
+ if (actorSpriteManager)
+ {
+ actorSpriteManager->removePickupItem(mNick);
+ if (socialWindow)
+ socialWindow->updatePickupFilter();
+ }
+ }
+ else if (link == "add pickup" && !mNick.empty())
+ {
+ if (actorSpriteManager)
+ {
+ actorSpriteManager->addPickupItem(mNick);
+ if (socialWindow)
+ socialWindow->updatePickupFilter();
+ }
+ }
+ else if (link == "add pickup ignore" && !mNick.empty())
+ {
+ if (actorSpriteManager)
+ {
+ actorSpriteManager->addIgnorePickupItem(mNick);
+ if (socialWindow)
+ socialWindow->updatePickupFilter();
+ }
+ }
+ else if (link == "attack moveup")
+ {
+ if (actorSpriteManager)
+ {
+ const int idx = actorSpriteManager->getAttackMobIndex(mNick);
+ if (idx > 0)
+ {
+ std::list<std::string> mobs
+ = actorSpriteManager->getAttackMobs();
+ std::list<std::string>::iterator it = mobs.begin();
+ std::list<std::string>::iterator it2 = mobs.begin();
+ while (it != mobs.end())
+ {
+ if (*it == mNick)
+ {
+ -- it2;
+ mobs.splice(it2, mobs, it);
+ actorSpriteManager->setAttackMobs(mobs);
+ actorSpriteManager->rebuildAttackMobs();
+ break;
+ }
+ ++ it;
+ ++ it2;
+ }
+
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ }
+ else if (link == "priority moveup")
+ {
+ if (actorSpriteManager)
+ {
+ const int idx = actorSpriteManager->
+ getPriorityAttackMobIndex(mNick);
+ if (idx > 0)
+ {
+ std::list<std::string> mobs
+ = actorSpriteManager->getPriorityAttackMobs();
+ std::list<std::string>::iterator it = mobs.begin();
+ std::list<std::string>::iterator it2 = mobs.begin();
+ while (it != mobs.end())
+ {
+ if (*it == mNick)
+ {
+ -- it2;
+ mobs.splice(it2, mobs, it);
+ actorSpriteManager->setPriorityAttackMobs(mobs);
+ actorSpriteManager->rebuildPriorityAttackMobs();
+ break;
+ }
+ ++ it;
+ ++ it2;
+ }
+
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ }
+ else if (link == "attack movedown")
+ {
+ if (actorSpriteManager)
+ {
+ const int idx = actorSpriteManager->getAttackMobIndex(mNick);
+ const int size = actorSpriteManager->getAttackMobsSize();
+ if (idx + 1 < size)
+ {
+ std::list<std::string> mobs
+ = actorSpriteManager->getAttackMobs();
+ std::list<std::string>::iterator it = mobs.begin();
+ std::list<std::string>::iterator it2 = mobs.begin();
+ while (it != mobs.end())
+ {
+ if (*it == mNick)
+ {
+ ++ it2;
+ if (it2 == mobs.end())
+ break;
+
+ mobs.splice(it, mobs, it2);
+ actorSpriteManager->setAttackMobs(mobs);
+ actorSpriteManager->rebuildAttackMobs();
+ break;
+ }
+ ++ it;
+ ++ it2;
+ }
+
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ }
+ else if (link == "priority movedown")
+ {
+ if (player_node)
+ {
+ const int idx = actorSpriteManager
+ ->getPriorityAttackMobIndex(mNick);
+ const int size = actorSpriteManager->getPriorityAttackMobsSize();
+ if (idx + 1 < size)
+ {
+ std::list<std::string> mobs
+ = actorSpriteManager->getPriorityAttackMobs();
+ std::list<std::string>::iterator it = mobs.begin();
+ std::list<std::string>::iterator it2 = mobs.begin();
+ while (it != mobs.end())
+ {
+ if (*it == mNick)
+ {
+ ++ it2;
+ if (it2 == mobs.end())
+ break;
+
+ mobs.splice(it, mobs, it2);
+ actorSpriteManager->setPriorityAttackMobs(mobs);
+ actorSpriteManager->rebuildPriorityAttackMobs();
+ break;
+ }
+ ++ it;
+ ++ it2;
+ }
+
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ }
+ else if (link == "attack remove")
+ {
+ if (actorSpriteManager)
+ {
+ if (mNick.empty())
+ {
+ if (actorSpriteManager->isInAttackList(mNick))
+ {
+ actorSpriteManager->removeAttackMob(mNick);
+ actorSpriteManager->addIgnoreAttackMob(mNick);
+ }
+ else
+ {
+ actorSpriteManager->removeAttackMob(mNick);
+ actorSpriteManager->addAttackMob(mNick);
+ }
+ }
+ else
+ {
+ actorSpriteManager->removeAttackMob(mNick);
+ }
+ if (socialWindow)
+ socialWindow->updateAttackFilter();
+ }
+ }
+ else if (link == "pickup remove")
+ {
+ if (actorSpriteManager)
+ {
+ if (mNick.empty())
+ {
+ if (actorSpriteManager->isInPickupList(mNick))
+ {
+ actorSpriteManager->removePickupItem(mNick);
+ actorSpriteManager->addIgnorePickupItem(mNick);
+ }
+ else
+ {
+ actorSpriteManager->removePickupItem(mNick);
+ actorSpriteManager->addPickupItem(mNick);
+ }
+ }
+ else
+ {
+ actorSpriteManager->removePickupItem(mNick);
+ }
+ if (socialWindow)
+ socialWindow->updatePickupFilter();
+ }
+ }
+ else if (link == "reset yellow")
+ {
+ if (player_node)
+ player_node->resetYellowBar();
+ }
+ else if (link == "bar to chat" && !mNick.empty())
+ {
+ if (chatWindow)
+ chatWindow->addInputText(mNick);
+ }
+ else if (link == "items" && being)
+ {
+ if (being == player_node)
+ {
+ if (equipmentWindow && !equipmentWindow->isWindowVisible())
+ equipmentWindow->setVisible(true);
+ }
+ else
+ {
+ if (beingEquipmentWindow)
+ {
+ beingEquipmentWindow->setBeing(being);
+ beingEquipmentWindow->setVisible(true);
+ }
+ }
+ }
+ else if (link == "undress item" && being && mItemId)
+ {
+ being->undressItemById(mItemId);
+ }
+ else if (link == "guild-pos" && !mNick.empty())
+ {
+ showChangePos(getX(), getY());
+ return;
+ }
+ else if (link == "clear outfit")
+ {
+ if (outfitWindow)
+ outfitWindow->clearCurrentOutfit();
+ }
+ else if (link == "clipboard copy")
+ {
+ if (mTextField)
+ mTextField->handleCopy();
+ }
+ else if (link == "clipboard paste")
+ {
+ if (mTextField)
+ mTextField->handlePaste();
+ }
+ else if (link == "open link" && !mNick.empty())
+ {
+ openBrowser(mNick);
+ }
+ else if (link == "clipboard link" && !mNick.empty())
+ {
+ sendBuffer(mNick);
+ }
+ else if (link == "goto" && !mNick.empty())
+ {
+ Net::getAdminHandler()->gotoName(mNick);
+ }
+ else if (link == "recall" && !mNick.empty())
+ {
+ Net::getAdminHandler()->recallName(mNick);
+ }
+ else if (link == "revive" && !mNick.empty())
+ {
+ Net::getAdminHandler()->reviveName(mNick);
+ }
+ else if (link == "ipcheck" && !mNick.empty())
+ {
+ Net::getAdminHandler()->ipcheckName(mNick);
+ }
+ else if (link == "gm" && !mNick.empty())
+ {
+ showGMPopup();
+ return;
+ }
+ else if (!link.compare(0, 10, "guild-pos-"))
+ {
+ if (player_node)
+ {
+ const int num = atoi(link.substr(10).c_str());
+ const Guild *const guild = player_node->getGuild();
+ if (guild)
+ {
+ Net::getGuildHandler()->changeMemberPostion(
+ guild->getMember(mNick), num);
+ }
+ }
+ }
+ else if (!link.compare(0, 7, "player_"))
+ {
+ if (actorSpriteManager)
+ {
+ mBeingId = atoi(link.substr(7).c_str());
+ being = actorSpriteManager->findBeing(mBeingId);
+ if (being)
+ {
+ showPopup(getX(), getY(), being);
+ return;
+ }
+ }
+ }
+ else if (!link.compare(0, 10, "flooritem_"))
+ {
+ if (actorSpriteManager)
+ {
+ const int id = atoi(link.substr(10).c_str());
+ if (id)
+ {
+ const FloorItem *const item = actorSpriteManager->findItem(id);
+ if (item)
+ {
+ mFloorItemId = item->getId();
+ showPopup(getX(), getY(), item);
+ return;
+ }
+ }
+ }
+ }
+ else if (!link.compare(0, 12, "hide button_"))
+ {
+ if (windowMenu)
+ windowMenu->showButton(link.substr(12), false);
+ }
+ else if (!link.compare(0, 12, "show button_"))
+ {
+ if (windowMenu)
+ windowMenu->showButton(link.substr(12), true);
+ }
+ else if (!link.compare(0, 9, "hide bar_"))
+ {
+ if (miniStatusWindow)
+ miniStatusWindow->showBar(link.substr(9), false);
+ }
+ else if (!link.compare(0, 9, "show bar_"))
+ {
+ if (miniStatusWindow)
+ miniStatusWindow->showBar(link.substr(9), true);
+ }
+ else if (!link.compare(0, 12, "show window_"))
+ {
+ const int id = atoi(link.substr(12).c_str());
+ if (id >= 0)
+ inputManager.executeAction(id);
+ }
+ // Unknown actions
+ else if (link != "cancel")
+ {
+ logger->log("PopupMenu: Warning, unknown action '%s'", link.c_str());
+ }
+
+ setVisible(false);
+
+ mBeingId = 0;
+ mFloorItemId = 0;
+ mItem = nullptr;
+ mItemId = 0;
+ mItemColor = 1;
+ mMapItem = nullptr;
+ mTab = nullptr;
+ mSpell = nullptr;
+ mWindow = nullptr;
+ mDialog = nullptr;
+ mButton = nullptr;
+ mNick.clear();
+ mTextField = nullptr;
+ mType = static_cast<int>(Being::UNKNOWN);
+ mX = 0;
+ mY = 0;
+}
+
+void PopupMenu::showPopup(Window *const parent, const int x, const int y,
+ Item *const item, const bool isInventory)
+{
+ if (!item)
+ return;
+
+ mItem = item;
+ mItemId = item->getId();
+ mItemColor = item->getColor();
+ mWindow = parent;
+ mX = x;
+ mY = y;
+ mNick.clear();
+ mBrowserBox->clearRows();
+
+ const int cnt = item->getQuantity();
+ const bool isProtected = PlayerInfo::isItemProtected(mItemId);
+
+ if (isInventory)
+ {
+ if (tradeWindow && tradeWindow->isWindowVisible() && !isProtected)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item to trade
+ mBrowserBox->addRow("addtrade", _("Add to trade"));
+ if (cnt > 1)
+ {
+ if (cnt > 10)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add 10 item amount to trade
+ mBrowserBox->addRow("addtrade 10", _("Add to trade 10"));
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add half item amount to trade
+ mBrowserBox->addRow("addtrade half", _("Add to trade half"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add all amount except one item to trade
+ mBrowserBox->addRow("addtrade all-1", _("Add to trade all-1"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add all amount item to trade
+ mBrowserBox->addRow("addtrade all", _("Add to trade all"));
+ }
+ mBrowserBox->addRow("##3---");
+ }
+ if (InventoryWindow::isStorageActive())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item to storage
+ mBrowserBox->addRow("store", _("Store"));
+ if (cnt > 1)
+ {
+ if (cnt > 10)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add 10 item amount to storage
+ mBrowserBox->addRow("store 10", _("Store 10"));
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add half item amount to storage
+ mBrowserBox->addRow("store half", _("Store half"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add all except one item amount to storage
+ mBrowserBox->addRow("store all-1", _("Store all-1"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add all item amount to storage
+ mBrowserBox->addRow("store all", _("Store all"));
+ }
+ mBrowserBox->addRow("##3---");
+ }
+
+ addUseDrop(item, isProtected);
+ }
+ // Assume in storage for now
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: get item from storage
+ mBrowserBox->addRow("retrieve", _("Retrieve"));
+ if (cnt > 1)
+ {
+ if (cnt > 10)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: get 10 item amount from storage
+ mBrowserBox->addRow("retrieve 10", _("Retrieve 10"));
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: get half item amount from storage
+ mBrowserBox->addRow("retrieve half", _("Retrieve half"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: get all except one item amount from storage
+ mBrowserBox->addRow("retrieve all-1", _("Retrieve all-1"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: get all item amount from storage
+ mBrowserBox->addRow("retrieve all", _("Retrieve all"));
+ }
+ }
+ addProtection();
+ if (config.getBoolValue("enablePickupFilter"))
+ {
+ mNick = item->getName();
+ mBrowserBox->addRow("##3---");
+ addPickupFilter(mNick);
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item name to chat
+ mBrowserBox->addRow("chat", _("Add to chat"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showItemPopup(const int x, const int y, const int itemId,
+ const unsigned char color)
+{
+ const Inventory *const inv = PlayerInfo::getInventory();
+ if (!inv)
+ return;
+
+ Item *const item = inv->findItem(itemId, color);
+ if (item)
+ {
+ showItemPopup(x, y, item);
+ }
+ else
+ {
+ mItem = nullptr;
+ mItemId = itemId;
+ mItemColor = color;
+ mX = x;
+ mY = y;
+ mBrowserBox->clearRows();
+
+ if (!PlayerInfo::isItemProtected(mItemId))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: use item
+ mBrowserBox->addRow("use", _("Use"));
+ }
+ addProtection();
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+ }
+}
+
+void PopupMenu::showItemPopup(const int x, const int y, Item *const item)
+{
+ mItem = item;
+ mX = x;
+ mY = y;
+ if (item)
+ {
+ mItemId = item->getId();
+ mItemColor = item->getColor();
+ }
+ else
+ {
+ mItemId = 0;
+ mItemColor = 1;
+ }
+ mNick.clear();
+ mBrowserBox->clearRows();
+
+ if (item)
+ {
+ const bool isProtected = PlayerInfo::isItemProtected(mItemId);
+ addUseDrop(item, isProtected);
+ if (InventoryWindow::isStorageActive())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item to storage
+ mBrowserBox->addRow("store", _("Store"));
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item name to chat
+ mBrowserBox->addRow("chat", _("Add to chat"));
+
+ if (config.getBoolValue("enablePickupFilter"))
+ {
+ mNick = item->getName();
+
+ mBrowserBox->addRow("##3---");
+ addPickupFilter(mNick);
+ }
+ }
+ addProtection();
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showDropPopup(const int x, const int y, Item *const item)
+{
+ mItem = item;
+ mX = x;
+ mY = y;
+ mNick.clear();
+ mBrowserBox->clearRows();
+
+ if (item)
+ {
+ mItemId = item->getId();
+ mItemColor = item->getColor();
+ const bool isProtected = PlayerInfo::isItemProtected(mItemId);
+ addUseDrop(item, isProtected);
+ if (InventoryWindow::isStorageActive())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item to storage
+ mBrowserBox->addRow("store", _("Store"));
+ }
+ addProtection();
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item name to chat
+ mBrowserBox->addRow("chat", _("Add to chat"));
+ if (config.getBoolValue("enablePickupFilter"))
+ {
+ mNick = item->getName();
+ mBrowserBox->addRow("##3---");
+ addPickupFilter(mNick);
+ }
+ }
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ mBrowserBox->addRow("clear drops", _("Clear drop window"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showPopup(const int x, const int y, Button *const button)
+{
+ if (!button || !windowMenu)
+ return;
+
+ mButton = button;
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+ std::vector<Button *> names = windowMenu->getButtons();
+ for (std::vector<Button *>::const_iterator it = names.begin(),
+ it_end = names.end(); it != it_end; ++ it)
+ {
+ const Button *const btn = dynamic_cast<Button*>(*it);
+ if (!btn || btn->getActionEventId() == "SET")
+ continue;
+
+ if (btn->isVisible())
+ {
+ mBrowserBox->addRow(strprintf("@@hide button_%s|%s %s (%s)@@",
+ // TRANSLATORS: popup menu item
+ btn->getActionEventId().c_str(), _("Hide"),
+ btn->getDescription().c_str(), btn->getCaption().c_str()));
+ }
+ else
+ {
+ mBrowserBox->addRow(strprintf("@@show button_%s|%s %s (%s)@@",
+ // TRANSLATORS: popup menu item
+ btn->getActionEventId().c_str(), _("Show"),
+ btn->getDescription().c_str(), btn->getCaption().c_str()));
+ }
+ }
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showPopup(const int x, const int y, const ProgressBar *const b)
+{
+ if (!b || !miniStatusWindow)
+ return;
+
+ mNick = b->text();
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+ std::vector <ProgressBar*> bars = miniStatusWindow->getBars();
+ ProgressBar *onlyBar = nullptr;
+ int cnt = 0;
+
+ // search for alone visible bar
+ for (std::vector <ProgressBar*>::const_iterator it = bars.begin(),
+ it_end = bars.end(); it != it_end; ++it)
+ {
+ ProgressBar *const bar = *it;
+ if (!bar)
+ continue;
+
+ if (bar->isVisible())
+ {
+ cnt ++;
+ onlyBar = bar;
+ }
+ }
+ if (cnt > 1)
+ onlyBar = nullptr;
+
+ for (std::vector <ProgressBar*>::const_iterator it = bars.begin(),
+ it_end = bars.end(); it != it_end; ++it)
+ {
+ ProgressBar *const bar = *it;
+ if (!bar || bar == onlyBar)
+ continue;
+
+ if (bar->isVisible())
+ {
+ mBrowserBox->addRow(strprintf("@@hide bar_%s|%s %s@@",
+ // TRANSLATORS: popup menu item
+ bar->getActionEventId().c_str(), _("Hide"),
+ bar->getId().c_str()));
+ }
+ else
+ {
+ mBrowserBox->addRow(strprintf("@@show bar_%s|%s %s@@",
+ // TRANSLATORS: popup menu item
+ bar->getActionEventId().c_str(), _("Show"),
+ bar->getId().c_str()));
+ }
+ }
+
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ mBrowserBox->addRow("reset yellow", _("Reset yellow bar"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: copy status to chat
+ mBrowserBox->addRow("bar to chat", _("Copy to chat"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showAttackMonsterPopup(const int x, const int y,
+ const std::string &name, const int type)
+{
+ if (!player_node || !actorSpriteManager)
+ return;
+
+ mNick = name;
+ mType = static_cast<int>(Being::MONSTER);
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+
+ if (name.empty())
+ {
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("(default)"));
+ }
+ else
+ {
+ mBrowserBox->addRow(name);
+ }
+ switch (type)
+ {
+ case MapItem::ATTACK:
+ {
+ const int idx = actorSpriteManager->getAttackMobIndex(name);
+ const int size = actorSpriteManager->getAttackMobsSize();
+ if (idx > 0)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move attack target up
+ mBrowserBox->addRow("attack moveup", _("Move up"));
+ }
+ if (idx + 1 < size)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move attack target down
+ mBrowserBox->addRow("attack movedown", _("Move down"));
+ }
+ break;
+ }
+ case MapItem::PRIORITY:
+ {
+ const int idx = actorSpriteManager->
+ getPriorityAttackMobIndex(name);
+ const int size = actorSpriteManager->getPriorityAttackMobsSize();
+ if (idx > 0)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move attack target up
+ mBrowserBox->addRow("priority moveup", _("Move up"));
+ }
+ if (idx + 1 < size)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: move attack target down
+ mBrowserBox->addRow("priority movedown", _("Move down"));
+ }
+ break;
+ }
+ case MapItem::IGNORE_:
+ break;
+ default:
+ break;
+ }
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove attack target
+ mBrowserBox->addRow("attack remove", _("Remove"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showPickupItemPopup(const int x, const int y, std::string name)
+{
+ if (!player_node || !actorSpriteManager)
+ return;
+
+ mNick = name;
+ mType = static_cast<int>(Being::FLOOR_ITEM);
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+
+ if (name.empty())
+ {
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("(default)"));
+ }
+ else
+ {
+ mBrowserBox->addRow(name);
+ }
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove item from pickup filter
+ mBrowserBox->addRow("pickup remove", _("Remove"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showUndressPopup(const int x, const int y,
+ const Being *const being, Item *const item)
+{
+ if (!being || !item)
+ return;
+
+ mBeingId = being->getId();
+ mItem = item;
+ mItemId = item->getId();
+ mItemColor = item->getColor();
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: undress item from player
+ mBrowserBox->addRow("undress item", _("Undress"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showTextFieldPopup(int x, int y, TextField *const input)
+{
+ mX = x;
+ mY = y;
+ mTextField = input;
+
+ mBrowserBox->clearRows();
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: copy text to clipboard
+ mBrowserBox->addRow("clipboard copy", _("Copy"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: paste text from clipboard
+ mBrowserBox->addRow("clipboard paste", _("Paste"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showLinkPopup(const int x, const int y,
+ const std::string &link)
+{
+ mX = x;
+ mY = y;
+ mNick = link;
+
+ mBrowserBox->clearRows();
+
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: open link in browser
+ mBrowserBox->addRow("open link", _("Open link"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: copy link to clipboard
+ mBrowserBox->addRow("clipboard link", _("Copy to clipboard"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showWindowsPopup(const int x, const int y)
+{
+ mX = x;
+ mY = y;
+
+ mBrowserBox->clearRows();
+ const std::vector<ButtonText*> &names = windowMenu->getButtonTexts();
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("Show window"));
+
+ FOR_EACH (std::vector<ButtonText*>::const_iterator, it, names)
+ {
+ const ButtonText *const btn = *it;
+ if (!btn)
+ continue;
+
+ mBrowserBox->addRow(strprintf("show window_%d", btn->key),
+ btn->text.c_str());
+ }
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showNpcDialogPopup(const int npcId, const int x, const int y)
+{
+ mBeingId = npcId;
+ mX = x;
+ mY = y;
+ mBrowserBox->clearRows();
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: copy npc text to clipboard
+ mBrowserBox->addRow("npc clipboard", _("Copy to clipboard"));
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(x, y);
+}
+
+void PopupMenu::showPopup(int x, int y)
+{
+ const int pad2 = 2 * mPadding;
+ const int bPad2 = 2 * mBrowserBox->getPadding();
+ mBrowserBox->setPosition(mPadding, mPadding);
+ mScrollArea->setPosition(mPadding, mPadding);
+ // add padding to initial size before draw browserbox
+ int height = mBrowserBox->getHeight();
+ if (height + pad2 >= mainGraphics->getHeight())
+ {
+ height = mainGraphics->getHeight() - bPad2 - pad2;
+ mBrowserBox->setWidth(mBrowserBox->getWidth() + bPad2 + 5);
+ mScrollArea->setWidth(mBrowserBox->getWidth() + pad2 + 10);
+ setContentSize(mBrowserBox->getWidth() + pad2 + 20,
+ height + pad2);
+ }
+ else
+ {
+ mBrowserBox->setWidth(mBrowserBox->getWidth() + bPad2);
+ mScrollArea->setWidth(mBrowserBox->getWidth() + pad2);
+ setContentSize(mBrowserBox->getWidth() + pad2,
+ height + pad2);
+ }
+ if (mainGraphics->mWidth < (x + getWidth() + 5))
+ x = mainGraphics->mWidth - getWidth();
+ if (mainGraphics->mHeight < (y + getHeight() + 5))
+ y = mainGraphics->mHeight - getHeight();
+ mScrollArea->setHeight(height);
+ setPosition(x, y);
+ setVisible(true);
+ requestMoveToTop();
+}
+
+void PopupMenu::addPlayerRelation(const std::string &name)
+{
+ switch (player_relations.getRelation(name))
+ {
+ case PlayerRelation::NEUTRAL:
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to friends list
+ mBrowserBox->addRow("friend", _("Be friend"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to disregarded list
+ mBrowserBox->addRow("disregard", _("Disregard"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to ignore list
+ mBrowserBox->addRow("ignore", _("Ignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to black list
+ mBrowserBox->addRow("blacklist", _("Black list"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to enemy list
+ mBrowserBox->addRow("enemy", _("Set as enemy"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to erased list
+ mBrowserBox->addRow("erase", _("Erase"));
+ break;
+
+ case PlayerRelation::FRIEND:
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to disregarded list
+ mBrowserBox->addRow("disregard", _("Disregard"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to ignore list
+ mBrowserBox->addRow("ignore", _("Ignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to black list
+ mBrowserBox->addRow("blacklist", _("Black list"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to enemy list
+ mBrowserBox->addRow("enemy", _("Set as enemy"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to erased list
+ mBrowserBox->addRow("erase", _("Erase"));
+ break;
+
+ case PlayerRelation::BLACKLISTED:
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove player from ignore list
+ mBrowserBox->addRow("unignore", _("Unignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to disregarded list
+ mBrowserBox->addRow("disregard", _("Disregard"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to ignore list
+ mBrowserBox->addRow("ignore", _("Ignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to enemy list
+ mBrowserBox->addRow("enemy", _("Set as enemy"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to erased list
+ mBrowserBox->addRow("erase", _("Erase"));
+ break;
+
+ case PlayerRelation::DISREGARDED:
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove player from ignore list
+ mBrowserBox->addRow("unignore", _("Unignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to completle ignore list
+ mBrowserBox->addRow("ignore", _("Completely ignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to erased list
+ mBrowserBox->addRow("erase", _("Erase"));
+ break;
+
+ case PlayerRelation::IGNORED:
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove player from ignore list
+ mBrowserBox->addRow("unignore", _("Unignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to erased list
+ mBrowserBox->addRow("erase", _("Erase"));
+ break;
+
+ case PlayerRelation::ENEMY2:
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove player from ignore list
+ mBrowserBox->addRow("unignore", _("Unignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to disregarded list
+ mBrowserBox->addRow("disregard", _("Disregard"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to ignore list
+ mBrowserBox->addRow("ignore", _("Ignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to black list
+ mBrowserBox->addRow("blacklist", _("Black list"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to erased list
+ mBrowserBox->addRow("erase", _("Erase"));
+ break;
+
+ case PlayerRelation::ERASED:
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove player from ignore list
+ mBrowserBox->addRow("unignore", _("Unignore"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to disregarded list
+ mBrowserBox->addRow("disregard", _("Disregard"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add player to ignore list
+ mBrowserBox->addRow("ignore", _("Completely ignore"));
+ break;
+
+ default:
+ break;
+ }
+}
+
+void PopupMenu::addFollow()
+{
+ // TRANSLATORS: popup menu item
+ mBrowserBox->addRow("follow", _("Follow"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: imitate player
+ mBrowserBox->addRow("imitation", _("Imitation"));
+}
+
+void PopupMenu::addBuySell(const Being *const being)
+{
+ if (player_relations.getDefault() & PlayerRelation::TRADE)
+ {
+ mBrowserBox->addRow("##3---");
+ if (being->isAdvanced())
+ {
+ if (being->isShopEnabled())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: buy item
+ mBrowserBox->addRow("buy", _("Buy"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: sell item
+ mBrowserBox->addRow("sell", _("Sell"));
+ }
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: buy item
+ mBrowserBox->addRow("buy", _("Buy (?)"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: sell item
+ mBrowserBox->addRow("sell", _("Sell (?)"));
+ }
+ }
+}
+
+void PopupMenu::addBuySellDefault()
+{
+ if (player_relations.getDefault() & PlayerRelation::TRADE)
+ {
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: buy item
+ mBrowserBox->addRow("buy", _("Buy (?)"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: sell item
+ mBrowserBox->addRow("sell", _("Sell (?)"));
+ }
+}
+
+void PopupMenu::addParty(const std::string &partyName)
+{
+ if (player_node->isInParty())
+ {
+ if (player_node->getParty())
+ {
+ if (player_node->getParty()->getName() != partyName)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: invite player to party
+ mBrowserBox->addRow("party", _("Invite to party"));
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: kick player from party
+ mBrowserBox->addRow("kick party", _("Kick from party"));
+ }
+ mBrowserBox->addRow("##3---");
+ }
+ }
+}
+
+void PopupMenu::addPlayerMisc()
+{
+ // TRANSLATORS: popup menu item
+ mBrowserBox->addRow("items", _("Show Items"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: undress player
+ mBrowserBox->addRow("undress", _("Undress"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add comment to player
+ mBrowserBox->addRow("addcomment", _("Add comment"));
+}
+
+void PopupMenu::addPickupFilter(const std::string &name)
+{
+ if (actorSpriteManager->isInPickupList(name)
+ || actorSpriteManager->isInIgnorePickupList(name))
+ {
+ mBrowserBox->addRow("remove pickup",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove item from pickup list
+ _("Remove from pickup list"));
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ mBrowserBox->addRow("add pickup", _("Add to pickup list"));
+ mBrowserBox->addRow("add pickup ignore",
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add item to pickup list
+ _("Add to ignore list"));
+ }
+}
+
+void PopupMenu::showPopup(const int x, const int y,
+ gcn::ListModel *const model)
+{
+ if (!model)
+ return;
+
+ mBrowserBox->clearRows();
+ for (int f = 0, sz = model->getNumberOfElements(); f < sz; f ++)
+ {
+ mBrowserBox->addRow(strprintf("dropdown_%d", f),
+ model->getElementAt(f).c_str());
+ }
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+ showPopup(x, y);
+}
+
+void PopupMenu::clear()
+{
+ if (mDialog)
+ {
+ mDialog->close();
+ mDialog = nullptr;
+ }
+ mItem = nullptr;
+ mMapItem = nullptr;
+ mTab = nullptr;
+ mSpell = nullptr;
+ mWindow = nullptr;
+ mButton = nullptr;
+ mTextField = nullptr;
+}
+
+void PopupMenu::addProtection()
+{
+ if (PlayerInfo::isItemProtected(mItemId))
+ {
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: remove protection from item
+ mBrowserBox->addRow("unprotect item", _("Unprotect item"));
+ }
+ else
+ {
+ if (mItemId < SPELL_MIN_ID)
+ {
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: add protection to item
+ mBrowserBox->addRow("protect item", _("Protect item"));
+ }
+ }
+}
+
+void PopupMenu::addUseDrop(const Item *const item, const bool isProtected)
+{
+ if (item->isEquipment())
+ {
+ if (item->isEquipped())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: unequip item
+ mBrowserBox->addRow("use", _("Unequip"));
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: use item
+ mBrowserBox->addRow("use", _("Equip"));
+ }
+ }
+ else
+ {
+ if (!isProtected)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: use item
+ mBrowserBox->addRow("use", _("Use"));
+ }
+ }
+
+ if (!isProtected)
+ {
+ mBrowserBox->addRow("##3---");
+ if (item->getQuantity() > 1)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: drop item
+ mBrowserBox->addRow("drop", _("Drop..."));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: drop all item amount
+ mBrowserBox->addRow("drop all", _("Drop all"));
+ }
+ else
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: drop item
+ mBrowserBox->addRow("drop", _("Drop"));
+ }
+ }
+
+ if (Net::getInventoryHandler()->canSplit(item))
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: split items
+ mBrowserBox->addRow("split", _("Split"));
+ }
+}
+
+void PopupMenu::addGmCommands()
+{
+ if (player_node->isGM())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: gm commands
+ mBrowserBox->addRow("gm", _("GM..."));
+ }
+}
+
+void PopupMenu::showGMPopup()
+{
+ mBrowserBox->clearRows();
+ // TRANSLATORS: popup menu header
+ mBrowserBox->addRow(_("GM commands"));
+ if (player_node->isGM())
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: check player ip
+ mBrowserBox->addRow("ipcheck", _("Check ip"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: go to player position
+ mBrowserBox->addRow("goto", _("Goto"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: recall player to current position
+ mBrowserBox->addRow("recall", _("Recall"));
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: revive player
+ mBrowserBox->addRow("revive", _("Revive"));
+ if (mBeingId)
+ {
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: kick player
+ mBrowserBox->addRow("admin-kick", _("Kick"));
+ }
+ }
+
+ mBrowserBox->addRow("##3---");
+ // TRANSLATORS: popup menu item
+ // TRANSLATORS: close menu
+ mBrowserBox->addRow("cancel", _("Cancel"));
+
+ showPopup(getX(), getY());
+}
+
+RenameListener::RenameListener() :
+ gcn::ActionListener(),
+ mMapItemX(0),
+ mMapItemY(0),
+ mDialog(nullptr)
+{
+}
+
+void RenameListener::setMapItem(MapItem *const mapItem)
+{
+ if (mapItem)
+ {
+ mMapItemX = mapItem->getX();
+ mMapItemY = mapItem->getY();
+ }
+ else
+ {
+ mMapItemX = 0;
+ mMapItemY = 0;
+ }
+}
+
+void RenameListener::action(const gcn::ActionEvent &event)
+{
+ if (event.getId() == "ok" && viewport && mDialog)
+ {
+ Map *const map = viewport->getMap();
+ if (!map)
+ return;
+
+ SpecialLayer *const sl = map->getSpecialLayer();
+ MapItem *item = nullptr;
+ if (sl)
+ {
+ item = sl->getTile(mMapItemX, mMapItemY);
+ if (item)
+ item->setComment(mDialog->getText());
+ }
+ item = map->findPortalXY(mMapItemX, mMapItemY);
+ if (item)
+ item->setComment(mDialog->getText());
+
+ if (socialWindow)
+ socialWindow->updatePortalNames();
+ }
+ mDialog = nullptr;
+}
+
+PlayerListener::PlayerListener() :
+ ActionListener(),
+ mNick(),
+ mDialog(nullptr),
+ mType(static_cast<int>(Being::UNKNOWN))
+{
+}
+
+void PlayerListener::action(const gcn::ActionEvent &event)
+{
+ if (event.getId() == "ok" && !mNick.empty() && mDialog)
+ {
+ std::string comment = mDialog->getText();
+ Being *const being = actorSpriteManager->findBeingByName(
+ mNick, static_cast<ActorSprite::Type>(mType));
+ if (being)
+ being->setComment(comment);
+ Being::saveComment(mNick, comment, mType);
+ }
+ mDialog = nullptr;
+}
diff --git a/src/gui/popups/popupmenu.h b/src/gui/popups/popupmenu.h
new file mode 100644
index 000000000..3865c2bd3
--- /dev/null
+++ b/src/gui/popups/popupmenu.h
@@ -0,0 +1,241 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GUI_POPUPMENU_H
+#define GUI_POPUPMENU_H
+
+#include "gui/widgets/linkhandler.h"
+#include "gui/widgets/popup.h"
+
+#include "being/actorsprite.h"
+
+#include <guichan/actionlistener.hpp>
+#include <guichan/listmodel.hpp>
+
+#include "localconsts.h"
+
+class Being;
+class BrowserBox;
+class Button;
+class ChatTab;
+class FloorItem;
+class Item;
+class MapItem;
+class ScrollArea;
+class TextCommand;
+class TextDialog;
+class TextField;
+class ProgressBar;
+class Window;
+
+class RenameListener final : public gcn::ActionListener
+{
+ public:
+ RenameListener();
+
+ A_DELETE_COPY(RenameListener)
+
+ void action(const gcn::ActionEvent &event) override;
+
+ void setMapItem(MapItem *const mapItem);
+
+ void setDialog(TextDialog *dialog)
+ { mDialog = dialog; }
+
+ private:
+ int mMapItemX;
+ int mMapItemY;
+ TextDialog *mDialog;
+};
+
+class PlayerListener : public gcn::ActionListener
+{
+ public:
+ PlayerListener();
+
+ A_DELETE_COPY(PlayerListener)
+
+ void action(const gcn::ActionEvent &event) override;
+
+ void setNick(std::string name)
+ { mNick = name; }
+
+ void setDialog(TextDialog *dialog)
+ { mDialog = dialog; }
+
+ void setType(int type)
+ { mType = type; }
+
+ private:
+ std::string mNick;
+ TextDialog *mDialog;
+ int mType;
+};
+
+/**
+ * Window showing popup menu.
+ */
+class PopupMenu final : public Popup, public LinkHandler
+{
+ public:
+ /**
+ * Constructor.
+ */
+ PopupMenu();
+
+ A_DELETE_COPY(PopupMenu)
+
+ /**
+ * Shows the being related popup menu at the specified mouse coords.
+ */
+ void showPopup(const int x, const int y, const Being *const being);
+
+ /**
+ * Shows the beings related popup menu at the specified mouse coords.
+ */
+ void showPopup(const int x, const int y,
+ std::vector<ActorSprite*> &beings);
+
+ void showPlayerPopup(const int x, const int y,
+ const std::string &nick);
+
+ /**
+ * Shows the floor item related popup menu at the specified
+ * mouse coords.
+ */
+ void showPopup(const int x, const int y,
+ const FloorItem *const floorItem);
+
+ /**
+ * Shows the related popup menu when right click on the inventory
+ * at the specified mouse coordinates.
+ */
+ void showPopup(Window *const parent, const int x, const int y,
+ Item *const item, const bool isInventory);
+
+ void showPopup(const int x, const int y, Button *const button);
+
+ void showPopup(const int x, const int y, const ProgressBar *const bar);
+
+ void showPopup(const int x, const int y, MapItem *const mapItem);
+
+ void showItemPopup(const int x, const int y, Item *const item);
+
+ void showItemPopup(const int x, const int y, const int itemId,
+ const unsigned char color);
+
+ void showDropPopup(const int x, const int y, Item *const item);
+
+ void showOutfitsPopup(const int x, const int y);
+
+ void showSpellPopup(const int x, const int y, TextCommand *const cmd);
+
+ void showAttackMonsterPopup(int x, int y, const std::string &name,
+ int type);
+
+ void showPickupItemPopup(int x, int y, std::string name);
+
+ void showUndressPopup(const int x, const int y,
+ const Being *const being,
+ Item *const item);
+
+ void showMapPopup(const int x, const int y,
+ const int x2, const int y2);
+
+ /**
+ * Shows the related popup menu when right click on the chat
+ * at the specified mouse coordinates.
+ */
+ void showChatPopup(const int x, const int y, ChatTab *const tab);
+
+ void showChangePos(const int x, const int y);
+
+ void showPopup(const int x, const int y, gcn::ListModel *const model);
+
+ void showTextFieldPopup(int x, int y, TextField *const input);
+
+ void showLinkPopup(const int x, const int y, const std::string &link);
+
+ void showWindowsPopup(const int x, const int y);
+
+ void showNpcDialogPopup(const int npcId, const int x, const int y);
+
+ /**
+ * Handles link action.
+ */
+ void handleLink(const std::string &link,
+ gcn::MouseEvent *event A_UNUSED) override;
+
+ void clear();
+
+ private:
+ void addPlayerRelation(const std::string &name);
+
+ void addFollow();
+
+ void addBuySell(const Being *const being);
+
+ void addParty(const std::string &partyName);
+
+ void addBuySellDefault();
+
+ void addPlayerMisc();
+
+ void addPickupFilter(const std::string &name);
+
+ void addProtection();
+
+ void addUseDrop(const Item *const item, const bool isProtected);
+
+ void addGmCommands();
+
+ void showGMPopup();
+
+ BrowserBox *mBrowserBox;
+ ScrollArea *mScrollArea;
+
+ int mBeingId;
+ int mFloorItemId;
+ Item *mItem;
+ int mItemId;
+ unsigned char mItemColor;
+ MapItem *mMapItem;
+ ChatTab *mTab;
+ TextCommand *mSpell;
+ Window *mWindow;
+ RenameListener mRenameListener;
+ PlayerListener mPlayerListener;
+ TextDialog *mDialog;
+ Button *mButton;
+ std::string mNick;
+ TextField *mTextField;
+ int mType;
+ int mX;
+ int mY;
+
+ /**
+ * Shared code for the various showPopup functions.
+ */
+ void showPopup(int x, int y);
+};
+
+#endif // GUI_POPUPMENU_H
diff --git a/src/gui/popups/speechbubble.cpp b/src/gui/popups/speechbubble.cpp
new file mode 100644
index 000000000..510bb9eae
--- /dev/null
+++ b/src/gui/popups/speechbubble.cpp
@@ -0,0 +1,97 @@
+/*
+ * Speech bubbles
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gui/popups/speechbubble.h"
+
+#include "gui/gui.h"
+#include "gui/sdlfont.h"
+
+#include "gui/widgets/label.h"
+#include "gui/widgets/textbox.h"
+
+#include <guichan/font.hpp>
+
+#include "debug.h"
+
+SpeechBubble::SpeechBubble() :
+ Popup("Speech", "speechbubble.xml"),
+ mText(),
+ mCaption(new Label(this)),
+ mSpeechBox(new TextBox(this))
+{
+ setContentSize(140, 46);
+ setMinWidth(29);
+ setMinHeight(29);
+
+ mCaption->setFont(boldFont);
+ mSpeechBox->setEditable(false);
+ mSpeechBox->setOpaque(false);
+ mSpeechBox->setForegroundColorAll(getThemeColor(Theme::BUBBLE_TEXT),
+ getThemeColor(Theme::BUBBLE_TEXT_OUTLINE));
+
+ add(mCaption);
+ add(mSpeechBox);
+}
+
+void SpeechBubble::setCaption(const std::string &name,
+ const gcn::Color *const color1,
+ const gcn::Color *const color2)
+{
+ mCaption->setCaption(name);
+ mCaption->adjustSize();
+ mCaption->setForegroundColorAll(*color1, *color2);
+}
+
+void SpeechBubble::setText(const std::string &text, const bool showName)
+{
+ if (text == mText && (mCaption->getWidth() <= mSpeechBox->getMinWidth()))
+ return;
+
+ mSpeechBox->setForegroundColorAll(getThemeColor(Theme::BUBBLE_TEXT),
+ getThemeColor(Theme::BUBBLE_TEXT_OUTLINE));
+
+ const int pad = mPadding;
+ const int pad2 = 2 * pad;
+ int width = mCaption->getWidth() + pad2;
+ mSpeechBox->setTextWrapped(text, 130 > width ? 130 : width);
+ const int speechWidth = mSpeechBox->getMinWidth() + pad2;
+
+ const int fontHeight = getFont()->getHeight();
+ const int nameHeight = showName ? mCaption->getHeight() + pad / 2 : 0;
+ const int numRows = mSpeechBox->getNumberOfRows();
+ const int height = (numRows * fontHeight) + nameHeight + pad;
+
+ if (width < speechWidth)
+ width = speechWidth;
+
+ width += pad2;
+
+ setContentSize(width, height);
+
+ const gcn::Rectangle &rect = mDimension;
+ const int xPos = ((rect.width - width) / 2);
+ const int yPos = ((rect.height - height) / 2) + nameHeight;
+
+ mCaption->setPosition(xPos, pad);
+ mSpeechBox->setPosition(xPos, yPos);
+}
diff --git a/src/gui/popups/speechbubble.h b/src/gui/popups/speechbubble.h
new file mode 100644
index 000000000..ca3c9668b
--- /dev/null
+++ b/src/gui/popups/speechbubble.h
@@ -0,0 +1,64 @@
+/*
+ * Speech bubbles
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GUI_SPEECHBUBBLE_H
+#define GUI_SPEECHBUBBLE_H
+
+#include "gui/theme.h"
+
+#include "gui/widgets/popup.h"
+
+class Label;
+class TextBox;
+
+class SpeechBubble final : public Popup
+{
+ public:
+ /**
+ * Constructor. Initializes the speech bubble.
+ */
+ SpeechBubble();
+
+ A_DELETE_COPY(SpeechBubble)
+
+ /**
+ * Sets the name displayed for the speech bubble, and in what color.
+ */
+ void setCaption(const std::string &name,
+ const gcn::Color *const color1 =
+ &Theme::getThemeColor(Theme::BUBBLE_NAME),
+ const gcn::Color *const color2 =
+ &Theme::getThemeColor(Theme::BUBBLE_NAME_OUTLINE));
+
+ /**
+ * Sets the text to be displayed.
+ */
+ void setText(const std::string &text, const bool showName = true);
+
+ private:
+ std::string mText;
+ Label *mCaption;
+ TextBox *mSpeechBox;
+};
+
+#endif // GUI_SPEECHBUBBLE_H
diff --git a/src/gui/popups/spellpopup.cpp b/src/gui/popups/spellpopup.cpp
new file mode 100644
index 000000000..4e06b3d87
--- /dev/null
+++ b/src/gui/popups/spellpopup.cpp
@@ -0,0 +1,116 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 Andrei Karas
+ * Copyright (C) 2011-2013 The ManaPlus developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gui/popups/spellpopup.h"
+
+#include "gui/gui.h"
+#include "gui/sdlfont.h"
+
+#include "gui/widgets/label.h"
+
+#include "debug.h"
+
+SpellPopup::SpellPopup():
+ Popup("SpellPopup", "spellpopup.xml"),
+ mItemName(new Label(this)),
+ mItemComment(new Label(this))
+{
+ mItemName->setFont(boldFont);
+ mItemName->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+ mItemComment->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+
+ add(mItemName);
+ add(mItemComment);
+
+ addMouseListener(this);
+}
+
+SpellPopup::~SpellPopup()
+{
+}
+
+void SpellPopup::setItem(const TextCommand *const spell)
+{
+ if (spell)
+ {
+ mItemName->setCaption(spell->getName());
+ mItemComment->setCaption(spell->getComment());
+ }
+ else
+ {
+ mItemName->setCaption("?");
+ mItemComment->setCaption("");
+ }
+
+ mItemName->adjustSize();
+ mItemComment->adjustSize();
+ int minWidth = mItemName->getWidth();
+ if (mItemComment->getWidth() > minWidth)
+ minWidth = mItemComment->getWidth();
+
+ mItemName->setPosition(0, 0);
+ const int fontHeight = mItemName->getHeight();
+ mItemComment->setPosition(0, fontHeight);
+ if (!mItemComment->getCaption().empty())
+ setContentSize(minWidth, 2 * fontHeight);
+ else
+ setContentSize(minWidth, fontHeight);
+}
+
+void SpellPopup::view(const int x, const int y)
+{
+ const int distance = 20;
+
+ int posX = std::max(0, x - getWidth() / 2);
+ int posY = y + distance;
+
+ const gcn::Rectangle &rect = mDimension;
+ const int w = rect.width;
+ const int h = rect.height;
+ if (posX + w > mainGraphics->mWidth)
+ {
+ if (mainGraphics->mWidth > w)
+ posX = mainGraphics->mWidth - w;
+ else
+ posX = 0;
+ }
+ if (posY + h > mainGraphics->mHeight)
+ {
+ if (y > h + distance)
+ posY = y - h - distance;
+ }
+
+ setPosition(posX, posY);
+ setVisible(true);
+ requestMoveToTop();
+}
+
+void SpellPopup::mouseMoved(gcn::MouseEvent &event)
+{
+ Popup::mouseMoved(event);
+
+ // When the mouse moved on top of the popup, hide it
+ setVisible(false);
+}
diff --git a/src/gui/popups/spellpopup.h b/src/gui/popups/spellpopup.h
new file mode 100644
index 000000000..b7a1f83b3
--- /dev/null
+++ b/src/gui/popups/spellpopup.h
@@ -0,0 +1,69 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 Andrei Karas
+ * Copyright (C) 2011-2013 The ManaPlus developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GUI_SPELLPOPUP_H
+#define GUI_SPELLPOPUP_H
+
+#include "gui/widgets/popup.h"
+
+#include "textcommand.h"
+
+class Label;
+
+/**
+ * A popup that displays information about an item.
+ */
+class SpellPopup final : public Popup
+{
+ public:
+ /**
+ * Constructor. Initializes the item popup.
+ */
+ SpellPopup();
+
+ A_DELETE_COPY(SpellPopup)
+
+ /**
+ * Destructor. Cleans up the item popup on deletion.
+ */
+ ~SpellPopup();
+
+ /**
+ * Sets the info to be displayed given a particular item.
+ */
+ void setItem(const TextCommand *const spell);
+
+ /**
+ * Sets the location to display the item popup.
+ */
+ void view(const int x, const int y);
+
+ void mouseMoved(gcn::MouseEvent &mouseEvent) override;
+
+ private:
+ Label *mItemName;
+
+ Label *mItemComment;
+};
+
+#endif // GUI_SPELLPOPUP_H
diff --git a/src/gui/popups/statuspopup.cpp b/src/gui/popups/statuspopup.cpp
new file mode 100644
index 000000000..fb2b161c2
--- /dev/null
+++ b/src/gui/popups/statuspopup.cpp
@@ -0,0 +1,169 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 Andrei Karas
+ * Copyright (C) 2011-2013 The ManaPlus developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gui/viewport.h"
+
+#include "gui/popups/statuspopup.h"
+
+#include "gui/widgets/label.h"
+
+#include "being/localplayer.h"
+
+#include "input/inputmanager.h"
+
+#include "utils/stringutils.h"
+
+#include <guichan/font.hpp>
+
+#include "debug.h"
+
+#define addLabel(num) \
+ { \
+ Label *const label = mLabels[num]; \
+ label->setPosition(0, y); \
+ label->setForegroundColorAll(getThemeColor(Theme::POPUP), \
+ getThemeColor(Theme::POPUP_OUTLINE)); \
+ add(label); \
+ y += fontHeight; \
+ }
+
+StatusPopup::StatusPopup() :
+ Popup("StatusPopup", "statuspopup.xml")
+{
+ const int fontHeight = getFont()->getHeight();
+
+ for (int f = 0; f < STATUSPOPUP_NUM_LABELS; f ++)
+ mLabels[f] = new Label(this);
+ int y = 0;
+
+ addLabel(0);
+ addLabel(1);
+ addLabel(2);
+ addLabel(3);
+ y += 4;
+ addLabel(4);
+ addLabel(5);
+ addLabel(9);
+ addLabel(10);
+ y += 4;
+ addLabel(6);
+ addLabel(7);
+ y += 4;
+ addLabel(8);
+ y += 4;
+ addLabel(12);
+ addLabel(13);
+ addLabel(14);
+ y += 4;
+ addLabel(11);
+}
+
+StatusPopup::~StatusPopup()
+{
+}
+
+void StatusPopup::update()
+{
+ updateLabels();
+
+ int maxWidth = mLabels[0]->getWidth();
+
+ for (int f = 0; f < STATUSPOPUP_NUM_LABELS; f ++)
+ {
+ const int width = mLabels[f]->getWidth();
+ if (width > maxWidth)
+ maxWidth = width;
+ }
+
+ const int pad2 = 2 * mPadding;
+ maxWidth += pad2;
+ setWidth(maxWidth);
+ setHeight(mLabels[11]->getY()
+ + mLabels[11]->getHeight() + pad2);
+}
+
+void StatusPopup::view(const int x, const int y)
+{
+ const int distance = 20;
+
+ int posX = std::max(0, x - getWidth() / 2);
+ int posY = y + distance;
+
+ if (posX + getWidth() > mainGraphics->mWidth)
+ posX = mainGraphics->mWidth - getWidth();
+ if (posY + getHeight() > mainGraphics->mHeight)
+ posY = y - getHeight() - distance;
+
+ update();
+
+ setPosition(posX, posY);
+ setVisible(true);
+ requestMoveToTop();
+}
+
+void StatusPopup::setLabelText(const int num,
+ const std::string &text,
+ const Input::KeyAction key) const
+{
+ Label *const label = mLabels[num];
+ label->setCaption(strprintf("%s %s", text.c_str(),
+ inputManager.getKeyValueString(static_cast<int>(key)).c_str()));
+ label->adjustSize();
+}
+
+void StatusPopup::updateLabels() const
+{
+ if (!player_node || !viewport)
+ return;
+
+ setLabelText(0, player_node->getInvertDirectionString(),
+ Input::KEY_INVERT_DIRECTION);
+ setLabelText(1, player_node->getCrazyMoveTypeString(),
+ Input::KEY_CHANGE_CRAZY_MOVES_TYPE);
+ setLabelText(2, player_node->getMoveToTargetTypeString(),
+ Input::KEY_CHANGE_MOVE_TO_TARGET);
+ setLabelText(3, player_node->getFollowModeString(),
+ Input::KEY_CHANGE_FOLLOW_MODE);
+ setLabelText(4, player_node->getAttackWeaponTypeString(),
+ Input::KEY_CHANGE_ATTACK_WEAPON_TYPE);
+ setLabelText(5, player_node->getAttackTypeString(),
+ Input::KEY_CHANGE_ATTACK_TYPE);
+ setLabelText(6, player_node->getQuickDropCounterString(),
+ Input::KEY_SWITCH_QUICK_DROP);
+ setLabelText(7, player_node->getPickUpTypeString(),
+ Input::KEY_CHANGE_PICKUP_TYPE);
+ setLabelText(8, player_node->getDebugPathString(),
+ Input::KEY_PATHFIND);
+ setLabelText(9, player_node->getMagicAttackString(),
+ Input::KEY_SWITCH_MAGIC_ATTACK);
+ setLabelText(10, player_node->getPvpAttackString(),
+ Input::KEY_SWITCH_PVP_ATTACK);
+ setLabelText(11, player_node->getGameModifiersString(),
+ Input::KEY_DISABLE_GAME_MODIFIERS);
+ setLabelText(12, player_node->getImitationModeString(),
+ Input::KEY_CHANGE_IMITATION_MODE);
+ setLabelText(13, player_node->getAwayModeString(),
+ Input::KEY_AWAY);
+ setLabelText(14, player_node->getCameraModeString(),
+ Input::KEY_CAMERA);
+}
diff --git a/src/gui/popups/statuspopup.h b/src/gui/popups/statuspopup.h
new file mode 100644
index 000000000..f7964741b
--- /dev/null
+++ b/src/gui/popups/statuspopup.h
@@ -0,0 +1,69 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 Andrei Karas
+ * Copyright (C) 2011-2013 The ManaPlus developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GUI_STATUSPOPUP_H
+#define GUI_STATUSPOPUP_H
+
+#include "input/keydata.h"
+
+#include "gui/widgets/popup.h"
+
+class Label;
+
+const int STATUSPOPUP_NUM_LABELS = 15;
+
+/**
+ * A popup that displays information about an item.
+ */
+class StatusPopup final : public Popup
+{
+ public:
+ /**
+ * Constructor. Initializes the item popup.
+ */
+ StatusPopup();
+
+ A_DELETE_COPY(StatusPopup)
+
+ /**
+ * Destructor. Cleans up the item popup on deletion.
+ */
+ ~StatusPopup();
+
+ /**
+ * Sets the location to display the item popup.
+ */
+ void view(const int x, const int y);
+
+ void update();
+
+ private:
+ void updateLabels() const;
+
+ void setLabelText(const int num, const std::string &text,
+ const Input::KeyAction key) const;
+
+ Label *mLabels[STATUSPOPUP_NUM_LABELS];
+};
+
+#endif // GUI_STATUSPOPUP_H
diff --git a/src/gui/popups/textpopup.cpp b/src/gui/popups/textpopup.cpp
new file mode 100644
index 000000000..2b89e388b
--- /dev/null
+++ b/src/gui/popups/textpopup.cpp
@@ -0,0 +1,105 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gui/popups/textpopup.h"
+
+#include "gui/widgets/label.h"
+
+#include <guichan/font.hpp>
+
+#include "debug.h"
+
+TextPopup::TextPopup():
+ Popup("TextPopup", "textpopup.xml"),
+ mText()
+{
+ const int fontHeight = getFont()->getHeight();
+ int y = 0;
+ for (int f = 0; f < TEXTPOPUPCOUNT; f ++)
+ {
+ Label *const label = new Label(this);
+ mText[f] = label;
+ label->setPosition(0, y);
+ label->setForegroundColorAll(getThemeColor(Theme::POPUP),
+ getThemeColor(Theme::POPUP_OUTLINE));
+ add(label);
+ y += fontHeight;
+ }
+ addMouseListener(this);
+}
+
+TextPopup::~TextPopup()
+{
+}
+
+void TextPopup::show(const int x, const int y, const std::string &str1,
+ const std::string &str2, const std::string &str3)
+{
+ mText[0]->setCaption(str1);
+ mText[1]->setCaption(str2);
+ mText[2]->setCaption(str3);
+
+ int minWidth = 0;
+ for (int f = 0; f < TEXTPOPUPCOUNT; f ++)
+ {
+ Label *const label = mText[f];
+ label->adjustSize();
+ const int width = label->getWidth();
+ if (width > minWidth)
+ minWidth = width;
+ }
+
+ const int pad2 = 2 * mPadding;
+ minWidth += pad2;
+ setWidth(minWidth);
+
+ int cnt = 1;
+ if (!str2.empty())
+ cnt ++;
+ if (!str3.empty())
+ cnt ++;
+
+ setHeight(pad2 + mText[0]->getFont()->getHeight() * cnt);
+ const int distance = 20;
+
+ const gcn::Rectangle &rect = mDimension;
+ int posX = std::max(0, x - rect.width / 2);
+ int posY = y + distance;
+
+ if (posX + rect.width > mainGraphics->mWidth)
+ posX = mainGraphics->mWidth - rect.width;
+ if (posY + rect.height > mainGraphics->mHeight)
+ posY = y - rect.height - distance;
+
+ setPosition(posX, posY);
+ setVisible(true);
+ requestMoveToTop();
+}
+
+void TextPopup::mouseMoved(gcn::MouseEvent &event)
+{
+ Popup::mouseMoved(event);
+
+ // When the mouse moved on top of the popup, hide it
+ setVisible(false);
+}
diff --git a/src/gui/popups/textpopup.h b/src/gui/popups/textpopup.h
new file mode 100644
index 000000000..b165c2f25
--- /dev/null
+++ b/src/gui/popups/textpopup.h
@@ -0,0 +1,79 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2008 The Legend of Mazzeroth Development Team
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GUI_TEXTPOPUP_H
+#define GUI_TEXTPOPUP_H
+
+#include "gui/widgets/popup.h"
+
+class Label;
+
+const int TEXTPOPUPCOUNT = 3;
+
+/**
+ * A popup that displays information about an item.
+ */
+class TextPopup final : public Popup
+{
+ public:
+ /**
+ * Constructor. Initializes the item popup.
+ */
+ TextPopup();
+
+ A_DELETE_COPY(TextPopup)
+
+ /**
+ * Destructor. Cleans up the item popup on deletion.
+ */
+ ~TextPopup();
+
+ /**
+ * Sets the text to be displayed.
+ */
+ void show(const int x, const int y, const std::string &str1)
+ {
+ show(x, y, str1, static_cast<const char*>(""),
+ static_cast<const char*>(""));
+ }
+
+ /**
+ * Sets the text to be displayed.
+ */
+ void show(const int x, const int y, const std::string &str1,
+ const std::string &str2)
+ { show(x, y, str1, str2, static_cast<const char*>("")); }
+
+ /**
+ * Sets the text to be displayed.
+ */
+ void show(const int x, const int y, const std::string &str1,
+ const std::string &str2, const std::string &str3);
+
+ void mouseMoved(gcn::MouseEvent &mouseEvent) override;
+
+ private:
+ Label *mText[TEXTPOPUPCOUNT];
+};
+
+#endif // GUI_TEXTPOPUP_H