summaryrefslogtreecommitdiff
path: root/src/gui/widgets
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2011-01-02 01:48:38 +0200
committerAndrei Karas <akaras@inbox.ru>2011-01-02 02:41:24 +0200
commit3eeae12c498d1a4dbe969462d2ba841f77ee3ccb (patch)
treeff8eab35e732bc0749fc11677c8873a7b3a58704 /src/gui/widgets
downloadplus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.gz
plus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.bz2
plus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.xz
plus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.zip
Initial commit.
This code based on mana client http://www.gitorious.org/mana/mana and my private repository.
Diffstat (limited to 'src/gui/widgets')
-rw-r--r--src/gui/widgets/avatarlistbox.cpp346
-rw-r--r--src/gui/widgets/avatarlistbox.h70
-rw-r--r--src/gui/widgets/battletab.cpp54
-rw-r--r--src/gui/widgets/battletab.h47
-rw-r--r--src/gui/widgets/browserbox.cpp534
-rw-r--r--src/gui/widgets/browserbox.h205
-rw-r--r--src/gui/widgets/button.cpp227
-rw-r--r--src/gui/widgets/button.h94
-rw-r--r--src/gui/widgets/channeltab.cpp132
-rw-r--r--src/gui/widgets/channeltab.h62
-rw-r--r--src/gui/widgets/chattab.cpp431
-rw-r--r--src/gui/widgets/chattab.h173
-rw-r--r--src/gui/widgets/checkbox.cpp187
-rw-r--r--src/gui/widgets/checkbox.h92
-rw-r--r--src/gui/widgets/container.cpp33
-rw-r--r--src/gui/widgets/container.h43
-rw-r--r--src/gui/widgets/desktop.cpp157
-rw-r--r--src/gui/widgets/desktop.h73
-rw-r--r--src/gui/widgets/dropdown.cpp303
-rw-r--r--src/gui/widgets/dropdown.h97
-rw-r--r--src/gui/widgets/dropshortcutcontainer.cpp303
-rw-r--r--src/gui/widgets/dropshortcutcontainer.h88
-rw-r--r--src/gui/widgets/emoteshortcutcontainer.cpp259
-rw-r--r--src/gui/widgets/emoteshortcutcontainer.h84
-rw-r--r--src/gui/widgets/flowcontainer.cpp88
-rw-r--r--src/gui/widgets/flowcontainer.h73
-rw-r--r--src/gui/widgets/icon.cpp60
-rw-r--r--src/gui/widgets/icon.h66
-rw-r--r--src/gui/widgets/inttextfield.cpp112
-rw-r--r--src/gui/widgets/inttextfield.h76
-rw-r--r--src/gui/widgets/itemcontainer.cpp475
-rw-r--r--src/gui/widgets/itemcontainer.h195
-rw-r--r--src/gui/widgets/itemlinkhandler.cpp66
-rw-r--r--src/gui/widgets/itemlinkhandler.h47
-rw-r--r--src/gui/widgets/itemshortcutcontainer.cpp375
-rw-r--r--src/gui/widgets/itemshortcutcontainer.h93
-rw-r--r--src/gui/widgets/label.cpp38
-rw-r--r--src/gui/widgets/label.h52
-rw-r--r--src/gui/widgets/layout.cpp362
-rw-r--r--src/gui/widgets/layout.h319
-rw-r--r--src/gui/widgets/layouthelper.cpp63
-rw-r--r--src/gui/widgets/layouthelper.h90
-rw-r--r--src/gui/widgets/linkhandler.h42
-rw-r--r--src/gui/widgets/listbox.cpp146
-rw-r--r--src/gui/widgets/listbox.h78
-rw-r--r--src/gui/widgets/passwordfield.cpp36
-rw-r--r--src/gui/widgets/passwordfield.h46
-rw-r--r--src/gui/widgets/playerbox.cpp120
-rw-r--r--src/gui/widgets/playerbox.h74
-rw-r--r--src/gui/widgets/popup.cpp174
-rw-r--r--src/gui/widgets/popup.h174
-rw-r--r--src/gui/widgets/progressbar.cpp225
-rw-r--r--src/gui/widgets/progressbar.h139
-rw-r--r--src/gui/widgets/progressindicator.cpp78
-rw-r--r--src/gui/widgets/progressindicator.h45
-rw-r--r--src/gui/widgets/radiobutton.cpp163
-rw-r--r--src/gui/widgets/radiobutton.h85
-rw-r--r--src/gui/widgets/resizegrip.cpp82
-rw-r--r--src/gui/widgets/resizegrip.h60
-rw-r--r--src/gui/widgets/scrollarea.cpp445
-rw-r--r--src/gui/widgets/scrollarea.h151
-rw-r--r--src/gui/widgets/setuptab.cpp31
-rw-r--r--src/gui/widgets/setuptab.h64
-rw-r--r--src/gui/widgets/shopitems.cpp118
-rw-r--r--src/gui/widgets/shopitems.h120
-rw-r--r--src/gui/widgets/shoplistbox.cpp185
-rw-r--r--src/gui/widgets/shoplistbox.h104
-rw-r--r--src/gui/widgets/shortcutcontainer.cpp67
-rw-r--r--src/gui/widgets/shortcutcontainer.h115
-rw-r--r--src/gui/widgets/slider.cpp298
-rw-r--r--src/gui/widgets/slider.h98
-rw-r--r--src/gui/widgets/spellshortcutcontainer.cpp285
-rw-r--r--src/gui/widgets/spellshortcutcontainer.h88
-rw-r--r--src/gui/widgets/tab.cpp196
-rw-r--r--src/gui/widgets/tab.h80
-rw-r--r--src/gui/widgets/tabbedarea.cpp221
-rw-r--r--src/gui/widgets/tabbedarea.h129
-rw-r--r--src/gui/widgets/table.cpp585
-rw-r--r--src/gui/widgets/table.h195
-rw-r--r--src/gui/widgets/tablemodel.cpp173
-rw-r--r--src/gui/widgets/tablemodel.h149
-rw-r--r--src/gui/widgets/textbox.cpp149
-rw-r--r--src/gui/widgets/textbox.h70
-rw-r--r--src/gui/widgets/textfield.cpp306
-rw-r--r--src/gui/widgets/textfield.h110
-rw-r--r--src/gui/widgets/textpreview.cpp82
-rw-r--r--src/gui/widgets/textpreview.h130
-rw-r--r--src/gui/widgets/tradetab.cpp59
-rw-r--r--src/gui/widgets/tradetab.h50
-rw-r--r--src/gui/widgets/vertcontainer.cpp53
-rw-r--r--src/gui/widgets/vertcontainer.h52
-rw-r--r--src/gui/widgets/whispertab.cpp164
-rw-r--r--src/gui/widgets/whispertab.h67
-rw-r--r--src/gui/widgets/window.cpp924
-rw-r--r--src/gui/widgets/window.h441
-rw-r--r--src/gui/widgets/windowcontainer.cpp40
-rw-r--r--src/gui/widgets/windowcontainer.h59
97 files changed, 15164 insertions, 0 deletions
diff --git a/src/gui/widgets/avatarlistbox.cpp b/src/gui/widgets/avatarlistbox.cpp
new file mode 100644
index 000000000..d665c81ce
--- /dev/null
+++ b/src/gui/widgets/avatarlistbox.cpp
@@ -0,0 +1,346 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/avatarlistbox.h"
+
+#include "actorspritemanager.h"
+#include "configuration.h"
+#include "graphics.h"
+#include "guild.h"
+#include "localplayer.h"
+
+#include "gui/chat.h"
+#include "gui/gui.h"
+#include "gui/palette.h"
+#include "gui/viewport.h"
+#include "gui/theme.h"
+
+#include "resources/image.h"
+#include "resources/resourcemanager.h"
+
+#include "utils/stringutils.h"
+
+#include <guichan/font.hpp>
+
+int AvatarListBox::instances = 0;
+Image *AvatarListBox::onlineIcon = 0;
+Image *AvatarListBox::offlineIcon = 0;
+
+AvatarListBox::AvatarListBox(AvatarListModel *model):
+ ListBox(model),
+ mShowGender(false),
+ mShowLevel(false)
+{
+ instances++;
+
+ if (instances == 1)
+ {
+ onlineIcon = Theme::getImageFromTheme("circle-green.png");
+ offlineIcon = Theme::getImageFromTheme("circle-gray.png");
+ }
+
+ setWidth(200);
+
+ mShowGender = config.getBoolValue("showgender");
+ mShowLevel = config.getBoolValue("showlevel");
+
+ config.addListener("showgender", this);
+ config.addListener("showlevel", this);
+}
+
+AvatarListBox::~AvatarListBox()
+{
+ config.removeListener("showgender", this);
+ config.removeListener("showlevel", this);
+
+ instances--;
+
+ if (instances == 0)
+ {
+ if (onlineIcon)
+ onlineIcon->decRef();
+ if (offlineIcon)
+ offlineIcon->decRef();
+ }
+}
+
+void AvatarListBox::draw(gcn::Graphics *gcnGraphics)
+{
+ if (!mListModel || !player_node)
+ return;
+
+ AvatarListModel *model = static_cast<AvatarListModel*>(mListModel);
+// Guild *guild = dynamic_cast<Guild*>(model);
+
+ updateAlpha();
+
+ Graphics *graphics = static_cast<Graphics*>(gcnGraphics);
+
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT,
+ static_cast<int>(mAlpha * 255.0f)));
+ graphics->setFont(getFont());
+
+ const int fontHeight = getFont()->getHeight();
+
+ Widget *parent = getParent();
+
+ const std::string name = player_node->getName();
+
+ // Draw the list elements
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ for (int i = 0, y = 0;
+ i < model->getNumberOfElements();
+ ++i, y += fontHeight)
+ {
+ Avatar *a = model->getAvatarAt(i);
+ if (!a)
+ continue;
+
+ // Draw online status
+ Image *icon = a->getOnline() ? onlineIcon : offlineIcon;
+ if (icon)
+ graphics->drawImage(icon, 2, y + 1);
+
+ if (a->getDisplayBold())
+ graphics->setFont(boldFont);
+
+ std::string text;
+
+ if (a->getMaxHp() > 0)
+ {
+ if (mShowLevel && a->getLevel() > 1)
+ {
+ text = strprintf("%s %d/%d (%d)", a->getComplexName().c_str(),
+ a->getHp(), a->getMaxHp(), a->getLevel());
+ }
+ else
+ {
+ text = strprintf("%s %d/%d", a->getComplexName().c_str(),
+ a->getHp(), a->getMaxHp());
+ }
+ if (parent && a->getMaxHp())
+ {
+ gcn::Color color = Theme::getProgressColor(
+ Theme::PROG_HP, static_cast<float>(a->getHp())
+ / static_cast<float>(a->getMaxHp()));
+ color.a = 80;
+ graphics->setColor(color);
+
+ graphics->fillRectangle(gcn::Rectangle(0, y,
+ parent->getWidth() * a->getHp() / a->getMaxHp(),
+ fontHeight));
+ }
+ }
+ else if (a->getDamageHp() != 0 && a->getName() != name)
+ {
+ if (mShowLevel && a->getLevel() > 1)
+ {
+ text = strprintf("%s -%d (%d)", a->getComplexName().c_str(),
+ a->getDamageHp(), a->getLevel());
+ }
+ else
+ {
+ text = strprintf("%s -%d", a->getComplexName().c_str(),
+ a->getDamageHp());
+ }
+
+ if (parent)
+ {
+// int diff;
+// if (a->getDamageHp() > 1024)
+// diff = 0;
+// else
+// diff = 1024 - a->getDamageHp();
+ gcn::Color color = Theme::getProgressColor(Theme::PROG_HP,
+ 1);
+// 0 / 1024);
+/*
+ if (a->getDamageHp() >= 400)
+ {
+ }
+ else
+ {
+// int intens = 1024/(400 - a->getDamageHp());
+ int intens = a->getDamageHp() / 1024;
+ if (intens > 1)
+ intens = 1;
+ color = Theme::getProgressColor(Theme::PROG_HP,
+ intens);
+ }
+*/
+
+ color.a = 80;
+ graphics->setColor(color);
+ graphics->fillRectangle(gcn::Rectangle(0, y,
+ parent->getWidth() * a->getDamageHp() / 1024,
+ fontHeight));
+
+ if (a->getLevel() > 1)
+ {
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ int minHp = 40 + ((a->getLevel() - 1) * 5);
+ if (minHp < 0)
+ minHp = 40;
+
+ graphics->drawLine(parent->getWidth()*minHp / 1024, y,
+ parent->getWidth() * minHp / 1024, y + fontHeight);
+ }
+ }
+ }
+ else
+ {
+ if (mShowLevel && a->getLevel() > 1)
+ {
+ text = strprintf("%s (%d)", a->getComplexName().c_str(),
+ a->getLevel());
+ }
+ else
+ {
+ text = a->getComplexName();
+ }
+ }
+
+ if (!a->getMap().empty())
+ {
+ if (a->getX() != -1)
+ {
+ text += strprintf(" [%d,%d %s]", a->getX(), a->getY(),
+ a->getMap().c_str());
+ }
+ else
+ {
+ text += strprintf(" [%s]", a->getMap().c_str());
+ }
+ }
+
+ if (mShowGender)
+ {
+ switch (a->getGender())
+ {
+ case GENDER_FEMALE:
+ text += strprintf(" \u2640 %s",
+ a->getAdditionString().c_str());
+ break;
+ case GENDER_MALE:
+ text += strprintf(" \u2642 %s",
+ a->getAdditionString().c_str());
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ text += a->getAdditionString();
+ }
+
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+
+ // Draw Name
+ graphics->drawText(text, 15, y);
+
+ if (a->getDisplayBold())
+ graphics->setFont(getFont());
+ }
+
+ setWidth(parent->getWidth() - 10);
+}
+
+void AvatarListBox::mousePressed(gcn::MouseEvent &event)
+{
+ if (!actorSpriteManager || !player_node || !viewport
+ || !getFont()->getHeight())
+ {
+ return;
+ }
+
+ int y = event.getY() / getFont()->getHeight();
+ if (!mListModel || y > mListModel->getNumberOfElements())
+ return;
+
+ setSelected(y);
+ distributeActionEvent();
+ int selected = getSelected();
+ AvatarListModel *model = static_cast<AvatarListModel*>(mListModel);
+ if (!model)
+ return;
+ Avatar *ava = model->getAvatarAt(selected);
+ if (!ava)
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ if (ava->getType() == AVATAR_PLAYER)
+ {
+ Being* being = actorSpriteManager->findBeingByName(ava->getName(),
+ Being::PLAYER);
+ if (being)
+ actorSpriteManager->heal(player_node, being);
+ }
+ else
+ {
+ player_node->navigateTo(ava->getX(), ava->getY());
+ }
+ }
+ else if (event.getButton() == gcn::MouseEvent::RIGHT)
+ {
+ if (ava->getType() == AVATAR_PLAYER)
+ {
+ Being* being = actorSpriteManager->findBeingByName(
+ model->getAvatarAt(selected)->getName(), Being::PLAYER);
+ if (being)
+ {
+ viewport->showPopup(event.getX(), event.getY(), being);
+ }
+ else
+ {
+ viewport->showPlayerPopup(
+ model->getAvatarAt(selected)->getName());
+ }
+ }
+ else
+ {
+ Map *map = viewport->getMap();
+ Avatar *ava = model->getAvatarAt(selected);
+ if (map && ava)
+ {
+ MapItem *mapItem = map->findPortalXY(ava->getX(), ava->getY());
+ viewport->showPopup(mapItem);
+ }
+ }
+ }
+
+ else if (event.getButton() == gcn::MouseEvent::MIDDLE)
+ {
+ if (ava->getType() == AVATAR_PLAYER && chatWindow)
+ {
+ chatWindow->addWhisperTab(model->getAvatarAt(selected)
+ ->getName(), true);
+ }
+ }
+}
+
+void AvatarListBox::optionChanged(const std::string &value)
+{
+ if (value == "showgender")
+ mShowGender = config.getBoolValue("showgender");
+ else if (value == "showlevel")
+ mShowLevel = config.getBoolValue("showlevel");
+} \ No newline at end of file
diff --git a/src/gui/widgets/avatarlistbox.h b/src/gui/widgets/avatarlistbox.h
new file mode 100644
index 000000000..c7bc11f7c
--- /dev/null
+++ b/src/gui/widgets/avatarlistbox.h
@@ -0,0 +1,70 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2010 The Mana Developers
+ *
+ * This file is part of The Mana 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_GUILDLISTBOX_H
+#define GUI_GUILDLISTBOX_H
+
+#include "avatar.h"
+
+#include "configlistener.h"
+
+#include "gui/widgets/listbox.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+class Image;
+
+class AvatarListModel : public gcn::ListModel
+{
+public:
+ virtual Avatar *getAvatarAt(int i) = 0;
+
+ std::string getElementAt(int i)
+ { return getAvatarAt(i)->getName(); }
+};
+
+class AvatarListBox : public ListBox, public ConfigListener
+{
+public:
+ AvatarListBox(AvatarListModel *model);
+
+ ~AvatarListBox();
+
+ /**
+ * Draws the list box.
+ */
+ void draw(gcn::Graphics *gcnGraphics);
+
+ void mousePressed(gcn::MouseEvent &event);
+
+ void optionChanged(const std::string &value);
+
+private:
+ bool mShowGender;
+ bool mShowLevel;
+
+ static int instances;
+ static Image *onlineIcon;
+ static Image *offlineIcon;
+};
+
+#endif
diff --git a/src/gui/widgets/battletab.cpp b/src/gui/widgets/battletab.cpp
new file mode 100644
index 000000000..68f1a0453
--- /dev/null
+++ b/src/gui/widgets/battletab.cpp
@@ -0,0 +1,54 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/battletab.h"
+
+#include "chatlog.h"
+#include "commandhandler.h"
+#include "localplayer.h"
+#include "log.h"
+
+#include "gui/theme.h"
+
+#include "net/net.h"
+
+#include "resources/iteminfo.h"
+#include "resources/itemdb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+BattleTab::BattleTab() :
+ ChatTab(_("Battle"))
+{
+ loadFromLogFile("#Battle");
+}
+
+BattleTab::~BattleTab()
+{
+}
+
+void BattleTab::saveToLogFile(std::string &msg)
+{
+ if (chatLogger)
+ chatLogger->log(std::string("#Battle"), std::string(msg));
+}
diff --git a/src/gui/widgets/battletab.h b/src/gui/widgets/battletab.h
new file mode 100644
index 000000000..fdfe626f0
--- /dev/null
+++ b/src/gui/widgets/battletab.h
@@ -0,0 +1,47 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 BATTLETAB_H
+#define BATTLETAB_H
+
+#include "gui/widgets/chattab.h"
+
+/**
+ * A tab for a party chat channel.
+ */
+class BattleTab : public ChatTab
+{
+ public:
+ BattleTab();
+
+ ~BattleTab();
+
+ int getType() const
+ { return ChatTab::TAB_BATTLE; }
+
+ void saveToLogFile(std::string &msg);
+};
+
+extern BattleTab *battleChatTab;
+#endif
+
+
+
diff --git a/src/gui/widgets/browserbox.cpp b/src/gui/widgets/browserbox.cpp
new file mode 100644
index 000000000..acb182c3c
--- /dev/null
+++ b/src/gui/widgets/browserbox.cpp
@@ -0,0 +1,534 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2009 Aethyra Development Team
+ *
+ * This file is part of The Mana 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/widgets/browserbox.h"
+
+#include "client.h"
+#include "log.h"
+
+#include "utils/stringutils.h"
+
+#include "gui/palette.h"
+#include "gui/theme.h"
+
+#include "gui/widgets/linkhandler.h"
+
+#include <guichan/graphics.hpp>
+#include <guichan/font.hpp>
+#include <guichan/cliprectangle.hpp>
+
+#include <algorithm>
+
+BrowserBox::BrowserBox(unsigned int mode, bool opaque):
+ gcn::Widget(),
+ mMode(mode), mHighMode(UNDERLINE | BACKGROUND),
+ mOpaque(opaque),
+ mUseLinksAndUserColors(true),
+ mSelectedLink(-1),
+ mMaxRows(0),
+ mHeight(0),
+ mWidth(0),
+ mYStart(0),
+ mUpdateTime(-1),
+ mAlwaysUpdate(true)
+{
+ setFocusable(true);
+ addMouseListener(this);
+}
+
+BrowserBox::~BrowserBox()
+{
+}
+
+void BrowserBox::setLinkHandler(LinkHandler* linkHandler)
+{
+ mLinkHandler = linkHandler;
+}
+
+void BrowserBox::setOpaque(bool opaque)
+{
+ mOpaque = opaque;
+}
+
+void BrowserBox::setHighlightMode(unsigned int highMode)
+{
+ mHighMode = highMode;
+}
+
+void BrowserBox::addRow(const std::string &row, bool atTop)
+{
+ std::string tmp = row;
+ std::string newRow;
+ std::string::size_type idx1, idx2, idx3;
+ gcn::Font *font = getFont();
+
+ // Use links and user defined colors
+ if (mUseLinksAndUserColors)
+ {
+ BROWSER_LINK bLink;
+
+ // Check for links in format "@@link|Caption@@"
+ idx1 = tmp.find("@@");
+ while (idx1 != std::string::npos)
+ {
+ idx2 = tmp.find("|", idx1);
+ idx3 = tmp.find("@@", idx2);
+
+ if (idx2 == std::string::npos || idx3 == std::string::npos)
+ break;
+ bLink.link = tmp.substr(idx1 + 2, idx2 - (idx1 + 2));
+ bLink.caption = tmp.substr(idx2 + 1, idx3 - (idx2 + 1));
+ bLink.y1 = static_cast<int>(mTextRows.size()) * font->getHeight();
+ bLink.y2 = bLink.y1 + font->getHeight();
+
+ newRow += tmp.substr(0, idx1);
+
+ std::string tmp2 = newRow;
+ idx1 = tmp2.find("##");
+ while (idx1 != std::string::npos)
+ {
+ tmp2.erase(idx1, 3);
+ idx1 = tmp2.find("##");
+ }
+ bLink.x1 = font->getWidth(tmp2) - 1;
+ bLink.x2 = bLink.x1 + font->getWidth(bLink.caption) + 1;
+
+ mLinks.push_back(bLink);
+
+ newRow += "##<" + bLink.caption;
+
+ tmp.erase(0, idx3 + 2);
+ if (!tmp.empty())
+ newRow += "##>";
+
+ idx1 = tmp.find("@@");
+ }
+
+ newRow += tmp;
+ }
+ // Don't use links and user defined colors
+ else
+ {
+ newRow = row;
+ }
+
+ if (atTop)
+ mTextRows.push_front(newRow);
+ else
+ mTextRows.push_back(newRow);
+
+ //discard older rows when a row limit has been set
+ if (mMaxRows > 0)
+ {
+ while (mTextRows.size() > mMaxRows)
+ {
+ mTextRows.pop_front();
+ for (unsigned int i = 0; i < mLinks.size(); i++)
+ {
+ mLinks[i].y1 -= font->getHeight();
+ mLinks[i].y2 -= font->getHeight();
+
+ if (mLinks[i].y1 < 0)
+ mLinks.erase(mLinks.begin() + i);
+ }
+ }
+ }
+
+ // Auto size mode
+ if (mMode == AUTO_SIZE)
+ {
+ std::string plain = newRow;
+ for (idx1 = plain.find("##");
+ idx1 != std::string::npos;
+ idx1 = plain.find("##"))
+ {
+ plain.erase(idx1, 3);
+ }
+
+ // Adjust the BrowserBox size
+ int w = font->getWidth(plain);
+ if (w > getWidth())
+ setWidth(w);
+ }
+
+ if (mMode == AUTO_WRAP)
+ {
+ unsigned int y = 0;
+ unsigned int nextChar;
+ const char *hyphen = "~";
+ int hyphenWidth = font->getWidth(hyphen);
+ int x = 0;
+
+ for (TextRowIterator i = mTextRows.begin(); i != mTextRows.end(); i++)
+ {
+ std::string row = *i;
+ for (unsigned int j = 0; j < row.size(); j++)
+ {
+ std::string character = row.substr(j, 1);
+ x += font->getWidth(character);
+ nextChar = j + 1;
+
+ // Wraping between words (at blank spaces)
+ if ((nextChar < row.size()) && (row.at(nextChar) == ' '))
+ {
+ int nextSpacePos = static_cast<int>(
+ row.find(" ", (nextChar + 1)));
+ if (nextSpacePos <= 0)
+ nextSpacePos = static_cast<int>(row.size()) - 1;
+
+ int nextWordWidth = font->getWidth(
+ row.substr(nextChar,
+ (nextSpacePos - nextChar)));
+
+ if ((x + nextWordWidth + 10) > getWidth())
+ {
+ x = 15; // Ident in new line
+ y += 1;
+ j++;
+ }
+ }
+ // Wrapping looong lines (brutal force)
+ else if ((x + 2 * hyphenWidth) > getWidth())
+ {
+ x = 15; // Ident in new line
+ y += 1;
+ }
+ }
+ }
+
+ setHeight(font->getHeight() * (static_cast<int>(
+ mTextRows.size()) + y));
+ }
+ else
+ {
+ setHeight(font->getHeight() * static_cast<int>(mTextRows.size()));
+ }
+ mUpdateTime = 0;
+ updateHeight();
+}
+
+void BrowserBox::clearRows()
+{
+ mTextRows.clear();
+ mLinks.clear();
+ setWidth(0);
+ setHeight(0);
+ mSelectedLink = -1;
+ mUpdateTime = 0;
+ updateHeight();
+}
+
+struct MouseOverLink
+{
+ MouseOverLink(int x, int y) : mX(x), mY(y)
+ { }
+
+ bool operator() (BROWSER_LINK &link)
+ {
+ return (mX >= link.x1 && mX < link.x2 &&
+ mY >= link.y1 && mY < link.y2);
+ }
+ int mX, mY;
+};
+
+void BrowserBox::mousePressed(gcn::MouseEvent &event)
+{
+ if (!mLinkHandler)
+ return;
+
+ LinkIterator i = find_if(mLinks.begin(), mLinks.end(),
+ MouseOverLink(event.getX(), event.getY()));
+
+ if (i != mLinks.end())
+ mLinkHandler->handleLink(i->link, &event);
+}
+
+void BrowserBox::mouseMoved(gcn::MouseEvent &event)
+{
+ LinkIterator i = find_if(mLinks.begin(), mLinks.end(),
+ MouseOverLink(event.getX(), event.getY()));
+
+ mSelectedLink = (i != mLinks.end())
+ ? static_cast<int>(i - mLinks.begin()) : -1;
+}
+
+void BrowserBox::draw(gcn::Graphics *graphics)
+{
+ gcn::ClipRectangle cr = graphics->getCurrentClipArea();
+ mYStart = cr.y - cr.yOffset;
+ int yEnd = mYStart + cr.height;
+ if (mYStart < 0)
+ mYStart = 0;
+
+ if (getWidth() != mWidth)
+ updateHeight();
+
+ if (mOpaque)
+ {
+ graphics->setColor(Theme::getThemeColor(Theme::BACKGROUND));
+ graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
+ }
+
+ if (mSelectedLink >= 0 && mSelectedLink < (signed)mLinks.size())
+ {
+ if ((mHighMode & BACKGROUND))
+ {
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT));
+ graphics->fillRectangle(gcn::Rectangle(
+ mLinks[mSelectedLink].x1,
+ mLinks[mSelectedLink].y1,
+ mLinks[mSelectedLink].x2 - mLinks[mSelectedLink].x1,
+ mLinks[mSelectedLink].y2 - mLinks[mSelectedLink].y1
+ ));
+ }
+
+ if ((mHighMode & UNDERLINE))
+ {
+ graphics->setColor(Theme::getThemeColor(Theme::HYPERLINK));
+ graphics->drawLine(
+ mLinks[mSelectedLink].x1,
+ mLinks[mSelectedLink].y2,
+ mLinks[mSelectedLink].x2,
+ mLinks[mSelectedLink].y2);
+ }
+ }
+
+ gcn::Font *font = getFont();
+
+ for (LinePartIterator i = mLineParts.begin();
+ i != mLineParts.end();
+ i ++)
+ {
+ const LinePart &part = *i;
+ if (part.mY + 50 < mYStart)
+ continue;
+ if (part.mY > yEnd)
+ break;
+ graphics->setColor(part.mColor);
+ font->drawString(graphics, part.mText, part.mX, part.mY);
+ }
+
+ return;
+}
+
+int BrowserBox::calcHeight()
+{
+ int x = 0, y = 0;
+ int wrappedLines = 0;
+ int link = 0;
+ gcn::Font *font = getFont();
+
+ int fontHeight = font->getHeight();
+ int fontWidthMinus = font->getWidth("-");
+ char const *hyphen = "~";
+ int hyphenWidth = font->getWidth(hyphen);
+
+ gcn::Color selColor = Theme::getThemeColor(Theme::TEXT);
+ const gcn::Color textColor = Theme::getThemeColor(Theme::TEXT);
+
+ mLineParts.clear();
+
+ for (TextRowIterator i = mTextRows.begin(); i != mTextRows.end(); i++)
+ {
+ const std::string row = *(i);
+ bool wrapped = false;
+ x = 0;
+
+ // Check for separator lines
+ if (row.find("---", 0) == 0)
+ {
+ const int dashWidth = fontWidthMinus;
+ for (x = 0; x < getWidth(); x++)
+ {
+ mLineParts.push_back(LinePart(x, y, selColor, "-"));
+ x += dashWidth - 2;
+ }
+
+ y += fontHeight;
+ continue;
+ }
+
+ gcn::Color prevColor = selColor;
+
+ // TODO: Check if we must take texture size limits into account here
+ // TODO: Check if some of the O(n) calls can be removed
+ for (std::string::size_type start = 0, end = std::string::npos;
+ start != std::string::npos;
+ start = end, end = std::string::npos)
+ {
+ // Wrapped line continuation shall be indented
+ if (wrapped)
+ {
+ y += fontHeight;
+ x = 15;
+ wrapped = false;
+ }
+
+ // "Tokenize" the string at control sequences
+ if (mUseLinksAndUserColors)
+ end = row.find("##", start + 1);
+
+ if (mUseLinksAndUserColors ||
+ (!mUseLinksAndUserColors && (start == 0)))
+ {
+ // Check for color change in format "##x", x = [L,P,0..9]
+ if (row.find("##", start) == start && row.size() > start + 2)
+ {
+ const char c = row.at(start + 2);
+
+ bool valid;
+ const gcn::Color col = Theme::getThemeColor(c, valid);
+
+ if (c == '>')
+ {
+ selColor = prevColor;
+ }
+ else if (c == '<')
+ {
+// link++;
+ prevColor = selColor;
+ selColor = col;
+ }
+ else if (valid)
+ {
+ selColor = col;
+ }
+ else
+ {
+
+ switch (c)
+ {
+ case '1': selColor = RED; break;
+ case '2': selColor = GREEN; break;
+ case '3': selColor = BLUE; break;
+ case '4': selColor = ORANGE; break;
+ case '5': selColor = YELLOW; break;
+ case '6': selColor = PINK; break;
+ case '7': selColor = PURPLE; break;
+ case '8': selColor = GRAY; break;
+ case '9': selColor = BROWN; break;
+ case '0':
+ default:
+ selColor = textColor;
+ }
+ }
+
+ if (c == '<' && link < (signed)mLinks.size())
+ {
+ const int size =
+ font->getWidth(mLinks[link].caption) + 1;
+
+ mLinks[link].x1 = x;
+ mLinks[link].y1 = y;
+ mLinks[link].x2 = mLinks[link].x1 + size;
+ mLinks[link].y2 = y + fontHeight - 1;
+ link++;
+ }
+ start += 3;
+
+ if (start == row.size())
+ break;
+ }
+ }
+
+ std::string::size_type len =
+ end == std::string::npos ? end : end - start;
+
+ if (start >= row.length())
+ break;
+
+ std::string part = row.substr(start, len);
+
+ // Auto wrap mode
+ if (mMode == AUTO_WRAP && getWidth() > 0
+ && font->getWidth(part) > 0
+ && (x + font->getWidth(part) + 10) > getWidth())
+ {
+ bool forced = false;
+
+ /* FIXME: This code layout makes it easy to crash remote
+ clients by talking garbage. Forged long utf-8 characters
+ will cause either a buffer underflow in substr or an
+ infinite loop in the main loop. */
+ do
+ {
+ if (!forced)
+ end = row.rfind(' ', end);
+
+ // Check if we have to (stupidly) force-wrap
+ if (end == std::string::npos || end <= start)
+ {
+ forced = true;
+ end = row.size();
+ x += hyphenWidth; // Account for the wrap-notifier
+ continue;
+ }
+
+ // Skip to the start of the current character
+ while ((row[end] & 192) == 128)
+ end--;
+ end--; // And then to the last byte of the previous one
+
+ part = row.substr(start, end - start + 1);
+ }
+ while (end > start && font->getWidth(part) > 0
+ && (x + font->getWidth(part) + 10) > getWidth());
+
+ if (forced)
+ {
+ x -= hyphenWidth; // Remove the wrap-notifier accounting
+ mLineParts.push_back(LinePart(getWidth() - hyphenWidth,
+ y, selColor, hyphen));
+ end++; // Skip to the next character
+ }
+ else
+ {
+ end += 2; // Skip to after the space
+ }
+
+ wrapped = true;
+ wrappedLines++;
+ }
+
+ mLineParts.push_back(LinePart(x, y, selColor, part.c_str()));
+
+ if (mMode == AUTO_WRAP && font->getWidth(part) == 0)
+ break;
+
+ x += font->getWidth(part);
+ }
+ y += fontHeight;
+ }
+ return (static_cast<int>(mTextRows.size()) + wrappedLines) * fontHeight;
+}
+
+void BrowserBox::updateHeight()
+{
+ if (mAlwaysUpdate || mUpdateTime != cur_time
+ || mTextRows.size() < 3 || !mUpdateTime)
+ {
+ mWidth = getWidth();
+ mHeight = calcHeight();
+ setHeight(mHeight);
+ mUpdateTime = cur_time;
+ }
+}
diff --git a/src/gui/widgets/browserbox.h b/src/gui/widgets/browserbox.h
new file mode 100644
index 000000000..cd9cc92de
--- /dev/null
+++ b/src/gui/widgets/browserbox.h
@@ -0,0 +1,205 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2009 Aethyra Development Team
+ *
+ * This file is part of The Mana 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 BROWSERBOX_H
+#define BROWSERBOX_H
+
+#include <guichan/mouselistener.hpp>
+#include <guichan/widget.hpp>
+
+#include <list>
+#include <vector>
+
+class LinkHandler;
+
+struct BROWSER_LINK
+{
+ int x1, x2, y1, y2; /**< Where link is placed */
+ std::string link;
+ std::string caption;
+};
+
+class LinePart
+{
+ public:
+ LinePart(int x, int y, gcn::Color color, std::string text) :
+ mX(x), mY(y), mColor(color), mText(text)
+ {
+ }
+
+ int mX, mY;
+ gcn::Color mColor;
+ std::string mText;
+};
+
+/**
+ * A simple browser box able to handle links and forward events to the
+ * parent conteiner.
+ */
+class BrowserBox : public gcn::Widget,
+ public gcn::MouseListener
+{
+ public:
+ /**
+ * Constructor.
+ */
+ BrowserBox(unsigned int mode = AUTO_SIZE, bool opaque = true);
+
+ /**
+ * Destructor.
+ */
+ ~BrowserBox();
+
+ /**
+ * Sets the handler for links.
+ */
+ void setLinkHandler(LinkHandler *linkHandler);
+
+ /**
+ * Sets the BrowserBox opacity.
+ */
+ void setOpaque(bool opaque);
+
+ /**
+ * Sets the Highlight mode for links.
+ */
+ void setHighlightMode(unsigned int highMode);
+
+ /**
+ * Sets the maximum numbers of rows in the browser box. 0 = no limit.
+ */
+ void setMaxRow(unsigned max) {mMaxRows = max; };
+
+ /**
+ * Disable links & user defined colors to be used in chat input.
+ */
+/*
+ void disableLinksAndUserColors();
+*/
+ /**
+ * Adds a text row to the browser.
+ */
+ void addRow(const std::string &row, bool atTop = false);
+
+ /**
+ * Remove all rows.
+ */
+ void clearRows();
+
+// void setSize(int width, int height);
+
+// void widgetResized(const gcn::Event &event);
+
+ /**
+ * Handles mouse actions.
+ */
+ void mousePressed(gcn::MouseEvent &event);
+ void mouseMoved(gcn::MouseEvent &event);
+
+ /**
+ * Draws the browser box.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ void updateHeight();
+
+// void widgetResized(const gcn::Event &event);
+
+ /**
+ * BrowserBox modes.
+ */
+ enum
+ {
+ AUTO_SIZE = 0,
+ AUTO_WRAP /**< Maybe it needs a fix or to be redone. */
+ };
+
+ /**
+ * BrowserBox colors.
+ *
+ * NOTES (by Javila):
+ * - color values is "0x" prefix followed by HTML color style.
+ * - we can add up to 10 different colors: [0..9].
+ * - not all colors will be fine with all backgrounds due transparent
+ * windows and widgets. So, I think it's better keep BrowserBox
+ * opaque (white background) by default.
+ */
+ enum
+ {
+ RED = 0xff0000, /**< Color 1 */
+ GREEN = 0x009000, /**< Color 2 */
+ BLUE = 0x0000ff, /**< Color 3 */
+ ORANGE = 0xe0980e, /**< Color 4 */
+ YELLOW = 0xf1dc27, /**< Color 5 */
+ PINK = 0xff00d8, /**< Color 6 */
+ PURPLE = 0x8415e2, /**< Color 7 */
+ GRAY = 0x919191, /**< Color 8 */
+ BROWN = 0x8e4c17 /**< Color 9 */
+ };
+
+ /**
+ * Highlight modes for links.
+ * This can be used for a bitmask.
+ */
+ enum
+ {
+ UNDERLINE = 1,
+ BACKGROUND = 2
+ };
+
+ typedef std::list<std::string> TextRows;
+
+ TextRows &getRows()
+ { return mTextRows; }
+
+ void setAlwaysUpdate(bool n)
+ { mAlwaysUpdate = n; }
+
+ private:
+ int calcHeight();
+
+ typedef TextRows::iterator TextRowIterator;
+ TextRows mTextRows;
+
+ typedef std::list<LinePart> LinePartList;
+ typedef LinePartList::iterator LinePartIterator;
+ LinePartList mLineParts;
+
+ typedef std::vector<BROWSER_LINK> Links;
+ typedef Links::iterator LinkIterator;
+ Links mLinks;
+
+ LinkHandler *mLinkHandler;
+ unsigned int mMode;
+ unsigned int mHighMode;
+ bool mOpaque;
+ bool mUseLinksAndUserColors;
+ int mSelectedLink;
+ unsigned int mMaxRows;
+ int mHeight;
+ int mWidth;
+ int mYStart;
+ int mUpdateTime;
+ bool mAlwaysUpdate;
+};
+
+#endif
diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp
new file mode 100644
index 000000000..3445928a1
--- /dev/null
+++ b/src/gui/widgets/button.cpp
@@ -0,0 +1,227 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/button.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+#include "log.h"
+
+#include "gui/palette.h"
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+#include "utils/dtor.h"
+
+#include <guichan/exception.hpp>
+#include <guichan/font.hpp>
+
+int Button::mInstances = 0;
+float Button::mAlpha = 1.0;
+
+enum
+{
+ BUTTON_STANDARD = 0, // 0
+ BUTTON_HIGHLIGHTED, // 1
+ BUTTON_PRESSED, // 2
+ BUTTON_DISABLED, // 3
+ BUTTON_COUNT // 4 - Must be last.
+};
+
+struct ButtonData
+{
+ char const *file;
+ int gridX;
+ int gridY;
+};
+
+static ButtonData const data[BUTTON_COUNT] =
+{
+ { "button.png", 0, 0 },
+ { "buttonhi.png", 9, 4 },
+ { "buttonpress.png", 16, 19 },
+ { "button_disabled.png", 25, 23 }
+};
+
+ImageRect Button::button[BUTTON_COUNT];
+
+Button::Button():
+ mDescription(""), mClickCount(0)
+{
+ init();
+}
+
+Button::Button(const std::string &caption, const std::string &actionEventId,
+ gcn::ActionListener *listener):
+ gcn::Button(caption),
+ mDescription(""), mClickCount(0)
+{
+ init();
+ setActionEventId(actionEventId);
+
+ if (listener)
+ addActionListener(listener);
+}
+
+void Button::init()
+{
+ setFrameSize(0);
+
+ if (mInstances == 0)
+ {
+ // Load the skin
+ Image *btn[BUTTON_COUNT];
+
+ int a, x, y, mode;
+
+ for (mode = 0; mode < BUTTON_COUNT; mode++)
+ {
+ btn[mode] = Theme::getImageFromTheme(data[mode].file);
+ if (!btn[mode])
+ continue;
+
+ a = 0;
+ for (y = 0; y < 3; y++)
+ {
+ for (x = 0; x < 3; x++)
+ {
+ button[mode].grid[a] = btn[mode]->getSubImage(
+ data[x].gridX, data[y].gridY,
+ data[x + 1].gridX - data[x].gridX + 1,
+ data[y + 1].gridY - data[y].gridY + 1);
+ a++;
+ }
+ }
+ if (btn[mode])
+ btn[mode]->decRef();
+ }
+ updateAlpha();
+ }
+ mInstances++;
+}
+
+Button::~Button()
+{
+ mInstances--;
+
+ if (mInstances == 0)
+ {
+ for (int mode = 0; mode < BUTTON_COUNT; mode++)
+ {
+ if (button[mode].grid)
+ {
+ for_each(button[mode].grid,
+ button[mode].grid + 9, dtor<Image*>());
+ }
+ }
+ }
+}
+
+void Button::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ if (mAlpha != alpha)
+ {
+ mAlpha = alpha;
+ for (int a = 0; a < 9; a++)
+ {
+ if (button[BUTTON_DISABLED].grid[a])
+ button[BUTTON_DISABLED].grid[a]->setAlpha(mAlpha);
+ if (button[BUTTON_PRESSED].grid[a])
+ button[BUTTON_PRESSED].grid[a]->setAlpha(mAlpha);
+ if (button[BUTTON_HIGHLIGHTED].grid[a])
+ button[BUTTON_HIGHLIGHTED].grid[a]->setAlpha(mAlpha);
+ if (button[BUTTON_STANDARD].grid[a])
+ button[BUTTON_STANDARD].grid[a]->setAlpha(mAlpha);
+ }
+ }
+}
+
+void Button::draw(gcn::Graphics *graphics)
+{
+ int mode;
+
+ if (!isEnabled())
+ mode = BUTTON_DISABLED;
+ else if (isPressed())
+ mode = BUTTON_PRESSED;
+ else if (mHasMouse || isFocused())
+ mode = BUTTON_HIGHLIGHTED;
+ else
+ mode = BUTTON_STANDARD;
+
+ updateAlpha();
+
+ static_cast<Graphics*>(graphics)->
+ drawImageRect(0, 0, getWidth(), getHeight(), button[mode]);
+
+ if (mode == BUTTON_DISABLED)
+ graphics->setColor(Theme::getThemeColor(Theme::BUTTON_DISABLED));
+ else
+ graphics->setColor(Theme::getThemeColor(Theme::BUTTON));
+
+ int textX;
+ int textY = getHeight() / 2 - getFont()->getHeight() / 2;
+
+ switch (getAlignment())
+ {
+ default:
+ case gcn::Graphics::LEFT:
+ textX = 4;
+ break;
+ case gcn::Graphics::CENTER:
+ textX = getWidth() / 2;
+ break;
+ case gcn::Graphics::RIGHT:
+ textX = getWidth() - 4;
+ break;
+// throw GCN_EXCEPTION("Button::draw. Unknown alignment.");
+ }
+
+ graphics->setFont(getFont());
+
+ if (isPressed())
+ graphics->drawText(getCaption(), textX + 1, textY + 1, getAlignment());
+ else
+ graphics->drawText(getCaption(), textX, textY, getAlignment());
+}
+
+void Button::mouseReleased(gcn::MouseEvent& mouseEvent)
+{
+ if (mouseEvent.getButton() == gcn::MouseEvent::LEFT
+ && mMousePressed && mHasMouse)
+ {
+ mMousePressed = false;
+ mClickCount = mouseEvent.getClickCount();
+ distributeActionEvent();
+ mouseEvent.consume();
+ }
+ else if (mouseEvent.getButton() == gcn::MouseEvent::LEFT)
+ {
+ mMousePressed = false;
+ mClickCount = 0;
+ mouseEvent.consume();
+ }
+}
diff --git a/src/gui/widgets/button.h b/src/gui/widgets/button.h
new file mode 100644
index 000000000..301d02fbe
--- /dev/null
+++ b/src/gui/widgets/button.h
@@ -0,0 +1,94 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 BUTTON_H
+#define BUTTON_H
+
+#include <guichan/widgets/button.hpp>
+#include <guichan/mouseevent.hpp>
+
+class ImageRect;
+
+/**
+ * Button widget. Same as the Guichan button but with custom look.
+ *
+ * \ingroup GUI
+ */
+class Button : public gcn::Button
+{
+ public:
+ /**
+ * Default constructor.
+ */
+ Button();
+
+ /**
+ * Constructor, sets the caption of the button to the given string and
+ * adds the given action listener.
+ */
+ Button(const std::string &caption, const std::string &actionEventId,
+ gcn::ActionListener *listener);
+
+ /**
+ * Destructor.
+ */
+ ~Button();
+
+ /**
+ * Draws the button.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Update the alpha value to the button components.
+ */
+ void updateAlpha();
+
+ virtual void mouseReleased(gcn::MouseEvent& mouseEvent);
+
+ void setDescription(std::string text)
+ { mDescription = text; }
+
+ std::string getDescription()
+ { return mDescription; }
+
+ unsigned getClickCount()
+ { return mClickCount; }
+
+ void setTag(int tag)
+ { mTag = tag; }
+
+ int getTag()
+ { return mTag; }
+
+ private:
+ void init();
+
+ static ImageRect button[4]; /**< Button state graphics */
+ static int mInstances; /**< Number of button instances */
+ static float mAlpha;
+
+ std::string mDescription;
+ unsigned mClickCount;
+ int mTag;
+};
+
+#endif
diff --git a/src/gui/widgets/channeltab.cpp b/src/gui/widgets/channeltab.cpp
new file mode 100644
index 000000000..a7370a4c5
--- /dev/null
+++ b/src/gui/widgets/channeltab.cpp
@@ -0,0 +1,132 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 "channeltab.h"
+
+#include "channel.h"
+
+#include "net/chathandler.h"
+#include "net/net.h"
+
+#include "utils/gettext.h"
+
+ChannelTab::ChannelTab(Channel *channel) :
+ ChatTab(channel->getName()),
+ mChannel(channel)
+{
+ channel->setTab(this);
+}
+
+ChannelTab::~ChannelTab()
+{
+}
+
+void ChannelTab::handleInput(const std::string &msg)
+{
+ Net::getChatHandler()->sendToChannel(getChannel()->getId(), msg);
+}
+
+void ChannelTab::showHelp()
+{
+ chatLog(_("/users > Lists the users in the current channel"));
+ chatLog(_("/topic > Set the topic of the current channel"));
+ chatLog(_("/quit > Leave a channel"));
+ chatLog(_("/op > Make a user a channel operator"));
+ chatLog(_("/kick > Kick a user from the channel"));
+}
+
+bool ChannelTab::handleCommand(const std::string &type,
+ const std::string &args)
+{
+ if (type == "help")
+ {
+ if (args == "users")
+ {
+ chatLog(_("Command: /users"));
+ chatLog(_("This command shows the users in this channel."));
+ }
+ else if (args == "topic")
+ {
+ chatLog(_("Command: /topic <message>"));
+ chatLog(_("This command sets the topic to <message>."));
+ }
+ else if (args == "quit")
+ {
+ chatLog(_("Command: /quit"));
+ chatLog(_("This command leaves the current channel."));
+ chatLog(_("If you're the last person in the channel, "
+ "it will be deleted."));
+ }
+ else if (args == "op")
+ {
+ chatLog(_("Command: /op <nick>"));
+ chatLog(_("This command makes <nick> a channel operator."));
+ chatLog(_("If the <nick> has spaces in it, enclose it in "
+ "double quotes (\")."));
+ chatLog(_("Channel operators can kick and op other users "
+ "from the channel."));
+ }
+ else if (args == "kick")
+ {
+ chatLog(_("Command: /kick <nick>"));
+ chatLog(_("This command makes <nick> leave the channel."));
+ chatLog(_("If the <nick> has spaces in it, enclose it in "
+ "double quotes (\")."));
+ }
+ else
+ return false;
+ }
+ else if (type == "users")
+ {
+ Net::getChatHandler()->userList(mChannel->getName());
+ }
+ else if (type == "topic")
+ {
+ Net::getChatHandler()->setChannelTopic(mChannel->getId(), args);
+ }
+ else if (type == "topic")
+ {
+ Net::getChatHandler()->setChannelTopic(mChannel->getId(), args);
+ }
+ else if (type == "quit")
+ {
+ Net::getChatHandler()->quitChannel(mChannel->getId());
+ }
+ else if (type == "op")
+ {
+ // set the user mode 'o' to op a user
+ if (args != "")
+ Net::getChatHandler()->setUserMode(mChannel->getId(), args, 'o');
+ else
+ chatLog(_("Need a user to op!"), BY_CHANNEL);
+ }
+ else if (type == "kick")
+ {
+ if (args != "")
+ Net::getChatHandler()->kickUser(mChannel->getId(), args);
+ else
+ chatLog(_("Need a user to kick!"), BY_CHANNEL);
+ }
+ else
+ return false;
+
+ return true;
+}
diff --git a/src/gui/widgets/channeltab.h b/src/gui/widgets/channeltab.h
new file mode 100644
index 000000000..842b80f7d
--- /dev/null
+++ b/src/gui/widgets/channeltab.h
@@ -0,0 +1,62 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 CHANNELTAB_H
+#define CHANNELTAB_H
+
+#include "chattab.h"
+
+class Channel;
+
+/**
+ * A tab for a chat channel.
+ */
+class ChannelTab : public ChatTab
+{
+ public:
+
+ Channel *getChannel() const { return mChannel; }
+
+ void showHelp();
+
+ bool handleCommand(const std::string &type,
+ const std::string &args);
+
+ protected:
+ friend class Channel;
+
+ /**
+ * Constructor.
+ */
+ ChannelTab(Channel *channel);
+
+ /**
+ * Destructor.
+ */
+ ~ChannelTab();
+
+ void handleInput(const std::string &msg);
+
+ private:
+ Channel *mChannel;
+};
+
+#endif // CHANNELTAB_H
diff --git a/src/gui/widgets/chattab.cpp b/src/gui/widgets/chattab.cpp
new file mode 100644
index 000000000..06ba3d3ed
--- /dev/null
+++ b/src/gui/widgets/chattab.cpp
@@ -0,0 +1,431 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/chattab.h"
+
+#include "actorspritemanager.h"
+#include "chatlog.h"
+#include "commandhandler.h"
+#include "configuration.h"
+#include "localplayer.h"
+#include "log.h"
+#include "sound.h"
+
+#include "gui/widgets/browserbox.h"
+#include "gui/widgets/scrollarea.h"
+#include "gui/widgets/itemlinkhandler.h"
+#include "gui/widgets/tradetab.h"
+
+#include "net/chathandler.h"
+#include "net/net.h"
+
+#include "resources/iteminfo.h"
+#include "resources/itemdb.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+#include <guichan/widgets/tabbedarea.hpp>
+
+#define MAX_WORD_SIZE 50
+
+ChatTab::ChatTab(const std::string &name) :
+ Tab(),
+ mAllowHightlight(true)
+{
+ setCaption(name);
+
+ mTextOutput = new BrowserBox(BrowserBox::AUTO_WRAP);
+ mTextOutput->setOpaque(false);
+ mTextOutput->setMaxRow((int) config.getIntValue("ChatLogLength"));
+ if (chatWindow)
+ mTextOutput->setLinkHandler(chatWindow->mItemLinkHandler);
+ mTextOutput->setAlwaysUpdate(false);
+
+ mScrollArea = new ScrollArea(mTextOutput);
+ mScrollArea->setScrollPolicy(gcn::ScrollArea::SHOW_NEVER,
+ gcn::ScrollArea::SHOW_ALWAYS);
+ mScrollArea->setScrollAmount(0, 1);
+ mScrollArea->setOpaque(false);
+
+ if (chatWindow)
+ chatWindow->addTab(this);
+}
+
+ChatTab::~ChatTab()
+{
+ if (chatWindow)
+ chatWindow->removeTab(this);
+
+ delete mTextOutput;
+ mTextOutput = 0;
+ delete mScrollArea;
+ mScrollArea = 0;
+}
+
+void ChatTab::chatLog(std::string line, Own own,
+ bool ignoreRecord, bool tryRemoveColors)
+{
+ // Trim whitespace
+ trim(line);
+
+ if (line.empty())
+ return;
+
+ if (tryRemoveColors && own == BY_OTHER &&
+ config.getBoolValue("removeColors"))
+ {
+ line = removeColors(line);
+ if (line.empty())
+ return;
+ }
+
+ unsigned lineLim = config.getIntValue("chatMaxCharLimit");
+ if (lineLim > 0 && line.length() > lineLim)
+ line = line.substr(0, lineLim);
+
+ if (line.empty())
+ return;
+
+ CHATLOG tmp;
+ tmp.own = own;
+ tmp.nick = "";
+ tmp.text = line;
+
+ std::string::size_type pos = line.find(" : ");
+ if (pos != std::string::npos)
+ {
+ if (line.length() <= pos + 3)
+ return;
+
+ tmp.nick = line.substr(0, pos);
+ tmp.text = line.substr(pos + 3);
+ }
+ else
+ {
+ // Fix the owner of welcome message.
+ if (line.length() > 7 && line.substr(0, 7) == "Welcome")
+ own = BY_SERVER;
+ }
+
+ // *implements actions in a backwards compatible way*
+ if ((own == BY_PLAYER || own == BY_OTHER) &&
+ tmp.text.at(0) == '*' &&
+ tmp.text.at(tmp.text.length()-1) == '*')
+ {
+ tmp.text[0] = ' ';
+ tmp.text.erase(tmp.text.length() - 1);
+ own = ACT_IS;
+ }
+
+ std::string lineColor = "##C";
+ switch (own)
+ {
+ case BY_GM:
+ if (tmp.nick.empty())
+ {
+ tmp.nick = std::string(_("Global announcement:"));
+ tmp.nick += " ";
+ lineColor = "##G";
+ }
+ else
+ {
+ tmp.nick = strprintf(_("Global announcement from %s:"),
+ tmp.nick.c_str());
+ tmp.nick += " ";
+ lineColor = "##1"; // Equiv. to BrowserBox::RED
+ }
+ break;
+ case BY_PLAYER:
+ tmp.nick += ": ";
+ lineColor = "##Y";
+ break;
+ case BY_OTHER:
+ tmp.nick += ": ";
+ lineColor = "##C";
+ break;
+ case BY_SERVER:
+ tmp.nick = _("Server:");
+ tmp.nick += " ";
+ tmp.text = line;
+ lineColor = "##S";
+ break;
+ case BY_CHANNEL:
+ tmp.nick = "";
+ // TODO: Use a predefined color
+ lineColor = "##2"; // Equiv. to BrowserBox::GREEN
+ break;
+ case ACT_WHISPER:
+ tmp.nick = strprintf(_("%s whispers: %s"), tmp.nick.c_str(), "");
+ lineColor = "##W";
+ break;
+ case ACT_IS:
+ lineColor = "##I";
+ break;
+ case BY_LOGGER:
+ tmp.nick = "";
+ tmp.text = line;
+ lineColor = "##L";
+ break;
+ default:
+ logger->log1("ChatTab::chatLog incorrect value in switch");
+ break;
+ }
+
+ if (tmp.nick == ": ")
+ {
+ tmp.nick = "";
+ lineColor = "##S";
+ }
+
+ // if configured, move magic messages log to debug chat tab
+ if (localChatTab && this == localChatTab
+ && ((config.getBoolValue("showMagicInDebug")
+ && own == BY_PLAYER && tmp.text.length() > 1
+ && tmp.text.length() > 1 && tmp.text.at(0) == '#'
+ && tmp.text.at(1) != '#')
+ || (config.getBoolValue("serverMsgInDebug") && (own == BY_SERVER
+ || tmp.nick.empty()))))
+ {
+ if (debugChatTab)
+ debugChatTab->chatLog(line, own, ignoreRecord, tryRemoveColors);
+ return;
+ }
+
+ // Get the current system time
+ time_t t;
+ time(&t);
+
+ // Format the time string properly
+ std::stringstream timeStr;
+ timeStr << "[" << ((((t / 60) / 60) % 24 < 10) ? "0" : "")
+ << static_cast<int>(((t / 60) / 60) % 24)
+ << ":" << (((t / 60) % 60 < 10) ? "0" : "")
+ << static_cast<int>((t / 60) % 60)
+ << "] ";
+
+ line = lineColor + timeStr.str() + tmp.nick + tmp.text;
+
+ if (config.getBoolValue("enableChatLog"))
+ saveToLogFile(line);
+
+ mTextOutput->setMaxRow(config.getIntValue("chatMaxLinesLimit"));
+
+ // We look if the Vertical Scroll Bar is set at the max before
+ // adding a row, otherwise the max will always be a row higher
+ // at comparison.
+ if (mScrollArea->getVerticalScrollAmount() >=
+ mScrollArea->getVerticalMaxScroll())
+ {
+ addRow(line);
+ mScrollArea->setVerticalScrollAmount(
+ mScrollArea->getVerticalMaxScroll());
+ }
+ else
+ {
+ addRow(line);
+ }
+
+ mScrollArea->logic();
+ if (own != BY_PLAYER)
+ {
+ if (own == BY_SERVER && (getType() == TAB_PARTY
+ || getType() == TAB_GUILD))
+ {
+ return;
+ }
+
+ if (!getTabbedArea())
+ return;
+
+ if (this != getTabbedArea()->getSelectedTab())
+ {
+ if (getFlash() == 0)
+ {
+ if (player_node)
+ {
+ std::string::size_type pos
+ = tmp.text.find(player_node->getName());
+ if (pos != std::string::npos)
+ setFlash(2);
+ else
+ setFlash(1);
+ }
+ else
+ {
+ setFlash(1);
+ }
+ }
+ }
+
+ if (getAllowHighlight() && (this != getTabbedArea()->getSelectedTab()
+ || (Client::getIsMinimized() || (!Client::getMouseFocused()
+ && !Client::getInputFocused()))))
+ {
+ if (own != BY_SERVER)
+ sound.playGuiSfx("system/newmessage.ogg");
+ }
+ }
+}
+
+void ChatTab::chatLog(const std::string &nick, std::string msg)
+{
+ Own byWho = (nick == player_node->getName() ? BY_PLAYER : BY_OTHER);
+ if (byWho == BY_OTHER && config.getBoolValue("removeColors"))
+ msg = removeColors(msg);
+ chatLog(nick + " : " + msg, byWho, false, false);
+}
+
+void ChatTab::chatInput(const std::string &message)
+{
+ std::string msg = message;
+ trim(msg);
+
+ if (msg.empty())
+ return;
+
+ // Check for item link
+ std::string::size_type start = msg.find('[');
+ while (start + 1 < msg.size() && start != std::string::npos
+ && msg[start + 1] != '@')
+ {
+ std::string::size_type end = msg.find(']', start);
+ if (start + 1 != end && end != std::string::npos)
+ {
+ // Catch multiple embeds and ignore them
+ // so it doesn't crash the client.
+ while ((msg.find('[', start + 1) != std::string::npos) &&
+ (msg.find('[', start + 1) < end))
+ {
+ start = msg.find('[', start + 1);
+ }
+
+ std::string temp = "";
+ if (start + 1 < msg.length() && end < msg.length()
+ && end > start + 1)
+ {
+ temp = msg.substr(start + 1, end - start - 1);
+
+ const ItemInfo itemInfo = ItemDB::get(temp);
+ if (itemInfo.getId() != 0)
+ {
+ msg.insert(end, "@@");
+ msg.insert(start + 1, "|");
+ msg.insert(start + 1, toString(itemInfo.getId()));
+ msg.insert(start + 1, "@@");
+ }
+ }
+ }
+ start = msg.find('[', start + 1);
+ }
+
+ // Prepare ordinary message
+ if (msg[0] != '/')
+ handleInput(msg);
+ else
+ handleCommand(std::string(msg, 1));
+}
+
+void ChatTab::scroll(int amount)
+{
+ int range = mScrollArea->getHeight() / 8 * amount;
+ gcn::Rectangle scr;
+ scr.y = mScrollArea->getVerticalScrollAmount() + range;
+ scr.height = abs(range);
+ mTextOutput->showPart(scr);
+}
+
+void ChatTab::clearText()
+{
+ mTextOutput->clearRows();
+}
+
+void ChatTab::handleInput(const std::string &msg)
+{
+ if (chatWindow)
+ Net::getChatHandler()->talk(chatWindow->doReplace(msg));
+ else
+ Net::getChatHandler()->talk(msg);
+}
+
+void ChatTab::handleCommand(const std::string &msg)
+{
+ if (commandHandler)
+ commandHandler->handleCommands(msg, this);
+}
+
+bool ChatTab::handleCommands(const std::string &type, const std::string &args)
+{
+ // need split to commands and call each
+
+ return handleCommand(type, args);
+}
+
+void ChatTab::saveToLogFile(std::string &msg)
+{
+ if (getType() == TAB_INPUT && chatLogger)
+ chatLogger->log(msg);
+}
+
+int ChatTab::getType() const
+{
+ if (getCaption() == "General" || getCaption() == _("General"))
+ return TAB_INPUT;
+ else if (getCaption() == "Debug" || getCaption() == _("Debug"))
+ return TAB_DEBUG;
+ else
+ return TAB_UNKNOWN;
+}
+
+void ChatTab::addRow(std::string &line)
+{
+ std::string::size_type idx = 0;
+
+ for (unsigned int f = 0; f < line.length(); f++)
+ {
+ if (line.at(f) == ' ')
+ {
+ idx = f;
+ }
+ else if (f - idx > MAX_WORD_SIZE)
+ {
+ line.insert(f, " ");
+ idx = f;
+ }
+ }
+ mTextOutput->addRow(line);
+}
+
+void ChatTab::loadFromLogFile(std::string name)
+{
+ if (chatLogger)
+ {
+ std::list<std::string> list;
+ chatLogger->loadLast(name, list, 5);
+ std::list<std::string>::iterator i = list.begin();
+ while (i != list.end())
+ {
+ std::string line = "##9" + *i;
+ addRow(line);
+ ++i;
+ }
+ }
+}
diff --git a/src/gui/widgets/chattab.h b/src/gui/widgets/chattab.h
new file mode 100644
index 000000000..ddc36d29c
--- /dev/null
+++ b/src/gui/widgets/chattab.h
@@ -0,0 +1,173 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 CHATTAB_H
+#define CHATTAB_H
+
+#include "gui/chat.h"
+
+#include "gui/widgets/browserbox.h"
+#include "gui/widgets/tab.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class ScrollArea;
+
+/**
+ * A tab for the chat window. This is special to ease chat handling.
+ */
+class ChatTab : public Tab
+{
+ public:
+ enum Type
+ {
+ TAB_UNKNOWN = 0,
+ TAB_INPUT,
+ TAB_WHISPER,
+ TAB_PARTY,
+ TAB_GUILD,
+ TAB_DEBUG,
+ TAB_TRADE,
+ TAB_BATTLE
+ };
+
+ /**
+ * Constructor.
+ */
+ ChatTab(const std::string &name);
+ ~ChatTab();
+
+ /**
+ * Adds a line of text to our message list. Parameters:
+ *
+ * @param line Text message.
+ * @param own Type of message (usually the owner-type).
+ * @param channelName which channel to send the message to.
+ * @param ignoreRecord should this not be recorded?
+ * @param removeColors try remove color if configured
+ */
+ void chatLog(std::string line, Own own = BY_SERVER,
+ bool ignoreRecord = false, bool tryRemoveColors = true);
+
+ /**
+ * Adds the text to the message list
+ *
+ * @param msg The message text which is to be sent.
+ */
+ void chatLog(const std::string &nick, std::string msg);
+
+ /**
+ * Determines whether the message is a command or message, then
+ * sends the given message to the game server to be said, or to the
+ * command handler
+ *
+ * @param msg The message text which is to be sent.
+ */
+ void chatInput(const std::string &msg);
+
+ /**
+ * Scrolls the chat window
+ *
+ * @param amount direction and amount to scroll. Negative numbers scroll
+ * up, positive numbers scroll down. The absolute amount indicates the
+ * amount of 1/8ths of chat window real estate that should be scrolled.
+ */
+ void scroll(int amount);
+
+ /**
+ * Clears the text from the tab
+ */
+ void clearText();
+
+ /**
+ * Add any extra help text to the output. Allows tabs to define help
+ * for commands defined by the tab itself.
+ */
+ virtual void showHelp() {}
+
+ /**
+ * Handle special commands. Allows a tab to handle commands it
+ * defines itself.
+ *
+ * @returns true if the command was handled
+ * false if the command was not handled
+ */
+ virtual bool handleCommand(const std::string &type _UNUSED_,
+ const std::string &args _UNUSED_)
+ { return false; }
+
+ /**
+ * Handle special commands. Allows a tab to handle commands it
+ * defines itself.
+ *
+ * @returns true if the command was handled
+ * false if the command was not handled
+ */
+ virtual bool handleCommands(const std::string &type,
+ const std::string &args);
+
+ /**
+ * Returns type of the being.
+ */
+ virtual int getType() const;
+
+ virtual void saveToLogFile(std::string &msg);
+
+ std::list<std::string> &getRows()
+ { return mTextOutput->getRows(); }
+
+ void loadFromLogFile(std::string name);
+
+ bool getAllowHighlight()
+ { return mAllowHightlight; }
+
+ void setAllowHighlight(bool n)
+ { mAllowHightlight = n; }
+
+ protected:
+ friend class ChatWindow;
+ friend class WhisperWindow;
+
+ virtual void setCurrent()
+ { setFlash(false); }
+
+ virtual void handleInput(const std::string &msg);
+
+ virtual void handleCommand(const std::string &msg);
+
+ virtual void getAutoCompleteList(std::vector<std::string>&) const
+ {}
+
+ void addRow(std::string &line);
+
+ ScrollArea *mScrollArea;
+ BrowserBox *mTextOutput;
+ bool mAllowHightlight;
+};
+
+extern ChatTab *localChatTab;
+extern ChatTab *debugChatTab;
+
+#endif // CHATTAB_H
diff --git a/src/gui/widgets/checkbox.cpp b/src/gui/widgets/checkbox.cpp
new file mode 100644
index 000000000..01331ddba
--- /dev/null
+++ b/src/gui/widgets/checkbox.cpp
@@ -0,0 +1,187 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/checkbox.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+
+#include "gui/palette.h"
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+#include <guichan/actionlistener.hpp>
+
+int CheckBox::instances = 0;
+float CheckBox::mAlpha = 1.0;
+Image *CheckBox::checkBoxNormal;
+Image *CheckBox::checkBoxChecked;
+Image *CheckBox::checkBoxDisabled;
+Image *CheckBox::checkBoxDisabledChecked;
+Image *CheckBox::checkBoxNormalHi;
+Image *CheckBox::checkBoxCheckedHi;
+
+CheckBox::CheckBox(const std::string &caption, bool selected,
+ gcn::ActionListener* listener, std::string eventId):
+ gcn::CheckBox(caption, selected),
+ mHasMouse(false)
+{
+ if (instances == 0)
+ {
+ Image *checkBox = Theme::getImageFromTheme("checkbox.png");
+ if (checkBox)
+ {
+ checkBoxNormal = checkBox->getSubImage(0, 0, 9, 10);
+ checkBoxChecked = checkBox->getSubImage(9, 0, 9, 10);
+ checkBoxDisabled = checkBox->getSubImage(18, 0, 9, 10);
+ checkBoxDisabledChecked = checkBox->getSubImage(27, 0, 9, 10);
+ checkBoxNormalHi = checkBox->getSubImage(36, 0, 9, 10);
+ checkBoxCheckedHi = checkBox->getSubImage(45, 0, 9, 10);
+ checkBoxNormal->setAlpha(mAlpha);
+ checkBoxChecked->setAlpha(mAlpha);
+ checkBoxDisabled->setAlpha(mAlpha);
+ checkBoxDisabledChecked->setAlpha(mAlpha);
+ checkBoxNormalHi->setAlpha(mAlpha);
+ checkBoxCheckedHi->setAlpha(mAlpha);
+ checkBox->decRef();
+ }
+ else
+ {
+ checkBoxNormal = 0;
+ checkBoxChecked = 0;
+ checkBoxDisabled = 0;
+ checkBoxDisabledChecked = 0;
+ checkBoxNormalHi = 0;
+ checkBoxCheckedHi = 0;
+ }
+ }
+
+ instances++;
+
+ if (!eventId.empty())
+ setActionEventId(eventId);
+
+ if (listener)
+ addActionListener(listener);
+}
+
+CheckBox::~CheckBox()
+{
+ instances--;
+
+ if (instances == 0)
+ {
+ delete checkBoxNormal;
+ checkBoxNormal = 0;
+ delete checkBoxChecked;
+ checkBoxChecked = 0;
+ delete checkBoxDisabled;
+ checkBoxDisabled = 0;
+ delete checkBoxDisabledChecked;
+ checkBoxDisabledChecked = 0;
+ delete checkBoxNormalHi;
+ checkBoxNormalHi = 0;
+ delete checkBoxCheckedHi;
+ checkBoxCheckedHi = 0;
+ }
+}
+
+void CheckBox::draw(gcn::Graphics* graphics)
+{
+ drawBox(graphics);
+
+ graphics->setFont(getFont());
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+
+ const int h = getHeight() + getHeight() / 2;
+
+ graphics->drawText(getCaption(), h - 2, 0);
+}
+
+void CheckBox::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ if (mAlpha != alpha)
+ {
+ mAlpha = alpha;
+ if (checkBoxNormal)
+ checkBoxNormal->setAlpha(mAlpha);
+ if (checkBoxChecked)
+ checkBoxChecked->setAlpha(mAlpha);
+ if (checkBoxDisabled)
+ checkBoxDisabled->setAlpha(mAlpha);
+ if (checkBoxDisabledChecked)
+ checkBoxDisabledChecked->setAlpha(mAlpha);
+ if (checkBoxNormal)
+ checkBoxNormal->setAlpha(mAlpha);
+ if (checkBoxCheckedHi)
+ checkBoxCheckedHi->setAlpha(mAlpha);
+ }
+}
+
+void CheckBox::drawBox(gcn::Graphics* graphics)
+{
+ Image *box;
+
+ if (isEnabled())
+ {
+ if (isSelected())
+ {
+ if (mHasMouse)
+ box = checkBoxCheckedHi;
+ else
+ box = checkBoxChecked;
+ }
+ else
+ {
+ if (mHasMouse)
+ box = checkBoxNormalHi;
+ else
+ box = checkBoxNormal;
+ }
+ }
+ else
+ {
+ if (isSelected())
+ box = checkBoxDisabledChecked;
+ else
+ box = checkBoxDisabled;
+ }
+
+ updateAlpha();
+
+ if (box)
+ static_cast<Graphics*>(graphics)->drawImage(box, 2, 2);
+}
+
+void CheckBox::mouseEntered(gcn::MouseEvent& event _UNUSED_)
+{
+ mHasMouse = true;
+}
+
+void CheckBox::mouseExited(gcn::MouseEvent& event _UNUSED_)
+{
+ mHasMouse = false;
+}
diff --git a/src/gui/widgets/checkbox.h b/src/gui/widgets/checkbox.h
new file mode 100644
index 000000000..b885e8922
--- /dev/null
+++ b/src/gui/widgets/checkbox.h
@@ -0,0 +1,92 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 CHECKBOX_H
+#define CHECKBOX_H
+
+#include <guichan/widgets/checkbox.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+
+/**
+ * Check box widget. Same as the Guichan check box but with custom look.
+ *
+ * \ingroup GUI
+ */
+class CheckBox : public gcn::CheckBox
+{
+ public:
+ /**
+ * Constructor.
+ */
+ CheckBox(const std::string &caption, bool selected = false,
+ gcn::ActionListener* listener = NULL,
+ std::string eventId = "");
+
+ /**
+ * Destructor.
+ */
+ ~CheckBox();
+
+ /**
+ * Draws the caption, then calls drawBox to draw the check box.
+ */
+ void draw(gcn::Graphics* graphics);
+
+ /**
+ * Update the alpha value to the checkbox components.
+ */
+ void updateAlpha();
+
+ /**
+ * Draws the check box, not the caption.
+ */
+ void drawBox(gcn::Graphics* graphics);
+
+ /**
+ * Called when the mouse enteres the widget area.
+ */
+ void mouseEntered(gcn::MouseEvent& event);
+
+ /**
+ * Called when the mouse leaves the widget area.
+ */
+ void mouseExited(gcn::MouseEvent& event);
+
+ private:
+ static int instances;
+ static float mAlpha;
+ bool mHasMouse;
+ static Image *checkBoxNormal;
+ static Image *checkBoxChecked;
+ static Image *checkBoxDisabled;
+ static Image *checkBoxDisabledChecked;
+ static Image *checkBoxNormalHi;
+ static Image *checkBoxCheckedHi;
+};
+
+#endif
diff --git a/src/gui/widgets/container.cpp b/src/gui/widgets/container.cpp
new file mode 100644
index 000000000..b788b0610
--- /dev/null
+++ b/src/gui/widgets/container.cpp
@@ -0,0 +1,33 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/container.h"
+
+Container::Container()
+{
+ setOpaque(false);
+}
+
+Container::~Container()
+{
+ while (!mWidgets.empty())
+ delete mWidgets.front();
+}
diff --git a/src/gui/widgets/container.h b/src/gui/widgets/container.h
new file mode 100644
index 000000000..c2696a65f
--- /dev/null
+++ b/src/gui/widgets/container.h
@@ -0,0 +1,43 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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_CONTAINER_H
+#define GUI_CONTAINER_H
+
+#include <guichan/widgets/container.hpp>
+
+/**
+ * A widget container.
+ *
+ * The main difference between the standard Guichan container and this one is
+ * that childs added to this container are automatically deleted when the
+ * container is deleted.
+ *
+ * This container is also non-opaque by default.
+ */
+class Container : public gcn::Container
+{
+ public:
+ Container();
+ ~Container();
+};
+
+#endif
diff --git a/src/gui/widgets/desktop.cpp b/src/gui/widgets/desktop.cpp
new file mode 100644
index 000000000..fa5b1698a
--- /dev/null
+++ b/src/gui/widgets/desktop.cpp
@@ -0,0 +1,157 @@
+/*
+ * Desktop widget
+ * Copyright (c) 2009-2010 The Mana World Development Team
+ *
+ * This file is part of The Mana 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/widgets/desktop.h"
+
+#include "configuration.h"
+#include "graphics.h"
+#include "log.h"
+#include "main.h"
+
+#include "gui/palette.h"
+#include "gui/theme.h"
+
+#include "gui/widgets/label.h"
+
+#include "resources/image.h"
+#include "resources/resourcemanager.h"
+#include "resources/wallpaper.h"
+
+#include "utils/stringutils.h"
+
+Desktop::Desktop()
+ : mWallpaper(0)
+{
+ addWidgetListener(this);
+
+ Wallpaper::loadWallpapers();
+
+ std::string appName = branding.getValue("appName", std::string(""));
+
+ if (appName.empty())
+ mVersionLabel = new Label(FULL_VERSION);
+ else
+ mVersionLabel = new Label(strprintf("%s (Mana %s)", appName.c_str(),
+ FULL_VERSION));
+
+ mVersionLabel->setBackgroundColor(gcn::Color(255, 255, 255, 128));
+ add(mVersionLabel, 25, 2);
+}
+
+Desktop::~Desktop()
+{
+ if (mWallpaper)
+ mWallpaper->decRef();
+}
+
+void Desktop::reloadWallpaper()
+{
+ Wallpaper::loadWallpapers();
+ setBestFittingWallpaper();
+}
+
+void Desktop::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ setBestFittingWallpaper();
+}
+
+void Desktop::draw(gcn::Graphics *graphics)
+{
+ Graphics *g = static_cast<Graphics *>(graphics);
+
+ if (!mWallpaper || (getWidth() > mWallpaper->getWidth() ||
+ getHeight() > mWallpaper->getHeight()))
+ {
+ // TODO: Color from palette
+ g->setColor(gcn::Color(64, 64, 64));
+ g->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
+ }
+
+ if (mWallpaper)
+ {
+ if (!mWallpaper->useOpenGL())
+ {
+ g->drawImage(mWallpaper,
+ (getWidth() - mWallpaper->getWidth()) / 2,
+ (getHeight() - mWallpaper->getHeight()) / 2);
+ }
+ else
+ {
+ g->drawRescaledImage(mWallpaper, 0, 0, 0, 0,
+ mWallpaper->getWidth(), mWallpaper->getHeight(),
+ getWidth(), getHeight(), false);
+ }
+ }
+
+ // Draw a thin border under the application version...
+ g->setColor(gcn::Color(255, 255, 255, 128));
+ g->fillRectangle(gcn::Rectangle(mVersionLabel->getDimension()));
+
+ Container::draw(graphics);
+}
+
+void Desktop::setBestFittingWallpaper()
+{
+ if (!config.getBoolValue("showBackground"))
+ return;
+
+ const std::string wallpaperName =
+ Wallpaper::getWallpaper(getWidth(), getHeight());
+
+ Image *nWallPaper = Theme::getImageFromTheme(wallpaperName);
+
+ if (nWallPaper)
+ {
+ if (mWallpaper)
+ mWallpaper->decRef();
+
+ if (!nWallPaper->useOpenGL()
+ && (nWallPaper->getWidth() != getWidth()
+ || nWallPaper->getHeight() != getHeight()))
+ {
+ // We rescale to obtain a fullscreen wallpaper...
+ Image *newRsclWlPpr = nWallPaper->SDLgetScaledImage(
+ getWidth(), getHeight());
+ std::string idPath = nWallPaper->getIdPath();
+
+ // We replace the resource in the resource manager
+ nWallPaper->decRef();
+ if (newRsclWlPpr)
+ {
+ ResourceManager::getInstance()->addResource(
+ idPath, newRsclWlPpr);
+
+ mWallpaper = newRsclWlPpr;
+ }
+ else
+ {
+ mWallpaper = nWallPaper;
+ }
+ }
+ else
+ {
+ mWallpaper = nWallPaper;
+ }
+ }
+ else
+ {
+ logger->log("Couldn't load %s as wallpaper", wallpaperName.c_str());
+ }
+}
diff --git a/src/gui/widgets/desktop.h b/src/gui/widgets/desktop.h
new file mode 100644
index 000000000..83568c66f
--- /dev/null
+++ b/src/gui/widgets/desktop.h
@@ -0,0 +1,73 @@
+/*
+ * Desktop widget
+ * Copyright (c) 2009-2010 The Mana World Development Team
+ *
+ * This file is part of The Mana 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 DESKTOP_H
+#define DESKTOP_H
+
+#include "guichanfwd.h"
+
+#include "gui/widgets/container.h"
+
+#include <guichan/widgetlistener.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+
+/**
+ * Desktop widget, for drawing a background image and color.
+ *
+ * It picks the best fitting background image. If the image doesn't fit, a
+ * background color is drawn and the image is centered.
+ *
+ * When the desktop widget is resized, the background image is automatically
+ * updated.
+ *
+ * The desktop also displays the client version in the top-right corner.
+ *
+ * \ingroup GUI
+ */
+class Desktop : public Container, gcn::WidgetListener
+{
+ public:
+ Desktop();
+ ~Desktop();
+
+ /**
+ * Has to be called after updates have been loaded.
+ */
+ void reloadWallpaper();
+
+ void widgetResized(const gcn::Event &event);
+
+ void draw(gcn::Graphics *graphics);
+
+ private:
+ void setBestFittingWallpaper();
+
+ Image *mWallpaper;
+ gcn::Label *mVersionLabel;
+};
+
+#endif // DESKTOP_H
diff --git a/src/gui/widgets/dropdown.cpp b/src/gui/widgets/dropdown.cpp
new file mode 100644
index 000000000..b8616643b
--- /dev/null
+++ b/src/gui/widgets/dropdown.cpp
@@ -0,0 +1,303 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2006-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/dropdown.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+
+#include "gui/palette.h"
+#include "gui/sdlinput.h"
+#include "gui/theme.h"
+
+#include "gui/widgets/listbox.h"
+#include "gui/widgets/scrollarea.h"
+
+#include "resources/image.h"
+
+#include "utils/dtor.h"
+
+#include <algorithm>
+
+int DropDown::instances = 0;
+Image *DropDown::buttons[2][2];
+ImageRect DropDown::skin;
+float DropDown::mAlpha = 1.0;
+
+DropDown::DropDown(gcn::ListModel *listModel):
+ gcn::DropDown::DropDown(listModel,
+ new ScrollArea,
+ new ListBox(listModel))
+{
+ setFrameSize(2);
+
+ // Initialize graphics
+ if (instances == 0)
+ {
+ // Load the background skin
+
+ // Get the button skin
+ buttons[1][0] = Theme::getImageFromTheme("vscroll_up_default.png");
+ buttons[0][0] = Theme::getImageFromTheme("vscroll_down_default.png");
+ buttons[1][1] = Theme::getImageFromTheme("vscroll_up_pressed.png");
+ buttons[0][1] = Theme::getImageFromTheme("vscroll_down_pressed.png");
+
+ if (buttons[0][0])
+ buttons[0][0]->setAlpha(mAlpha);
+ if (buttons[0][1])
+ buttons[0][1]->setAlpha(mAlpha);
+ if (buttons[1][0])
+ buttons[1][0]->setAlpha(mAlpha);
+ if (buttons[1][1])
+ buttons[1][1]->setAlpha(mAlpha);
+
+ // get the border skin
+ Image *boxBorder = Theme::getImageFromTheme("deepbox.png");
+ if (boxBorder)
+ {
+ int gridx[4] = {0, 3, 28, 31};
+ int gridy[4] = {0, 3, 28, 31};
+ int a = 0, x, y;
+
+ for (y = 0; y < 3; y++)
+ {
+ for (x = 0; x < 3; x++)
+ {
+ skin.grid[a] = boxBorder->getSubImage(gridx[x], gridy[y],
+ gridx[x + 1] -
+ gridx[x] + 1,
+ gridy[y + 1] -
+ gridy[y] + 1);
+ if (skin.grid[a])
+ skin.grid[a]->setAlpha(mAlpha);
+ a++;
+ }
+ }
+
+ boxBorder->decRef();
+ }
+ }
+
+ instances++;
+}
+
+DropDown::~DropDown()
+{
+ instances--;
+ // Free images memory
+ if (instances == 0)
+ {
+ if (buttons[0][0])
+ buttons[0][0]->decRef();
+ if (buttons[0][1])
+ buttons[0][1]->decRef();
+ if (buttons[1][0])
+ buttons[1][0]->decRef();
+ if (buttons[1][1])
+ buttons[1][1]->decRef();
+
+ for_each(skin.grid, skin.grid + 9, dtor<Image*>());
+ }
+
+ delete mScrollArea;
+ mScrollArea = 0;
+}
+
+void DropDown::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ if (mAlpha != alpha)
+ {
+ mAlpha = alpha;
+
+ if (buttons[0][0])
+ buttons[0][0]->setAlpha(mAlpha);
+ if (buttons[0][1])
+ buttons[0][1]->setAlpha(mAlpha);
+ if (buttons[1][0])
+ buttons[1][0]->setAlpha(mAlpha);
+ if (buttons[1][1])
+ buttons[1][1]->setAlpha(mAlpha);
+
+ for (int a = 0; a < 9; a++)
+ {
+ if (skin.grid[a])
+ skin.grid[a]->setAlpha(mAlpha);
+ }
+ }
+}
+
+void DropDown::draw(gcn::Graphics* graphics)
+{
+ int h;
+
+ if (mDroppedDown)
+ h = mFoldedUpHeight;
+ else
+ h = getHeight();
+
+ updateAlpha();
+
+ const int alpha = static_cast<int>(mAlpha * 255.0f);
+ gcn::Color faceColor = getBaseColor();
+ faceColor.a = alpha;
+ const gcn::Color *highlightColor = &Theme::getThemeColor(Theme::HIGHLIGHT,
+ alpha);
+ gcn::Color shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ if (mListBox->getListModel() && mListBox->getSelected() >= 0)
+ {
+ graphics->setFont(getFont());
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ graphics->drawText(mListBox->getListModel()->getElementAt(
+ mListBox->getSelected()), 1, 0);
+ }
+
+ if (isFocused())
+ {
+ if (highlightColor)
+ graphics->setColor(*highlightColor);
+ graphics->drawRectangle(gcn::Rectangle(0, 0, getWidth() - h, h));
+ }
+
+ drawButton(graphics);
+
+ if (mDroppedDown)
+ {
+ drawChildren(graphics);
+
+ // Draw two lines separating the ListBox with selected
+ // element view.
+ if (highlightColor)
+ graphics->setColor(*highlightColor);
+ graphics->drawLine(0, h, getWidth(), h);
+ graphics->setColor(shadowColor);
+ graphics->drawLine(0, h + 1, getWidth(), h + 1);
+ }
+}
+
+void DropDown::drawFrame(gcn::Graphics *graphics)
+{
+ const int bs = getFrameSize();
+ const int w = getWidth() + bs * 2;
+ const int h = getHeight() + bs * 2;
+
+ static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin);
+}
+
+void DropDown::drawButton(gcn::Graphics *graphics)
+{
+ int height = mDroppedDown ? mFoldedUpHeight : getHeight();
+
+ if (buttons[mDroppedDown][mPushed])
+ {
+ static_cast<Graphics*>(graphics)->
+ drawImage(buttons[mDroppedDown][mPushed],
+ getWidth() - height + 2, 1);
+ }
+}
+
+// -- KeyListener notifications
+void DropDown::keyPressed(gcn::KeyEvent& keyEvent)
+{
+ if (keyEvent.isConsumed())
+ return;
+
+ gcn::Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
+ dropDown();
+ else if (key.getValue() == Key::UP)
+ setSelected(getSelected() - 1);
+ else if (key.getValue() == Key::DOWN)
+ setSelected(getSelected() + 1);
+ else if (key.getValue() == Key::HOME)
+ setSelected(0);
+ else if (key.getValue() == Key::END && mListBox->getListModel())
+ setSelected(mListBox->getListModel()->getNumberOfElements() - 1);
+ else
+ return;
+
+ keyEvent.consume();
+}
+
+void DropDown::focusLost(const gcn::Event& event)
+{
+ gcn::DropDown::focusLost(event);
+ releaseModalMouseInputFocus();
+}
+
+void DropDown::mousePressed(gcn::MouseEvent& mouseEvent)
+{
+ gcn::DropDown::mousePressed(mouseEvent);
+
+ if (0 <= mouseEvent.getY() && mouseEvent.getY() < getHeight() &&
+ mouseEvent.getX() >= 0 && mouseEvent.getX() < getWidth() &&
+ mouseEvent.getButton() == gcn::MouseEvent::LEFT && mDroppedDown &&
+ mouseEvent.getSource() == mListBox)
+ {
+ mPushed = false;
+ foldUp();
+ releaseModalMouseInputFocus();
+ distributeActionEvent();
+ }
+}
+
+void DropDown::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent)
+{
+ setSelected(getSelected() - 1);
+ mouseEvent.consume();
+}
+
+void DropDown::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent)
+{
+ setSelected(getSelected() + 1);
+ mouseEvent.consume();
+}
+
+void DropDown::setSelectedString(std::string str)
+{
+ gcn::ListModel *listModel = mListBox->getListModel();
+ if (!listModel)
+ return;
+
+ for (int f = 0; f < listModel->getNumberOfElements(); f ++)
+ {
+ if (listModel->getElementAt(f) == str)
+ {
+ setSelected(f);
+ break;
+ }
+ }
+}
+
+std::string DropDown::getSelectedString() const
+{
+ gcn::ListModel *listModel = mListBox->getListModel();
+ if (!listModel)
+ return "";
+
+ return listModel->getElementAt(getSelected());
+}
diff --git a/src/gui/widgets/dropdown.h b/src/gui/widgets/dropdown.h
new file mode 100644
index 000000000..f6e347b2b
--- /dev/null
+++ b/src/gui/widgets/dropdown.h
@@ -0,0 +1,97 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2006-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 DROPDOWN_H
+#define DROPDOWN_H
+
+#include <guichan/widgets/dropdown.hpp>
+
+class Image;
+class ImageRect;
+
+/**
+ * A drop down box from which you can select different values.
+ *
+ * A ListModel provides the contents of the drop down. To be able to use
+ * DropDown you must give DropDown an implemented ListModel which represents
+ * your list.
+ */
+class DropDown : public gcn::DropDown
+{
+ public:
+ /**
+ * Contructor.
+ *
+ * @param listModel the ListModel to use.
+ * @param scrollArea the ScrollArea to use.
+ * @param listBox the listBox to use.
+ * @see ListModel, ScrollArea, ListBox.
+ */
+ DropDown(gcn::ListModel *listModel = 0);
+
+ ~DropDown();
+
+ /**
+ * Update the alpha value to the graphic components.
+ */
+ void updateAlpha();
+
+ void draw(gcn::Graphics *graphics);
+
+ void drawFrame(gcn::Graphics *graphics);
+
+ // Inherited from FocusListener
+
+ void focusLost(const gcn::Event& event);
+
+ // Inherited from KeyListener
+
+ void keyPressed(gcn::KeyEvent& keyEvent);
+
+ // Inherited from MouseListener
+
+ void mousePressed(gcn::MouseEvent& mouseEvent);
+
+ void mouseWheelMovedUp(gcn::MouseEvent& mouseEvent);
+
+ void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent);
+
+ void setSelectedString(std::string str);
+
+ std::string getSelectedString() const;
+
+ protected:
+ /**
+ * Draws the button with the little down arrow.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ void drawButton(gcn::Graphics *graphics);
+
+ // Add own Images.
+ static int instances;
+ static Image *buttons[2][2];
+ static ImageRect skin;
+ static float mAlpha;
+};
+
+#endif // end DROPDOWN_H
+
diff --git a/src/gui/widgets/dropshortcutcontainer.cpp b/src/gui/widgets/dropshortcutcontainer.cpp
new file mode 100644
index 000000000..c3aaed829
--- /dev/null
+++ b/src/gui/widgets/dropshortcutcontainer.cpp
@@ -0,0 +1,303 @@
+/*
+ * The Mana World
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 Andrei Karas
+ *
+ * This file is part of The Mana World.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "gui/widgets/dropshortcutcontainer.h"
+
+#include "gui/inventorywindow.h"
+#include "gui/itempopup.h"
+#include "gui/palette.h"
+#include "gui/theme.h"
+#include "gui/viewport.h"
+
+#include "configuration.h"
+#include "dropshortcut.h"
+#include "graphics.h"
+#include "inventory.h"
+#include "item.h"
+#include "keyboardconfig.h"
+#include "localplayer.h"
+#include "playerinfo.h"
+
+#include "resources/image.h"
+#include "resources/iteminfo.h"
+#include "resources/resourcemanager.h"
+
+#include "utils/stringutils.h"
+
+DropShortcutContainer::DropShortcutContainer():
+ ShortcutContainer(),
+ mItemClicked(false),
+ mItemMoved(NULL)
+{
+ addMouseListener(this);
+ addWidgetListener(this);
+
+ mItemPopup = new ItemPopup;
+
+ mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png");
+ if (dropShortcut)
+ mMaxItems = dropShortcut->getItemCount();
+ else
+ mMaxItems = 0;
+
+ if (mBackgroundImg)
+ {
+ mBackgroundImg->setAlpha(Client::getGuiAlpha());
+ mBoxHeight = mBackgroundImg->getHeight();
+ mBoxWidth = mBackgroundImg->getWidth();
+ }
+ else
+ {
+ mBoxHeight = 1;
+ mBoxWidth = 1;
+ }
+}
+
+DropShortcutContainer::~DropShortcutContainer()
+{
+ if (mBackgroundImg)
+ mBackgroundImg->decRef();
+ delete mItemPopup;
+ mItemPopup = 0;
+}
+
+void DropShortcutContainer::draw(gcn::Graphics *graphics)
+{
+ if (!dropShortcut)
+ return;
+
+ if (Client::getGuiAlpha() != mAlpha)
+ {
+ mAlpha = Client::getGuiAlpha();
+ if (mBackgroundImg)
+ mBackgroundImg->setAlpha(mAlpha);
+ }
+
+ Graphics *g = static_cast<Graphics*>(graphics);
+
+ graphics->setFont(getFont());
+
+ for (unsigned i = 0; i < mMaxItems; i++)
+ {
+ const int itemX = (i % mGridWidth) * mBoxWidth;
+ const int itemY = (i / mGridWidth) * mBoxHeight;
+
+ if (mBackgroundImg)
+ g->drawImage(mBackgroundImg, itemX, itemY);
+
+/* // Draw item keyboard shortcut.
+ const char *key = SDL_GetKeyName(
+ (SDLKey) keyboard.getKeyValue(keyboard.KEY_SHORTCUT_1 + i));
+ graphics->setColor(guiPalette->getColor(Palette::TEXT));
+ g->drawText(key, itemX + 2, itemY + 2, gcn::Graphics::LEFT);
+*/
+ if (dropShortcut->getItem(i) < 0)
+ continue;
+
+ Inventory *inv = PlayerInfo::getInventory();
+ if (!inv)
+ return;
+
+ Item *item = inv->findItem(dropShortcut->getItem(i));
+
+ if (item)
+ {
+ // Draw item icon.
+ Image* image = item->getImage();
+
+ if (image)
+ {
+ std::string caption;
+ if (item->getQuantity() > 1)
+ caption = toString(item->getQuantity());
+ else if (item->isEquipped())
+ caption = "Eq.";
+
+ image->setAlpha(1.0f);
+ g->drawImage(image, itemX, itemY);
+ if (item->isEquipped())
+ g->setColor(Theme::getThemeColor(Theme::ITEM_EQUIPPED));
+ else
+ g->setColor(Theme::getThemeColor(Theme::TEXT));
+ g->drawText(caption, itemX + mBoxWidth / 2,
+ itemY + mBoxHeight - 14, gcn::Graphics::CENTER);
+ }
+ }
+ }
+
+ if (mItemMoved)
+ {
+ // Draw the item image being dragged by the cursor.
+ Image* image = mItemMoved->getImage();
+ if (image)
+ {
+ const int tPosX = mCursorPosX - (image->getWidth() / 2);
+ const int tPosY = mCursorPosY - (image->getHeight() / 2);
+
+ g->drawImage(image, tPosX, tPosY);
+ g->drawText(toString(mItemMoved->getQuantity()),
+ tPosX + mBoxWidth / 2, tPosY + mBoxHeight - 14,
+ gcn::Graphics::CENTER);
+ }
+ }
+}
+
+void DropShortcutContainer::mouseDragged(gcn::MouseEvent &event)
+{
+ if (!dropShortcut)
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ if (!mItemMoved && mItemClicked)
+ {
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ const int itemId = dropShortcut->getItem(index);
+
+ if (itemId < 0)
+ return;
+
+ Inventory *inv = PlayerInfo::getInventory();
+ if (!inv)
+ return;
+
+ Item *item = inv->findItem(itemId);
+
+ if (item)
+ {
+ mItemMoved = item;
+ dropShortcut->removeItem(index);
+ }
+ }
+ if (mItemMoved)
+ {
+ mCursorPosX = event.getX();
+ mCursorPosY = event.getY();
+ }
+ }
+}
+
+void DropShortcutContainer::mousePressed(gcn::MouseEvent &event)
+{
+ if (!dropShortcut || !inventoryWindow)
+ return;
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ // Stores the selected item if theirs one.
+ if (dropShortcut->isItemSelected() && inventoryWindow->isVisible())
+ {
+ dropShortcut->setItem(index);
+ dropShortcut->setItemSelected(-1);
+ }
+ else if (dropShortcut->getItem(index))
+ {
+ mItemClicked = true;
+ }
+ }
+ else if (event.getButton() == gcn::MouseEvent::RIGHT)
+ {
+ Inventory *inv = PlayerInfo::getInventory();
+ if (!inv)
+ return;
+
+ Item *item = inv->findItem(dropShortcut->getItem(index));
+
+ if (viewport)
+ viewport->showDropPopup(item);
+ }
+}
+
+void DropShortcutContainer::mouseReleased(gcn::MouseEvent &event)
+{
+ if (!dropShortcut)
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ if (dropShortcut->isItemSelected())
+ dropShortcut->setItemSelected(-1);
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+ if (index == -1)
+ {
+ mItemMoved = NULL;
+ return;
+ }
+ if (mItemMoved)
+ {
+ dropShortcut->setItems(index, mItemMoved->getId());
+ mItemMoved = NULL;
+ }
+
+ if (mItemClicked)
+ mItemClicked = false;
+ }
+}
+
+// Show ItemTooltip
+void DropShortcutContainer::mouseMoved(gcn::MouseEvent &event)
+{
+ if (!dropShortcut)
+ return;
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ const int itemId = dropShortcut->getItem(index);
+
+ if (itemId < 0)
+ return;
+
+ Inventory *inv = PlayerInfo::getInventory();
+ if (!inv)
+ return;
+
+ Item *item = inv->findItem(itemId);
+
+ if (item && viewport)
+ {
+ mItemPopup->setItem(item);
+ mItemPopup->position(viewport->getMouseX(), viewport->getMouseY());
+ }
+ else
+ {
+ mItemPopup->setVisible(false);
+ }
+}
+
+// Hide ItemTooltip
+void DropShortcutContainer::mouseExited(gcn::MouseEvent &event _UNUSED_)
+{
+ mItemPopup->setVisible(false);
+}
diff --git a/src/gui/widgets/dropshortcutcontainer.h b/src/gui/widgets/dropshortcutcontainer.h
new file mode 100644
index 000000000..c072f0613
--- /dev/null
+++ b/src/gui/widgets/dropshortcutcontainer.h
@@ -0,0 +1,88 @@
+/*
+ * The Mana World
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 Andrei Karas
+ *
+ * This file is part of The Mana World.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DROPSHORTCUTCONTAINER_H
+#define DROPSHORTCUTCONTAINER_H
+
+#include <guichan/mouselistener.hpp>
+
+#include "gui/widgets/shortcutcontainer.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+class Item;
+class ItemPopup;
+
+/**
+ * An item shortcut container. Used to quickly use items.
+ *
+ * \ingroup GUI
+ */
+class DropShortcutContainer : public ShortcutContainer
+{
+ public:
+ /**
+ * Constructor. Initializes the graphic.
+ */
+ DropShortcutContainer();
+
+ /**
+ * Destructor.
+ */
+ virtual ~DropShortcutContainer();
+
+ /**
+ * Draws the items.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Handles mouse when dragged.
+ */
+ void mouseDragged(gcn::MouseEvent &event);
+
+ /**
+ * Handles mouse when pressed.
+ */
+ void mousePressed(gcn::MouseEvent &event);
+
+ /**
+ * Handles mouse release.
+ */
+ void mouseReleased(gcn::MouseEvent &event);
+
+ private:
+ void mouseExited(gcn::MouseEvent &event);
+ void mouseMoved(gcn::MouseEvent &event);
+
+ bool mItemClicked;
+ Item *mItemMoved;
+
+ ItemPopup *mItemPopup;
+};
+
+#endif
diff --git a/src/gui/widgets/emoteshortcutcontainer.cpp b/src/gui/widgets/emoteshortcutcontainer.cpp
new file mode 100644
index 000000000..a9e435540
--- /dev/null
+++ b/src/gui/widgets/emoteshortcutcontainer.cpp
@@ -0,0 +1,259 @@
+/*
+ * Extended support for activating emotes
+ * Copyright (C) 2009 Aethyra Development Team
+ *
+ * This file is part of The Mana 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/widgets/emoteshortcutcontainer.h"
+
+#include "animatedsprite.h"
+#include "configuration.h"
+#include "emoteshortcut.h"
+#include "graphics.h"
+#include "inventory.h"
+#include "item.h"
+#include "itemshortcut.h"
+#include "keyboardconfig.h"
+#include "localplayer.h"
+#include "log.h"
+
+#include "gui/palette.h"
+#include "gui/textpopup.h"
+#include "gui/theme.h"
+#include "gui/viewport.h"
+
+#include "resources/emotedb.h"
+#include "resources/image.h"
+
+#include "utils/dtor.h"
+
+static const int MAX_ITEMS = 42;
+
+EmoteShortcutContainer::EmoteShortcutContainer():
+ ShortcutContainer(),
+ mEmoteClicked(false),
+ mEmoteMoved(0),
+ mEmotePopup(new TextPopup)
+{
+ addMouseListener(this);
+ addWidgetListener(this);
+
+ mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png");
+
+ if (mBackgroundImg)
+ mBackgroundImg->setAlpha(Client::getGuiAlpha());
+
+ // Setup emote sprites
+ for (int i = 0; i <= EmoteDB::getLast(); i++)
+ {
+ const EmoteSprite* sprite = EmoteDB::getSprite(i, true);
+ if (sprite && sprite->sprite)
+ mEmoteImg.push_back(sprite);
+ }
+
+// mMaxItems = EmoteDB::getLast() < MAX_ITEMS ? EmoteDB::getLast() : MAX_ITEMS;
+ mMaxItems = MAX_ITEMS;
+
+ if (mBackgroundImg)
+ {
+ mBoxHeight = mBackgroundImg->getHeight();
+ mBoxWidth = mBackgroundImg->getWidth();
+ }
+ else
+ {
+ mBoxHeight = 1;
+ mBoxWidth = 1;
+ }
+}
+
+EmoteShortcutContainer::~EmoteShortcutContainer()
+{
+ delete mEmotePopup;
+
+ if (mBackgroundImg)
+ mBackgroundImg->decRef();
+}
+
+void EmoteShortcutContainer::draw(gcn::Graphics *graphics)
+{
+ if (!emoteShortcut)
+ return;
+
+ mAlpha = Client::getGuiAlpha();
+ if (Client::getGuiAlpha() != mAlpha && mBackgroundImg)
+ mBackgroundImg->setAlpha(mAlpha);
+
+ Graphics *g = static_cast<Graphics*>(graphics);
+
+ graphics->setFont(getFont());
+
+ for (unsigned i = 0; i < mMaxItems; i++)
+ {
+ const int emoteX = (i % mGridWidth) * mBoxWidth;
+ const int emoteY = (i / mGridWidth) * mBoxHeight;
+
+ if (mBackgroundImg)
+ g->drawImage(mBackgroundImg, emoteX, emoteY);
+
+ // Draw emote keyboard shortcut.
+ std::string key = keyboard.getKeyValueString(
+ keyboard.KEY_EMOTE_1 + i);
+
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ g->drawText(key, emoteX + 2, emoteY + 2, gcn::Graphics::LEFT);
+
+/*
+ if (emoteShortcut->getEmote(i)
+ && static_cast<unsigned>(emoteShortcut->getEmote(i)) - 1
+ < mEmoteImg.size()
+ && mEmoteImg[emoteShortcut->getEmote(i) - 1])
+ {
+ mEmoteImg[emoteShortcut->getEmote(i) - 1]->draw(g, emoteX + 2,
+ emoteY + 10);
+ }
+*/
+
+ if (i < mEmoteImg.size() && mEmoteImg[i] && mEmoteImg[i]->sprite)
+ mEmoteImg[i]->sprite->draw(g, emoteX + 2, emoteY + 10);
+ }
+
+ if (mEmoteMoved && mEmoteMoved < (unsigned)mEmoteImg.size() + 1
+ && mEmoteMoved > 0)
+ {
+ // Draw the emote image being dragged by the cursor.
+ const EmoteSprite* sprite = mEmoteImg[mEmoteMoved - 1];
+ if (sprite && sprite->sprite)
+ {
+ const AnimatedSprite *spr = sprite->sprite;
+ const int tPosX = mCursorPosX - (spr->getWidth() / 2);
+ const int tPosY = mCursorPosY - (spr->getHeight() / 2);
+
+ spr->draw(g, tPosX, tPosY);
+ }
+ }
+}
+
+void EmoteShortcutContainer::mouseDragged(gcn::MouseEvent &event)
+{
+ if (!emoteShortcut)
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ if (!mEmoteMoved && mEmoteClicked)
+ {
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+// const unsigned char emoteId = emoteShortcut->getEmote(index);
+ const unsigned char emoteId = index + 1;
+
+ if (emoteId)
+ {
+ mEmoteMoved = emoteId;
+ emoteShortcut->removeEmote(index);
+ }
+ }
+ if (mEmoteMoved)
+ {
+ mCursorPosX = event.getX();
+ mCursorPosY = event.getY();
+ }
+ }
+}
+
+void EmoteShortcutContainer::mousePressed(gcn::MouseEvent &event)
+{
+ if (!emoteShortcut)
+ return;
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ // Stores the selected emote if there is one.
+ if (emoteShortcut->isEmoteSelected())
+ {
+ emoteShortcut->setEmote(index);
+ emoteShortcut->setEmoteSelected(0);
+ }
+ else if (emoteShortcut->getEmote(index))
+ {
+ mEmoteClicked = true;
+ }
+}
+
+void EmoteShortcutContainer::mouseReleased(gcn::MouseEvent &event)
+{
+ if (!emoteShortcut)
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (emoteShortcut->isEmoteSelected())
+ emoteShortcut->setEmoteSelected(0);
+
+ if (index == -1)
+ {
+ mEmoteMoved = 0;
+ return;
+ }
+
+ if (mEmoteMoved)
+ {
+ emoteShortcut->setEmotes(index, mEmoteMoved);
+ mEmoteMoved = 0;
+ }
+ else if (emoteShortcut->getEmote(index) && mEmoteClicked)
+ {
+ emoteShortcut->useEmote(index + 1);
+ }
+
+ if (mEmoteClicked)
+ mEmoteClicked = false;
+ }
+}
+
+void EmoteShortcutContainer::mouseMoved(gcn::MouseEvent &event)
+{
+ if (!emoteShortcut || !mEmotePopup)
+ return;
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ mEmotePopup->setVisible(false);
+
+ if ((unsigned)index < mEmoteImg.size() && mEmoteImg[index])
+ {
+ mEmotePopup->show(viewport->getMouseX(), viewport->getMouseY(),
+ mEmoteImg[index]->name);
+ }
+}
+
+void EmoteShortcutContainer::mouseExited(gcn::MouseEvent &event _UNUSED_)
+{
+ if (mEmotePopup)
+ mEmotePopup->setVisible(false);
+} \ No newline at end of file
diff --git a/src/gui/widgets/emoteshortcutcontainer.h b/src/gui/widgets/emoteshortcutcontainer.h
new file mode 100644
index 000000000..e841e6dfb
--- /dev/null
+++ b/src/gui/widgets/emoteshortcutcontainer.h
@@ -0,0 +1,84 @@
+/*
+ * Extended support for activating emotes
+ * Copyright (C) 2009 Aethyra Development Team
+ *
+ * This file is part of The Mana 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 EMOTESHORTCUTCONTAINER_H
+#define EMOTESHORTCUTCONTAINER_H
+
+#include "gui/widgets/shortcutcontainer.h"
+
+#include "resources/emotedb.h"
+
+#include <vector>
+
+class AnimatedSprite;
+class Image;
+class TextPopup;
+
+/**
+ * An emote shortcut container. Used to quickly use emoticons.
+ *
+ * \ingroup GUI
+ */
+class EmoteShortcutContainer : public ShortcutContainer
+{
+ public:
+ /**
+ * Constructor. Initializes the graphic.
+ */
+ EmoteShortcutContainer();
+
+ /**
+ * Destructor.
+ */
+ virtual ~EmoteShortcutContainer();
+
+ /**
+ * Draws the items.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Handles mouse when dragged.
+ */
+ void mouseDragged(gcn::MouseEvent &event);
+
+ /**
+ * Handles mouse when pressed.
+ */
+ void mousePressed(gcn::MouseEvent &event);
+
+ /**
+ * Handles mouse release.
+ */
+ void mouseReleased(gcn::MouseEvent &event);
+
+ void mouseMoved(gcn::MouseEvent &event);
+
+ void mouseExited(gcn::MouseEvent &event);
+
+ private:
+ std::vector<const EmoteSprite*> mEmoteImg;
+
+ bool mEmoteClicked;
+ unsigned char mEmoteMoved;
+ TextPopup *mEmotePopup;
+};
+
+#endif
diff --git a/src/gui/widgets/flowcontainer.cpp b/src/gui/widgets/flowcontainer.cpp
new file mode 100644
index 000000000..a93818abc
--- /dev/null
+++ b/src/gui/widgets/flowcontainer.cpp
@@ -0,0 +1,88 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 "flowcontainer.h"
+
+FlowContainer::FlowContainer(int boxWidth, int boxHeight):
+ mBoxWidth(boxWidth), mBoxHeight(boxHeight),
+ mGridWidth(1), mGridHeight(1)
+{
+ addWidgetListener(this);
+ if (!mBoxWidth)
+ mBoxWidth = 1;
+ if (!mBoxHeight)
+ mBoxHeight = 1;
+}
+
+void FlowContainer::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ if (getWidth() < mBoxWidth)
+ {
+ setWidth(mBoxWidth);
+ return;
+ }
+
+ int itemCount = static_cast<int>(mWidgets.size());
+
+ if (!mBoxWidth)
+ mGridWidth = getWidth();
+ else
+ mGridWidth = getWidth() / mBoxWidth;
+
+ if (mGridWidth < 1)
+ mGridWidth = 1;
+
+ mGridHeight = itemCount / mGridWidth;
+
+ if (itemCount % mGridWidth != 0 || mGridHeight < 1)
+ ++mGridHeight;
+
+ int height = mGridHeight * mBoxHeight;
+
+ if (getHeight() != height)
+ {
+ setHeight(height);
+ return;
+ }
+
+ int i = 0;
+ height = 0;
+ for (WidgetList::iterator it = mWidgets.begin();
+ it != mWidgets.end(); it++)
+ {
+ int x = i % mGridWidth * mBoxWidth;
+ (*it)->setPosition(x, height);
+
+ i++;
+
+ if (i % mGridWidth == 0)
+ height += mBoxHeight;
+ }
+}
+
+void FlowContainer::add(gcn::Widget *widget)
+{
+ if (!widget)
+ return;
+
+ Container::add(widget);
+ widget->setSize(mBoxWidth, mBoxHeight);
+ widgetResized(NULL);
+}
diff --git a/src/gui/widgets/flowcontainer.h b/src/gui/widgets/flowcontainer.h
new file mode 100644
index 000000000..f738be209
--- /dev/null
+++ b/src/gui/widgets/flowcontainer.h
@@ -0,0 +1,73 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 FLOWCONTAINER_H
+#define FLOWCONTAINER_H
+
+#include "container.h"
+
+#include <guichan/widgetlistener.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+/**
+ * A container that arranges its contents like words on a page.
+ *
+ * \ingroup GUI
+ */
+class FlowContainer : public Container,
+ public gcn::WidgetListener
+{
+ public:
+ /**
+ * Constructor. Initializes the shortcut container.
+ */
+ FlowContainer(int boxWidth, int boxHeight);
+
+ /**
+ * Destructor.
+ */
+ ~FlowContainer() {}
+
+ /**
+ * Invoked when a widget changes its size. This is used to determine
+ * the new height of the container.
+ */
+ void widgetResized(const gcn::Event &event);
+
+ int getBoxWidth() const
+ { return mBoxWidth; }
+
+ int getBoxHeight() const
+ { return mBoxHeight; }
+
+ void add(gcn::Widget *widget);
+
+ private:
+ int mBoxWidth;
+ int mBoxHeight;
+ int mGridWidth, mGridHeight;
+};
+
+#endif
diff --git a/src/gui/widgets/icon.cpp b/src/gui/widgets/icon.cpp
new file mode 100644
index 000000000..4e5902128
--- /dev/null
+++ b/src/gui/widgets/icon.cpp
@@ -0,0 +1,60 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/icon.h"
+
+#include "graphics.h"
+
+#include "resources/image.h"
+#include "resources/resourcemanager.h"
+
+Icon::Icon(const std::string &file)
+ : mImage(0)
+{
+ mImage = ResourceManager::getInstance()->getImage(file);
+ if (mImage)
+ setSize(mImage->getWidth(), mImage->getHeight());
+}
+
+Icon::Icon(Image *image)
+ : mImage(image)
+{
+ if (mImage)
+ setSize(mImage->getWidth(), mImage->getHeight());
+}
+
+void Icon::setImage(Image *image)
+{
+ mImage = image;
+ if (mImage)
+ setSize(mImage->getWidth(), mImage->getHeight());
+}
+
+void Icon::draw(gcn::Graphics *g)
+{
+ if (mImage)
+ {
+ Graphics *graphics = static_cast<Graphics*>(g);
+ const int x = (getWidth() - mImage->getWidth()) / 2;
+ const int y = (getHeight() - mImage->getHeight()) / 2;
+ graphics->drawImage(mImage, x, y);
+ }
+}
diff --git a/src/gui/widgets/icon.h b/src/gui/widgets/icon.h
new file mode 100644
index 000000000..27ed0db8e
--- /dev/null
+++ b/src/gui/widgets/icon.h
@@ -0,0 +1,66 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 ICON_H
+#define ICON_H
+
+#include <guichan/widget.hpp>
+
+class Image;
+
+/**
+ * An icon.
+ *
+ * \ingroup GUI
+ */
+class Icon : public gcn::Widget
+{
+ public:
+ /**
+ * Constructor.
+ */
+ Icon(const std::string &filename);
+
+ /**
+ * Constructor, uses an existing Image.
+ */
+ Icon(Image *image);
+
+ /**
+ * Gets the current Image.
+ */
+ Image *getImage() const { return mImage; }
+
+ /**
+ * Sets the image to display.
+ */
+ void setImage(Image *image);
+
+ /**
+ * Draws the Icon.
+ */
+ void draw(gcn::Graphics *g);
+
+ private:
+ Image *mImage;
+};
+
+#endif // ICON_H
diff --git a/src/gui/widgets/inttextfield.cpp b/src/gui/widgets/inttextfield.cpp
new file mode 100644
index 000000000..d0ffaebc5
--- /dev/null
+++ b/src/gui/widgets/inttextfield.cpp
@@ -0,0 +1,112 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/inttextfield.h"
+
+#include "gui/sdlinput.h"
+
+#include "utils/stringutils.h"
+
+IntTextField::IntTextField(int def, int min, int max,
+ bool enabled, int width):
+ TextField(toString(def)),
+ mDefault(def),
+ mValue(def)
+{
+ if (min != 0 || max != 0)
+ setRange(min, max);
+
+ setEnabled(enabled);
+ if (width != 0)
+ setWidth(width);
+}
+
+void IntTextField::keyPressed(gcn::KeyEvent &event)
+{
+ const gcn::Key &key = event.getKey();
+
+ if (key.getValue() == Key::BACKSPACE ||
+ key.getValue() == Key::DELETE)
+ {
+ setText(std::string());
+ event.consume();
+ }
+
+ if (!key.isNumber())
+ return;
+
+ TextField::keyPressed(event);
+
+ std::istringstream s(getText());
+ int i;
+ s >> i;
+ setValue(i);
+}
+
+void IntTextField::setRange(int min, int max)
+{
+ mMin = min;
+ mMax = max;
+
+ if (mValue < mMin)
+ mValue = mMin;
+ else if (mValue > mMax)
+ mValue = mMax;
+
+ if (mDefault < mMin)
+ mDefault = mMin;
+ else if (mDefault > mMax)
+ mDefault = mMax;
+}
+
+int IntTextField::getValue()
+{
+ return getText().empty() ? mMin : mValue;
+}
+
+void IntTextField::setValue(int i)
+{
+ if (i < mMin)
+ mValue = mMin;
+ else if (i > mMax)
+ mValue = mMax;
+ else
+ mValue = i;
+
+ const std::string valStr = toString(mValue);
+ setText(valStr);
+ setCaretPosition(static_cast<unsigned>(valStr.length()) + 1);
+}
+
+void IntTextField::setDefaultValue(int value)
+{
+ if (value < mMin)
+ mDefault = mMin;
+ else if (value > mMax)
+ mDefault = mMax;
+ else
+ mDefault = value;
+}
+
+void IntTextField::reset()
+{
+ setValue(mDefault);
+}
diff --git a/src/gui/widgets/inttextfield.h b/src/gui/widgets/inttextfield.h
new file mode 100644
index 000000000..69c8a74d7
--- /dev/null
+++ b/src/gui/widgets/inttextfield.h
@@ -0,0 +1,76 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 INTTEXTFIELD_H
+#define INTTEXTFIELD_H
+
+#include "textfield.h"
+
+/**
+ * TextBox which only accepts numbers as input.
+ */
+class IntTextField : public TextField
+{
+ public:
+ /**
+ * Constructor, sets default value.
+ */
+ IntTextField(int def = 0, int min = 0, int max = 0,
+ bool enabled = true, int width = 0);
+
+ /**
+ * Sets the minimum and maximum values of the text box.
+ */
+ void setRange(int minimum, int maximum);
+
+ /**
+ * Returns the value in the text box.
+ */
+ int getValue();
+
+ /**
+ * Reset the field to the default value.
+ */
+ void reset();
+
+ /**
+ * Set the value of the text box to the specified value.
+ */
+ void setValue(int value);
+
+ /**
+ * Set the default value of the text box to the specified value.
+ */
+ void setDefaultValue(int value);
+
+ /**
+ * Responds to key presses.
+ */
+ void keyPressed(gcn::KeyEvent &event);
+
+ private:
+ int mMin; /**< Minimum value */
+ int mMax; /**< Maximum value */
+ int mDefault; /**< Default value */
+ int mValue; /**< Current value */
+};
+
+#endif
diff --git a/src/gui/widgets/itemcontainer.cpp b/src/gui/widgets/itemcontainer.cpp
new file mode 100644
index 000000000..8b5b1914a
--- /dev/null
+++ b/src/gui/widgets/itemcontainer.cpp
@@ -0,0 +1,475 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/itemcontainer.h"
+
+#include "graphics.h"
+#include "inventory.h"
+#include "item.h"
+#include "itemshortcut.h"
+#include "dropshortcut.h"
+#include "log.h"
+
+#include "gui/chat.h"
+#include "gui/itempopup.h"
+#include "gui/outfitwindow.h"
+#include "gui/palette.h"
+#include "gui/shopwindow.h"
+#include "gui/shortcutwindow.h"
+#include "gui/sdlinput.h"
+#include "gui/theme.h"
+#include "gui/viewport.h"
+
+#include "net/net.h"
+#include "net/inventoryhandler.h"
+
+#include "resources/image.h"
+#include "resources/iteminfo.h"
+
+#include "utils/stringutils.h"
+
+#include <guichan/mouseinput.hpp>
+#include <guichan/selectionlistener.hpp>
+
+// TODO: Add support for adding items to the item shortcut window (global
+// itemShortcut).
+
+static const int BOX_WIDTH = 35;
+static const int BOX_HEIGHT = 43;
+
+ItemContainer::ItemContainer(Inventory *inventory, bool forceQuantity):
+ mInventory(inventory),
+ mGridColumns(1),
+ mGridRows(1),
+ mSelectedIndex(-1),
+ mHighlightedIndex(-1),
+ mLastUsedSlot(-1),
+ mSelectionStatus(SEL_NONE),
+ mForceQuantity(forceQuantity),
+ mSwapItems(false),
+ mDescItems(false)
+{
+ mItemPopup = new ItemPopup;
+ setFocusable(true);
+
+ mSelImg = Theme::getImageFromTheme("selection.png");
+ if (!mSelImg)
+ logger->log1("Error: Unable to load selection.png");
+
+ addKeyListener(this);
+ addMouseListener(this);
+ addWidgetListener(this);
+}
+
+ItemContainer::~ItemContainer()
+{
+ if (mSelImg)
+ {
+ mSelImg->decRef();
+ mSelImg = 0;
+ }
+ delete mItemPopup;
+ mItemPopup = 0;
+}
+
+void ItemContainer::logic()
+{
+ gcn::Widget::logic();
+
+ if (!mInventory)
+ return;
+
+ const int lastUsedSlot = mInventory->getLastUsedSlot();
+
+ if (lastUsedSlot != mLastUsedSlot)
+ {
+ mLastUsedSlot = lastUsedSlot;
+ adjustHeight();
+ }
+}
+
+void ItemContainer::draw(gcn::Graphics *graphics)
+{
+ if (!mInventory)
+ return;
+
+ Graphics *g = static_cast<Graphics*>(graphics);
+
+ g->setFont(getFont());
+
+ for (int i = 0; i < mGridColumns; i++)
+ {
+ for (int j = 0; j < mGridRows; j++)
+ {
+ int itemX = i * BOX_WIDTH;
+ int itemY = j * BOX_HEIGHT;
+ int itemIndex = (j * mGridColumns) + i;
+ Item *item = mInventory->getItem(itemIndex);
+
+ if (!item || item->getId() == 0)
+ continue;
+
+ Image *image = item->getImage();
+ if (image)
+ {
+ if (itemIndex == mSelectedIndex)
+ {
+ if (mSelectionStatus == SEL_DRAGGING)
+ {
+ // Reposition the coords to that of the cursor.
+ itemX = mDragPosX - (BOX_WIDTH / 2);
+ itemY = mDragPosY - (BOX_HEIGHT / 2);
+ }
+ else
+ {
+ // Draw selection border image.
+ if (mSelImg)
+ g->drawImage(mSelImg, itemX, itemY);
+ }
+ }
+ image->setAlpha(1.0f); // ensure the image if fully drawn...
+ g->drawImage(image, itemX, itemY);
+ }
+ // Draw item caption
+ std::string caption;
+ if (item->getQuantity() > 1 || mForceQuantity)
+ caption = toString(item->getQuantity());
+ else if (item->isEquipped())
+ caption = "Eq.";
+
+ if (item->isEquipped())
+ g->setColor(Theme::getThemeColor(Theme::ITEM_EQUIPPED));
+ else
+ g->setColor(gcn::Color(0, 0, 0));
+
+ g->drawText(caption, itemX + BOX_WIDTH / 2,
+ itemY + BOX_HEIGHT - 14, gcn::Graphics::CENTER);
+ }
+ }
+
+ // Draw an orange box around the selected item
+ if (isFocused() && mHighlightedIndex != -1 && mGridColumns)
+ {
+ const int itemX = (mHighlightedIndex % mGridColumns) * BOX_WIDTH;
+ const int itemY = (mHighlightedIndex / mGridColumns) * BOX_HEIGHT;
+ g->setColor(gcn::Color(255, 128, 0));
+ g->drawRectangle(gcn::Rectangle(itemX, itemY, BOX_WIDTH, BOX_HEIGHT));
+ }
+}
+
+void ItemContainer::selectNone()
+{
+ setSelectedIndex(-1);
+ mSelectionStatus = SEL_NONE;
+ if (outfitWindow)
+ outfitWindow->setItemSelected(-1);
+ if (shopWindow)
+ shopWindow->setItemSelected(-1);
+}
+
+void ItemContainer::setSelectedIndex(int newIndex)
+{
+ if (mSelectedIndex != newIndex)
+ {
+ mSelectedIndex = newIndex;
+ distributeValueChangedEvent();
+ }
+}
+
+Item *ItemContainer::getSelectedItem() const
+{
+ if (mInventory)
+ return mInventory->getItem(mSelectedIndex);
+ else
+ return 0;
+}
+
+void ItemContainer::distributeValueChangedEvent()
+{
+ SelectionListenerIterator i, i_end;
+
+ for (i = mSelectionListeners.begin(), i_end = mSelectionListeners.end();
+ i != i_end; ++i)
+ {
+ if (*i)
+ {
+ gcn::SelectionEvent event(this);
+ (*i)->valueChanged(event);
+ }
+ }
+}
+
+void ItemContainer::keyPressed(gcn::KeyEvent &event _UNUSED_)
+{
+ /*switch (event.getKey().getValue())
+ {
+ case Key::LEFT:
+ moveHighlight(Left);
+ break;
+ case Key::RIGHT:
+ moveHighlight(Right);
+ break;
+ case Key::UP:
+ moveHighlight(Up);
+ break;
+ case Key::DOWN:
+ moveHighlight(Down);
+ break;
+ case Key::SPACE:
+ keyAction();
+ break;
+ case Key::LEFT_ALT:
+ case Key::RIGHT_ALT:
+ mSwapItems = true;
+ break;
+ case Key::RIGHT_CONTROL:
+ mDescItems = true;
+ break;
+ }*/
+}
+
+void ItemContainer::keyReleased(gcn::KeyEvent &event _UNUSED_)
+{
+ /*switch (event.getKey().getValue())
+ {
+ case Key::LEFT_ALT:
+ case Key::RIGHT_ALT:
+ mSwapItems = false;
+ break;
+ case Key::RIGHT_CONTROL:
+ mDescItems = false;
+ break;
+ }*/
+}
+
+void ItemContainer::mousePressed(gcn::MouseEvent &event)
+{
+ if (!mInventory)
+ return;
+
+ const int button = event.getButton();
+ if (button == gcn::MouseEvent::LEFT || button == gcn::MouseEvent::RIGHT)
+ {
+ const int index = getSlotIndex(event.getX(), event.getY());
+ if (index == Inventory::NO_SLOT_INDEX)
+ return;
+
+ Item *item = mInventory->getItem(index);
+
+ // put item name into chat window
+ if (item && mDescItems && chatWindow)
+ chatWindow->addItemText(item->getInfo().getName());
+
+ if (mSelectedIndex == index)
+ {
+ mSelectionStatus = SEL_DESELECTING;
+ }
+ else if (item && item->getId())
+ {
+ setSelectedIndex(index);
+ mSelectionStatus = SEL_SELECTING;
+
+ int num = itemShortcutWindow->getTabIndex();
+ if (num >= 0 && num < SHORTCUT_TABS)
+ {
+ if (itemShortcut[num])
+ itemShortcut[num]->setItemSelected(item->getId());
+ }
+ if (dropShortcut)
+ dropShortcut->setItemSelected(item->getId());
+ if (item->isEquipment() && outfitWindow)
+ outfitWindow->setItemSelected(item->getId());
+ if (shopWindow)
+ shopWindow->setItemSelected(item->getId());
+ }
+ else
+ {
+ selectNone();
+ }
+ }
+}
+
+void ItemContainer::mouseDragged(gcn::MouseEvent &event)
+{
+ if (mSelectionStatus != SEL_NONE)
+ {
+ mSelectionStatus = SEL_DRAGGING;
+ mDragPosX = event.getX();
+ mDragPosY = event.getY();
+ }
+}
+
+void ItemContainer::mouseReleased(gcn::MouseEvent &event)
+{
+ switch (mSelectionStatus)
+ {
+ case SEL_SELECTING:
+ mSelectionStatus = SEL_SELECTED;
+ return;
+ case SEL_DESELECTING:
+ selectNone();
+ return;
+ case SEL_DRAGGING:
+ mSelectionStatus = SEL_SELECTED;
+ break;
+ default:
+ return;
+ };
+
+ int index = getSlotIndex(event.getX(), event.getY());
+ if (index == Inventory::NO_SLOT_INDEX)
+ return;
+ if (index == mSelectedIndex || mSelectedIndex == -1)
+ return;
+ Net::getInventoryHandler()->moveItem(mSelectedIndex, index);
+ selectNone();
+}
+
+
+// Show ItemTooltip
+void ItemContainer::mouseMoved(gcn::MouseEvent &event)
+{
+ if (!mInventory)
+ return;
+
+ Item *item = mInventory->getItem(getSlotIndex(event.getX(), event.getY()));
+
+ if (item && viewport)
+ {
+ mItemPopup->setItem(item);
+ mItemPopup->position(viewport->getMouseX(), viewport->getMouseY());
+ }
+ else
+ {
+ mItemPopup->setVisible(false);
+ }
+}
+
+// Hide ItemTooltip
+void ItemContainer::mouseExited(gcn::MouseEvent &event _UNUSED_)
+{
+ mItemPopup->setVisible(false);
+}
+
+void ItemContainer::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ mGridColumns = std::max(1, getWidth() / BOX_WIDTH);
+ adjustHeight();
+}
+
+void ItemContainer::adjustHeight()
+{
+ if (!mGridColumns)
+ return;
+
+ mGridRows = (mLastUsedSlot + 1) / mGridColumns;
+ if (mGridRows == 0 || (mLastUsedSlot + 1) % mGridColumns > 0)
+ ++mGridRows;
+
+ setHeight(mGridRows * BOX_HEIGHT);
+}
+
+int ItemContainer::getSlotIndex(int x, int y) const
+{
+ if (x < getWidth() && y < getHeight())
+ return (y / BOX_HEIGHT) * mGridColumns + (x / BOX_WIDTH);
+
+ return Inventory::NO_SLOT_INDEX;
+}
+
+void ItemContainer::keyAction()
+{
+ // If there is no highlight then return.
+ if (mHighlightedIndex == -1)
+ return;
+
+ // If the highlight is on the selected item, then deselect it.
+ if (mHighlightedIndex == mSelectedIndex)
+ {
+ selectNone();
+ }
+ // Check and swap items if necessary.
+ else if (mSwapItems && mSelectedIndex != -1 && mHighlightedIndex != -1)
+ {
+ Net::getInventoryHandler()->moveItem(
+ mSelectedIndex, mHighlightedIndex);
+ setSelectedIndex(mHighlightedIndex);
+ }
+ // If the highlight is on an item then select it.
+ else if (mHighlightedIndex != -1)
+ {
+ setSelectedIndex(mHighlightedIndex);
+ mSelectionStatus = SEL_SELECTED;
+ }
+ // If the highlight is on a blank space then move it.
+ else if (mSelectedIndex != -1)
+ {
+ Net::getInventoryHandler()->moveItem(
+ mSelectedIndex, mHighlightedIndex);
+ selectNone();
+ }
+}
+
+void ItemContainer::moveHighlight(Direction direction)
+{
+ if (mHighlightedIndex == -1)
+ {
+ if (mSelectedIndex != -1)
+ mHighlightedIndex = mSelectedIndex;
+ else
+ mHighlightedIndex = 0;
+ return;
+ }
+
+ switch (direction)
+ {
+ case Left:
+ if (mHighlightedIndex % mGridColumns == 0)
+ mHighlightedIndex += mGridColumns;
+ mHighlightedIndex--;
+ break;
+ case Right:
+ if ((mHighlightedIndex % mGridColumns) ==
+ (mGridColumns - 1))
+ {
+ mHighlightedIndex -= mGridColumns;
+ }
+ mHighlightedIndex++;
+ break;
+ case Up:
+ if (mHighlightedIndex / mGridColumns == 0)
+ mHighlightedIndex += (mGridColumns * mGridRows);
+ mHighlightedIndex -= mGridColumns;
+ break;
+ case Down:
+ if ((mHighlightedIndex / mGridColumns) ==
+ (mGridRows - 1))
+ {
+ mHighlightedIndex -= (mGridColumns * mGridRows);
+ }
+ mHighlightedIndex += mGridColumns;
+ break;
+ default:
+ logger->log("warning moveHighlight unknown direction:"
+ + toString(static_cast<unsigned>(direction)));
+ break;
+ }
+}
diff --git a/src/gui/widgets/itemcontainer.h b/src/gui/widgets/itemcontainer.h
new file mode 100644
index 000000000..8aaa236b4
--- /dev/null
+++ b/src/gui/widgets/itemcontainer.h
@@ -0,0 +1,195 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 ITEMCONTAINER_H
+#define ITEMCONTAINER_H
+
+#include <guichan/keylistener.hpp>
+#include <guichan/mouselistener.hpp>
+#include <guichan/widget.hpp>
+#include <guichan/widgetlistener.hpp>
+
+#include <list>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+class Inventory;
+class Item;
+class ItemPopup;
+
+namespace gcn
+{
+ class SelectionListener;
+}
+
+/**
+ * An item container. Used to show items in inventory and trade dialog.
+ *
+ * \ingroup GUI
+ */
+class ItemContainer : public gcn::Widget,
+ public gcn::KeyListener,
+ public gcn::MouseListener,
+ public gcn::WidgetListener
+{
+ public:
+ /**
+ * Constructor. Initializes the graphic.
+ *
+ * @param inventory
+ * @param gridColumns Amount of columns in grid.
+ * @param gridRows Amount of rows in grid.
+ * @param offset Index offset
+ */
+ ItemContainer(Inventory *inventory, bool forceQuantity = false);
+
+ /**
+ * Destructor.
+ */
+ virtual ~ItemContainer();
+
+ /**
+ * Necessary for checking how full the inventory is.
+ */
+ void logic();
+
+ /**
+ * Draws the items.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ // KeyListener
+ void keyPressed(gcn::KeyEvent &event);
+ void keyReleased(gcn::KeyEvent &event);
+
+ // MouseListener
+ void mousePressed(gcn::MouseEvent &event);
+ void mouseDragged(gcn::MouseEvent &event);
+ void mouseReleased(gcn::MouseEvent &event);
+ void mouseMoved(gcn::MouseEvent &event);
+ void mouseExited(gcn::MouseEvent &event);
+
+ // WidgetListener
+ void widgetResized(const gcn::Event &event);
+
+ /**
+ * Returns the selected item.
+ */
+ Item *getSelectedItem() const;
+
+ /**
+ * Sets selected item to NULL.
+ */
+ void selectNone();
+
+ /**
+ * Adds a listener to the list that's notified each time a change to
+ * the selection occurs.
+ */
+ void addSelectionListener(gcn::SelectionListener *listener)
+ { mSelectionListeners.push_back(listener); }
+
+ /**
+ * Removes a listener from the list that's notified each time a change
+ * to the selection occurs.
+ */
+ void removeSelectionListener(gcn::SelectionListener *listener)
+ { mSelectionListeners.remove(listener); }
+
+ private:
+ enum Direction
+ {
+ Left = 0,
+ Right,
+ Up,
+ Down
+ };
+
+ enum SelectionState
+ {
+ SEL_NONE = 0,
+ SEL_SELECTED,
+ SEL_SELECTING,
+ SEL_DESELECTING,
+ SEL_DRAGGING
+ };
+
+ /**
+ * Execute all the functionality associated with the action key.
+ */
+ void keyAction();
+
+ /**
+ * Moves the highlight in the direction specified.
+ *
+ * @param direction The move direction of the highlighter.
+ */
+ void moveHighlight(Direction direction);
+
+ /**
+ * Sets the currently selected item.
+ */
+ void setSelectedIndex(int index);
+
+ /**
+ * Determine and set the height of the container.
+ */
+ void adjustHeight();
+
+ /**
+ * Sends out selection events to the list of selection listeners.
+ */
+ void distributeValueChangedEvent();
+
+ /**
+ * Gets the slot index based on the cursor position.
+ *
+ * @param x The X coordinate position.
+ * @param y The Y coordinate position.
+ * @return The slot index on success, -1 on failure.
+ */
+ int getSlotIndex(int x, int y) const;
+
+ Inventory *mInventory;
+ int mGridColumns, mGridRows;
+ Image *mSelImg;
+ int mSelectedIndex, mHighlightedIndex;
+ int mLastUsedSlot;
+ SelectionState mSelectionStatus;
+ bool mForceQuantity;
+ bool mSwapItems;
+ bool mDescItems;
+ int mDragPosX, mDragPosY;
+
+ ItemPopup *mItemPopup;
+
+ typedef std::list<gcn::SelectionListener*> SelectionListenerList;
+ typedef SelectionListenerList::iterator SelectionListenerIterator;
+
+ SelectionListenerList mSelectionListeners;
+};
+
+#endif
diff --git a/src/gui/widgets/itemlinkhandler.cpp b/src/gui/widgets/itemlinkhandler.cpp
new file mode 100644
index 000000000..7c0a65bcb
--- /dev/null
+++ b/src/gui/widgets/itemlinkhandler.cpp
@@ -0,0 +1,66 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 <sstream>
+#include <string>
+
+#include "item.h"
+
+#include "gui/itempopup.h"
+#include "gui/viewport.h"
+
+#include "gui/widgets/itemlinkhandler.h"
+
+#include "resources/itemdb.h"
+
+ItemLinkHandler::ItemLinkHandler()
+{
+ mItemPopup = new ItemPopup;
+}
+
+ItemLinkHandler::~ItemLinkHandler()
+{
+ delete mItemPopup;
+ mItemPopup = 0;
+}
+
+void ItemLinkHandler::handleLink(const std::string &link,
+ gcn::MouseEvent *event _UNUSED_)
+{
+ if (!mItemPopup)
+ return;
+
+ int id = 0;
+ std::stringstream stream;
+ stream << link;
+ stream >> id;
+
+ if (id > 0)
+ {
+ const ItemInfo &itemInfo = ItemDB::get(id);
+ mItemPopup->setItem(itemInfo, true);
+
+ if (mItemPopup->isVisible())
+ mItemPopup->setVisible(false);
+ else if (viewport)
+ mItemPopup->position(viewport->getMouseX(), viewport->getMouseY());
+ }
+}
diff --git a/src/gui/widgets/itemlinkhandler.h b/src/gui/widgets/itemlinkhandler.h
new file mode 100644
index 000000000..57e135fed
--- /dev/null
+++ b/src/gui/widgets/itemlinkhandler.h
@@ -0,0 +1,47 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 ITEM_LINK_HANDLER_H
+#define ITEM_LINK_HANDLER_H
+
+#include "gui/widgets/linkhandler.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class ItemPopup;
+
+class ItemLinkHandler : public LinkHandler
+{
+ public:
+ ItemLinkHandler();
+ ~ItemLinkHandler();
+ void handleLink(const std::string &link,
+ gcn::MouseEvent *event _UNUSED_);
+
+ private:
+ ItemPopup *mItemPopup;
+};
+
+#endif
diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp
new file mode 100644
index 000000000..2ab4a7e19
--- /dev/null
+++ b/src/gui/widgets/itemshortcutcontainer.cpp
@@ -0,0 +1,375 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2007-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/itemshortcutcontainer.h"
+
+#include "configuration.h"
+#include "graphics.h"
+#include "inventory.h"
+#include "item.h"
+#include "itemshortcut.h"
+#include "spellshortcut.h"
+#include "keyboardconfig.h"
+#include "localplayer.h"
+#include "playerinfo.h"
+#include "spellmanager.h"
+#include "textcommand.h"
+
+#include "gui/inventorywindow.h"
+#include "gui/itempopup.h"
+#include "gui/palette.h"
+#include "gui/spellpopup.h"
+#include "gui/theme.h"
+#include "gui/viewport.h"
+
+#include "resources/image.h"
+#include "resources/iteminfo.h"
+
+#include "utils/stringutils.h"
+
+ItemShortcutContainer::ItemShortcutContainer(unsigned number):
+ ShortcutContainer(),
+ mItemClicked(false),
+ mItemMoved(NULL),
+ mNumber(number)
+{
+ addMouseListener(this);
+ addWidgetListener(this);
+
+ mItemPopup = new ItemPopup;
+ mSpellPopup = new SpellPopup;
+
+ mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png");
+ if (itemShortcut[mNumber])
+ mMaxItems = itemShortcut[mNumber]->getItemCount();
+ else
+ mMaxItems = 0;
+
+ if (mBackgroundImg)
+ {
+ mBackgroundImg->setAlpha(Client::getGuiAlpha());
+ mBoxHeight = mBackgroundImg->getHeight();
+ mBoxWidth = mBackgroundImg->getWidth();
+ }
+ else
+ {
+ mBoxHeight = 1;
+ mBoxWidth = 1;
+ }
+}
+
+ItemShortcutContainer::~ItemShortcutContainer()
+{
+ if (mBackgroundImg)
+ {
+ mBackgroundImg->decRef();
+ mBackgroundImg = 0;
+ }
+ delete mItemPopup;
+ mItemPopup = 0;
+ delete mSpellPopup;
+ mSpellPopup = 0;
+}
+
+void ItemShortcutContainer::draw(gcn::Graphics *graphics)
+{
+ if (!itemShortcut[mNumber])
+ return;
+
+ mAlpha = Client::getGuiAlpha();
+ if (Client::getGuiAlpha() != mAlpha)
+ {
+ if (mBackgroundImg)
+ mBackgroundImg->setAlpha(mAlpha);
+ }
+
+ Graphics *g = static_cast<Graphics*>(graphics);
+
+ graphics->setFont(getFont());
+
+ for (unsigned i = 0; i < mMaxItems; i++)
+ {
+ const int itemX = (i % mGridWidth) * mBoxWidth;
+ const int itemY = (i / mGridWidth) * mBoxHeight;
+
+ if (mBackgroundImg)
+ g->drawImage(mBackgroundImg, itemX, itemY);
+
+ // Draw item keyboard shortcut.
+ std::string key = keyboard.getKeyValueString(
+ keyboard.KEY_SHORTCUT_1 + i);
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+
+ g->drawText(key, itemX + 2, itemY + 2, gcn::Graphics::LEFT);
+
+ int itemId = itemShortcut[mNumber]->getItem(i);
+
+ if (itemId < 0)
+ continue;
+
+ // this is item
+ if (itemId < SPELL_MIN_ID)
+ {
+ if (!PlayerInfo::getInventory())
+ continue;
+
+ Item *item = PlayerInfo::getInventory()->findItem(itemId);
+
+ if (item)
+ {
+ // Draw item icon.
+ Image* image = item->getImage();
+
+ if (image)
+ {
+ std::string caption;
+ if (item->getQuantity() > 1)
+ caption = toString(item->getQuantity());
+ else if (item->isEquipped())
+ caption = "Eq.";
+
+ image->setAlpha(1.0f);
+ g->drawImage(image, itemX, itemY);
+ if (item->isEquipped())
+ {
+ g->setColor(Theme::getThemeColor(
+ Theme::ITEM_EQUIPPED));
+ }
+ else
+ {
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ }
+ g->drawText(caption, itemX + mBoxWidth / 2,
+ itemY + mBoxHeight - 14, gcn::Graphics::CENTER);
+ }
+ }
+ }
+ else if (spellManager) // this is magic shortcut
+ {
+ TextCommand *spell = spellManager->getSpellByItem(itemId);
+ if (spell)
+ {
+ if (!spell->isEmpty())
+ {
+ Image* image = spell->getImage();
+
+ if (image)
+ {
+ image->setAlpha(1.0f);
+ g->drawImage(image, itemX, itemY);
+ }
+ }
+
+ g->drawText(spell->getSymbol(), itemX + 2,
+ itemY + mBoxHeight / 2, gcn::Graphics::LEFT);
+ }
+ }
+
+ }
+
+ if (mItemMoved)
+ {
+ // Draw the item image being dragged by the cursor.
+ Image* image = mItemMoved->getImage();
+ if (image)
+ {
+ const int tPosX = mCursorPosX - (image->getWidth() / 2);
+ const int tPosY = mCursorPosY - (image->getHeight() / 2);
+
+ g->drawImage(image, tPosX, tPosY);
+ g->drawText(toString(mItemMoved->getQuantity()),
+ tPosX + mBoxWidth / 2, tPosY + mBoxHeight - 14,
+ gcn::Graphics::CENTER);
+ }
+ }
+}
+
+void ItemShortcutContainer::mouseDragged(gcn::MouseEvent &event)
+{
+ if (!itemShortcut[mNumber])
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ if (!mItemMoved && mItemClicked)
+ {
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ const int itemId = itemShortcut[mNumber]->getItem(index);
+
+ if (itemId < 0)
+ return;
+
+ if (itemId < SPELL_MIN_ID)
+ {
+ if (!PlayerInfo::getInventory())
+ return;
+
+ Item *item = PlayerInfo::getInventory()->findItem(itemId);
+
+ if (item)
+ {
+ mItemMoved = item;
+ itemShortcut[mNumber]->removeItem(index);
+ }
+ }
+ else if (spellManager)
+ {
+ TextCommand *spell = spellManager->getSpellByItem(itemId);
+ if (spell)
+ itemShortcut[mNumber]->removeItem(index);
+ }
+ }
+ if (mItemMoved)
+ {
+ mCursorPosX = event.getX();
+ mCursorPosY = event.getY();
+ }
+ }
+}
+
+void ItemShortcutContainer::mousePressed(gcn::MouseEvent &event)
+{
+ if (!itemShortcut[mNumber])
+ return;
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ // Stores the selected item if theirs one.
+ if (itemShortcut[mNumber]->isItemSelected() &&
+ (inventoryWindow && (inventoryWindow->isVisible() ||
+ itemShortcut[mNumber]->getSelectedItem() >= SPELL_MIN_ID)))
+ {
+ itemShortcut[mNumber]->setItem(index);
+ itemShortcut[mNumber]->setItemSelected(-1);
+ if (spellShortcut)
+ spellShortcut->setItemSelected(-1);
+ }
+ else if (itemShortcut[mNumber]->getItem(index))
+ {
+ mItemClicked = true;
+ }
+ }
+ else if (event.getButton() == gcn::MouseEvent::RIGHT)
+ {
+// Item *item = PlayerInfo::getInventory()->findItem(id);
+
+ if (viewport && itemShortcut[mNumber])
+ viewport->showItemPopup(itemShortcut[mNumber]->getItem(index));
+ }
+}
+
+void ItemShortcutContainer::mouseReleased(gcn::MouseEvent &event)
+{
+ if (!itemShortcut[mNumber])
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ if (itemShortcut[mNumber]->isItemSelected())
+ itemShortcut[mNumber]->setItemSelected(-1);
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+ if (index == -1)
+ {
+ mItemMoved = NULL;
+ return;
+ }
+ if (mItemMoved)
+ {
+ itemShortcut[mNumber]->setItems(index, mItemMoved->getId());
+ mItemMoved = NULL;
+ }
+ else if (itemShortcut[mNumber]->getItem(index) && mItemClicked)
+ {
+ itemShortcut[mNumber]->useItem(index);
+ }
+
+ if (mItemClicked)
+ mItemClicked = false;
+ }
+}
+
+// Show ItemTooltip
+void ItemShortcutContainer::mouseMoved(gcn::MouseEvent &event)
+{
+ if (!itemShortcut[mNumber])
+ return;
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ const int itemId = itemShortcut[mNumber]->getItem(index);
+
+ if (itemId < 0)
+ return;
+
+ if (itemId < SPELL_MIN_ID)
+ {
+ mSpellPopup->setVisible(false);
+
+ if (!PlayerInfo::getInventory())
+ return;
+
+ Item *item = PlayerInfo::getInventory()->findItem(itemId);
+
+ if (item && viewport)
+ {
+ mItemPopup->setItem(item);
+ mItemPopup->position(viewport->getMouseX(), viewport->getMouseY());
+ }
+ else
+ {
+ mItemPopup->setVisible(false);
+ }
+ }
+ else if (spellManager)
+ {
+ mItemPopup->setVisible(false);
+ TextCommand *spell = spellManager->getSpellByItem(itemId);
+ if (spell && viewport)
+ {
+ mSpellPopup->setItem(spell);
+ mSpellPopup->view(viewport->getMouseX(), viewport->getMouseY());
+ }
+ else
+ {
+ mSpellPopup->setVisible(false);
+ }
+ }
+}
+
+// Hide ItemTooltip
+void ItemShortcutContainer::mouseExited(gcn::MouseEvent &event _UNUSED_)
+{
+ mItemPopup->setVisible(false);
+ mSpellPopup->setVisible(false);
+}
diff --git a/src/gui/widgets/itemshortcutcontainer.h b/src/gui/widgets/itemshortcutcontainer.h
new file mode 100644
index 000000000..0f7067e38
--- /dev/null
+++ b/src/gui/widgets/itemshortcutcontainer.h
@@ -0,0 +1,93 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2007-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 ITEMSHORTCUTCONTAINER_H
+#define ITEMSHORTCUTCONTAINER_H
+
+#include "spellmanager.h"
+
+#include "gui/widgets/shortcutcontainer.h"
+
+#include <guichan/mouselistener.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+class Item;
+class ItemPopup;
+class SpellPopup;
+
+/**
+ * An item shortcut container. Used to quickly use items.
+ *
+ * \ingroup GUI
+ */
+class ItemShortcutContainer : public ShortcutContainer
+{
+ public:
+ /**
+ * Constructor. Initializes the graphic.
+ */
+ ItemShortcutContainer(unsigned number);
+
+ /**
+ * Destructor.
+ */
+ virtual ~ItemShortcutContainer();
+
+ /**
+ * Draws the items.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Handles mouse when dragged.
+ */
+ void mouseDragged(gcn::MouseEvent &event);
+
+ /**
+ * Handles mouse when pressed.
+ */
+ void mousePressed(gcn::MouseEvent &event);
+
+ /**
+ * Handles mouse release.
+ */
+ void mouseReleased(gcn::MouseEvent &event);
+
+ private:
+ void mouseExited(gcn::MouseEvent &event);
+ void mouseMoved(gcn::MouseEvent &event);
+
+ bool mItemClicked;
+ Item *mItemMoved;
+ unsigned mNumber;
+
+ ItemPopup *mItemPopup;
+ SpellPopup *mSpellPopup;
+};
+
+//extern SpellManager *spellManager;
+#endif
diff --git a/src/gui/widgets/label.cpp b/src/gui/widgets/label.cpp
new file mode 100644
index 000000000..e9b4cb0f6
--- /dev/null
+++ b/src/gui/widgets/label.cpp
@@ -0,0 +1,38 @@
+/*
+ * The Mana Client
+ * Copyright (c) 2009 Aethyra Development Team
+ *
+ * This file is part of The Mana 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/widgets/label.h"
+
+#include "gui/theme.h"
+
+Label::Label()
+{
+}
+
+Label::Label(const std::string &caption) :
+ gcn::Label(caption)
+{
+ setForegroundColor(Theme::getThemeColor(Theme::TEXT));
+}
+
+void Label::draw(gcn::Graphics *graphics)
+{
+ gcn::Label::draw(static_cast<gcn::Graphics*>(graphics));
+}
diff --git a/src/gui/widgets/label.h b/src/gui/widgets/label.h
new file mode 100644
index 000000000..2dfa254c4
--- /dev/null
+++ b/src/gui/widgets/label.h
@@ -0,0 +1,52 @@
+/*
+ * The Mana Client
+ * Copyright (c) 2009 Aethyra Development Team
+ *
+ * This file is part of The Mana 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 LABEL_H
+#define LABEL_H
+
+#include <guichan/widgets/label.hpp>
+
+/**
+ * Label widget. Same as the Guichan label but modified to use the palette
+ * system.
+ *
+ * \ingroup GUI
+ */
+class Label : public gcn::Label
+{
+ public:
+ /**
+ * Constructor.
+ */
+ Label();
+
+ /**
+ * Constructor. This version of the constructor sets the label with an
+ * inintialization string.
+ */
+ Label(const std::string &caption);
+
+ /**
+ * Draws the label.
+ */
+ void draw(gcn::Graphics *graphics);
+};
+
+#endif
diff --git a/src/gui/widgets/layout.cpp b/src/gui/widgets/layout.cpp
new file mode 100644
index 000000000..38c6bb471
--- /dev/null
+++ b/src/gui/widgets/layout.cpp
@@ -0,0 +1,362 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2007-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/layout.h"
+
+#include "log.h"
+
+#include <cassert>
+
+ContainerPlacer ContainerPlacer::at(int x, int y)
+{
+ return ContainerPlacer(mContainer, &mCell->at(x, y));
+}
+
+LayoutCell &ContainerPlacer::operator()
+ (int x, int y, gcn::Widget *wg, int w, int h)
+{
+ mContainer->add(wg);
+ return mCell->place(wg, x, y, w, h);
+}
+
+LayoutCell::~LayoutCell()
+{
+ if (mType == ARRAY)
+ {
+ delete mArray;
+ mArray = 0;
+ }
+}
+
+LayoutArray &LayoutCell::getArray()
+{
+ assert(mType != WIDGET);
+ if (mType == ARRAY)
+ return *mArray;
+
+ mArray = new LayoutArray;
+ mType = ARRAY;
+ mExtent[0] = 1;
+ mExtent[1] = 1;
+ mPadding = 0;
+ mAlign[0] = FILL;
+ mAlign[1] = FILL;
+ return *mArray;
+}
+
+void LayoutCell::reflow(int nx, int ny, int nw, int nh)
+{
+ assert(mType != NONE);
+ nx += mPadding;
+ ny += mPadding;
+ nw -= 2 * mPadding;
+ nh -= 2 * mPadding;
+ if (mType == ARRAY)
+ mArray->reflow(nx, ny, nw, nh);
+ else
+ mWidget->setDimension(gcn::Rectangle(nx, ny, nw, nh));
+}
+
+void LayoutCell::computeSizes()
+{
+ assert(mType == ARRAY);
+
+ std::vector< std::vector< LayoutCell * > >::iterator
+ i = mArray->mCells.begin();
+
+ while (i != mArray->mCells.end())
+ {
+ std::vector< LayoutCell * >::iterator j = i->begin();
+ while (j != i->end())
+ {
+ LayoutCell *cell = *j;
+ if (cell && cell->mType == ARRAY)
+ cell->computeSizes();
+
+ ++j;
+ }
+ ++i;
+ }
+
+ mSize[0] = mArray->getSize(0);
+ mSize[1] = mArray->getSize(1);
+}
+
+LayoutArray::LayoutArray(): mSpacing(4)
+{
+}
+
+LayoutArray::~LayoutArray()
+{
+ std::vector< std::vector< LayoutCell * > >::iterator i = mCells.begin();
+ while (i != mCells.end())
+ {
+ std::vector< LayoutCell * >::iterator j = i->begin();
+ while (j != i->end())
+ {
+ delete *j;
+ ++j;
+ }
+ ++i;
+ }
+}
+
+LayoutCell &LayoutArray::at(int x, int y, int w, int h)
+{
+ resizeGrid(x + w, y + h);
+ LayoutCell *&cell = mCells[y][x];
+ if (!cell)
+ cell = new LayoutCell;
+ return *cell;
+}
+
+void LayoutArray::resizeGrid(int w, int h)
+{
+ bool extW = w && w > static_cast<int>(mSizes[0].size()),
+ extH = h && h > static_cast<int>(mSizes[1].size());
+
+ if (!extW && !extH)
+ return;
+
+ if (extH)
+ {
+ mSizes[1].resize(h, Layout::AUTO_DEF);
+ mCells.resize(h);
+ if (!extW)
+ w = static_cast<int>(mSizes[0].size());
+ }
+
+ if (extW)
+ mSizes[0].resize(w, Layout::AUTO_DEF);
+
+ std::vector< std::vector< LayoutCell * > >::iterator i = mCells.begin();
+ while (i != mCells.end())
+ {
+ i->resize(w, 0);
+ ++i;
+ }
+}
+
+void LayoutArray::setColWidth(int n, int w)
+{
+ resizeGrid(n + 1, 0);
+ mSizes[0][n] = w;
+}
+
+void LayoutArray::setRowHeight(int n, int h)
+{
+ resizeGrid(0, n + 1);
+ mSizes[1][n] = h;
+}
+
+void LayoutArray::matchColWidth(int n1, int n2)
+{
+ resizeGrid(std::max(n1, n2) + 1, 0);
+ std::vector<int> widths = getSizes(0, Layout::AUTO_DEF);
+ int s = std::max(widths[n1], widths[n2]);
+ mSizes[0][n1] = s;
+ mSizes[0][n2] = s;
+}
+
+void LayoutArray::extend(int x, int y, int w, int h)
+{
+ LayoutCell &cell = at(x, y, w, h);
+ cell.mExtent[0] = w;
+ cell.mExtent[1] = h;
+}
+
+LayoutCell &LayoutArray::place(gcn::Widget *widget, int x, int y, int w, int h)
+{
+ LayoutCell &cell = at(x, y, w, h);
+ assert(cell.mType == LayoutCell::NONE);
+ cell.mType = LayoutCell::WIDGET;
+ cell.mWidget = widget;
+ if (widget)
+ {
+ cell.mSize[0] = w == 1 ? widget->getWidth() : 0;
+ cell.mSize[1] = h == 1 ? widget->getHeight() : 0;
+ }
+ else
+ {
+ cell.mSize[0] = 1;
+ cell.mSize[1] = 1;
+ }
+ cell.mExtent[0] = w;
+ cell.mExtent[1] = h;
+ cell.mPadding = 0;
+ cell.mAlign[0] = LayoutCell::FILL;
+ cell.mAlign[1] = LayoutCell::FILL;
+ int &cs = mSizes[0][x], &rs = mSizes[1][y];
+ if (cs == Layout::AUTO_DEF && w == 1)
+ cs = 0;
+ if (rs == Layout::AUTO_DEF && h == 1)
+ rs = 0;
+ return cell;
+}
+
+void LayoutArray::align(int &pos, int &size, int dim,
+ LayoutCell const &cell, int *sizes) const
+{
+ int size_max = sizes[0];
+ for (int i = 1; i < cell.mExtent[dim]; ++i)
+ size_max += sizes[i] + mSpacing;
+ size = std::min<int>(cell.mSize[dim], size_max);
+
+ switch (cell.mAlign[dim])
+ {
+ case LayoutCell::LEFT:
+ return;
+ case LayoutCell::RIGHT:
+ pos += size_max - size;
+ return;
+ case LayoutCell::CENTER:
+ pos += (size_max - size) / 2;
+ return;
+ case LayoutCell::FILL:
+ size = size_max;
+ return;
+ default:
+ logger->log1("LayoutArray::align unknown layout");
+ return;
+ }
+}
+
+std::vector<int> LayoutArray::getSizes(int dim, int upp) const
+{
+ int gridW = static_cast<int>(mSizes[0].size()),
+ gridH = static_cast<int>(mSizes[1].size());
+ std::vector<int> sizes = mSizes[dim];
+
+ // Compute minimum sizes.
+ for (int gridY = 0; gridY < gridH; ++gridY)
+ {
+ for (int gridX = 0; gridX < gridW; ++gridX)
+ {
+ LayoutCell const *cell = mCells[gridY][gridX];
+ if (!cell || cell->mType == LayoutCell::NONE)
+ continue;
+
+ if (cell->mExtent[dim] == 1)
+ {
+ int n = (dim == 0 ? gridX : gridY);
+ int s = cell->mSize[dim] + cell->mPadding * 2;
+ if (s > sizes[n]) sizes[n] = s;
+ }
+ }
+ }
+
+ if (upp == Layout::AUTO_DEF) return sizes;
+
+ // Compute the FILL sizes.
+ int nb = static_cast<int>(sizes.size());
+ int nbFill = 0;
+ for (int i = 0; i < nb; ++i)
+ {
+ if (mSizes[dim][i] <= Layout::AUTO_DEF)
+ {
+ ++nbFill;
+ if (mSizes[dim][i] == Layout::AUTO_SET ||
+ sizes[i] <= Layout::AUTO_DEF)
+ {
+ sizes[i] = 0;
+ }
+ }
+ upp -= sizes[i] + mSpacing;
+ }
+ upp = upp + mSpacing;
+
+ if (nbFill == 0)
+ return sizes;
+
+ for (int i = 0; i < nb; ++i)
+ {
+ if (mSizes[dim][i] > Layout::AUTO_DEF)
+ continue;
+
+ int s = upp / nbFill;
+ sizes[i] += s;
+ upp -= s;
+ --nbFill;
+ }
+
+ return sizes;
+}
+
+int LayoutArray::getSize(int dim) const
+{
+ std::vector<int> sizes = getSizes(dim, Layout::AUTO_DEF);
+ int size = 0;
+ int nb = static_cast<int>(sizes.size());
+ for (int i = 0; i < nb; ++i)
+ {
+ if (sizes[i] > Layout::AUTO_DEF)
+ size += sizes[i];
+ size += mSpacing;
+ }
+ return size - mSpacing;
+}
+
+void LayoutArray::reflow(int nx, int ny, int nw, int nh)
+{
+ int gridW = static_cast<int>(mSizes[0].size()),
+ gridH = static_cast<int>(mSizes[1].size());
+
+ std::vector<int> widths = getSizes(0, nw);
+ std::vector<int> heights = getSizes(1, nh);
+
+ int y = ny;
+ for (int gridY = 0; gridY < gridH; ++gridY)
+ {
+ int x = nx;
+ for (int gridX = 0; gridX < gridW; ++gridX)
+ {
+ LayoutCell *cell = mCells[gridY][gridX];
+ if (cell && cell->mType != LayoutCell::NONE)
+ {
+ int dx = x, dy = y, dw, dh;
+ align(dx, dw, 0, *cell, &widths[gridX]);
+ align(dy, dh, 1, *cell, &heights[gridY]);
+ cell->reflow(dx, dy, dw, dh);
+ }
+ x += widths[gridX] + mSpacing;
+ }
+ y += heights[gridY] + mSpacing;
+ }
+}
+
+Layout::Layout(): mComputed(false)
+{
+ getArray();
+ setPadding(6);
+}
+
+void Layout::reflow(int &nw, int &nh)
+{
+ if (!mComputed)
+ {
+ computeSizes();
+ mComputed = true;
+ }
+
+ nw = (nw == 0 ? mSize[0] + 2 * mPadding : nw);
+ nh = (nh == 0 ? mSize[1] + 2 * mPadding : nh);
+ LayoutCell::reflow(0, 0, nw, nh);
+}
diff --git a/src/gui/widgets/layout.h b/src/gui/widgets/layout.h
new file mode 100644
index 000000000..4c1e40bb9
--- /dev/null
+++ b/src/gui/widgets/layout.h
@@ -0,0 +1,319 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2007-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 WIDGET_LAYOUT_H
+#define WIDGET_LAYOUT_H
+
+#include <guichan/widgets/container.hpp>
+
+#include <vector>
+
+class LayoutCell;
+
+/**
+ * This class is a helper for adding widgets to nested tables in a window.
+ */
+class ContainerPlacer
+{
+ public:
+ ContainerPlacer(gcn::Container *c = NULL, LayoutCell *l = NULL):
+ mContainer(c), mCell(l)
+ {}
+
+ /**
+ * Gets the pointed cell.
+ */
+ LayoutCell &getCell()
+ { return *mCell; }
+
+ /**
+ * Returns a placer for the same container but to an inner cell.
+ */
+ ContainerPlacer at(int x, int y);
+
+ /**
+ * Adds the given widget to the container and places it in the layout.
+ * @see LayoutArray::place
+ */
+ LayoutCell &operator()
+ (int x, int y, gcn::Widget *, int w = 1, int h = 1);
+
+ private:
+ gcn::Container *mContainer;
+ LayoutCell *mCell;
+};
+
+/**
+ * This class contains a rectangular array of cells.
+ */
+class LayoutArray
+{
+ friend class LayoutCell;
+
+ public:
+
+ LayoutArray();
+
+ ~LayoutArray();
+
+ /**
+ * Returns a reference on the cell at given position.
+ */
+ LayoutCell &at(int x, int y, int w = 1, int h = 1);
+
+ /**
+ * Places a widget in a given cell.
+ * @param w number of columns the widget spawns.
+ * @param h number of rows the widget spawns.
+ * @note When @a w is 1, the width of column @a x is reset to zero if
+ * it was AUTO_DEF. Similarly for @a h.
+ */
+ LayoutCell &place(gcn::Widget *, int x, int y, int w = 1, int h = 1);
+
+ /**
+ * Sets the minimum width of a column.
+ */
+ void setColWidth(int n, int w);
+
+ /**
+ * Sets the minimum height of a row.
+ */
+ void setRowHeight(int n, int h);
+
+ /**
+ * Sets the widths of two columns to the maximum of their widths.
+ */
+ void matchColWidth(int n1, int n2);
+
+ /**
+ * Spawns a cell over several columns/rows.
+ */
+ void extend(int x, int y, int w, int h);
+
+ /**
+ * Computes and sets the positions of all the widgets.
+ * @param nW width of the array, used to resize the AUTO_ columns.
+ * @param nH height of the array, used to resize the AUTO_ rows.
+ */
+ void reflow(int nX, int nY, int nW, int nH);
+
+ private:
+
+ // Copy not allowed, as the array owns all its cells.
+ LayoutArray(LayoutArray const &);
+ LayoutArray &operator=(LayoutArray const &);
+
+ /**
+ * Gets the position and size of a widget along a given axis
+ */
+ void align(int &pos, int &size, int dim,
+ LayoutCell const &cell, int *sizes) const;
+
+ /**
+ * Ensures the private vectors are large enough.
+ */
+ void resizeGrid(int w, int h);
+
+ /**
+ * Gets the column/row sizes along a given axis.
+ * @param upp target size for the array. Ignored if AUTO_DEF.
+ */
+ std::vector<int> getSizes(int dim, int upp) const;
+
+ /**
+ * Gets the total size along a given axis.
+ */
+ int getSize(int dim) const;
+
+ std::vector<int> mSizes[2];
+ std::vector< std::vector < LayoutCell * > > mCells;
+
+ int mSpacing;
+};
+
+/**
+ * This class describes the formatting of a widget in the cell of a layout
+ * table. Horizontally, a widget can either fill the width of the cell (minus
+ * the cell padding), or it can retain its size and be flushed left, or flush
+ * right, or centered in the cell. The process is similar for the vertical
+ * alignment, except that top is represented by LEFT and bottom by RIGHT.
+ */
+class LayoutCell
+{
+ friend class Layout;
+ friend class LayoutArray;
+
+ public:
+ enum Alignment
+ {
+ LEFT = 0,
+ RIGHT,
+ CENTER,
+ FILL
+ };
+
+ LayoutCell(): mType(NONE) {}
+
+ ~LayoutCell();
+
+ /**
+ * Sets the padding around the cell content.
+ */
+ LayoutCell &setPadding(int p)
+ { mPadding = p; return *this; }
+
+ /**
+ * Sets the horizontal alignment of the cell content.
+ */
+ LayoutCell &setHAlign(Alignment a)
+ { mAlign[0] = a; return *this; }
+
+ /**
+ * Sets the vertical alignment of the cell content.
+ */
+ LayoutCell &setVAlign(Alignment a)
+ { mAlign[1] = a; return *this; }
+
+ /**
+ * @see LayoutArray::at
+ */
+ LayoutCell &at(int x, int y)
+ { return getArray().at(x, y); }
+
+ /**
+ * @see LayoutArray::place
+ */
+ LayoutCell &place(gcn::Widget *wg, int x, int y, int w = 1, int h = 1)
+ { return getArray().place(wg, x, y, w, h); }
+
+ /**
+ * @see LayoutArray::matchColWidth
+ */
+ void matchColWidth(int n1, int n2)
+ { getArray().matchColWidth(n1, n2); }
+
+ /**
+ * @see LayoutArray::setColWidth
+ */
+ void setColWidth(int n, int w)
+ { getArray().setColWidth(n, w); }
+
+ /**
+ * @see LayoutArray::setRowHeight
+ */
+ void setRowHeight(int n, int h)
+ { getArray().setRowHeight(n, h); }
+
+ /**
+ * @see LayoutArray::extend.
+ */
+ void extend(int x, int y, int w, int h)
+ { getArray().extend(x, y, w, h); }
+
+ /**
+ * Sets the minimum widths and heights of this cell and of all the
+ * inner cells.
+ */
+ void computeSizes();
+
+ private:
+ // Copy not allowed, as the cell may own an array.
+ LayoutCell(LayoutCell const &);
+ LayoutCell &operator=(LayoutCell const &);
+
+ union
+ {
+ gcn::Widget *mWidget;
+ LayoutArray *mArray;
+ };
+
+ enum
+ {
+ NONE = 0,
+ WIDGET,
+ ARRAY
+ };
+
+ /**
+ * Returns the embedded array. Creates it if the cell does not contain
+ * anything yet. Aborts if it contains a widget.
+ */
+ LayoutArray &getArray();
+
+ /**
+ * @see LayoutArray::reflow
+ */
+ void reflow(int nx, int ny, int nw, int nh);
+
+ int mSize[2];
+ int mPadding;
+ int mExtent[2];
+ int mAlign[2];
+ int mNbFill[2];
+ int mType;
+};
+
+/**
+ * This class is an helper for setting the position of widgets. They are
+ * positioned along the cells of some rectangular tables. The layout may either
+ * be a single table or a tree of nested tables.
+ *
+ * The size of a given table column can either be set manually or be chosen
+ * from the widest widget of the column. An empty column has a AUTO_DEF width,
+ * which means it will be extended so that the layout fits its minimum width.
+ *
+ * The process is similar for table rows. By default, there is a spacing of 4
+ * pixels between rows and between columns, and a margin of 6 pixels around the
+ * whole layout.
+ */
+class Layout : public LayoutCell
+{
+ public:
+ Layout();
+
+ /**
+ * Sets the margin around the layout.
+ */
+ void setMargin(int m)
+ { setPadding(m); }
+
+ /**
+ * Sets the positions of all the widgets.
+ * @see LayoutArray::reflow
+ */
+ void reflow(int &nW, int &nH);
+
+ /**
+ * When the minimum size of the layout is less than the available size,
+ * the remaining pixels are equally split amongst the FILL items.
+ */
+ enum
+ {
+ AUTO_DEF = -42, /**< Default value, behaves like AUTO_ADD. */
+ AUTO_SET = -43, /**< Uses the share as the new size. */
+ AUTO_ADD = -44 /**< Adds the share to the current size. */
+ };
+
+ private:
+ bool mComputed;
+};
+
+#endif // WIDGET_LAYOUT_H
diff --git a/src/gui/widgets/layouthelper.cpp b/src/gui/widgets/layouthelper.cpp
new file mode 100644
index 000000000..a12de9bff
--- /dev/null
+++ b/src/gui/widgets/layouthelper.cpp
@@ -0,0 +1,63 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/layouthelper.h"
+
+LayoutHelper::LayoutHelper(gcn::Container *container):
+ mContainer(container)
+{
+ mContainer->addWidgetListener(this);
+}
+
+LayoutHelper::~LayoutHelper()
+{
+ mContainer->removeWidgetListener(this);
+}
+
+Layout &LayoutHelper::getLayout()
+{
+ return mLayout;
+}
+
+LayoutCell &LayoutHelper::place(int x, int y, gcn::Widget *wg, int w, int h)
+{
+ mContainer->add(wg);
+ return mLayout.place(wg, x, y, w, h);
+}
+
+ContainerPlacer LayoutHelper::getPlacer(int x, int y)
+{
+ return ContainerPlacer(mContainer, &mLayout.at(x, y));
+}
+
+void LayoutHelper::reflowLayout(int w, int h)
+{
+ mLayout.reflow(w, h);
+ mContainer->setSize(w, h);
+}
+
+void LayoutHelper::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ const gcn::Rectangle area = mContainer->getChildrenArea();
+ int w = area.width;
+ int h = area.height;
+ mLayout.reflow(w, h);
+}
diff --git a/src/gui/widgets/layouthelper.h b/src/gui/widgets/layouthelper.h
new file mode 100644
index 000000000..033457429
--- /dev/null
+++ b/src/gui/widgets/layouthelper.h
@@ -0,0 +1,90 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 LAYOUTHELPER_H
+#define LAYOUTHELPER_H
+
+#include "gui/widgets/layout.h"
+
+#include <guichan/widgetlistener.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+/**
+ * A helper class for adding a layout to a Guichan container widget. The layout
+ * will register itself as a widget listener and relayout the widgets in the
+ * container dynamically on resize.
+ */
+class LayoutHelper : public gcn::WidgetListener
+{
+ public:
+ /**
+ * Constructor.
+ */
+ LayoutHelper(gcn::Container *container);
+
+ /**
+ * Destructor.
+ */
+ ~LayoutHelper();
+
+ /**
+ * Gets the layout handler.
+ */
+ Layout &getLayout();
+
+ /**
+ * Computes the position of the widgets according to the current
+ * layout. Resizes the managed container so that the layout fits.
+ *
+ * @note This function is meant to be called with fixed-size
+ * containers.
+ *
+ * @param w if non-zero, force the container to this width.
+ * @param h if non-zero, force the container to this height.
+ */
+ void reflowLayout(int w = 0, int h = 0);
+
+ /**
+ * Adds a widget to the container and sets it at given cell.
+ */
+ LayoutCell &place(int x, int y, gcn::Widget *, int w = 1, int h = 1);
+
+ /**
+ * Returns a proxy for adding widgets in an inner table of the layout.
+ */
+ ContainerPlacer getPlacer(int x, int y);
+
+ /**
+ * Called whenever the managed container changes size.
+ */
+ void widgetResized(const gcn::Event &event);
+
+ private:
+ Layout mLayout; /**< Layout handler */
+ gcn::Container *mContainer; /**< Managed container */
+};
+
+#endif // LAYOUTHELPER_H
diff --git a/src/gui/widgets/linkhandler.h b/src/gui/widgets/linkhandler.h
new file mode 100644
index 000000000..d9d0f1161
--- /dev/null
+++ b/src/gui/widgets/linkhandler.h
@@ -0,0 +1,42 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 LINK_HANDLER_H
+#define LINK_HANDLER_H
+
+#include <string>
+
+#include <guichan/mouselistener.hpp>
+
+/**
+ * A simple interface to windows that need to handle links from BrowserBox
+ * widget.
+ */
+class LinkHandler
+{
+ public:
+ virtual ~LinkHandler() { }
+
+ virtual void handleLink(const std::string &link,
+ gcn::MouseEvent *event) = 0;
+};
+
+#endif
diff --git a/src/gui/widgets/listbox.cpp b/src/gui/widgets/listbox.cpp
new file mode 100644
index 000000000..8dcc4ae67
--- /dev/null
+++ b/src/gui/widgets/listbox.cpp
@@ -0,0 +1,146 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/listbox.h"
+
+#include "client.h"
+#include "configuration.h"
+
+#include "gui/palette.h"
+#include "gui/sdlinput.h"
+#include "gui/theme.h"
+
+#include <guichan/font.hpp>
+#include <guichan/graphics.hpp>
+#include <guichan/key.hpp>
+#include <guichan/listmodel.hpp>
+
+float ListBox::mAlpha = 1.0;
+
+ListBox::ListBox(gcn::ListModel *listModel):
+ gcn::ListBox(listModel)
+{
+}
+
+ListBox::~ListBox()
+{
+}
+
+void ListBox::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ if (mAlpha != alpha)
+ mAlpha = alpha;
+}
+
+void ListBox::draw(gcn::Graphics *graphics)
+{
+ if (!mListModel)
+ return;
+
+ updateAlpha();
+
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT,
+ static_cast<int>(mAlpha * 255.0f)));
+ graphics->setFont(getFont());
+
+ const int height = getRowHeight();
+
+ // Draw filled rectangle around the selected list element
+ if (mSelected >= 0)
+ {
+ graphics->fillRectangle(gcn::Rectangle(0, height * mSelected,
+ getWidth(), height));
+ }
+
+ // Draw the list elements
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ for (int i = 0, y = 0; i < mListModel->getNumberOfElements();
+ ++i, y += height)
+ {
+ graphics->drawText(mListModel->getElementAt(i), 1, y);
+ }
+}
+
+void ListBox::keyPressed(gcn::KeyEvent& keyEvent)
+{
+ gcn::Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
+ {
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::UP)
+ {
+ if (getSelected() > 0)
+ setSelected(mSelected - 1);
+ else if (getSelected() == 0 && mWrappingEnabled && getListModel())
+ setSelected(getListModel()->getNumberOfElements() - 1);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::DOWN)
+ {
+ if (getSelected() < (getListModel()->getNumberOfElements() - 1))
+ {
+ setSelected(mSelected + 1);
+ }
+ else if (getSelected() == (getListModel()->getNumberOfElements() - 1)
+ && mWrappingEnabled)
+ {
+ setSelected(0);
+ }
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::HOME)
+ {
+ setSelected(0);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::END && getListModel())
+ {
+ setSelected(getListModel()->getNumberOfElements() - 1);
+ keyEvent.consume();
+ }
+}
+
+// Don't do anything on scrollwheel. ScrollArea will deal with that.
+
+void ListBox::mouseWheelMovedUp(gcn::MouseEvent &mouseEvent _UNUSED_)
+{
+}
+
+void ListBox::mouseWheelMovedDown(gcn::MouseEvent &mouseEvent _UNUSED_)
+{
+}
+
+void ListBox::mouseDragged(gcn::MouseEvent &event)
+{
+ if (event.getButton() != gcn::MouseEvent::LEFT || getRowHeight() == 0)
+ return;
+
+ // Make list selection update on drag, but guard against negative y
+ int y = std::max(0, event.getY());
+ if (getRowHeight())
+ setSelected(y / getRowHeight());
+}
diff --git a/src/gui/widgets/listbox.h b/src/gui/widgets/listbox.h
new file mode 100644
index 000000000..721e1bd36
--- /dev/null
+++ b/src/gui/widgets/listbox.h
@@ -0,0 +1,78 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 LISTBOX_H
+#define LISTBOX_H
+
+#include <guichan/widgets/listbox.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class SelectionListener;
+
+/**
+ * A list box, meant to be used inside a scroll area. Same as the Guichan list
+ * box except this one doesn't have a background, instead completely relying
+ * on the scroll area. It also adds selection listener functionality.
+ *
+ * \ingroup GUI
+ */
+class ListBox : public gcn::ListBox
+{
+ public:
+ /**
+ * Constructor.
+ */
+ ListBox(gcn::ListModel *listModel);
+
+ ~ListBox();
+
+ /**
+ * Draws the list box.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Update the alpha value to the graphic components.
+ */
+ void updateAlpha();
+
+ // Inherited from KeyListener
+
+ void keyPressed(gcn::KeyEvent& keyEvent);
+
+ // Inherited from MouseListener
+
+ void mouseWheelMovedUp(gcn::MouseEvent& mouseEvent);
+
+ void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent);
+
+ void mouseDragged(gcn::MouseEvent &event);
+
+ protected:
+ static float mAlpha;
+};
+
+#endif
diff --git a/src/gui/widgets/passwordfield.cpp b/src/gui/widgets/passwordfield.cpp
new file mode 100644
index 000000000..14b924bbd
--- /dev/null
+++ b/src/gui/widgets/passwordfield.cpp
@@ -0,0 +1,36 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 "passwordfield.h"
+
+PasswordField::PasswordField(const std::string &text):
+ TextField(text)
+{
+}
+
+void PasswordField::draw(gcn::Graphics *graphics)
+{
+ // std::string uses cow, thus cheap copy
+ const std::string original = mText;
+ mText.assign(mText.length(), '*');
+ TextField::draw(graphics);
+ mText = original;
+}
diff --git a/src/gui/widgets/passwordfield.h b/src/gui/widgets/passwordfield.h
new file mode 100644
index 000000000..619cd8420
--- /dev/null
+++ b/src/gui/widgets/passwordfield.h
@@ -0,0 +1,46 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 PASSWORDFIELD_H
+#define PASSWORDFIELD_H
+
+#include "textfield.h"
+
+/**
+ * A password field.
+ *
+ * \ingroup GUI
+ */
+class PasswordField : public TextField
+{
+ public:
+ /**
+ * Constructor, initializes the password field with the given string.
+ */
+ PasswordField(const std::string &text = "");
+
+ /**
+ * Draws the password field.
+ */
+ void draw(gcn::Graphics *graphics);
+};
+
+#endif
diff --git a/src/gui/widgets/playerbox.cpp b/src/gui/widgets/playerbox.cpp
new file mode 100644
index 000000000..e3a660120
--- /dev/null
+++ b/src/gui/widgets/playerbox.cpp
@@ -0,0 +1,120 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/playerbox.h"
+
+#include "animatedsprite.h"
+#include "being.h"
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+#include "utils/dtor.h"
+
+int PlayerBox::instances = 0;
+float PlayerBox::mAlpha = 1.0;
+ImageRect PlayerBox::background;
+
+PlayerBox::PlayerBox(const Being *being):
+ mBeing(being)
+{
+ setFrameSize(2);
+
+ if (instances == 0)
+ {
+ // Load the background skin
+ Image *textbox = Theme::getImageFromTheme("deepbox.png");
+ int bggridx[4] = {0, 3, 28, 31};
+ int bggridy[4] = {0, 3, 28, 31};
+ int a = 0, x, y;
+
+ for (y = 0; y < 3; y++)
+ {
+ for (x = 0; x < 3; x++)
+ {
+ if (textbox)
+ {
+ background.grid[a] = textbox->getSubImage(
+ bggridx[x], bggridy[y],
+ bggridx[x + 1] - bggridx[x] + 1,
+ bggridy[y + 1] - bggridy[y] + 1);
+ if (background.grid[a])
+ background.grid[a]->setAlpha(Client::getGuiAlpha());
+ }
+ else
+ {
+ background.grid[a] = 0;
+ }
+ a++;
+ }
+ }
+
+ if (textbox)
+ textbox->decRef();
+ }
+
+ instances++;
+}
+
+PlayerBox::~PlayerBox()
+{
+ instances--;
+
+ mBeing = 0;
+
+ if (instances == 0)
+ for_each(background.grid, background.grid + 9, dtor<Image*>());
+}
+
+void PlayerBox::draw(gcn::Graphics *graphics)
+{
+ if (mBeing)
+ {
+ // Draw character
+ const int bs = getFrameSize();
+ const int x = getWidth() / 2 + bs - 16;
+ const int y = getHeight() - bs - 32;
+ mBeing->drawSpriteAt(static_cast<Graphics*>(graphics), x, y);
+ }
+
+ if (Client::getGuiAlpha() != mAlpha)
+ {
+ for (int a = 0; a < 9; a++)
+ {
+ if (background.grid[a])
+ background.grid[a]->setAlpha(Client::getGuiAlpha());
+ }
+ }
+}
+
+void PlayerBox::drawFrame(gcn::Graphics *graphics)
+{
+ int w, h, bs;
+ bs = getFrameSize();
+ w = getWidth() + bs * 2;
+ h = getHeight() + bs * 2;
+
+ static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, background);
+}
diff --git a/src/gui/widgets/playerbox.h b/src/gui/widgets/playerbox.h
new file mode 100644
index 000000000..4505367f8
--- /dev/null
+++ b/src/gui/widgets/playerbox.h
@@ -0,0 +1,74 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 PLAYERBOX_H
+#define PLAYERBOX_H
+
+#include <guichan/widgets/scrollarea.hpp>
+
+class Being;
+class ImageRect;
+
+/**
+ * A box showing a player character.
+ *
+ * \ingroup GUI
+ */
+class PlayerBox : public gcn::ScrollArea
+{
+ public:
+ /**
+ * Constructor. Takes the initial player character that this box should
+ * display, which defaults to <code>NULL</code>.
+ */
+ PlayerBox(const Being *being = 0);
+
+ /**
+ * Destructor.
+ */
+ ~PlayerBox();
+
+ /**
+ * Sets a new player character to be displayed by this box. Setting the
+ * player to <code>NULL</code> causes the box not to draw any
+ * character.
+ */
+ void setPlayer(const Being *being) { mBeing = being; }
+
+ /**
+ * Draws the scroll area.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Draws the background and border of the scroll area.
+ */
+ void drawFrame(gcn::Graphics *graphics);
+
+ private:
+ const Being *mBeing; /**< The character used for display */
+
+ static float mAlpha;
+ static int instances;
+ static ImageRect background;
+};
+
+#endif
diff --git a/src/gui/widgets/popup.cpp b/src/gui/widgets/popup.cpp
new file mode 100644
index 000000000..fa955c8f3
--- /dev/null
+++ b/src/gui/widgets/popup.cpp
@@ -0,0 +1,174 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2009 Aethyra Development Team
+ *
+ * This file is part of The Mana 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/widgets/popup.h"
+
+#include "configuration.h"
+#include "graphics.h"
+#include "log.h"
+
+#include "gui/theme.h"
+#include "gui/viewport.h"
+
+#include "gui/widgets/windowcontainer.h"
+
+#include "resources/image.h"
+
+#include <guichan/exception.hpp>
+
+Popup::Popup(const std::string &name, const std::string &skin):
+ mPopupName(name),
+ mMinWidth(100),
+ mMinHeight(40),
+ mMaxWidth(graphics->getWidth()),
+ mMaxHeight(graphics->getHeight())
+{
+ logger->log("Popup::Popup(\"%s\")", name.c_str());
+
+ if (!windowContainer)
+ throw GCN_EXCEPTION("Popup::Popup(): no windowContainer set");
+
+ setPadding(3);
+
+ // Loads the skin
+ mSkin = Theme::instance()->load(skin);
+
+ // Add this window to the window container
+ windowContainer->add(this);
+
+ // Popups are invisible by default
+ setVisible(false);
+}
+
+Popup::~Popup()
+{
+ logger->log("Popup::~Popup(\"%s\")", mPopupName.c_str());
+
+ if (mSkin)
+ mSkin->instances--;
+}
+
+void Popup::setWindowContainer(WindowContainer *wc)
+{
+ windowContainer = wc;
+}
+
+void Popup::draw(gcn::Graphics *graphics)
+{
+ Graphics *g = static_cast<Graphics*>(graphics);
+
+ g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder());
+
+ drawChildren(graphics);
+}
+
+gcn::Rectangle Popup::getChildrenArea()
+{
+ return gcn::Rectangle(getPadding(), 0, getWidth() - getPadding() * 2,
+ getHeight() - getPadding() * 2);
+}
+
+void Popup::setContentSize(int width, int height)
+{
+ width += 2 * getPadding();
+ height += 2 * getPadding();
+
+ if (getMinWidth() > width)
+ width = getMinWidth();
+ else if (getMaxWidth() < width)
+ width = getMaxWidth();
+ if (getMinHeight() > height)
+ height = getMinHeight();
+ else if (getMaxHeight() < height)
+ height = getMaxHeight();
+
+ setSize(width, height);
+}
+
+void Popup::setLocationRelativeTo(gcn::Widget *widget)
+{
+ if (!widget)
+ return;
+
+ int wx, wy;
+ int x, y;
+
+ widget->getAbsolutePosition(wx, wy);
+ getAbsolutePosition(x, y);
+
+ setPosition(getX() + (wx + (widget->getWidth() - getWidth()) / 2 - x),
+ getY() + (wy + (widget->getHeight() - getHeight()) / 2 - y));
+}
+
+void Popup::setMinWidth(int width)
+{
+ mMinWidth = width > mSkin->getMinWidth() ? width : mSkin->getMinWidth();
+}
+
+void Popup::setMinHeight(int height)
+{
+ mMinHeight = height > mSkin->getMinHeight() ?
+ height : mSkin->getMinHeight();
+}
+
+void Popup::setMaxWidth(int width)
+{
+ mMaxWidth = width;
+}
+
+void Popup::setMaxHeight(int height)
+{
+ mMaxHeight = height;
+}
+
+void Popup::scheduleDelete()
+{
+ windowContainer->scheduleDelete(this);
+}
+
+void Popup::position(int x, int y)
+{
+ const int distance = 20;
+
+ int posX = std::max(0, x - getWidth() / 2);
+ int posY = y + distance;
+
+ if (posX + getWidth() > graphics->getWidth())
+ posX = graphics->getWidth() - getWidth();
+ if (posY + getHeight() > graphics->getHeight())
+ posY = y - getHeight() - distance;
+
+ setPosition(posX, posY);
+ setVisible(true);
+ requestMoveToTop();
+}
+
+void Popup::mouseMoved(gcn::MouseEvent &event _UNUSED_)
+{
+ if (viewport)
+ viewport->hideBeingPopup();
+}
+
+void Popup::hide()
+{
+ setVisible(false);
+} \ No newline at end of file
diff --git a/src/gui/widgets/popup.h b/src/gui/widgets/popup.h
new file mode 100644
index 000000000..c83368e52
--- /dev/null
+++ b/src/gui/widgets/popup.h
@@ -0,0 +1,174 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2009 Aethyra Development Team
+ *
+ * This file is part of The Mana 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 POPUP_H
+#define POPUP_H
+
+#include "configuration.h"
+#include "guichanfwd.h"
+
+#include "gui/widgets/container.h"
+
+#include <guichan/mouselistener.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Skin;
+class WindowContainer;
+
+/**
+ * A light version of the Window class. Particularly suited for popup type
+ * functionality that doesn't need to be resized or moved around by the mouse
+ * once created, but only needs to display some simple content, like a static
+ * message.
+ *
+ * Popups, in general, shouldn't also need to update their content once
+ * created, although this is not an explicit requirement to use the popup
+ * class.
+ *
+ * \ingroup GUI
+ */
+class Popup : public Container, public gcn::MouseListener
+{
+ public:
+ /**
+ * Constructor. Initializes the title to the given text and hooks
+ * itself into the popup container.
+ *
+ * @param name A human readable name for the popup. Only useful for
+ * debugging purposes.
+ * @param skin The location where the Popup's skin XML can be found.
+ */
+ Popup(const std::string &name = "",
+ const std::string &skin = "window.xml");
+
+ /**
+ * Destructor. Deletes all the added widgets.
+ */
+ ~Popup();
+
+ /**
+ * Sets the window container to be used by new popups.
+ */
+ static void setWindowContainer(WindowContainer *windowContainer);
+
+ /**
+ * Draws the popup.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Sets the size of this popup.
+ */
+ void setContentSize(int width, int height);
+
+ /**
+ * Sets the location relative to the given widget.
+ */
+ void setLocationRelativeTo(gcn::Widget *widget);
+
+ void mouseMoved(gcn::MouseEvent &event);
+
+ /**
+ * Sets the minimum width of the popup.
+ */
+ void setMinWidth(int width);
+
+ int getMinWidth() const { return mMinWidth; }
+
+ /**
+ * Sets the minimum height of the popup.
+ */
+ void setMinHeight(int height);
+
+ int getMinHeight() const { return mMinHeight; }
+
+ /**
+ * Sets the maximum width of the popup.
+ */
+ void setMaxWidth(int width);
+
+ int getMaxWidth() const { return mMaxWidth; }
+
+ /**
+ * Sets the minimum height of the popup.
+ */
+ void setMaxHeight(int height);
+
+ int getMaxHeight() const { return mMaxHeight; }
+
+ /**
+ * Gets the padding of the popup. The padding is the distance between
+ * the popup border and the content.
+ *
+ * @return The padding of the popup.
+ * @see setPadding
+ */
+ int getPadding() const { return mPadding; }
+
+ void setPadding(int padding) { mPadding = padding; }
+
+ /**
+ * Sets the name of the popup. This is only useful for debug purposes.
+ */
+ void setPopupName(const std::string &name)
+ { mPopupName = name; }
+
+ const std::string &getPopupName() const
+ { return mPopupName; }
+
+ /**
+ * Schedule this popup for deletion. It will be deleted at the start
+ * of the next logic update.
+ */
+ void scheduleDelete();
+
+ // Inherited from BasicContainer
+
+ virtual gcn::Rectangle getChildrenArea();
+
+ /**
+ * Sets the location to display the popup. Tries to horizontally center
+ * the popup and provide a vertical buffer between the given point and
+ * the popup. Prevents the popup from extending off-screen, if
+ * possible.
+ */
+ void position(int x, int y);
+
+ void hide();
+
+ private:
+ std::string mPopupName; /**< Name of the popup */
+ int mMinWidth; /**< Minimum popup width */
+ int mMinHeight; /**< Minimum popup height */
+ int mMaxWidth; /**< Maximum popup width */
+ int mMaxHeight; /**< Maximum popup height */
+ int mPadding; /**< Holds the padding of the popup. */
+
+ Skin *mSkin; /**< Skin in use by this popup */
+};
+
+#endif
diff --git a/src/gui/widgets/progressbar.cpp b/src/gui/widgets/progressbar.cpp
new file mode 100644
index 000000000..5275aacf6
--- /dev/null
+++ b/src/gui/widgets/progressbar.cpp
@@ -0,0 +1,225 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/progressbar.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+#include "textrenderer.h"
+
+#include "gui/gui.h"
+#include "gui/palette.h"
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+#include "utils/dtor.h"
+
+#include <guichan/font.hpp>
+
+ImageRect ProgressBar::mBorder;
+int ProgressBar::mInstances = 0;
+float ProgressBar::mAlpha = 1.0;
+
+ProgressBar::ProgressBar(float progress,
+ int width, int height,
+ int color):
+ gcn::Widget(),
+ mSmoothProgress(true),
+ mProgressPalette(color),
+ mSmoothColorChange(true)
+{
+ // The progress value is directly set at load time:
+ if (progress > 1.0f || progress < 0.0f)
+ progress = 1.0f;
+
+ mProgress = progress;
+ mProgressToGo = progress;
+
+ mColor = Theme::getProgressColor(color >= 0 ? color : 0, mProgress);
+ mColorToGo = mColor;
+
+ setSize(width, height);
+
+ if (mInstances == 0)
+ {
+ Image *dBorders = Theme::getImageFromTheme("vscroll_grey.png");
+ if (dBorders)
+ {
+ mBorder.grid[0] = dBorders->getSubImage(0, 0, 4, 4);
+ mBorder.grid[1] = dBorders->getSubImage(4, 0, 3, 4);
+ mBorder.grid[2] = dBorders->getSubImage(7, 0, 4, 4);
+ mBorder.grid[3] = dBorders->getSubImage(0, 4, 4, 10);
+ mBorder.grid[4] = dBorders->getSubImage(4, 4, 3, 10);
+ mBorder.grid[5] = dBorders->getSubImage(7, 4, 4, 10);
+ mBorder.grid[6] = dBorders->getSubImage(0, 15, 4, 4);
+ mBorder.grid[7] = dBorders->getSubImage(4, 15, 3, 4);
+ mBorder.grid[8] = dBorders->getSubImage(7, 15, 4, 4);
+
+ for (int i = 0; i < 9; i++)
+ mBorder.grid[i]->setAlpha(mAlpha);
+
+ dBorders->decRef();
+ }
+ else
+ {
+ for (int f = 0; f < 9; f ++)
+ mBorder.grid[f] = 0;
+ }
+
+ }
+
+ mInstances++;
+}
+
+ProgressBar::~ProgressBar()
+{
+ mInstances--;
+
+ if (mInstances == 0)
+ for_each(mBorder.grid, mBorder.grid + 9, dtor<Image*>());
+}
+
+void ProgressBar::logic()
+{
+ if (mSmoothColorChange && mColorToGo != mColor)
+ {
+ // Smoothly changing the color for a nicer effect.
+ if (mColorToGo.r > mColor.r)
+ mColor.r++;
+ if (mColorToGo.g > mColor.g)
+ mColor.g++;
+ if (mColorToGo.b > mColor.b)
+ mColor.b++;
+ if (mColorToGo.r < mColor.r)
+ mColor.r--;
+ if (mColorToGo.g < mColor.g)
+ mColor.g--;
+ if (mColorToGo.b < mColor.b)
+ mColor.b--;
+ }
+
+ if (mSmoothProgress && mProgressToGo != mProgress)
+ {
+ // Smoothly showing the progressbar changes.
+ if (mProgressToGo > mProgress)
+ mProgress = std::min(1.0f, mProgress + 0.005f);
+ if (mProgressToGo < mProgress)
+ mProgress = std::max(0.0f, mProgress - 0.005f);
+ }
+}
+
+void ProgressBar::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ if (mAlpha != alpha)
+ {
+ mAlpha = alpha;
+ for (int i = 0; i < 9; i++)
+ {
+ if (mBorder.grid[i])
+ mBorder.grid[i]->setAlpha(mAlpha);
+ }
+ }
+
+}
+
+void ProgressBar::draw(gcn::Graphics *graphics)
+{
+ updateAlpha();
+
+ mColor.a = static_cast<int>(mAlpha * 255);
+
+ gcn::Rectangle rect = getDimension();
+ rect.x = 0;
+ rect.y = 0;
+
+ render(static_cast<Graphics*>(graphics), rect, mColor,
+ mProgress, mText);
+}
+
+void ProgressBar::setProgress(float progress)
+{
+ const float p = std::min(1.0f, std::max(0.0f, progress));
+ mProgressToGo = p;
+
+ if (!mSmoothProgress)
+ mProgress = p;
+
+ if (mProgressPalette >= 0)
+ mColorToGo = Theme::getProgressColor(mProgressPalette, progress);
+}
+
+void ProgressBar::setProgressPalette(int progressPalette)
+{
+ int oldPalette = mProgressPalette;
+ mProgressPalette = progressPalette;
+
+ if (mProgressPalette != oldPalette && mProgressPalette >= 0)
+ mColorToGo = Theme::getProgressColor(mProgressPalette, mProgressToGo);
+}
+
+void ProgressBar::setColor(const gcn::Color &color)
+{
+ mColorToGo = color;
+
+ if (!mSmoothColorChange)
+ mColor = color;
+}
+
+void ProgressBar::render(Graphics *graphics, const gcn::Rectangle &area,
+ const gcn::Color &color, float progress,
+ const std::string &text)
+{
+ gcn::Font *oldFont = graphics->getFont();
+ gcn::Color oldColor = graphics->getColor();
+
+ graphics->drawImageRect(area, mBorder);
+
+ // The bar
+ if (progress > 0)
+ {
+ graphics->setColor(color);
+ graphics->fillRectangle(gcn::Rectangle(static_cast<int>(area.x + 4),
+ static_cast<int>(area.y + 4),
+ static_cast<int>(static_cast<float>(progress)
+ * static_cast<float>(area.width - 8)),
+ static_cast<int>(area.height - 8)));
+ }
+
+ // The label
+ if (!text.empty())
+ {
+ const int textX = area.x + area.width / 2;
+ const int textY = area.y + (area.height - boldFont->getHeight()) / 2;
+
+ TextRenderer::renderText(graphics, text, textX, textY,
+ gcn::Graphics::CENTER,
+ Theme::getThemeColor(Theme::PROGRESS_BAR),
+ gui->getFont(), true, false);
+ }
+
+ graphics->setFont(oldFont);
+ graphics->setColor(oldColor);
+}
diff --git a/src/gui/widgets/progressbar.h b/src/gui/widgets/progressbar.h
new file mode 100644
index 000000000..56bcb0a0f
--- /dev/null
+++ b/src/gui/widgets/progressbar.h
@@ -0,0 +1,139 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 PROGRESSBAR_H
+#define PROGRESSBAR_H
+
+#include <guichan/widget.hpp>
+
+#include <string>
+
+class Graphics;
+class ImageRect;
+
+/**
+ * A progress bar.
+ *
+ * \ingroup GUI
+ */
+class ProgressBar : public gcn::Widget
+{
+ public:
+ /**
+ * Constructor, initializes the progress with the given value.
+ */
+ ProgressBar(float progress = 0.0f,
+ int width = 40, int height = 7,
+ int color = -1);
+
+ ~ProgressBar();
+
+ /**
+ * Performs progress bar logic (fading colors)
+ */
+ void logic();
+
+ /**
+ * Update the alpha value to the graphic components.
+ */
+ void updateAlpha();
+
+ /**
+ * Draws the progress bar.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Sets the current progress.
+ */
+ void setProgress(float progress);
+
+ /**
+ * Returns the current progress.
+ */
+ float getProgress() const { return mProgress; }
+
+ /**
+ * Change the ProgressPalette for this ProgressBar to follow or -1 to
+ * disable this and manage color manually.
+ */
+ void setProgressPalette(int progressPalette);
+
+ /**
+ * Change the color of the progress bar.
+ */
+ void setColor(const gcn::Color &color);
+
+ /**
+ * Returns the color of the progress bar.
+ */
+ const gcn::Color &getColor() const { return mColor; }
+
+ /**
+ * Sets the text shown on the progress bar.
+ */
+ void setText(const std::string &text)
+ { mText = text; }
+
+ /**
+ * Returns the text shown on the progress bar.
+ */
+ const std::string &text() const
+ { return mText; }
+
+ /**
+ * Set whether the progress is moved smoothly.
+ */
+ void setSmoothProgress(bool smoothProgress)
+ { mSmoothProgress = smoothProgress; }
+
+ /**
+ * Set whether the color changing is made smoothly.
+ */
+ void setSmoothColorChange(bool smoothColorChange)
+ { mSmoothColorChange = smoothColorChange; }
+
+ /**
+ * Renders a progressbar with the given properties.
+ */
+ static void render(Graphics *graphics, const gcn::Rectangle &area,
+ const gcn::Color &color, float progress,
+ const std::string &text = "");
+
+ private:
+ float mProgress, mProgressToGo;
+ bool mSmoothProgress;
+
+ int mProgressPalette; /** < Entry in ProgressPalette or -1 for none. */
+ gcn::Color mColor;
+ gcn::Color mColorToGo;
+ bool mSmoothColorChange;
+
+ std::string mText;
+
+ static ImageRect mBorder;
+ static int mInstances;
+ static float mAlpha;
+
+ static const gcn::Color TEXT_COLOR;
+};
+
+#endif
diff --git a/src/gui/widgets/progressindicator.cpp b/src/gui/widgets/progressindicator.cpp
new file mode 100644
index 000000000..9f1a8f032
--- /dev/null
+++ b/src/gui/widgets/progressindicator.cpp
@@ -0,0 +1,78 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 "progressindicator.h"
+
+#include "graphics.h"
+#include "simpleanimation.h"
+
+#include "gui/theme.h"
+
+#include "resources/animation.h"
+#include "resources/imageset.h"
+#include "resources/resourcemanager.h"
+
+#include <guichan/widgets/label.hpp>
+
+ProgressIndicator::ProgressIndicator()
+{
+ ImageSet *images = Theme::getImageSetFromTheme("progress-indicator.png",
+ 32, 32);
+
+ Animation *anim = new Animation;
+ if (images)
+ {
+ for (ImageSet::size_type i = 0; i < images->size(); ++i)
+ anim->addFrame(images->get(i), 100, 0, 0);
+
+ mIndicator = new SimpleAnimation(anim);
+
+ images->decRef();
+ }
+ else
+ {
+ mIndicator = 0;
+ }
+
+ setSize(32, 32);
+}
+
+ProgressIndicator::~ProgressIndicator()
+{
+ delete mIndicator;
+ mIndicator = 0;
+}
+
+void ProgressIndicator::logic()
+{
+ if (mIndicator)
+ mIndicator->update(10);
+}
+
+void ProgressIndicator::draw(gcn::Graphics *graphics)
+{
+ if (mIndicator)
+ {
+ // Draw the indicator centered on the widget
+ const int x = (getWidth() - 32) / 2;
+ const int y = (getHeight() - 32) / 2;
+ mIndicator->draw(static_cast<Graphics*>(graphics), x, y);
+ }
+}
diff --git a/src/gui/widgets/progressindicator.h b/src/gui/widgets/progressindicator.h
new file mode 100644
index 000000000..b990d8bef
--- /dev/null
+++ b/src/gui/widgets/progressindicator.h
@@ -0,0 +1,45 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 PROGRESSINDICATOR_H
+#define PROGRESSINDICATOR_H
+
+#include <guichan/widget.hpp>
+
+class SimpleAnimation;
+
+/**
+ * A widget that indicates progress. Suitable to use instead of a progress bar
+ * in cases where it is unknown how long something is going to take.
+ */
+class ProgressIndicator : public gcn::Widget
+{
+public:
+ ProgressIndicator();
+ ~ProgressIndicator();
+
+ void logic();
+ void draw(gcn::Graphics *graphics);
+
+private:
+ SimpleAnimation *mIndicator;
+};
+
+#endif // PROGRESSINDICATOR_H
diff --git a/src/gui/widgets/radiobutton.cpp b/src/gui/widgets/radiobutton.cpp
new file mode 100644
index 000000000..c9738e3cd
--- /dev/null
+++ b/src/gui/widgets/radiobutton.cpp
@@ -0,0 +1,163 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/radiobutton.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+int RadioButton::instances = 0;
+float RadioButton::mAlpha = 1.0;
+Image *RadioButton::radioNormal;
+Image *RadioButton::radioChecked;
+Image *RadioButton::radioDisabled;
+Image *RadioButton::radioDisabledChecked;
+Image *RadioButton::radioNormalHi;
+Image *RadioButton::radioCheckedHi;
+
+RadioButton::RadioButton(const std::string &caption, const std::string &group,
+ bool marked):
+ gcn::RadioButton(caption, group, marked),
+ mHasMouse(false)
+{
+ if (instances == 0)
+ {
+ radioNormal = Theme::getImageFromTheme("radioout.png");
+ radioChecked = Theme::getImageFromTheme("radioin.png");
+ radioDisabled = Theme::getImageFromTheme("radioout.png");
+ radioDisabledChecked = Theme::getImageFromTheme("radioin.png");
+ radioNormalHi = Theme::getImageFromTheme("radioout_highlight.png");
+ radioCheckedHi = Theme::getImageFromTheme("radioin_highlight.png");
+ if (radioNormal)
+ radioNormal->setAlpha(mAlpha);
+ if (radioChecked)
+ radioChecked->setAlpha(mAlpha);
+ if (radioDisabled)
+ radioDisabled->setAlpha(mAlpha);
+ if (radioDisabledChecked)
+ radioDisabledChecked->setAlpha(mAlpha);
+ if (radioNormalHi)
+ radioNormalHi->setAlpha(mAlpha);
+ if (radioCheckedHi)
+ radioCheckedHi->setAlpha(mAlpha);
+ }
+
+ instances++;
+}
+
+RadioButton::~RadioButton()
+{
+ instances--;
+
+ if (instances == 0)
+ {
+ if (radioNormal)
+ radioNormal->decRef();
+ if (radioChecked)
+ radioChecked->decRef();
+ if (radioDisabled)
+ radioDisabled->decRef();
+ if (radioDisabledChecked)
+ radioDisabledChecked->decRef();
+ if (radioNormalHi)
+ radioNormalHi->decRef();
+ if (radioCheckedHi)
+ radioCheckedHi->decRef();
+ }
+}
+
+void RadioButton::drawBox(gcn::Graphics* graphics)
+{
+ if (Client::getGuiAlpha() != mAlpha)
+ {
+ mAlpha = Client::getGuiAlpha();
+ if (radioNormal)
+ radioNormal->setAlpha(mAlpha);
+ if (radioChecked)
+ radioChecked->setAlpha(mAlpha);
+ if (radioDisabled)
+ radioDisabled->setAlpha(mAlpha);
+ if (radioDisabledChecked)
+ radioDisabledChecked->setAlpha(mAlpha);
+ if (radioNormalHi)
+ radioNormalHi->setAlpha(mAlpha);
+ if (radioCheckedHi)
+ radioCheckedHi->setAlpha(mAlpha);
+ }
+
+ Image *box = NULL;
+
+ if (isEnabled())
+ {
+ if (isSelected())
+ if (mHasMouse)
+ box = radioCheckedHi;
+ else
+ box = radioChecked;
+ else
+ if (mHasMouse)
+ box = radioNormalHi;
+ else
+ box = radioNormal;
+ }
+ else
+ {
+ if (isSelected())
+ box = radioDisabledChecked;
+ else
+ box = radioDisabled;
+ }
+
+ if (box)
+ static_cast<Graphics*>(graphics)->drawImage(box, 2, 2);
+}
+
+void RadioButton::draw(gcn::Graphics* graphics)
+{
+ graphics->pushClipArea(gcn::Rectangle(1, 1, getWidth() - 1,
+ getHeight() - 1));
+
+ drawBox(graphics);
+
+ graphics->popClipArea();
+
+ graphics->setFont(getFont());
+ graphics->setColor(getForegroundColor());
+
+ int h = getHeight() + getHeight() / 2;
+ graphics->drawText(getCaption(), h - 2, 0);
+}
+
+void RadioButton::mouseEntered(gcn::MouseEvent& event _UNUSED_)
+{
+ mHasMouse = true;
+}
+
+void RadioButton::mouseExited(gcn::MouseEvent& event _UNUSED_)
+{
+ mHasMouse = false;
+}
+
diff --git a/src/gui/widgets/radiobutton.h b/src/gui/widgets/radiobutton.h
new file mode 100644
index 000000000..b7383aa7c
--- /dev/null
+++ b/src/gui/widgets/radiobutton.h
@@ -0,0 +1,85 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 RADIOBUTTON_H
+#define RADIOBUTTON_H
+
+#include <guichan/widgets/radiobutton.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+
+/**
+ * Guichan based RadioButton with custom look
+ */
+class RadioButton : public gcn::RadioButton
+{
+ public:
+ /**
+ * Constructor.
+ */
+ RadioButton(const std::string &caption, const std::string &group,
+ bool marked = false);
+
+ /**
+ * Destructor.
+ */
+ ~RadioButton();
+
+ /**
+ * Draws the radiobutton, not the caption.
+ */
+ void drawBox(gcn::Graphics* graphics);
+
+ /**
+ * Implementation of the draw methods.
+ * Thus, avoiding the rhomb around the radio button.
+ */
+ void draw(gcn::Graphics* graphics);
+
+ /**
+ * Called when the mouse enteres the widget area.
+ */
+ void mouseEntered(gcn::MouseEvent& event);
+
+ /**
+ * Called when the mouse leaves the widget area.
+ */
+ void mouseExited(gcn::MouseEvent& event);
+
+ private:
+ static int instances;
+ static float mAlpha;
+ bool mHasMouse;
+ static Image *radioNormal;
+ static Image *radioChecked;
+ static Image *radioDisabled;
+ static Image *radioDisabledChecked;
+ static Image *radioNormalHi;
+ static Image *radioCheckedHi;
+};
+
+#endif // RADIOBUTTON_H
diff --git a/src/gui/widgets/resizegrip.cpp b/src/gui/widgets/resizegrip.cpp
new file mode 100644
index 000000000..e8ccd0740
--- /dev/null
+++ b/src/gui/widgets/resizegrip.cpp
@@ -0,0 +1,82 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/resizegrip.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+#include <guichan/graphics.hpp>
+
+Image *ResizeGrip::gripImage = 0;
+int ResizeGrip::mInstances = 0;
+float ResizeGrip::mAlpha = 1.0;
+
+ResizeGrip::ResizeGrip(const std::string &image)
+{
+ if (mInstances == 0)
+ {
+ // Load the grip image
+ gripImage = Theme::getImageFromTheme(image);
+ if (gripImage)
+ gripImage->setAlpha(mAlpha);
+ }
+
+ mInstances++;
+
+ if (gripImage)
+ {
+ setWidth(gripImage->getWidth() + 2);
+ setHeight(gripImage->getHeight() + 2);
+ }
+ else
+ {
+ setWidth(2);
+ setHeight(2);
+ }
+}
+
+ResizeGrip::~ResizeGrip()
+{
+ mInstances--;
+
+ if (mInstances == 0 && gripImage)
+ gripImage->decRef();
+}
+
+void ResizeGrip::draw(gcn::Graphics *graphics)
+{
+ if (!gripImage)
+ return;
+
+ if (Client::getGuiAlpha() != mAlpha)
+ {
+ mAlpha = Client::getGuiAlpha();
+ gripImage->setAlpha(mAlpha);
+ }
+
+ static_cast<Graphics*>(graphics)->drawImage(gripImage, 0, 0);
+}
diff --git a/src/gui/widgets/resizegrip.h b/src/gui/widgets/resizegrip.h
new file mode 100644
index 000000000..5ef93f297
--- /dev/null
+++ b/src/gui/widgets/resizegrip.h
@@ -0,0 +1,60 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 RESIZEGRIP_H
+#define RESIZEGRIP_H
+
+#include <guichan/widget.hpp>
+
+class Image;
+
+/**
+ * Resize grip. The resize grip is part of a resizable Window. It relies on the
+ * fact that uncaught mouse events are automatically routed to the parent
+ * window.
+ *
+ * \ingroup GUI
+ */
+class ResizeGrip : public gcn::Widget
+{
+ public:
+ /**
+ * Constructor.
+ */
+ ResizeGrip(const std::string &image = "resize.png");
+
+ /**
+ * Destructor.
+ */
+ ~ResizeGrip();
+
+ /**
+ * Draws the resize grip.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ private:
+ static Image *gripImage; /**< Resize grip image */
+ static int mInstances; /**< Number of resize grip instances */
+ static float mAlpha;
+};
+
+#endif
diff --git a/src/gui/widgets/scrollarea.cpp b/src/gui/widgets/scrollarea.cpp
new file mode 100644
index 000000000..187794b1d
--- /dev/null
+++ b/src/gui/widgets/scrollarea.cpp
@@ -0,0 +1,445 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/scrollarea.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+#include "log.h"
+
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+#include "utils/dtor.h"
+
+int ScrollArea::instances = 0;
+float ScrollArea::mAlpha = 1.0;
+ImageRect ScrollArea::background;
+ImageRect ScrollArea::vMarker;
+ImageRect ScrollArea::vMarkerHi;
+Image *ScrollArea::buttons[4][2];
+
+ScrollArea::ScrollArea():
+ gcn::ScrollArea(),
+ mX(0),
+ mY(0),
+ mHasMouse(false),
+ mOpaque(true)
+{
+ addWidgetListener(this);
+ init();
+}
+
+ScrollArea::ScrollArea(gcn::Widget *widget):
+ gcn::ScrollArea(widget),
+ mX(0),
+ mY(0),
+ mHasMouse(false),
+ mOpaque(true)
+{
+ init();
+}
+
+ScrollArea::~ScrollArea()
+{
+ // Garbage collection
+ delete getContent();
+
+ instances--;
+
+ if (instances == 0)
+ {
+ for_each(background.grid, background.grid + 9, dtor<Image*>());
+ for_each(vMarker.grid, vMarker.grid + 9, dtor<Image*>());
+ for_each(vMarkerHi.grid, vMarkerHi.grid + 9, dtor<Image*>());
+
+ if (buttons[UP][0])
+ buttons[UP][0]->decRef();
+ if (buttons[UP][1])
+ buttons[UP][1]->decRef();
+ if (buttons[DOWN][0])
+ buttons[DOWN][0]->decRef();
+ if (buttons[DOWN][1])
+ buttons[DOWN][1]->decRef();
+ if (buttons[LEFT][0])
+ buttons[LEFT][0]->decRef();
+ if (buttons[LEFT][1])
+ buttons[LEFT][1]->decRef();
+ if (buttons[RIGHT][0])
+ buttons[RIGHT][0]->decRef();
+ if (buttons[RIGHT][1])
+ buttons[RIGHT][1]->decRef();
+ }
+}
+
+void ScrollArea::init()
+{
+ // Draw background by default
+ setOpaque(true);
+
+ setUpButtonScrollAmount(2);
+ setDownButtonScrollAmount(2);
+ setLeftButtonScrollAmount(2);
+ setRightButtonScrollAmount(2);
+
+ if (instances == 0)
+ {
+ // Load the background skin
+ Image *textbox = Theme::getImageFromTheme("deepbox.png");
+ const int bggridx[4] = {0, 3, 28, 31};
+ const int bggridy[4] = {0, 3, 28, 31};
+ int a = 0, x, y;
+
+ for (y = 0; y < 3; y++)
+ {
+ for (x = 0; x < 3; x++)
+ {
+ if (textbox)
+ {
+ background.grid[a] = textbox->getSubImage(
+ bggridx[x], bggridy[y],
+ bggridx[x + 1] - bggridx[x] + 1,
+ bggridy[y + 1] - bggridy[y] + 1);
+ background.grid[a]->setAlpha(
+ Client::getGuiAlpha());
+ }
+ else
+ {
+ background.grid[a] = 0;
+ }
+ a++;
+ }
+ }
+
+ textbox->decRef();
+
+ // Load vertical scrollbar skin
+ Image *vscroll = Theme::getImageFromTheme("vscroll_grey.png");
+ Image *vscrollHi = Theme::getImageFromTheme("vscroll_highlight.png");
+
+ int vsgridx[4] = {0, 4, 7, 11};
+ int vsgridy[4] = {0, 4, 15, 19};
+ a = 0;
+
+ for (y = 0; y < 3; y++)
+ {
+ for (x = 0; x < 3; x++)
+ {
+ if (vscroll)
+ {
+ vMarker.grid[a] = vscroll->getSubImage(
+ vsgridx[x], vsgridy[y],
+ vsgridx[x + 1] - vsgridx[x],
+ vsgridy[y + 1] - vsgridy[y]);
+ vMarker.grid[a]->setAlpha(
+ Client::getGuiAlpha());
+ }
+ else
+ {
+ vMarker.grid[a] = 0;
+ }
+ if (vscrollHi)
+ {
+ vMarkerHi.grid[a] = vscrollHi->getSubImage(
+ vsgridx[x], vsgridy[y],
+ vsgridx[x + 1] - vsgridx[x],
+ vsgridy[y + 1] - vsgridy[y]);
+ vMarkerHi.grid[a]->setAlpha(
+ Client::getGuiAlpha());
+ }
+ else
+ {
+ vMarkerHi.grid[a] = 0;
+ }
+ a++;
+ }
+ }
+
+ if (vscroll)
+ vscroll->decRef();
+ if (vscrollHi)
+ vscrollHi->decRef();
+
+ buttons[UP][0] =
+ Theme::getImageFromTheme("vscroll_up_default.png");
+ buttons[DOWN][0] =
+ Theme::getImageFromTheme("vscroll_down_default.png");
+ buttons[LEFT][0] =
+ Theme::getImageFromTheme("hscroll_left_default.png");
+ buttons[RIGHT][0] =
+ Theme::getImageFromTheme("hscroll_right_default.png");
+ buttons[UP][1] =
+ Theme::getImageFromTheme("vscroll_up_pressed.png");
+ buttons[DOWN][1] =
+ Theme::getImageFromTheme("vscroll_down_pressed.png");
+ buttons[LEFT][1] =
+ Theme::getImageFromTheme("hscroll_left_pressed.png");
+ buttons[RIGHT][1] =
+ Theme::getImageFromTheme("hscroll_right_pressed.png");
+ }
+
+ instances++;
+}
+
+void ScrollArea::logic()
+{
+ if (!isVisible())
+ return;
+
+ gcn::ScrollArea::logic();
+ gcn::Widget *content = getContent();
+
+ // When no scrollbar in a certain direction, adapt content size to match
+ // the content dimension exactly.
+ if (content)
+ {
+ if (getHorizontalScrollPolicy() == gcn::ScrollArea::SHOW_NEVER)
+ {
+ content->setWidth(getChildrenArea().width -
+ 2 * content->getFrameSize());
+ }
+ if (getVerticalScrollPolicy() == gcn::ScrollArea::SHOW_NEVER)
+ {
+ content->setHeight(getChildrenArea().height -
+ 2 * content->getFrameSize());
+ }
+ }
+
+ if (mUpButtonPressed)
+ {
+ setVerticalScrollAmount(getVerticalScrollAmount() -
+ mUpButtonScrollAmount);
+ }
+ else if (mDownButtonPressed)
+ {
+ setVerticalScrollAmount(getVerticalScrollAmount() +
+ mDownButtonScrollAmount);
+ }
+ else if (mLeftButtonPressed)
+ {
+ setHorizontalScrollAmount(getHorizontalScrollAmount() -
+ mLeftButtonScrollAmount);
+ }
+ else if (mRightButtonPressed)
+ {
+ setHorizontalScrollAmount(getHorizontalScrollAmount() +
+ mRightButtonScrollAmount);
+ }
+}
+
+void ScrollArea::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ if (alpha != mAlpha)
+ {
+ mAlpha = alpha;
+ for (int a = 0; a < 9; a++)
+ {
+ if (background.grid[a])
+ background.grid[a]->setAlpha(mAlpha);
+ if (vMarker.grid[a])
+ vMarker.grid[a]->setAlpha(mAlpha);
+ if (vMarkerHi.grid[a])
+ vMarkerHi.grid[a]->setAlpha(mAlpha);
+ }
+ }
+}
+
+void ScrollArea::draw(gcn::Graphics *graphics)
+{
+ if (mVBarVisible)
+ {
+ drawUpButton(graphics);
+ drawDownButton(graphics);
+ drawVBar(graphics);
+ drawVMarker(graphics);
+ }
+
+ if (mHBarVisible)
+ {
+ drawLeftButton(graphics);
+ drawRightButton(graphics);
+ drawHBar(graphics);
+ drawHMarker(graphics);
+ }
+
+ if (mHBarVisible && mVBarVisible)
+ {
+ graphics->setColor(getBaseColor());
+ graphics->fillRectangle(gcn::Rectangle(getWidth() - mScrollbarWidth,
+ getHeight() - mScrollbarWidth,
+ mScrollbarWidth,
+ mScrollbarWidth));
+ }
+
+ updateAlpha();
+
+ drawChildren(graphics);
+}
+
+void ScrollArea::drawFrame(gcn::Graphics *graphics)
+{
+ if (mOpaque)
+ {
+ const int bs = getFrameSize();
+ const int w = getWidth() + bs * 2;
+ const int h = getHeight() + bs * 2;
+
+ static_cast<Graphics*>(graphics)->
+ drawImageRect(0, 0, w, h, background);
+ }
+}
+
+void ScrollArea::setOpaque(bool opaque)
+{
+ mOpaque = opaque;
+ setFrameSize(mOpaque ? 2 : 0);
+}
+
+void ScrollArea::drawButton(gcn::Graphics *graphics, BUTTON_DIR dir)
+{
+ int state = 0;
+ gcn::Rectangle dim;
+
+ switch (dir)
+ {
+ case UP:
+ state = mUpButtonPressed ? 1 : 0;
+ dim = getUpButtonDimension();
+ break;
+ case DOWN:
+ state = mDownButtonPressed ? 1 : 0;
+ dim = getDownButtonDimension();
+ break;
+ case LEFT:
+ state = mLeftButtonPressed ? 1 : 0;
+ dim = getLeftButtonDimension();
+ break;
+ case RIGHT:
+ state = mRightButtonPressed ? 1 : 0;
+ dim = getRightButtonDimension();
+ break;
+ default:
+ logger->log("ScrollArea::drawButton unknown dir: "
+ + toString(static_cast<unsigned>(dir)));
+ break;
+ }
+
+ if (buttons[dir][state])
+ {
+ static_cast<Graphics*>(graphics)->
+ drawImage(buttons[dir][state], dim.x, dim.y);
+ }
+}
+
+void ScrollArea::drawUpButton(gcn::Graphics *graphics)
+{
+ drawButton(graphics, UP);
+}
+
+void ScrollArea::drawDownButton(gcn::Graphics *graphics)
+{
+ drawButton(graphics, DOWN);
+}
+
+void ScrollArea::drawLeftButton(gcn::Graphics *graphics)
+{
+ drawButton(graphics, LEFT);
+}
+
+void ScrollArea::drawRightButton(gcn::Graphics *graphics)
+{
+ drawButton(graphics, RIGHT);
+}
+
+void ScrollArea::drawVBar(gcn::Graphics *graphics)
+{
+ const gcn::Rectangle dim = getVerticalBarDimension();
+ graphics->setColor(gcn::Color(0, 0, 0, 32));
+ graphics->fillRectangle(dim);
+ graphics->setColor(gcn::Color(255, 255, 255));
+}
+
+void ScrollArea::drawHBar(gcn::Graphics *graphics)
+{
+ const gcn::Rectangle dim = getHorizontalBarDimension();
+ graphics->setColor(gcn::Color(0, 0, 0, 32));
+ graphics->fillRectangle(dim);
+ graphics->setColor(gcn::Color(255, 255, 255));
+}
+
+void ScrollArea::drawVMarker(gcn::Graphics *graphics)
+{
+ gcn::Rectangle dim = getVerticalMarkerDimension();
+
+ if ((mHasMouse) && (mX > (getWidth() - getScrollbarWidth())))
+ {
+ static_cast<Graphics*>(graphics)->
+ drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarkerHi);
+ }
+ else
+ {
+ static_cast<Graphics*>(graphics)->
+ drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarker);
+ }
+}
+
+void ScrollArea::drawHMarker(gcn::Graphics *graphics)
+{
+ gcn::Rectangle dim = getHorizontalMarkerDimension();
+
+ if ((mHasMouse) && (mY > (getHeight() - getScrollbarWidth())))
+ {
+ static_cast<Graphics*>(graphics)->
+ drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarkerHi);
+ }
+ else
+ {
+ static_cast<Graphics*>(graphics)->
+ drawImageRect(dim.x, dim.y, dim.width, dim.height, vMarker);
+ }
+}
+
+void ScrollArea::mouseMoved(gcn::MouseEvent& event)
+{
+ mX = event.getX();
+ mY = event.getY();
+}
+
+void ScrollArea::mouseEntered(gcn::MouseEvent& event _UNUSED_)
+{
+ mHasMouse = true;
+}
+
+void ScrollArea::mouseExited(gcn::MouseEvent& event _UNUSED_)
+{
+ mHasMouse = false;
+}
+
+void ScrollArea::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ getContent()->setSize(getWidth() - 2 * getFrameSize(),
+ getHeight() - 2 * getFrameSize());
+}
diff --git a/src/gui/widgets/scrollarea.h b/src/gui/widgets/scrollarea.h
new file mode 100644
index 000000000..4f6a07f82
--- /dev/null
+++ b/src/gui/widgets/scrollarea.h
@@ -0,0 +1,151 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 SCROLLAREA_H
+#define SCROLLAREA_H
+
+#include <guichan/widgets/scrollarea.hpp>
+#include <guichan/widgetlistener.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+class ImageRect;
+
+/**
+ * A scroll area.
+ *
+ * Contrary to Guichan's scroll area, this scroll area takes ownership over its
+ * content. However, it won't delete a previously set content widget when
+ * setContent is called!
+ *
+ * \ingroup GUI
+ */
+class ScrollArea : public gcn::ScrollArea, public gcn::WidgetListener
+{
+ public:
+ /**
+ * Constructor that takes no content. Needed for use with the DropDown
+ * class.
+ */
+ ScrollArea();
+
+ /**
+ * Constructor.
+ *
+ * @param content the initial content to show in the scroll area
+ */
+ ScrollArea(gcn::Widget *content);
+
+ /**
+ * Destructor. Also deletes the content.
+ */
+ ~ScrollArea();
+
+ /**
+ * Logic function optionally adapts width or height of contents. This
+ * depends on the scrollbar settings.
+ */
+ void logic();
+
+ /**
+ * Update the alpha value to the graphic components.
+ */
+ void updateAlpha();
+
+ /**
+ * Draws the scroll area.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Draws the background and border of the scroll area.
+ */
+ void drawFrame(gcn::Graphics *graphics);
+
+ /**
+ * Sets whether the widget should draw its background or not.
+ */
+ void setOpaque(bool opaque);
+
+ /**
+ * Returns whether the widget draws its background or not.
+ */
+ bool isOpaque() const { return mOpaque; }
+
+ /**
+ * Called when the mouse moves in the widget area.
+ */
+ void mouseMoved(gcn::MouseEvent& event);
+
+ /**
+ * Called when the mouse enteres the widget area.
+ */
+ void mouseEntered(gcn::MouseEvent& event);
+
+ /**
+ * Called when the mouse leaves the widget area.
+ */
+ void mouseExited(gcn::MouseEvent& event);
+
+ void widgetResized(const gcn::Event &event);
+
+ protected:
+ enum BUTTON_DIR
+ {
+ UP = 0,
+ DOWN,
+ LEFT,
+ RIGHT
+ };
+
+ /**
+ * Initializes the scroll area.
+ */
+ void init();
+
+ void drawButton(gcn::Graphics *graphics, BUTTON_DIR dir);
+ void drawUpButton(gcn::Graphics *graphics);
+ void drawDownButton(gcn::Graphics *graphics);
+ void drawLeftButton(gcn::Graphics *graphics);
+ void drawRightButton(gcn::Graphics *graphics);
+ void drawVBar(gcn::Graphics *graphics);
+ void drawHBar(gcn::Graphics *graphics);
+ void drawVMarker(gcn::Graphics *graphics);
+ void drawHMarker(gcn::Graphics *graphics);
+
+ static int instances;
+ static float mAlpha;
+ static ImageRect background;
+ static ImageRect vMarker;
+ static ImageRect vMarkerHi;
+ static Image *buttons[4][2];
+
+ int mX, mY;
+ bool mHasMouse;
+ bool mOpaque;
+};
+
+#endif
diff --git a/src/gui/widgets/setuptab.cpp b/src/gui/widgets/setuptab.cpp
new file mode 100644
index 000000000..3c10e6d93
--- /dev/null
+++ b/src/gui/widgets/setuptab.cpp
@@ -0,0 +1,31 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/setuptab.h"
+
+SetupTab::SetupTab()
+{
+ setOpaque(false);
+}
+
+void SetupTab::externalUpdated()
+{
+} \ No newline at end of file
diff --git a/src/gui/widgets/setuptab.h b/src/gui/widgets/setuptab.h
new file mode 100644
index 000000000..2d8742123
--- /dev/null
+++ b/src/gui/widgets/setuptab.h
@@ -0,0 +1,64 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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_SETUPTAB_H
+#define GUI_SETUPTAB_H
+
+#include "gui/widgets/container.h"
+
+#include <string>
+
+/**
+ * A container for the contents of a tab in the setup window.
+ */
+class SetupTab : public Container
+{
+public:
+ SetupTab();
+
+ const std::string &getName() const
+ { return mName; }
+
+ /**
+ * Called when the Apply button is pressed in the setup window.
+ */
+ virtual void apply() = 0;
+
+ /**
+ * Called when the Cancel button is pressed in the setup window.
+ */
+ virtual void cancel() = 0;
+
+ virtual void externalUpdated();
+
+protected:
+ /**
+ * Sets the name displayed on the tab. Should be set in the
+ * constructor of a subclass.
+ */
+ void setName(const std::string &name)
+ { mName = name; }
+
+private:
+ std::string mName;
+};
+
+#endif
diff --git a/src/gui/widgets/shopitems.cpp b/src/gui/widgets/shopitems.cpp
new file mode 100644
index 000000000..0aff3d5b9
--- /dev/null
+++ b/src/gui/widgets/shopitems.cpp
@@ -0,0 +1,118 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/shopitems.h"
+
+#include "shopitem.h"
+
+#include "utils/dtor.h"
+
+ShopItems::ShopItems(bool mergeDuplicates) :
+ mMergeDuplicates(mergeDuplicates)
+{
+}
+
+ShopItems::~ShopItems()
+{
+ clear();
+}
+
+int ShopItems::getNumberOfElements()
+{
+ return static_cast<int>(mShopItems.size());
+}
+
+std::string ShopItems::getElementAt(int i)
+{
+ if (i < 0 || (unsigned)i >= mShopItems.size() || !mShopItems.at(i))
+ return "";
+
+ return mShopItems.at(i)->getDisplayName();
+}
+
+void ShopItems::addItem(int id, int amount, int price)
+{
+ mShopItems.push_back(new ShopItem(-1, id, amount, price));
+}
+
+void ShopItems::addItemNoDup(int id, int amount, int price)
+{
+ ShopItem *item = findItem(id);
+ if (!item)
+ mShopItems.push_back(new ShopItem(-1, id, amount, price));
+}
+
+void ShopItems::addItem(int inventoryIndex, int id, int quantity, int price)
+{
+ ShopItem *item = 0;
+ if (mMergeDuplicates)
+ item = findItem(id);
+
+ if (item)
+ {
+ item->addDuplicate (inventoryIndex, quantity);
+ }
+ else
+ {
+ item = new ShopItem(inventoryIndex, id, quantity, price);
+ mShopItems.push_back(item);
+ }
+}
+
+ShopItem *ShopItems::at(unsigned int i) const
+{
+ if (i >= mShopItems.size())
+ return 0;
+
+ return mShopItems.at(i);
+}
+
+void ShopItems::erase(unsigned int i)
+{
+ if (i >= mShopItems.size())
+ return;
+
+ mShopItems.erase(mShopItems.begin() + i);
+}
+
+void ShopItems::clear()
+{
+ delete_all(mShopItems);
+ mShopItems.clear();
+}
+
+ShopItem *ShopItems::findItem(int id)
+{
+ ShopItem *item;
+
+ std::vector<ShopItem*>::iterator it = mShopItems.begin();
+ std::vector<ShopItem*>::iterator e = mShopItems.end();
+ while (it != e)
+ {
+ item = *(it);
+ if (item->getId() == id)
+ return item;
+
+ ++it;
+ }
+
+ return 0;
+}
diff --git a/src/gui/widgets/shopitems.h b/src/gui/widgets/shopitems.h
new file mode 100644
index 000000000..ba325bfa5
--- /dev/null
+++ b/src/gui/widgets/shopitems.h
@@ -0,0 +1,120 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 SHOPITEMS_H
+#define SHOPITEMS_H
+
+#include <guichan/listmodel.hpp>
+
+#include <string>
+#include <vector>
+
+class ShopItem;
+
+/**
+ * This class handles the list of items available in a shop.
+ *
+ * The addItem routine can automatically check, if an item already exists and
+ * only adds duplicates to the old item, if one is found. The original
+ * distribution of the duplicates can be retrieved from the item.
+ *
+ * This functionality can be enabled in the constructor.
+ */
+class ShopItems : public gcn::ListModel
+{
+ public:
+ /**
+ * Constructor.
+ *
+ * @param mergeDuplicates lets the Shop look for duplicate entries and
+ * merges them to one item.
+ */
+ ShopItems(bool mergeDuplicates = false);
+
+ ~ShopItems();
+
+ /**
+ * Adds an item to the list.
+ */
+ void addItem(int id, int amount, int price);
+
+ /**
+ * Adds an item to the list (used by sell dialog). Looks for
+ * duplicate entries, if mergeDuplicates was turned on.
+ *
+ * @param inventoryIndex the inventory index of the item
+ * @param id the id of the item
+ * @param quantity number of available copies of the item
+ * @param price price of the item
+ */
+ void addItem(int inventoryIndex, int id, int amount, int price);
+
+ void addItemNoDup(int id, int amount, int price);
+
+ /**
+ * Returns the number of items in the shop.
+ */
+ int getNumberOfElements();
+
+ /**
+ * Returns the name of item number i in the shop.
+ *
+ * @param i the index to retrieve
+ */
+ std::string getElementAt(int i);
+
+ /**
+ * Returns the item number i in the shop.
+ */
+ ShopItem *at(unsigned int i) const;
+
+ /**
+ * Removes an element from the shop.
+ *
+ * @param i index to remove
+ */
+ void erase(unsigned int i);
+
+ /**
+ * Clears the list of items in the shop.
+ */
+ void clear();
+
+ std::vector<ShopItem*> &items()
+ { return mShopItems; }
+
+ private:
+ /**
+ * Searches the current items in the shop for the specified
+ * id and returns the item if found, or 0 else.
+ *
+ * @return the item found or 0
+ */
+ ShopItem *findItem(int id);
+
+ /** The list of items in the shop. */
+ std::vector<ShopItem*> mShopItems;
+
+ /** Look for duplicate entries on addition. */
+ bool mMergeDuplicates;
+};
+
+#endif // SHOPITEMS_H
diff --git a/src/gui/widgets/shoplistbox.cpp b/src/gui/widgets/shoplistbox.cpp
new file mode 100644
index 000000000..a0577db03
--- /dev/null
+++ b/src/gui/widgets/shoplistbox.cpp
@@ -0,0 +1,185 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/shoplistbox.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+#include "shopitem.h"
+
+#include "gui/itempopup.h"
+#include "gui/theme.h"
+#include "gui/viewport.h"
+
+#include "gui/widgets/shopitems.h"
+
+#include "resources/image.h"
+
+#include <guichan/font.hpp>
+#include <guichan/listmodel.hpp>
+
+const int ITEM_ICON_SIZE = 32;
+
+float ShopListBox::mAlpha = 1.0;
+
+ShopListBox::ShopListBox(gcn::ListModel *listModel):
+ ListBox(listModel),
+ mPlayerMoney(0),
+ mShopItems(0)
+{
+ mRowHeight = getFont()->getHeight();
+ mPriceCheck = true;
+
+ mItemPopup = new ItemPopup;
+}
+
+ShopListBox::ShopListBox(gcn::ListModel *listModel, ShopItems *shopListModel):
+ ListBox(listModel),
+ mPlayerMoney(0),
+ mShopItems(shopListModel)
+{
+ mRowHeight = std::max(getFont()->getHeight(), ITEM_ICON_SIZE);
+ mPriceCheck = true;
+
+ mItemPopup = new ItemPopup;
+}
+
+void ShopListBox::setPlayersMoney(int money)
+{
+ mPlayerMoney = money;
+}
+
+void ShopListBox::draw(gcn::Graphics *gcnGraphics)
+{
+ if (!mListModel || !mShopItems)
+ return;
+
+ if (Client::getGuiAlpha() != mAlpha)
+ mAlpha = Client::getGuiAlpha();
+
+ int alpha = static_cast<int>(mAlpha * 255.0f);
+ const gcn::Color* highlightColor =
+ &Theme::getThemeColor(Theme::HIGHLIGHT, alpha);
+
+ Graphics *graphics = static_cast<Graphics*>(gcnGraphics);
+
+ graphics->setFont(getFont());
+
+ // Draw the list elements
+ for (int i = 0, y = 0;
+ i < mListModel->getNumberOfElements();
+ ++i, y += mRowHeight)
+ {
+ gcn::Color temp;
+ const gcn::Color* backgroundColor =
+ &Theme::getThemeColor(Theme::BACKGROUND, alpha);
+
+ if (mShopItems && mShopItems->at(i) &&
+ mPlayerMoney < mShopItems->at(i)->getPrice() && mPriceCheck)
+ {
+ if (i != mSelected)
+ {
+ backgroundColor = &Theme::getThemeColor(Theme::SHOP_WARNING,
+ alpha);
+ }
+ else
+ {
+ temp = Theme::getThemeColor(Theme::SHOP_WARNING, alpha);
+ temp.r = (temp.r + highlightColor->r) / 2;
+ temp.g = (temp.g + highlightColor->g) / 2;
+ temp.b = (temp.g + highlightColor->b) / 2;
+ backgroundColor = &temp;
+ }
+ }
+ else if (i == mSelected)
+ {
+ backgroundColor = highlightColor;
+ }
+
+ graphics->setColor(*backgroundColor);
+ graphics->fillRectangle(gcn::Rectangle(0, y, getWidth(), mRowHeight));
+
+ if (mShopItems)
+ {
+ Image *icon = mShopItems->at(i)->getImage();
+ if (icon)
+ {
+ icon->setAlpha(1.0f);
+ graphics->drawImage(icon, 1, y);
+ }
+ }
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ graphics->drawText(mListModel->getElementAt(i), ITEM_ICON_SIZE + 5,
+ y + (ITEM_ICON_SIZE - getFont()->getHeight()) / 2);
+ }
+}
+
+void ShopListBox::adjustSize()
+{
+ if (mListModel)
+ setHeight(mRowHeight * mListModel->getNumberOfElements());
+}
+
+void ShopListBox::setPriceCheck(bool check)
+{
+ mPriceCheck = check;
+}
+
+void ShopListBox::mouseMoved(gcn::MouseEvent &event)
+{
+ if (!mItemPopup)
+ return;
+
+ if (!mShopItems)
+ {
+ mItemPopup->hide();
+ return;
+ }
+
+ int index = event.getY() / mRowHeight;
+
+ if (index < 0 || index >= mShopItems->getNumberOfElements())
+ {
+ mItemPopup->hide();
+ }
+ else
+ {
+ Item *item = mShopItems->at(index);
+ if (item)
+ {
+ mItemPopup->setItem(item);
+ mItemPopup->position(viewport->getMouseX(), viewport->getMouseY());
+ }
+ else
+ {
+ mItemPopup->setVisible(false);
+ }
+ }
+}
+
+void ShopListBox::mouseExited(gcn::MouseEvent& mouseEvent _UNUSED_)
+{
+ if (!mItemPopup)
+ return;
+
+ mItemPopup->hide();
+} \ No newline at end of file
diff --git a/src/gui/widgets/shoplistbox.h b/src/gui/widgets/shoplistbox.h
new file mode 100644
index 000000000..ab77c5969
--- /dev/null
+++ b/src/gui/widgets/shoplistbox.h
@@ -0,0 +1,104 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 SHOPLISTBOX_H
+#define SHOPLISTBOX_H
+
+#include "gui/widgets/listbox.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class ShopItems;
+class ItemPopup;
+
+/**
+ * A list box, meant to be used inside a scroll area. Same as the Guichan list
+ * box except this one doesn't have a background, instead completely relying
+ * on the scroll area. It also adds selection listener functionality.
+ *
+ * \ingroup GUI
+ */
+class ShopListBox : public ListBox
+{
+ public:
+ /**
+ * Constructor.
+ */
+ ShopListBox(gcn::ListModel *listModel);
+
+ /**
+ * Constructor with shopitems
+ */
+ ShopListBox(gcn::ListModel *listModel, ShopItems *shopListModel);
+
+ /**
+ * Draws the list box.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Returns the height of a row.
+ */
+ unsigned int getRowHeight() const { return mRowHeight; }
+
+ /**
+ * gives information about the current player's money
+ */
+ void setPlayersMoney(int money);
+
+ /**
+ * Adjust List draw size
+ */
+ void adjustSize();
+
+ /**
+ * Set on/off the disabling of too expensive items.
+ * (Good for selling mode.)
+ */
+ void setPriceCheck(bool check);
+
+ void mouseMoved(gcn::MouseEvent &event);
+
+ void mouseExited(gcn::MouseEvent& mouseEvent _UNUSED_);
+
+ private:
+ int mPlayerMoney;
+
+ /**
+ * Keeps another pointer to the same listModel, permitting to
+ * use the ShopItems specific functions.
+ */
+ ShopItems *mShopItems;
+
+ ItemPopup *mItemPopup;
+
+ unsigned int mRowHeight; /**< Row Height */
+
+ static float mAlpha;
+
+ bool mPriceCheck;
+};
+
+#endif // SHOPLISTBOX_H
diff --git a/src/gui/widgets/shortcutcontainer.cpp b/src/gui/widgets/shortcutcontainer.cpp
new file mode 100644
index 000000000..167296410
--- /dev/null
+++ b/src/gui/widgets/shortcutcontainer.cpp
@@ -0,0 +1,67 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2007-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/shortcutcontainer.h"
+
+#include "configuration.h"
+
+#include "resources/image.h"
+
+#include "utils/stringutils.h"
+
+float ShortcutContainer::mAlpha = 1.0;
+
+ShortcutContainer::ShortcutContainer():
+ mGridWidth(1),
+ mGridHeight(1)
+{
+}
+
+void ShortcutContainer::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ mGridWidth = getWidth() / mBoxWidth;
+
+ if (mGridWidth < 1)
+ mGridWidth = 1;
+
+ mGridHeight = mMaxItems / mGridWidth;
+
+ if (mMaxItems % mGridWidth != 0 || mGridHeight < 1)
+ ++mGridHeight;
+
+ setHeight(mGridHeight * mBoxHeight);
+}
+
+int ShortcutContainer::getIndexFromGrid(int pointX, int pointY) const
+{
+ const gcn::Rectangle tRect = gcn::Rectangle(0, 0, mGridWidth * mBoxWidth,
+ mGridHeight * mBoxHeight);
+
+ int index = ((pointY / mBoxHeight) * mGridWidth) + pointX / mBoxWidth;
+
+ if (!tRect.isPointInRect(pointX, pointY) ||
+ index >= (int)mMaxItems || index < 0)
+ {
+ index = -1;
+ }
+
+ return index;
+}
diff --git a/src/gui/widgets/shortcutcontainer.h b/src/gui/widgets/shortcutcontainer.h
new file mode 100644
index 000000000..85d08d0b4
--- /dev/null
+++ b/src/gui/widgets/shortcutcontainer.h
@@ -0,0 +1,115 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2007-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 SHORTCUTCONTAINER_H
+#define SHORTCUTCONTAINER_H
+
+#include <guichan/mouselistener.hpp>
+#include <guichan/widget.hpp>
+#include <guichan/widgetlistener.hpp>
+
+#include "gui/widgets/tab.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+
+/**
+ * A generic shortcut container.
+ *
+ * \ingroup GUI
+ */
+class ShortcutContainer : public gcn::Widget,
+ public gcn::WidgetListener,
+ public gcn::MouseListener
+{
+ public:
+ /**
+ * Constructor. Initializes the shortcut container.
+ */
+ ShortcutContainer();
+
+ /**
+ * Destructor.
+ */
+ ~ShortcutContainer() {}
+
+ /**
+ * Draws the shortcuts
+ */
+ virtual void draw(gcn::Graphics *graphics) = 0;
+
+ /**
+ * Invoked when a widget changes its size. This is used to determine
+ * the new height of the container.
+ */
+ virtual void widgetResized(const gcn::Event &event);
+
+ /**
+ * Handles mouse when dragged.
+ */
+ virtual void mouseDragged(gcn::MouseEvent &event) = 0;
+
+ /**
+ * Handles mouse when pressed.
+ */
+ virtual void mousePressed(gcn::MouseEvent &event) = 0;
+
+ /**
+ * Handles mouse release.
+ */
+ virtual void mouseReleased(gcn::MouseEvent &event) = 0;
+
+ int getMaxItems() const
+ { return mMaxItems; }
+
+ int getBoxWidth() const
+ { return mBoxWidth; }
+
+ int getBoxHeight() const
+ { return mBoxHeight; }
+
+ protected:
+ /**
+ * Gets the index from the grid provided the point is in an item box.
+ *
+ * @param pointX X coordinate of the point.
+ * @param pointY Y coordinate of the point.
+ * @return index on success, -1 on failure.
+ */
+ int getIndexFromGrid(int pointX, int pointY) const;
+
+ Image *mBackgroundImg;
+
+ static float mAlpha;
+
+ unsigned mMaxItems;
+ int mBoxWidth;
+ int mBoxHeight;
+ int mCursorPosX, mCursorPosY;
+ int mGridWidth, mGridHeight;
+};
+
+#endif
diff --git a/src/gui/widgets/slider.cpp b/src/gui/widgets/slider.cpp
new file mode 100644
index 000000000..9513d5308
--- /dev/null
+++ b/src/gui/widgets/slider.cpp
@@ -0,0 +1,298 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/slider.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+Image *Slider::hStart, *Slider::hMid, *Slider::hEnd, *Slider::hGrip;
+Image *Slider::vStart, *Slider::vMid, *Slider::vEnd, *Slider::vGrip;
+Image *Slider::hStartHi, *Slider::hMidHi, *Slider::hEndHi, *Slider::hGripHi;
+Image *Slider::vStartHi, *Slider::vMidHi, *Slider::vEndHi, *Slider::vGripHi;
+float Slider::mAlpha = 1.0;
+int Slider::mInstances = 0;
+
+Slider::Slider(double scaleEnd):
+ gcn::Slider(scaleEnd),
+ mHasMouse(false)
+{
+ init();
+}
+
+Slider::Slider(double scaleStart, double scaleEnd):
+ gcn::Slider(scaleStart, scaleEnd),
+ mHasMouse(false)
+{
+ init();
+}
+
+Slider::~Slider()
+{
+ mInstances--;
+
+ if (mInstances == 0)
+ {
+ delete hStart;
+ delete hMid;
+ delete hEnd;
+ delete hGrip;
+ delete vStart;
+ delete vMid;
+ delete vEnd;
+ delete vGrip;
+ delete hStartHi;
+ delete hMidHi;
+ delete hEndHi;
+ delete hGripHi;
+ delete vStartHi;
+ delete vMidHi;
+ delete vEndHi;
+ delete vGripHi;
+ }
+}
+
+void Slider::init()
+{
+ setFrameSize(0);
+
+ // Load resources
+ if (mInstances == 0)
+ {
+ int x, y, w, h, o1, o2;
+
+ Image *slider = Theme::getImageFromTheme("slider.png");
+ Image *sliderHi = Theme::getImageFromTheme("slider_hilight.png");
+
+ x = 0; y = 0;
+ w = 15; h = 6;
+ o1 = 4; o2 = 11;
+ if (slider)
+ {
+ hStart = slider->getSubImage(x, y, o1 - x, h);
+ hMid = slider->getSubImage(o1, y, o2 - o1, h);
+ hEnd = slider->getSubImage(o2, y, w - o2 + x, h);
+ }
+ else
+ {
+ hStart = 0;
+ hMid = 0;
+ hEnd = 0;
+ }
+ if (sliderHi)
+ {
+ hStartHi = sliderHi->getSubImage(x, y, o1 - x, h);
+ hMidHi = sliderHi->getSubImage(o1, y, o2 - o1, h);
+ hEndHi = sliderHi->getSubImage(o2, y, w - o2 + x, h);
+ }
+ else
+ {
+ hStartHi = 0;
+ hMidHi = 0;
+ hEndHi = 0;
+ }
+
+ x = 6; y = 8;
+ w = 9; h = 10;
+ if (slider)
+ hGrip = slider->getSubImage(x, y, w, h);
+ else
+ hGrip = 0;
+ if (sliderHi)
+ hGripHi = sliderHi->getSubImage(x, y, w, h);
+ else
+ hGripHi = 0;
+
+ x = 0; y = 6;
+ w = 6; h = 21;
+ o1 = 10; o2 = 18;
+ if (slider)
+ {
+ vStart = slider->getSubImage(x, y, w, o1 - y);
+ vMid = slider->getSubImage(x, o1, w, o2 - o1);
+ vEnd = slider->getSubImage(x, o2, w, h - o2 + y);
+ }
+ else
+ {
+ vStart = 0;
+ vMid = 0;
+ vEnd = 0;
+ }
+ if (sliderHi)
+ {
+ vStartHi = sliderHi->getSubImage(x, y, w, o1 - y);
+ vMidHi = sliderHi->getSubImage(x, o1, w, o2 - o1);
+ vEndHi = sliderHi->getSubImage(x, o2, w, h - o2 + y);
+ }
+ else
+ {
+ vStartHi = 0;
+ vMidHi = 0;
+ vEndHi = 0;
+ }
+
+ x = 6; y = 8;
+ w = 9; h = 10;
+ if (slider)
+ vGrip = slider->getSubImage(x, y, w, h);
+ else
+ vGrip = 0;
+
+ if (sliderHi)
+ vGripHi = sliderHi->getSubImage(x, y, w, h);
+ else
+ vGripHi = 0;
+
+ if (slider)
+ slider->decRef();
+ if (sliderHi)
+ sliderHi->decRef();
+ }
+
+ mInstances++;
+
+ if (hGrip)
+ setMarkerLength(hGrip->getWidth());
+}
+
+void Slider::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ if (alpha != mAlpha)
+ {
+ mAlpha = alpha;
+ if (hStart)
+ hStart->setAlpha(mAlpha);
+ if (hMid)
+ hMid->setAlpha(mAlpha);
+ if (hEnd)
+ hEnd->setAlpha(mAlpha);
+ if (hGrip)
+ hGrip->setAlpha(mAlpha);
+ if (hStartHi)
+ hStartHi->setAlpha(mAlpha);
+ if (hMidHi)
+ hMidHi->setAlpha(mAlpha);
+ if (hEndHi)
+ hEndHi->setAlpha(mAlpha);
+ if (hGripHi)
+ hGripHi->setAlpha(mAlpha);
+
+ if (vStart)
+ vStart->setAlpha(mAlpha);
+ if (vMid)
+ vMid->setAlpha(mAlpha);
+ if (vEnd)
+ vEnd->setAlpha(mAlpha);
+ if (vGrip)
+ vGrip->setAlpha(mAlpha);
+ if (vStartHi)
+ vStartHi->setAlpha(mAlpha);
+ if (vMidHi)
+ vMidHi->setAlpha(mAlpha);
+ if (vEndHi)
+ vEndHi->setAlpha(mAlpha);
+ if (vGripHi)
+ vGripHi->setAlpha(mAlpha);
+ }
+
+}
+
+void Slider::draw(gcn::Graphics *graphics)
+{
+ if (!hStart || !hStartHi)
+ return;
+
+ int w = getWidth();
+ int h = getHeight();
+ int x = 0;
+ int y = mHasMouse ? (h - hStartHi->getHeight()) / 2 :
+ (h - hStart->getHeight()) / 2;
+
+ updateAlpha();
+
+ if (!mHasMouse)
+ {
+ static_cast<Graphics*>(graphics)->drawImage(hStart, x, y);
+
+ w -= hStart->getWidth() + hEnd->getWidth();
+ x += hStart->getWidth();
+
+ if (hMid)
+ {
+ static_cast<Graphics*>(graphics)->
+ drawImagePattern(hMid, x, y, w, hMid->getHeight());
+ }
+
+ x += w;
+ if (hEnd)
+ static_cast<Graphics*>(graphics)->drawImage(hEnd, x, y);
+ }
+ else
+ {
+ static_cast<Graphics*>(graphics)->drawImage(hStartHi, x, y);
+
+ w -= hStartHi->getWidth();
+ if (hEndHi)
+ w -= hEndHi->getWidth();
+ x += hStartHi->getWidth();
+
+ if (hMidHi)
+ {
+ static_cast<Graphics*>(graphics)->
+ drawImagePattern(hMidHi, x, y, w, hMidHi->getHeight());
+ }
+
+ x += w;
+ if (hEndHi)
+ static_cast<Graphics*>(graphics)->drawImage(hEndHi, x, y);
+ }
+
+ drawMarker(graphics);
+}
+
+void Slider::drawMarker(gcn::Graphics *graphics)
+{
+ if (!(mHasMouse?hGripHi:hGrip))
+ return;
+
+ static_cast<Graphics*>(graphics)->
+ drawImage(mHasMouse?hGripHi:hGrip, getMarkerPosition(),
+ (getHeight() - (mHasMouse?hGripHi:hGrip)->getHeight()) / 2);
+}
+
+void Slider::mouseEntered(gcn::MouseEvent& event _UNUSED_)
+{
+ mHasMouse = true;
+}
+
+void Slider::mouseExited(gcn::MouseEvent& event _UNUSED_)
+{
+ mHasMouse = false;
+}
+
diff --git a/src/gui/widgets/slider.h b/src/gui/widgets/slider.h
new file mode 100644
index 000000000..be27b73f1
--- /dev/null
+++ b/src/gui/widgets/slider.h
@@ -0,0 +1,98 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 SLIDER_H
+#define SLIDER_H
+
+#include <guichan/widgets/slider.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+
+/**
+ * Slider widget. Same as the Guichan slider but with custom look.
+ *
+ * \ingroup GUI
+ */
+class Slider : public gcn::Slider
+{
+ public:
+ /**
+ * Constructor with scale start equal to 0.
+ */
+ Slider(double scaleEnd = 1.0);
+
+ /**
+ * Constructor.
+ */
+ Slider(double scaleStart, double scaleEnd);
+
+ /**
+ * Destructor.
+ */
+ ~Slider();
+
+ /**
+ * Update the alpha value to the graphic components.
+ */
+ void updateAlpha();
+
+ /**
+ * Draws the slider.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Draws the marker.
+ */
+ void drawMarker(gcn::Graphics *graphics);
+
+ /**
+ * Called when the mouse enteres the widget area.
+ */
+ void mouseEntered(gcn::MouseEvent& event);
+
+ /**
+ * Called when the mouse leaves the widget area.
+ */
+ void mouseExited(gcn::MouseEvent& event);
+
+ private:
+ /**
+ * Used to initialize instances.
+ */
+ void init();
+
+ static Image *hStart, *hMid, *hEnd, *hGrip;
+ static Image *vStart, *vMid, *vEnd, *vGrip;
+ static Image *hStartHi, *hMidHi, *hEndHi, *hGripHi;
+ static Image *vStartHi, *vMidHi, *vEndHi, *vGripHi;
+ bool mHasMouse;
+ static float mAlpha;
+ static int mInstances;
+};
+
+#endif
diff --git a/src/gui/widgets/spellshortcutcontainer.cpp b/src/gui/widgets/spellshortcutcontainer.cpp
new file mode 100644
index 000000000..18482369d
--- /dev/null
+++ b/src/gui/widgets/spellshortcutcontainer.cpp
@@ -0,0 +1,285 @@
+/*
+ * The Mana World
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 Andrei Karas
+ *
+ * This file is part of The Mana World.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "gui/widgets/spellshortcutcontainer.h"
+
+#include "gui/inventorywindow.h"
+#include "gui/okdialog.h"
+#include "gui/palette.h"
+#include "gui/shortcutwindow.h"
+#include "gui/spellpopup.h"
+#include "gui/viewport.h"
+#include "gui/textcommandeditor.h"
+#include "gui/theme.h"
+
+#include "configuration.h"
+#include "graphics.h"
+#include "inventory.h"
+#include "spellshortcut.h"
+#include "itemshortcut.h"
+#include "keyboardconfig.h"
+#include "localplayer.h"
+#include "spellmanager.h"
+#include "log.h"
+
+#include "resources/image.h"
+#include "textcommand.h"
+#include "resources/resourcemanager.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+SpellShortcutContainer::SpellShortcutContainer():
+ ShortcutContainer(),
+ mSpellClicked(false),
+ mSpellMoved(NULL)
+{
+ mBoxWidth = mBoxWidth;
+
+ addMouseListener(this);
+ addWidgetListener(this);
+
+ mSpellPopup = new SpellPopup;
+
+ mBackgroundImg = Theme::getImageFromTheme("item_shortcut_bgr.png");
+ if (spellShortcut)
+ mMaxItems = spellShortcut->getSpellsCount();
+ else
+ mMaxItems = 0;
+
+ if (mBackgroundImg)
+ {
+ mBackgroundImg->setAlpha(Client::getGuiAlpha());
+ mBoxHeight = mBackgroundImg->getHeight();
+ mBoxWidth = mBackgroundImg->getWidth();
+ }
+ else
+ {
+ mBoxHeight = 1;
+ mBoxWidth = 1;
+ }
+}
+
+SpellShortcutContainer::~SpellShortcutContainer()
+{
+ if (mBackgroundImg)
+ mBackgroundImg->decRef();
+ mBackgroundImg = 0;
+ delete mSpellPopup;
+ mSpellPopup = 0;
+}
+
+void SpellShortcutContainer::draw(gcn::Graphics *graphics)
+{
+ if (!spellShortcut)
+ return;
+
+ if (Client::getGuiAlpha() != mAlpha)
+ {
+ mAlpha = Client::getGuiAlpha();
+ if (mBackgroundImg)
+ mBackgroundImg->setAlpha(mAlpha);
+ }
+
+ Graphics *g = static_cast<Graphics*>(graphics);
+
+ graphics->setColor(gcn::Color(0, 0, 0, 255));
+ graphics->setFont(getFont());
+
+ int selectedId = spellShortcut->getSelectedItem();
+ g->setColor(Theme::getThemeColor(Theme::TEXT));
+
+ for (unsigned i = 0; i < mMaxItems; i++)
+ {
+ const int itemX = (i % mGridWidth) * mBoxWidth;
+ const int itemY = (i / mGridWidth) * mBoxHeight;
+
+ g->drawImage(mBackgroundImg, itemX, itemY);
+
+ int itemId = spellShortcut->getItem(i);
+ if (selectedId >= 0 && itemId == selectedId)
+ {
+ g->drawRectangle(gcn::Rectangle(
+ itemX + 1, itemY + 1,
+ mBoxWidth - 1, mBoxHeight - 1));
+ }
+
+ if (!spellManager)
+ continue;
+
+ TextCommand *spell = spellManager->getSpell(itemId);
+ if (spell)
+ {
+ if (!spell->isEmpty())
+ {
+ Image* image = spell->getImage();
+
+ if (image)
+ {
+ image->setAlpha(1.0f);
+ g->drawImage(image, itemX, itemY);
+ }
+ }
+
+ g->drawText(spell->getSymbol(), itemX + 2,
+ itemY + mBoxHeight / 2, gcn::Graphics::LEFT);
+ }
+ }
+
+ if (mSpellMoved)
+ {
+ // Draw the item image being dragged by the cursor.
+ }
+
+}
+
+void SpellShortcutContainer::mouseDragged(gcn::MouseEvent &event)
+{
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ if (!mSpellMoved && mSpellClicked)
+ {
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ const int itemId = spellShortcut->getItem(index);
+
+ if (itemId < 0)
+ return;
+ }
+ if (mSpellMoved)
+ {
+ mCursorPosX = event.getX();
+ mCursorPosY = event.getY();
+ }
+ }
+}
+
+void SpellShortcutContainer::mousePressed(gcn::MouseEvent &event)
+{
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ // Stores the selected item if theirs one.
+ }
+ else if (event.getButton() == gcn::MouseEvent::RIGHT)
+ {
+ }
+ else if (event.getButton() == gcn::MouseEvent::MIDDLE)
+ {
+ if (!spellShortcut || !spellManager)
+ return;
+
+ const int itemId = spellShortcut->getItem(index);
+ spellManager->invoke(itemId);
+ }
+}
+
+void SpellShortcutContainer::mouseReleased(gcn::MouseEvent &event)
+{
+ if (!spellShortcut || !spellManager)
+ return;
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ const int itemId = spellShortcut->getItem(index);
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ if (itemId < 0)
+ return;
+
+ const int selectedId = spellShortcut->getSelectedItem();
+
+ if (selectedId != itemId)
+ {
+ TextCommand *spell = spellManager->getSpell(itemId);
+ if (spell && !spell->isEmpty())
+ {
+ int num = itemShortcutWindow->getTabIndex();
+ if (num >= 0 && num < SHORTCUT_TABS && itemShortcut[num])
+ {
+ itemShortcut[num]->setItemSelected(
+ spell->getId() + SPELL_MIN_ID);
+ }
+ spellShortcut->setItemSelected(spell->getId());
+ }
+ }
+ else
+ {
+ int num = itemShortcutWindow->getTabIndex();
+ if (num >= 0 && num < SHORTCUT_TABS && itemShortcut[num])
+ itemShortcut[num]->setItemSelected(-1);
+ spellShortcut->setItemSelected(-1);
+ }
+ }
+ else if (event.getButton() == gcn::MouseEvent::RIGHT)
+ {
+ TextCommand *spell = NULL;
+ if (itemId >= 0)
+ spell = spellManager->getSpell(itemId);
+
+ if (spell && viewport)
+ viewport->showSpellPopup(spell);
+ }
+}
+
+// Show ItemTooltip
+void SpellShortcutContainer::mouseMoved(gcn::MouseEvent &event)
+{
+ if (!mSpellPopup || !spellShortcut || !spellManager)
+ return;
+
+ const int index = getIndexFromGrid(event.getX(), event.getY());
+
+ if (index == -1)
+ return;
+
+ const int itemId = spellShortcut->getItem(index);
+
+ mSpellPopup->setVisible(false);
+ TextCommand *spell = spellManager->getSpell(itemId);
+ if (spell && !spell->isEmpty())
+ {
+ mSpellPopup->setItem(spell);
+ mSpellPopup->view(viewport->getMouseX(), viewport->getMouseY());
+ }
+ else
+ {
+ mSpellPopup->setVisible(false);
+ }
+}
+
+// Hide SpellTooltip
+void SpellShortcutContainer::mouseExited(gcn::MouseEvent &event _UNUSED_)
+{
+ mSpellPopup->setVisible(false);
+}
diff --git a/src/gui/widgets/spellshortcutcontainer.h b/src/gui/widgets/spellshortcutcontainer.h
new file mode 100644
index 000000000..8f1c4b221
--- /dev/null
+++ b/src/gui/widgets/spellshortcutcontainer.h
@@ -0,0 +1,88 @@
+/*
+ * The Mana World
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 Andrei Karas
+ *
+ * This file is part of The Mana World.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SPELLSHORTCUTCONTAINER_H
+#define SPELLSHORTCUTCONTAINER_H
+
+#include <guichan/mouselistener.hpp>
+
+#include "gui/widgets/shortcutcontainer.h"
+//#include "textcommand.h"
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Image;
+class SpellPopup;
+class TextCommand;
+
+/**
+ * An item shortcut container. Used to quickly use items.
+ *
+ * \ingroup GUI
+ */
+class SpellShortcutContainer : public ShortcutContainer
+{
+ public:
+ /**
+ * Constructor. Initializes the graphic.
+ */
+ SpellShortcutContainer();
+
+ /**
+ * Destructor.
+ */
+ virtual ~SpellShortcutContainer();
+
+ /**
+ * Draws the items.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Handles mouse when dragged.
+ */
+ void mouseDragged(gcn::MouseEvent &event);
+
+ /**
+ * Handles mouse when pressed.
+ */
+ void mousePressed(gcn::MouseEvent &event);
+
+ /**
+ * Handles mouse release.
+ */
+ void mouseReleased(gcn::MouseEvent &event);
+
+ private:
+ void mouseExited(gcn::MouseEvent &event);
+ void mouseMoved(gcn::MouseEvent &event);
+
+ bool mSpellClicked;
+ TextCommand *mSpellMoved;
+ SpellPopup *mSpellPopup;
+};
+
+#endif
diff --git a/src/gui/widgets/tab.cpp b/src/gui/widgets/tab.cpp
new file mode 100644
index 000000000..43b1d154e
--- /dev/null
+++ b/src/gui/widgets/tab.cpp
@@ -0,0 +1,196 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/tab.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+#include "log.h"
+
+#include "gui/palette.h"
+#include "gui/theme.h"
+
+#include "gui/widgets/tabbedarea.h"
+
+#include "resources/image.h"
+
+#include "utils/dtor.h"
+
+#include <guichan/widgets/label.hpp>
+
+int Tab::mInstances = 0;
+float Tab::mAlpha = 1.0;
+
+enum
+{
+ TAB_STANDARD = 0, // 0
+ TAB_HIGHLIGHTED, // 1
+ TAB_SELECTED, // 2
+ TAB_UNUSED, // 3
+ TAB_COUNT // 4 - Must be last.
+};
+
+struct TabData
+{
+ char const *file;
+ int gridX;
+ int gridY;
+};
+
+static TabData const data[TAB_COUNT] =
+{
+ { "tab.png", 0, 0 },
+ { "tab_hilight.png", 9, 4 },
+ { "tabselected.png", 16, 19 },
+ { "tab.png", 25, 23 }
+};
+
+ImageRect Tab::tabImg[TAB_COUNT];
+
+Tab::Tab() : gcn::Tab(),
+ mTabColor(&Theme::getThemeColor(Theme::TAB))
+{
+ init();
+}
+
+Tab::~Tab()
+{
+ mInstances--;
+
+ if (mInstances == 0)
+ {
+ for (int mode = 0; mode < TAB_COUNT; mode++)
+ for_each(tabImg[mode].grid, tabImg[mode].grid + 9, dtor<Image*>());
+ }
+}
+
+void Tab::init()
+{
+ setFocusable(false);
+ setFrameSize(0);
+ mFlash = 0;
+
+ if (mInstances == 0)
+ {
+ // Load the skin
+ Image *tab[TAB_COUNT];
+
+ int a, x, y, mode;
+
+ for (mode = 0; mode < TAB_COUNT; mode++)
+ {
+ tab[mode] = Theme::getImageFromTheme(data[mode].file);
+ a = 0;
+ for (y = 0; y < 3; y++)
+ {
+ for (x = 0; x < 3; x++)
+ {
+ tabImg[mode].grid[a] = tab[mode]->getSubImage(
+ data[x].gridX, data[y].gridY,
+ data[x + 1].gridX - data[x].gridX + 1,
+ data[y + 1].gridY - data[y].gridY + 1);
+ if (tabImg[mode].grid[a])
+ tabImg[mode].grid[a]->setAlpha(mAlpha);
+ a++;
+ }
+ }
+ if (tab[mode])
+ tab[mode]->decRef();
+ }
+ }
+ mInstances++;
+}
+
+void Tab::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ // TODO We don't need to do this for every tab on every draw
+ // Maybe use a config listener to do it as the value changes.
+ if (alpha != mAlpha)
+ {
+ mAlpha = alpha;
+ for (int a = 0; a < 9; a++)
+ {
+ for (int t = 0; t < TAB_COUNT; t++)
+ {
+ if (tabImg[t].grid[a])
+ tabImg[t].grid[a]->setAlpha(mAlpha);
+ }
+ }
+ }
+}
+
+void Tab::draw(gcn::Graphics *graphics)
+{
+ int mode = TAB_STANDARD;
+
+ // check which type of tab to draw
+ if (mTabbedArea)
+ {
+ mLabel->setForegroundColor(*mTabColor);
+ if (mTabbedArea->isTabSelected(this))
+ {
+ mode = TAB_SELECTED;
+ // if tab is selected, it doesnt need to highlight activity
+ mFlash = 0;
+ }
+ else if (mHasMouse)
+ {
+ mode = TAB_HIGHLIGHTED;
+ }
+
+ switch (mFlash)
+ {
+ case 1:
+ mLabel->setForegroundColor(Theme::getThemeColor(
+ Theme::TAB_FLASH));
+ break;
+ case 2:
+ mLabel->setForegroundColor(Theme::getThemeColor(
+ Theme::TAB_PLAYER_FLASH));
+ break;
+ default:
+ break;
+ }
+ }
+
+ updateAlpha();
+
+ // draw tab
+ static_cast<Graphics*>(graphics)->
+ drawImageRect(0, 0, getWidth(), getHeight(), tabImg[mode]);
+
+ // draw label
+ drawChildren(graphics);
+}
+
+void Tab::setTabColor(const gcn::Color *color)
+{
+ mTabColor = color;
+}
+
+void Tab::setFlash(int flash)
+{
+ mFlash = flash;
+}
diff --git a/src/gui/widgets/tab.h b/src/gui/widgets/tab.h
new file mode 100644
index 000000000..b76717bcd
--- /dev/null
+++ b/src/gui/widgets/tab.h
@@ -0,0 +1,80 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 TAB_H
+#define TAB_H
+
+#include <guichan/widgets/tab.hpp>
+
+class ImageRect;
+class TabbedArea;
+
+/**
+ * A tab, the same as the Guichan tab in 0.8, but extended to allow
+ * transparency.
+ */
+class Tab : public gcn::Tab
+{
+ public:
+ Tab();
+ ~Tab();
+
+ /**
+ * Update the alpha value to the graphic components.
+ */
+ void updateAlpha();
+
+ /**
+ * Draw the tabbed area.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Set the normal color fo the tab's text.
+ */
+ void setTabColor(const gcn::Color *color);
+
+ /**
+ * Set tab flashing state
+ */
+ void setFlash(int flash);
+
+ int getFlash()
+ { return mFlash; }
+
+ protected:
+ friend class TabbedArea;
+ virtual void setCurrent()
+ { }
+
+ private:
+ /** Load images if no other instances exist yet */
+ void init();
+
+ static ImageRect tabImg[4]; /**< Tab state graphics */
+ static int mInstances; /**< Number of tab instances */
+ static float mAlpha;
+
+ const gcn::Color *mTabColor;
+ int mFlash;
+};
+
+#endif
diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp
new file mode 100644
index 000000000..232664860
--- /dev/null
+++ b/src/gui/widgets/tabbedarea.cpp
@@ -0,0 +1,221 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/tabbedarea.h"
+
+#include "gui/widgets/tab.h"
+
+#include "log.h"
+
+#include <guichan/widgets/container.hpp>
+
+TabbedArea::TabbedArea() : gcn::TabbedArea()
+{
+ mWidgetContainer->setOpaque(false);
+ addWidgetListener(this);
+
+ widgetResized(NULL);
+}
+
+int TabbedArea::getNumberOfTabs() const
+{
+ return static_cast<int>(mTabs.size());
+}
+
+Tab *TabbedArea::getTab(const std::string &name) const
+{
+ TabContainer::const_iterator itr = mTabs.begin(), itr_end = mTabs.end();
+ while (itr != itr_end)
+ {
+ if ((*itr).first->getCaption() == name)
+ return static_cast<Tab*>((*itr).first);
+
+ ++itr;
+ }
+ return NULL;
+}
+
+void TabbedArea::draw(gcn::Graphics *graphics)
+{
+ if (mTabs.empty())
+ return;
+
+ drawChildren(graphics);
+}
+
+gcn::Widget *TabbedArea::getWidget(const std::string &name) const
+{
+ TabContainer::const_iterator itr = mTabs.begin(), itr_end = mTabs.end();
+ while (itr != itr_end)
+ {
+ if ((*itr).first->getCaption() == name)
+ return (*itr).second;
+
+ ++itr;
+ }
+
+ return NULL;
+}
+
+gcn::Widget *TabbedArea::getCurrentWidget()
+{
+ gcn::Tab *tab = getSelectedTab();
+
+ if (tab)
+ return getWidget(tab->getCaption());
+ else
+ return NULL;
+}
+
+void TabbedArea::addTab(gcn::Tab* tab, gcn::Widget* widget)
+{
+ if (!tab || !widget)
+ return;
+
+ gcn::TabbedArea::addTab(tab, widget);
+
+ int width = getWidth() - 2 * getFrameSize();
+ int height = getHeight() - 2 * getFrameSize() - mTabContainer->getHeight();
+ widget->setSize(width, height);
+}
+
+void TabbedArea::addTab(const std::string &caption, gcn::Widget *widget)
+{
+ Tab *tab = new Tab;
+ tab->setCaption(caption);
+ mTabsToDelete.push_back(tab);
+
+ addTab(tab, widget);
+}
+
+void TabbedArea::removeTab(Tab *tab)
+{
+ int tabIndexToBeSelected = -1;
+
+ if (tab == mSelectedTab)
+ {
+ int index = getSelectedTabIndex();
+
+ if (index == static_cast<int>(mTabs.size()) - 1 && mTabs.size() == 1)
+ tabIndexToBeSelected = -1;
+ else
+ tabIndexToBeSelected = index - 1;
+ }
+
+ TabContainer::iterator iter;
+ for (iter = mTabs.begin(); iter != mTabs.end(); iter++)
+ {
+ if (iter->first == tab)
+ {
+ mTabContainer->remove(tab);
+ mTabs.erase(iter);
+ break;
+ }
+ }
+
+ std::vector<gcn::Tab*>::iterator iter2;
+ for (iter2 = mTabsToDelete.begin(); iter2 != mTabsToDelete.end(); iter2++)
+ {
+ if (*iter2 == tab)
+ {
+ mTabsToDelete.erase(iter2);
+ delete tab;
+ break;
+ }
+ }
+
+ if (tabIndexToBeSelected >= (signed)mTabs.size())
+ tabIndexToBeSelected = mTabs.size() - 1;
+ if (tabIndexToBeSelected < -1)
+ tabIndexToBeSelected = -1;
+
+ if (tabIndexToBeSelected == -1)
+ {
+ mSelectedTab = 0;
+ mWidgetContainer->clear();
+ }
+ else
+ {
+ setSelectedTab(tabIndexToBeSelected);
+ }
+
+ adjustSize();
+ adjustTabPositions();
+}
+
+void TabbedArea::logic()
+{
+ logicChildren();
+}
+
+void TabbedArea::mousePressed(gcn::MouseEvent &mouseEvent)
+{
+ if (mouseEvent.isConsumed())
+ return;
+
+ if (mouseEvent.getButton() == gcn::MouseEvent::LEFT)
+ {
+ gcn::Widget *widget = mTabContainer->getWidgetAt(mouseEvent.getX(),
+ mouseEvent.getY());
+ gcn::Tab *tab = dynamic_cast<gcn::Tab*>(widget);
+
+ if (tab)
+ {
+ setSelectedTab(tab);
+ requestFocus();
+ }
+ }
+}
+
+void TabbedArea::setSelectedTab(gcn::Tab *tab)
+{
+ gcn::TabbedArea::setSelectedTab(tab);
+
+ Tab *newTab = dynamic_cast<Tab*>(tab);
+
+ if (newTab)
+ newTab->setCurrent();
+
+ widgetResized(NULL);
+}
+
+void TabbedArea::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ int width = getWidth() - 2 * getFrameSize()
+ - 2 * mWidgetContainer->getFrameSize();
+ int height = getHeight() - 2 * getFrameSize() - mWidgetContainer->getY()
+ - 2 * mWidgetContainer->getFrameSize();
+ mWidgetContainer->setSize(width, height);
+
+ gcn::Widget *w = getCurrentWidget();
+ if (w)
+ w->setSize(width, height);
+}
+
+/*
+void TabbedArea::moveLeft(gcn::Tab *tab)
+{
+}
+
+void TabbedArea::moveRight(gcn::Tab *tab)
+{
+}
+*/
diff --git a/src/gui/widgets/tabbedarea.h b/src/gui/widgets/tabbedarea.h
new file mode 100644
index 000000000..de2ae4b0a
--- /dev/null
+++ b/src/gui/widgets/tabbedarea.h
@@ -0,0 +1,129 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 TABBEDAREA_H
+#define TABBEDAREA_H
+
+#include <guichan/widget.hpp>
+#include <guichan/widgetlistener.hpp>
+#include <guichan/widgets/container.hpp>
+#include <guichan/widgets/tabbedarea.hpp>
+
+#include <string>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class Tab;
+
+/**
+ * A tabbed area, the same as the guichan tabbed area in 0.8, but extended
+ */
+class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener
+{
+ public:
+ /**
+ * Constructor.
+ */
+ TabbedArea();
+
+ /**
+ * Draw the tabbed area.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Return how many tabs have been created.
+ *
+ * @todo Remove this method when upgrading to Guichan 0.9.0
+ */
+ int getNumberOfTabs() const;
+
+ /**
+ * Return tab with specified name as caption.
+ */
+ Tab *getTab(const std::string &name) const;
+
+ /**
+ * Returns the widget with the tab that has specified caption
+ */
+ gcn::Widget *getWidget(const std::string &name) const;
+
+ /**
+ * Returns the widget for the current tab
+ */
+ gcn::Widget *getCurrentWidget();
+
+ using gcn::TabbedArea::addTab;
+
+ /**
+ * Add a tab. Overridden since it needs to size the widget.
+ *
+ * @param tab The tab widget for the tab.
+ * @param widget The widget to view when the tab is selected.
+ */
+ void addTab(gcn::Tab* tab, gcn::Widget* widget);
+
+ /**
+ * Add a tab. Overridden since it needs to create an instance of Tab
+ * instead of gcn::Tab.
+ *
+ * @param caption The Caption to display
+ * @param widget The widget to show when tab is selected
+ */
+ void addTab(const std::string &caption, gcn::Widget *widget);
+
+ /**
+ * Overload the remove tab function as it's broken in guichan 0.8.
+ */
+ void removeTab(Tab *tab);
+
+ /**
+ * Overload the logic function since it's broken in guichan 0.8.
+ */
+ void logic();
+
+ int getContainerHeight() const
+ { return mWidgetContainer->getHeight(); }
+
+ using gcn::TabbedArea::setSelectedTab;
+
+ void setSelectedTab(gcn::Tab *tab);
+
+ void widgetResized(const gcn::Event &event);
+
+/*
+ void moveLeft(gcn::Tab *tab);
+
+ void moveRight(gcn::Tab *tab);
+*/
+ // Inherited from MouseListener
+
+ void mousePressed(gcn::MouseEvent &mouseEvent);
+
+ private:
+ typedef std::vector< std::pair<gcn::Tab*, gcn::Widget*> > TabContainer;
+};
+
+#endif
diff --git a/src/gui/widgets/table.cpp b/src/gui/widgets/table.cpp
new file mode 100644
index 000000000..39ef719ef
--- /dev/null
+++ b/src/gui/widgets/table.cpp
@@ -0,0 +1,585 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/table.h"
+
+#include "client.h"
+#include "configuration.h"
+
+#include "gui/sdlinput.h"
+#include "gui/theme.h"
+
+#include "utils/dtor.h"
+
+#include <guichan/actionlistener.hpp>
+#include <guichan/graphics.hpp>
+#include <guichan/key.hpp>
+
+float GuiTable::mAlpha = 1.0;
+
+class GuiTableActionListener : public gcn::ActionListener
+{
+public:
+ GuiTableActionListener(GuiTable *_table, gcn::Widget *_widget,
+ int _row, int _column);
+
+ virtual ~GuiTableActionListener();
+
+ virtual void action(const gcn::ActionEvent& actionEvent);
+
+protected:
+ GuiTable *mTable;
+ int mRow;
+ int mColumn;
+ gcn::Widget *mWidget;
+};
+
+
+GuiTableActionListener::GuiTableActionListener(GuiTable *table,
+ gcn::Widget *widget, int row,
+ int column) :
+ mTable(table),
+ mRow(row),
+ mColumn(column),
+ mWidget(widget)
+{
+ if (widget)
+ {
+ widget->addActionListener(this);
+ widget->_setParent(table);
+ }
+}
+
+GuiTableActionListener::~GuiTableActionListener()
+{
+ if (mWidget)
+ {
+ mWidget->removeActionListener(this);
+ mWidget->_setParent(NULL);
+ }
+}
+
+void GuiTableActionListener::action(const gcn::ActionEvent
+ &actionEvent _UNUSED_)
+{
+ mTable->setSelected(mRow, mColumn);
+ mTable->distributeActionEvent();
+}
+
+
+GuiTable::GuiTable(TableModel *initial_model, gcn::Color background,
+ bool opacity) :
+ mLinewiseMode(false),
+ mWrappingEnabled(false),
+ mOpaque(opacity),
+ mBackgroundColor(background),
+ mModel(NULL),
+ mSelectedRow(0),
+ mSelectedColumn(0),
+ mTopWidget(NULL)
+{
+ setModel(initial_model);
+ setFocusable(true);
+
+ addMouseListener(this);
+ addKeyListener(this);
+}
+
+GuiTable::~GuiTable()
+{
+ uninstallActionListeners();
+ delete mModel;
+ mModel = 0;
+}
+
+TableModel *GuiTable::getModel() const
+{
+ return mModel;
+}
+
+void GuiTable::setModel(TableModel *new_model)
+{
+ if (mModel)
+ {
+ uninstallActionListeners();
+ mModel->removeListener(this);
+ }
+
+ mModel = new_model;
+ installActionListeners();
+
+ if (new_model)
+ {
+ new_model->installListener(this);
+ recomputeDimensions();
+ }
+}
+
+void GuiTable::recomputeDimensions()
+{
+ if (!mModel)
+ return;
+
+ int rows_nr = mModel->getRows();
+ int columns_nr = mModel->getColumns();
+ int width = 0;
+ int height = 0;
+
+ if (mSelectedRow >= rows_nr)
+ mSelectedRow = rows_nr - 1;
+
+ if (mSelectedColumn >= columns_nr)
+ mSelectedColumn = columns_nr - 1;
+
+ for (int i = 0; i < columns_nr; i++)
+ width += getColumnWidth(i);
+
+ height = getRowHeight() * rows_nr;
+
+ setWidth(width);
+ setHeight(height);
+}
+
+void GuiTable::setSelected(int row, int column)
+{
+ mSelectedColumn = column;
+ mSelectedRow = row;
+}
+
+int GuiTable::getSelectedRow() const
+{
+ return mSelectedRow;
+}
+
+int GuiTable::getSelectedColumn() const
+{
+ return mSelectedColumn;
+}
+
+void GuiTable::setLinewiseSelection(bool linewise)
+{
+ mLinewiseMode = linewise;
+}
+
+int GuiTable::getRowHeight() const
+{
+ if (mModel)
+ return mModel->getRowHeight() + 1; // border
+ else
+ return 0;
+}
+
+int GuiTable::getColumnWidth(int i) const
+{
+ if (mModel)
+ return mModel->getColumnWidth(i) + 1; // border
+ else
+ return 0;
+}
+
+void GuiTable::setSelectedRow(int selected)
+{
+ if (!mModel)
+ {
+ mSelectedRow = -1;
+ }
+ else
+ {
+ if (selected < 0 && !mWrappingEnabled)
+ {
+ mSelectedRow = -1;
+ }
+ else if (selected >= mModel->getRows() && mWrappingEnabled)
+ {
+ mSelectedRow = 0;
+ }
+ else if ((selected >= mModel->getRows() && !mWrappingEnabled) ||
+ (selected < 0 && mWrappingEnabled))
+ {
+ mSelectedRow = mModel->getRows() - 1;
+ }
+ else
+ {
+ mSelectedRow = selected;
+ }
+ }
+}
+
+void GuiTable::setSelectedColumn(int selected)
+{
+ if (!mModel)
+ {
+ mSelectedColumn = -1;
+ }
+ else
+ {
+ if ((selected >= mModel->getColumns() && mWrappingEnabled) ||
+ (selected < 0 && !mWrappingEnabled))
+ {
+ mSelectedColumn = 0;
+ }
+ else if ((selected >= mModel->getColumns() && !mWrappingEnabled) ||
+ (selected < 0 && mWrappingEnabled))
+ {
+ mSelectedColumn = mModel->getColumns() - 1;
+ }
+ else
+ {
+ mSelectedColumn = selected;
+ }
+ }
+}
+
+void GuiTable::uninstallActionListeners()
+{
+ delete_all(mActionListeners);
+ mActionListeners.clear();
+}
+
+void GuiTable::installActionListeners()
+{
+ if (!mModel)
+ return;
+
+ int rows = mModel->getRows();
+ int columns = mModel->getColumns();
+
+ for (int row = 0; row < rows; ++row)
+ {
+ for (int column = 0; column < columns; ++column)
+ {
+ gcn::Widget *widget = mModel->getElementAt(row, column);
+ if (widget)
+ {
+ mActionListeners.push_back(new GuiTableActionListener(
+ this, widget, row, column));
+ }
+ }
+ }
+
+ _setFocusHandler(_getFocusHandler()); // propagate focus handler to widgets
+}
+
+// -- widget ops
+void GuiTable::draw(gcn::Graphics* graphics)
+{
+ if (!mModel || !getRowHeight())
+ return;
+
+ if (Client::getGuiAlpha() != mAlpha)
+ mAlpha = Client::getGuiAlpha();
+
+ if (mOpaque)
+ {
+ graphics->setColor(Theme::getThemeColor(Theme::BACKGROUND,
+ static_cast<int>(mAlpha * 255.0f)));
+ graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
+ }
+
+ // First, determine how many rows we need to draw, and where we should start.
+ int first_row = -(getY() / getRowHeight());
+
+ if (first_row < 0)
+ first_row = 0;
+
+ int rows_nr = 1 + (getHeight() / getRowHeight()); // May overestimate
+ // by one.
+
+ int max_rows_nr = mModel->getRows() - first_row; // clip if neccessary:
+ if (max_rows_nr < rows_nr)
+ rows_nr = max_rows_nr;
+
+ // Now determine the first and last column
+ // Take the easy way out; these are usually bounded and all visible.
+ int first_column = 0;
+ int last_column1 = mModel->getColumns();
+
+ // Set up everything for drawing
+ int height = getRowHeight();
+ int y_offset = first_row * height;
+
+ for (int r = first_row; r < first_row + rows_nr; ++r)
+ {
+ int x_offset = 0;
+
+ for (int c = first_column; c + 1 <= last_column1; ++c)
+ {
+ gcn::Widget *widget = mModel->getElementAt(r, c);
+ int width = getColumnWidth(c);
+ if (widget)
+ {
+ gcn::Rectangle bounds(x_offset, y_offset, width, height);
+
+ if (widget == mTopWidget)
+ {
+ bounds.height = widget->getHeight();
+ bounds.width = widget->getWidth();
+ }
+
+ widget->setDimension(bounds);
+
+ graphics->setColor(Theme::getThemeColor(Theme::HIGHLIGHT,
+ static_cast<int>(mAlpha * 255.0f)));
+
+ if (mLinewiseMode && r == mSelectedRow && c == 0)
+ {
+ graphics->fillRectangle(gcn::Rectangle(0, y_offset,
+ getWidth(), height));
+ }
+ else if (!mLinewiseMode &&
+ c == mSelectedColumn && r == mSelectedRow)
+ {
+ graphics->fillRectangle(gcn::Rectangle(x_offset, y_offset,
+ width, height));
+ }
+
+ graphics->pushClipArea(bounds);
+ widget->draw(graphics);
+ graphics->popClipArea();
+ }
+
+ x_offset += width;
+ }
+
+ y_offset += height;
+ }
+
+ if (mTopWidget)
+ {
+ gcn::Rectangle bounds = mTopWidget->getDimension();
+ graphics->pushClipArea(bounds);
+ mTopWidget->draw(graphics);
+ graphics->popClipArea();
+ }
+}
+
+void GuiTable::moveToTop(gcn::Widget *widget)
+{
+ gcn::Widget::moveToTop(widget);
+ mTopWidget = widget;
+}
+
+void GuiTable::moveToBottom(gcn::Widget *widget)
+{
+ gcn::Widget::moveToBottom(widget);
+ if (widget == mTopWidget)
+ mTopWidget = NULL;
+}
+
+gcn::Rectangle GuiTable::getChildrenArea() const
+{
+ return gcn::Rectangle(0, 0, getWidth(), getHeight());
+}
+
+// -- KeyListener notifications
+void GuiTable::keyPressed(gcn::KeyEvent& keyEvent)
+{
+ gcn::Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
+ {
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::UP)
+ {
+ setSelectedRow(mSelectedRow - 1);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::DOWN)
+ {
+ setSelectedRow(mSelectedRow + 1);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::LEFT)
+ {
+ setSelectedColumn(mSelectedColumn - 1);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::RIGHT)
+ {
+ setSelectedColumn(mSelectedColumn + 1);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::HOME)
+ {
+ setSelectedRow(0);
+ setSelectedColumn(0);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::END && mModel)
+ {
+ setSelectedRow(mModel->getRows() - 1);
+ setSelectedColumn(mModel->getColumns() - 1);
+ keyEvent.consume();
+ }
+}
+
+// -- MouseListener notifications
+void GuiTable::mousePressed(gcn::MouseEvent& mouseEvent)
+{
+ if (!mModel)
+ return;
+
+ if (mouseEvent.getButton() == gcn::MouseEvent::LEFT)
+ {
+ int row = getRowForY(mouseEvent.getY());
+ int column = getColumnForX(mouseEvent.getX());
+
+ if (row > -1 && column > -1 &&
+ row < mModel->getRows() && column < mModel->getColumns())
+ {
+ mSelectedColumn = column;
+ mSelectedRow = row;
+ }
+
+ distributeActionEvent();
+ }
+}
+
+void GuiTable::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent)
+{
+ if (isFocused())
+ {
+ if (getSelectedRow() > 0 || (getSelectedRow() == 0
+ && mWrappingEnabled))
+ {
+ setSelectedRow(getSelectedRow() - 1);
+ }
+
+ mouseEvent.consume();
+ }
+}
+
+void GuiTable::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent)
+{
+ if (isFocused())
+ {
+ setSelectedRow(getSelectedRow() + 1);
+
+ mouseEvent.consume();
+ }
+}
+
+void GuiTable::mouseDragged(gcn::MouseEvent& mouseEvent)
+{
+ if (mouseEvent.getButton() != gcn::MouseEvent::LEFT)
+ return;
+
+ // Make table selection update on drag
+ const int x = std::max(0, mouseEvent.getX());
+ const int y = std::max(0, mouseEvent.getY());
+
+ setSelectedRow(getRowForY(y));
+ setSelectedColumn(getColumnForX(x));
+}
+
+// -- TableModelListener notifications
+void GuiTable::modelUpdated(bool completed)
+{
+ if (completed)
+ {
+ recomputeDimensions();
+ installActionListeners();
+ }
+ else
+ { // before the update?
+ mTopWidget = NULL; // No longer valid in general
+ uninstallActionListeners();
+ }
+}
+
+gcn::Widget *GuiTable::getWidgetAt(int x, int y) const
+{
+ int row = getRowForY(y);
+ int column = getColumnForX(x);
+
+ if (mTopWidget && mTopWidget->getDimension().isPointInRect(x, y))
+ return mTopWidget;
+
+ if (mModel && row > -1 && column > -1)
+ {
+ gcn::Widget *w = mModel->getElementAt(row, column);
+ if (w && w->isFocusable())
+ return w;
+ else
+ return NULL; // Grab the event locally
+ }
+ else
+ return NULL;
+}
+
+int GuiTable::getRowForY(int y) const
+{
+ int row = -1;
+
+ if (getRowHeight() > 0)
+ row = y / getRowHeight();
+
+ if (!mModel || row < 0 || row >= mModel->getRows())
+ return -1;
+ else
+ return row;
+}
+
+int GuiTable::getColumnForX(int x) const
+{
+ if (!mModel)
+ return -1;
+
+ int column;
+ int delta = 0;
+
+ for (column = 0; column < mModel->getColumns(); column++)
+ {
+ delta += getColumnWidth(column);
+ if (x <= delta)
+ break;
+ }
+
+ if (column < 0 || column >= mModel->getColumns())
+ return -1;
+ else
+ return column;
+}
+
+void GuiTable::_setFocusHandler(gcn::FocusHandler* focusHandler)
+{
+// add check for focusHandler. may be need remove it?
+
+ if (!mModel || !focusHandler)
+ return;
+
+ gcn::Widget::_setFocusHandler(focusHandler);
+
+ if (mModel)
+ {
+ for (int r = 0; r < mModel->getRows(); ++r)
+ {
+ for (int c = 0; c < mModel->getColumns(); ++c)
+ {
+ gcn::Widget *w = mModel->getElementAt(r, c);
+ if (w)
+ w->_setFocusHandler(focusHandler);
+ }
+ }
+ }
+}
diff --git a/src/gui/widgets/table.h b/src/gui/widgets/table.h
new file mode 100644
index 000000000..61c7302b2
--- /dev/null
+++ b/src/gui/widgets/table.h
@@ -0,0 +1,195 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 TABLE_H
+#define TABLE_H
+
+#include "tablemodel.h"
+
+#include <guichan/keylistener.hpp>
+#include <guichan/mouselistener.hpp>
+#include <guichan/widget.hpp>
+
+#include <vector>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class GuiTableActionListener;
+
+/**
+ * A table, with rows and columns made out of sub-widgets. Largely inspired by
+ * (and can be thought of as a generalisation of) the guichan listbox
+ * implementation.
+ *
+ * Normally you want this within a ScrollArea.
+ *
+ * \ingroup GUI
+ */
+class GuiTable : public gcn::Widget,
+ public gcn::MouseListener,
+ public gcn::KeyListener,
+ public TableModelListener
+{
+ // so that the action listener can call distributeActionEvent
+ friend class GuiTableActionListener;
+
+public:
+ GuiTable(TableModel * initial_model = NULL,
+ gcn::Color background = 0xffffff,
+ bool opacity = true);
+
+ virtual ~GuiTable();
+
+ /**
+ * Retrieves the active table model
+ */
+ TableModel *getModel() const;
+
+ /**
+ * Sets the table model
+ *
+ * Note that actions issued by widgets returned from the model will update
+ * the table selection, but only AFTER any event handlers installed within
+ * the widget have been triggered. To be notified after such an update, add
+ * an action listener to the table instead.
+ */
+ void setModel(TableModel *m);
+
+ void setSelected(int row, int column);
+
+ int getSelectedRow() const;
+
+ int getSelectedColumn() const;
+
+ void setSelectedRow(int selected);
+
+ void setSelectedColumn(int selected);
+
+ bool isWrappingEnabled() const
+ { return mWrappingEnabled; }
+
+ void setWrappingEnabled(bool wrappingEnabled)
+ { mWrappingEnabled = wrappingEnabled; }
+
+ gcn::Rectangle getChildrenArea() const;
+
+ /**
+ * Toggle whether to use linewise selection mode, in which the table selects
+ * an entire line at a time, rather than a single cell.
+ *
+ * Note that column information is tracked even in linewise selection mode;
+ * this mode therefore only affects visualisation.
+ *
+ * Disabled by default.
+ *
+ * \param linewise: Whether to enable linewise selection mode
+ */
+ void setLinewiseSelection(bool linewise);
+
+ // Inherited from Widget
+ virtual void draw(gcn::Graphics* graphics);
+
+ virtual gcn::Widget *getWidgetAt(int x, int y) const;
+
+ virtual void moveToTop(gcn::Widget *child);
+
+ virtual void moveToBottom(gcn::Widget *child);
+
+ virtual void _setFocusHandler(gcn::FocusHandler* focusHandler);
+
+ // Inherited from KeyListener
+ virtual void keyPressed(gcn::KeyEvent& keyEvent);
+
+ /**
+ * Sets the table to be opaque, that is sets the table
+ * to display its background.
+ *
+ * @param opaque True if the table should be opaque, false otherwise.
+ */
+ virtual void setOpaque(bool opaque)
+ { mOpaque = opaque; }
+
+ /**
+ * Checks if the table is opaque, that is if the table area displays its
+ * background.
+ *
+ * @return True if the table is opaque, false otherwise.
+ */
+ virtual bool isOpaque() const
+ { return mOpaque; }
+
+ // Inherited from MouseListener
+ virtual void mousePressed(gcn::MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedUp(gcn::MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(gcn::MouseEvent& mouseEvent);
+
+ // Constraints inherited from TableModelListener
+ virtual void modelUpdated(bool);
+
+protected:
+ /** Frees all action listeners on inner widgets. */
+ virtual void uninstallActionListeners();
+ /** Installs all action listeners on inner widgets. */
+ virtual void installActionListeners();
+
+ virtual int getRowHeight() const;
+ virtual int getColumnWidth(int i) const;
+
+private:
+ int getRowForY(int y) const; // -1 on error
+ int getColumnForX(int x) const; // -1 on error
+ void recomputeDimensions();
+ bool mLinewiseMode;
+ bool mWrappingEnabled;
+ bool mOpaque;
+
+ static float mAlpha;
+
+ /**
+ * Holds the background color of the table.
+ */
+ gcn::Color mBackgroundColor;
+
+ TableModel *mModel;
+
+ int mSelectedRow;
+ int mSelectedColumn;
+
+ /** Number of frames to skip upwards when drawing the selected widget. */
+ int mPopFramesNr;
+
+ /** If someone moves a fresh widget to the top, we must display it. */
+ gcn::Widget *mTopWidget;
+
+ /** Vector for compactness; used as a list in practice. */
+ std::vector<GuiTableActionListener *> mActionListeners;
+};
+
+
+#endif // TABLE_H
diff --git a/src/gui/widgets/tablemodel.cpp b/src/gui/widgets/tablemodel.cpp
new file mode 100644
index 000000000..f1d583ef6
--- /dev/null
+++ b/src/gui/widgets/tablemodel.cpp
@@ -0,0 +1,173 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/tablemodel.h"
+
+#include "utils/dtor.h"
+
+#include <guichan/widget.hpp>
+
+void TableModel::installListener(TableModelListener *listener)
+{
+ if (listener)
+ listeners.insert(listener);
+}
+
+void TableModel::removeListener(TableModelListener *listener)
+{
+ if (listener)
+ listeners.erase(listener);
+}
+
+void TableModel::signalBeforeUpdate()
+{
+ for (std::set<TableModelListener *>::const_iterator it = listeners.begin();
+ it != listeners.end(); it++)
+ {
+ (*it)->modelUpdated(false);
+ }
+}
+
+void TableModel::signalAfterUpdate()
+{
+ for (std::set<TableModelListener *>::const_iterator it = listeners.begin();
+ it != listeners.end(); it++)
+ {
+ if (*it)
+ (*it)->modelUpdated(true);
+ }
+}
+
+
+#define WIDGET_AT(row, column) (((row) * mColumns) + (column))
+#define DYN_SIZE(h) ((h) >= 0)
+
+StaticTableModel::StaticTableModel(int row, int column) :
+ mRows(row),
+ mColumns(column),
+ mHeight(1)
+{
+ mTableModel.resize(row * column, NULL);
+ mWidths.resize(column, 1);
+}
+
+StaticTableModel::~StaticTableModel()
+{
+ delete_all(mTableModel);
+ mTableModel.clear();
+}
+
+void StaticTableModel::resize()
+{
+ mRows = getRows();
+ mColumns = getColumns();
+ mTableModel.resize(mRows * mColumns, NULL);
+}
+
+void StaticTableModel::set(int row, int column, gcn::Widget *widget)
+{
+ if (!widget || row >= mRows || row < 0
+ || column >= mColumns || column < 0)
+ {
+ // raise exn?
+ return;
+ }
+
+ if (DYN_SIZE(mHeight)
+ && widget->getHeight() > mHeight)
+ {
+ mHeight = widget->getHeight();
+ }
+
+ if (DYN_SIZE(mWidths[column])
+ && widget->getWidth() > mWidths[column])
+ {
+ mWidths[column] = widget->getWidth();
+ }
+
+ signalBeforeUpdate();
+
+ delete mTableModel[WIDGET_AT(row, column)];
+
+ mTableModel[WIDGET_AT(row, column)] = widget;
+
+ signalAfterUpdate();
+}
+
+gcn::Widget *StaticTableModel::getElementAt(int row, int column) const
+{
+ return mTableModel[WIDGET_AT(row, column)];
+}
+
+void StaticTableModel::fixColumnWidth(int column, int width)
+{
+ if (width < 0 || column < 0 || column >= mColumns)
+ return;
+
+ mWidths[column] = -width; // Negate to tag as fixed
+}
+
+void StaticTableModel::fixRowHeight(int height)
+{
+ if (height < 0)
+ return;
+
+ mHeight = -height;
+}
+
+int StaticTableModel::getRowHeight() const
+{
+ return abs(mHeight);
+}
+
+int StaticTableModel::getColumnWidth(int column) const
+{
+ if (column < 0 || column >= mColumns)
+ return 0;
+
+ return abs(mWidths[column]);
+}
+
+int StaticTableModel::getRows() const
+{
+ return mRows;
+}
+
+int StaticTableModel::getColumns() const
+{
+ return mColumns;
+}
+
+int StaticTableModel::getWidth() const
+{
+ int width = 0;
+
+ for (unsigned int i = 0; i < mWidths.size(); i++)
+ width += mWidths[i];
+
+ return width;
+}
+
+int StaticTableModel::getHeight() const
+{
+ return mColumns * mHeight;
+}
+
diff --git a/src/gui/widgets/tablemodel.h b/src/gui/widgets/tablemodel.h
new file mode 100644
index 000000000..2b8729341
--- /dev/null
+++ b/src/gui/widgets/tablemodel.h
@@ -0,0 +1,149 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 TABLE_MODEL_H
+#define TABLE_MODEL_H
+
+#include <guichanfwd.h>
+
+#include <set>
+#include <vector>
+
+class TableModelListener
+{
+public:
+ /**
+ * Must be invoked by the TableModel whenever a global change is about to
+ * occur or has occurred (e.g., when a row or column is being removed or
+ * added).
+ *
+ * This method is triggered twice, once before and once after the update.
+ *
+ * \param completed whether we are signalling the end of the update
+ */
+ virtual void modelUpdated(bool completed) = 0;
+
+ virtual ~TableModelListener() {}
+};
+
+/**
+ * A model for a regular table of widgets.
+ */
+class TableModel
+{
+public:
+ virtual ~TableModel()
+ { }
+
+ /**
+ * Determines the number of rows (lines) in the table
+ */
+ virtual int getRows() const = 0;
+
+ /**
+ * Determines the number of columns in each row
+ */
+ virtual int getColumns() const = 0;
+
+ /**
+ * Determines the height for each row
+ */
+ virtual int getRowHeight() const = 0;
+
+ /**
+ * Determines the width of each individual column
+ */
+ virtual int getColumnWidth(int index) const = 0;
+
+ /**
+ * Retrieves the widget stored at the specified location within the table.
+ */
+ virtual gcn::Widget *getElementAt(int row, int column) const = 0;
+
+ virtual void installListener(TableModelListener *listener);
+
+ virtual void removeListener(TableModelListener *listener);
+
+protected:
+ /**
+ * Tells all listeners that the table is about to see an update
+ */
+ virtual void signalBeforeUpdate();
+
+ /**
+ * Tells all listeners that the table has seen an update
+ */
+ virtual void signalAfterUpdate();
+
+private:
+ std::set<TableModelListener *> listeners;
+};
+
+
+class StaticTableModel : public TableModel
+{
+public:
+ StaticTableModel(int width, int height);
+ virtual ~StaticTableModel();
+
+ /**
+ * Inserts a widget into the table model.
+ * The model is resized to accomodate the widget's width and height,
+ * unless column width / row height have been fixed.
+ */
+ virtual void set(int row, int column, gcn::Widget *widget);
+
+ /**
+ * Fixes the column width for a given column; this overrides dynamic width
+ * inference.
+ *
+ * Semantics are undefined for width 0.
+ */
+ virtual void fixColumnWidth(int column, int width);
+
+ /**
+ * Fixes the row height; this overrides dynamic height inference.
+ *
+ * Semantics are undefined for width 0.
+ */
+ virtual void fixRowHeight(int height);
+
+ /**
+ * Resizes the table model
+ */
+ virtual void resize();
+
+ virtual int getRows() const;
+ virtual int getColumns() const;
+ virtual int getRowHeight() const;
+ virtual int getWidth() const;
+ virtual int getHeight() const;
+ virtual int getColumnWidth(int index) const;
+ virtual gcn::Widget *getElementAt(int row, int column) const;
+
+protected:
+ int mRows, mColumns;
+ int mHeight;
+ std::vector<gcn::Widget *> mTableModel;
+ std::vector<int> mWidths;
+};
+
+#endif // TABLE_MODEL_H
diff --git a/src/gui/widgets/textbox.cpp b/src/gui/widgets/textbox.cpp
new file mode 100644
index 000000000..f248f35d2
--- /dev/null
+++ b/src/gui/widgets/textbox.cpp
@@ -0,0 +1,149 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/textbox.h"
+
+#include "gui/theme.h"
+
+#include <guichan/font.hpp>
+
+#include <sstream>
+
+TextBox::TextBox() :
+ mTextColor(&Theme::getThemeColor(Theme::TEXT))
+{
+ setOpaque(false);
+ setFrameSize(0);
+ mMinWidth = getWidth();
+}
+
+void TextBox::setTextWrapped(const std::string &text, int minDimension)
+{
+ // Make sure parent scroll area sets width of this widget
+ if (getParent())
+ getParent()->logic();
+
+ // Take the supplied minimum dimension as a starting point and try to beat it
+ mMinWidth = minDimension;
+
+ std::stringstream wrappedStream;
+ std::string::size_type spacePos, newlinePos, lastNewlinePos = 0;
+ int minWidth = 0;
+ int xpos;
+
+ spacePos = text.rfind(" ", text.size());
+
+ if (spacePos != std::string::npos)
+ {
+ const std::string word = text.substr(spacePos + 1);
+ const int length = getFont()->getWidth(word);
+
+ if (length > mMinWidth)
+ mMinWidth = length;
+ }
+
+ do
+ {
+ // Determine next piece of string to wrap
+ newlinePos = text.find("\n", lastNewlinePos);
+
+ if (newlinePos == std::string::npos)
+ newlinePos = text.size();
+
+ std::string line =
+ text.substr(lastNewlinePos, newlinePos - lastNewlinePos);
+ std::string::size_type lastSpacePos = 0;
+ xpos = 0;
+
+ do
+ {
+ spacePos = line.find(" ", lastSpacePos);
+
+ if (spacePos == std::string::npos)
+ spacePos = line.size();
+
+ std::string word =
+ line.substr(lastSpacePos, spacePos - lastSpacePos);
+
+ int width = getFont()->getWidth(word);
+
+ if (xpos == 0 && width > mMinWidth)
+ {
+ mMinWidth = width;
+ xpos = width;
+ wrappedStream << word;
+ }
+ else if (xpos != 0 && xpos + getFont()->getWidth(" ") + width <=
+ mMinWidth)
+ {
+ xpos += getFont()->getWidth(" ") + width;
+ wrappedStream << " " << word;
+ }
+ else if (lastSpacePos == 0)
+ {
+ xpos += width;
+ wrappedStream << word;
+ }
+ else
+ {
+ if (xpos > minWidth)
+ minWidth = xpos;
+
+ // The window wasn't big enough. Resize it and try again.
+ if (minWidth > mMinWidth)
+ {
+ mMinWidth = minWidth;
+ wrappedStream.clear();
+ wrappedStream.str("");
+ spacePos = 0;
+ lastNewlinePos = 0;
+ newlinePos = text.find("\n", lastNewlinePos);
+ if (newlinePos == std::string::npos)
+ newlinePos = text.size();
+ line = text.substr(lastNewlinePos, newlinePos -
+ lastNewlinePos);
+ width = 0;
+ break;
+ }
+ else
+ {
+ wrappedStream << "\n" << word;
+ }
+ xpos = width;
+ }
+ lastSpacePos = spacePos + 1;
+ }
+ while (spacePos != line.size());
+
+ if (text.find("\n", lastNewlinePos) != std::string::npos)
+ wrappedStream << "\n";
+
+ lastNewlinePos = newlinePos + 1;
+ }
+ while (newlinePos != text.size());
+
+ if (xpos > minWidth)
+ minWidth = xpos;
+
+ mMinWidth = minWidth;
+
+ gcn::TextBox::setText(wrappedStream.str());
+}
diff --git a/src/gui/widgets/textbox.h b/src/gui/widgets/textbox.h
new file mode 100644
index 000000000..dffaf2736
--- /dev/null
+++ b/src/gui/widgets/textbox.h
@@ -0,0 +1,70 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 TEXTBOX_H
+#define TEXTBOX_H
+
+#include <guichan/widgets/textbox.hpp>
+
+/**
+ * A text box, meant to be used inside a scroll area. Same as the Guichan text
+ * box except this one doesn't have a background or border, instead completely
+ * relying on the scroll area.
+ *
+ * \ingroup GUI
+ */
+class TextBox : public gcn::TextBox
+{
+ public:
+ /**
+ * Constructor.
+ */
+ TextBox();
+
+ inline void setTextColor(const gcn::Color *color)
+ { mTextColor = color; }
+
+ /**
+ * Sets the text after wrapping it to the current width of the widget.
+ */
+ void setTextWrapped(const std::string &text, int minDimension);
+
+ /**
+ * Get the minimum text width for the text box.
+ */
+ int getMinWidth() const
+ { return mMinWidth; }
+
+ /**
+ * Draws the text.
+ */
+ inline void draw(gcn::Graphics *graphics)
+ {
+ setForegroundColor(*mTextColor);
+ gcn::TextBox::draw(graphics);
+ }
+
+ private:
+ int mMinWidth;
+ const gcn::Color *mTextColor;
+};
+
+#endif
diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp
new file mode 100644
index 000000000..9a5b2de33
--- /dev/null
+++ b/src/gui/widgets/textfield.cpp
@@ -0,0 +1,306 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/textfield.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "graphics.h"
+#include "log.h"
+
+#include "gui/palette.h"
+#include "gui/sdlinput.h"
+#include "gui/theme.h"
+
+#include "resources/image.h"
+
+#include "utils/copynpaste.h"
+#include "utils/dtor.h"
+
+#include <guichan/font.hpp>
+
+#undef DELETE //Win32 compatibility hack
+
+int TextField::instances = 0;
+float TextField::mAlpha = 1.0;
+ImageRect TextField::skin;
+
+TextField::TextField(const std::string &text, bool loseFocusOnTab,
+ gcn::ActionListener* listener, std::string eventId):
+ gcn::TextField(text),
+ mNumeric(false)
+{
+ setFrameSize(2);
+
+ mLoseFocusOnTab = loseFocusOnTab;
+
+ if (instances == 0)
+ {
+ // Load the skin
+ Image *textbox = Theme::getImageFromTheme("deepbox.png");
+ int gridx[4] = {0, 3, 28, 31};
+ int gridy[4] = {0, 3, 28, 31};
+ int a = 0, x, y;
+
+ for (y = 0; y < 3; y++)
+ {
+ for (x = 0; x < 3; x++)
+ {
+ if (textbox)
+ {
+ skin.grid[a] = textbox->getSubImage(
+ gridx[x], gridy[y],
+ gridx[x + 1] - gridx[x] + 1,
+ gridy[y + 1] - gridy[y] + 1);
+ if (skin.grid[a])
+ skin.grid[a]->setAlpha(Client::getGuiAlpha());
+ }
+ else
+ {
+ skin.grid[a] = 0;
+ }
+ a++;
+ }
+ }
+
+ if (textbox)
+ textbox->decRef();
+ }
+
+ instances++;
+
+ if (!eventId.empty())
+ setActionEventId(eventId);
+
+ if (listener)
+ addActionListener(listener);
+}
+
+TextField::~TextField()
+{
+ instances--;
+
+ if (instances == 0)
+ for_each(skin.grid, skin.grid + 9, dtor<Image*>());
+}
+
+void TextField::updateAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+
+ if (alpha != mAlpha)
+ {
+ mAlpha = alpha;
+ for (int a = 0; a < 9; a++)
+ {
+ if (skin.grid[a])
+ skin.grid[a]->setAlpha(mAlpha);
+ }
+ }
+}
+
+void TextField::draw(gcn::Graphics *graphics)
+{
+ updateAlpha();
+
+ if (isFocused())
+ {
+ drawCaret(graphics,
+ getFont()->getWidth(mText.substr(0, mCaretPosition)) -
+ mXScroll);
+ }
+
+ graphics->setColor(Theme::getThemeColor(Theme::TEXT));
+ graphics->setFont(getFont());
+ graphics->drawText(mText, 1 - mXScroll, 1);
+}
+
+void TextField::drawFrame(gcn::Graphics *graphics)
+{
+ //updateAlpha(); -> Not useful...
+
+ int w, h, bs;
+ bs = getFrameSize();
+ w = getWidth() + bs * 2;
+ h = getHeight() + bs * 2;
+
+ static_cast<Graphics*>(graphics)->drawImageRect(0, 0, w, h, skin);
+}
+
+void TextField::setNumeric(bool numeric)
+{
+ mNumeric = numeric;
+ if (!numeric)
+ return;
+
+ const char *text = mText.c_str();
+ for (const char *textPtr = text; *textPtr; ++textPtr)
+ {
+ if (*textPtr < '0' || *textPtr > '9')
+ {
+ setText(mText.substr(0, textPtr - text));
+ return;
+ }
+ }
+}
+
+int TextField::getValue() const
+{
+ if (!mNumeric)
+ return 0;
+
+ int value = atoi(mText.c_str());
+ if (value < mMinimum)
+ return mMinimum;
+
+ if (value > mMaximum)
+ return mMaximum;
+
+ return value;
+}
+
+void TextField::keyPressed(gcn::KeyEvent &keyEvent)
+{
+ int val = keyEvent.getKey().getValue();
+
+ if (val >= 32)
+ {
+ int l;
+ if (val < 128)
+ l = 1; // 0xxxxxxx
+ else if (val < 0x800)
+ l = 2; // 110xxxxx 10xxxxxx
+ else if (val < 0x10000)
+ l = 3; // 1110xxxx 10xxxxxx 10xxxxxx
+ else
+ l = 4; // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+ char buf[4];
+ for (int i = 0; i < l; ++i)
+ {
+ buf[i] = static_cast<char>(val >> (6 * (l - i - 1)));
+ if (i > 0)
+ buf[i] = static_cast<char>((buf[i] & 63) | 128);
+ }
+
+ if (l > 1)
+ buf[0] |= static_cast<char>(255 << (8 - l));
+
+ mText.insert(mCaretPosition, std::string(buf, buf + l));
+ mCaretPosition += l;
+ }
+
+ /* In UTF-8, 10xxxxxx is only used for inner parts of characters. So skip
+ them when processing key presses. */
+
+ switch (val)
+ {
+ case Key::LEFT:
+ {
+ while (mCaretPosition > 0)
+ {
+ --mCaretPosition;
+ if ((mText[mCaretPosition] & 192) != 128)
+ break;
+ }
+ } break;
+
+ case Key::RIGHT:
+ {
+ unsigned sz = static_cast<unsigned>(mText.size());
+ while (mCaretPosition < sz)
+ {
+ ++mCaretPosition;
+ if (mCaretPosition == sz ||
+ (mText[mCaretPosition] & 192) != 128)
+ {
+ break;
+ }
+ }
+ } break;
+
+ case Key::DELETE:
+ {
+ unsigned sz = static_cast<unsigned>(mText.size());
+ while (mCaretPosition < sz)
+ {
+ --sz;
+ mText.erase(mCaretPosition, 1);
+ if (mCaretPosition == sz ||
+ (mText[mCaretPosition] & 192) != 128)
+ {
+ break;
+ }
+ }
+ } break;
+
+ case Key::BACKSPACE:
+ {
+ while (mCaretPosition > 0)
+ {
+ --mCaretPosition;
+ int v = mText[mCaretPosition];
+ mText.erase(mCaretPosition, 1);
+ if ((v & 192) != 128)
+ break;
+ }
+ } break;
+
+ case Key::ENTER:
+ distributeActionEvent();
+ break;
+
+ case Key::HOME:
+ mCaretPosition = 0;
+ break;
+
+ case Key::END:
+ mCaretPosition = static_cast<unsigned>(mText.size());
+ break;
+
+ case Key::TAB:
+ if (mLoseFocusOnTab)
+ return;
+ break;
+
+ case 22: // Control code 22, SYNCHRONOUS IDLE, sent on Ctrl+v
+ handlePaste();
+ break;
+ default:
+ break;
+ }
+
+ keyEvent.consume();
+ fixScroll();
+}
+
+void TextField::handlePaste()
+{
+ std::string text = getText();
+ std::string::size_type caretPos = getCaretPosition();
+
+ if (RetrieveBuffer(text, caretPos))
+ {
+ setText(text);
+ setCaretPosition(static_cast<unsigned>(caretPos));
+ }
+}
diff --git a/src/gui/widgets/textfield.h b/src/gui/widgets/textfield.h
new file mode 100644
index 000000000..b894fdc85
--- /dev/null
+++ b/src/gui/widgets/textfield.h
@@ -0,0 +1,110 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 TEXTFIELD_H
+#define TEXTFIELD_H
+
+#include <guichan/widgets/textfield.hpp>
+
+class ImageRect;
+class TextField;
+
+/**
+ * A text field.
+ *
+ * \ingroup GUI
+ */
+class TextField : public gcn::TextField
+{
+ public:
+ /**
+ * Constructor, initializes the text field with the given string.
+ */
+ TextField(const std::string &text = "", bool loseFocusOnTab = true,
+ gcn::ActionListener* listener = NULL,
+ std::string eventId = "");
+
+ ~TextField();
+
+ /**
+ * Draws the text field.
+ */
+ virtual void draw(gcn::Graphics *graphics);
+
+ /**
+ * Update the alpha value to the graphic components.
+ */
+ void updateAlpha();
+
+ /**
+ * Draws the background and border.
+ */
+ void drawFrame(gcn::Graphics *graphics);
+
+ /**
+ * Determine whether the field should be numeric or not
+ */
+ void setNumeric(bool numeric);
+
+ /**
+ * Set the range on the field if it is numeric
+ */
+ void setRange(int min, int max)
+ {
+ mMinimum = min;
+ mMaximum = max;
+ }
+
+ /**
+ * Processes one keypress.
+ */
+ void keyPressed(gcn::KeyEvent &keyEvent);
+
+ /**
+ * Set the minimum value for a range
+ */
+ void setMinimum(int min)
+ { mMinimum = min; }
+
+ /**
+ * Set the maximum value for a range
+ */
+ void setMaximum(int max)
+ { mMaximum = max; }
+
+ /**
+ * Return the value for a numeric field
+ */
+ int getValue() const;
+
+ private:
+ void handlePaste();
+
+ static int instances;
+ static float mAlpha;
+ static ImageRect skin;
+ bool mNumeric;
+ int mMinimum;
+ int mMaximum;
+ bool mLoseFocusOnTab;
+};
+
+#endif
diff --git a/src/gui/widgets/textpreview.cpp b/src/gui/widgets/textpreview.cpp
new file mode 100644
index 000000000..bd38d8a80
--- /dev/null
+++ b/src/gui/widgets/textpreview.cpp
@@ -0,0 +1,82 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2006-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/textpreview.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "textrenderer.h"
+
+#include "gui/gui.h"
+#include "gui/palette.h"
+#include "gui/truetypefont.h"
+
+#include <typeinfo>
+
+float TextPreview::mAlpha = 1.0;
+
+TextPreview::TextPreview(const std::string &text):
+ mText(text)
+{
+ mTextAlpha = false;
+ mFont = gui->getFont();
+ mTextColor = &Theme::getThemeColor(Theme::TEXT);
+ mTextBGColor = NULL;
+ mBGColor = &Theme::getThemeColor(Theme::BACKGROUND);
+ mOpaque = false;
+}
+
+void TextPreview::draw(gcn::Graphics* graphics)
+{
+ if (Client::getGuiAlpha() != mAlpha)
+ mAlpha = Client::getGuiAlpha();
+
+ int alpha = static_cast<int>(mAlpha * 255.0f);
+
+ if (!mTextAlpha)
+ alpha = 255;
+
+ if (mOpaque)
+ {
+ graphics->setColor(gcn::Color(static_cast<int>(mBGColor->r),
+ static_cast<int>(mBGColor->g),
+ static_cast<int>(mBGColor->b),
+ static_cast<int>(mAlpha * 255.0f)));
+ graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
+ }
+
+ if (mTextBGColor && typeid(*mFont) == typeid(TrueTypeFont))
+ {
+ TrueTypeFont *font = static_cast<TrueTypeFont*>(mFont);
+ int x = font->getWidth(mText) + 1 + 2 * ((mOutline || mShadow) ? 1 :0);
+ int y = font->getHeight() + 1 + 2 * ((mOutline || mShadow) ? 1 : 0);
+ graphics->setColor(gcn::Color(static_cast<int>(mTextBGColor->r),
+ static_cast<int>(mTextBGColor->g),
+ static_cast<int>(mTextBGColor->b),
+ static_cast<int>(mAlpha * 255.0f)));
+ graphics->fillRectangle(gcn::Rectangle(1, 1, x, y));
+ }
+
+ TextRenderer::renderText(graphics, mText, 2, 2, gcn::Graphics::LEFT,
+ gcn::Color(mTextColor->r, mTextColor->g,
+ mTextColor->b, alpha),
+ mFont, mOutline, mShadow);
+}
diff --git a/src/gui/widgets/textpreview.h b/src/gui/widgets/textpreview.h
new file mode 100644
index 000000000..a34ab3853
--- /dev/null
+++ b/src/gui/widgets/textpreview.h
@@ -0,0 +1,130 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2006-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 TEXTPREVIEW_H
+#define TEXTPREVIEW_H
+
+#include <guichan/color.hpp>
+#include <guichan/font.hpp>
+#include <guichan/widget.hpp>
+
+/**
+ * Preview widget for particle colors, etc.
+ */
+class TextPreview : public gcn::Widget
+{
+ public:
+ TextPreview(const std::string &text);
+
+ /**
+ * Sets the color the text is printed in.
+ *
+ * @param color the color to set
+ */
+ inline void setTextColor(const gcn::Color *color)
+ { mTextColor = color; }
+
+ /**
+ * Sets the text to use the set alpha value.
+ *
+ * @param alpha whether to use alpha values for the text or not
+ */
+ inline void useTextAlpha(bool alpha)
+ { mTextAlpha = alpha; }
+
+ /**
+ * Sets the color the text background is drawn in. This is only the
+ * rectangle directly behind the text, not to full widget.
+ *
+ * @param color the color to set
+ */
+ inline void setTextBGColor(const gcn::Color *color)
+ { mTextBGColor = color; }
+
+ /**
+ * Sets the background color of the widget.
+ *
+ * @param color the color to set
+ */
+ inline void setBGColor(const gcn::Color *color)
+ { mBGColor = color; }
+
+ /**
+ * Sets the font to render the text in.
+ *
+ * @param font the font to use.
+ */
+ inline void setFont(gcn::Font *font)
+ { mFont = font; }
+
+ /**
+ * Sets whether to use a shadow while rendering.
+ *
+ * @param shadow true, if a shadow is wanted, false else
+ */
+ inline void setShadow(bool shadow)
+ { mShadow = shadow; }
+
+ /**
+ * Sets whether to use an outline while rendering.
+ *
+ * @param outline true, if an outline is wanted, false else
+ */
+ inline void setOutline(bool outline)
+ { mOutline = outline; }
+
+ /**
+ * Widget's draw method. Does the actual job.
+ *
+ * @param graphics graphics to draw into
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Set opacity for this widget (whether or not to show the background
+ * color)
+ *
+ * @param opaque Whether the widget should be opaque or not
+ */
+ void setOpaque(bool opaque)
+ { mOpaque = opaque; }
+
+ /**
+ * Gets opacity for this widget (whether or not the background color
+ * is shown below the widget)
+ */
+ bool isOpaque() const
+ { return mOpaque; }
+
+ private:
+ gcn::Font *mFont;
+ std::string mText;
+ const gcn::Color *mTextColor;
+ const gcn::Color *mBGColor;
+ const gcn::Color *mTextBGColor;
+ static float mAlpha;
+ bool mTextAlpha;
+ bool mOpaque;
+ bool mShadow;
+ bool mOutline;
+};
+
+#endif
diff --git a/src/gui/widgets/tradetab.cpp b/src/gui/widgets/tradetab.cpp
new file mode 100644
index 000000000..fb4a57fd5
--- /dev/null
+++ b/src/gui/widgets/tradetab.cpp
@@ -0,0 +1,59 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/tradetab.h"
+
+#include "chatlog.h"
+#include "commandhandler.h"
+#include "localplayer.h"
+#include "log.h"
+
+#include "gui/theme.h"
+
+#include "net/net.h"
+
+#include "resources/iteminfo.h"
+#include "resources/itemdb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+TradeTab::TradeTab() :
+ ChatTab(_("Trade"))
+{
+}
+
+TradeTab::~TradeTab()
+{
+}
+
+void TradeTab::handleInput(const std::string &msg)
+{
+ std::string str = "\302\202" + msg;
+ ChatTab::handleInput(str);
+}
+
+void TradeTab::saveToLogFile(std::string &msg)
+{
+ if (chatLogger)
+ chatLogger->log(std::string("#Trade"), std::string(msg));
+}
diff --git a/src/gui/widgets/tradetab.h b/src/gui/widgets/tradetab.h
new file mode 100644
index 000000000..fceeb1e40
--- /dev/null
+++ b/src/gui/widgets/tradetab.h
@@ -0,0 +1,50 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 TRADETAB_H
+#define TRADETAB_H
+
+#include "gui/widgets/chattab.h"
+
+/**
+ * A tab for a party chat channel.
+ */
+class TradeTab : public ChatTab
+{
+ public:
+ TradeTab();
+
+ ~TradeTab();
+
+ int getType() const
+ { return ChatTab::TAB_TRADE; }
+
+ void saveToLogFile(std::string &msg);
+
+ protected:
+ void handleInput(const std::string &msg);
+};
+
+extern TradeTab *tradeChatTab;
+#endif
+
+
+
diff --git a/src/gui/widgets/vertcontainer.cpp b/src/gui/widgets/vertcontainer.cpp
new file mode 100644
index 000000000..4dac2a617
--- /dev/null
+++ b/src/gui/widgets/vertcontainer.cpp
@@ -0,0 +1,53 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/vertcontainer.h"
+
+VertContainer::VertContainer(int spacing):
+ mSpacing(spacing),
+ mCount(0)
+{
+ addWidgetListener(this);
+}
+
+void VertContainer::add(gcn::Widget *widget)
+{
+ if (!widget)
+ return;
+
+ Container::add(widget);
+ widget->setPosition(0, mCount * mSpacing);
+ widget->setSize(getWidth(), mSpacing);
+ mCount++;
+ setHeight(mCount * mSpacing);
+}
+
+void VertContainer::clear()
+{
+ Container::clear();
+
+ mCount = 0;
+}
+
+void VertContainer::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ for (WidgetListIterator it = mWidgets.begin(); it != mWidgets.end(); it++)
+ (*it)->setWidth(getWidth());
+}
diff --git a/src/gui/widgets/vertcontainer.h b/src/gui/widgets/vertcontainer.h
new file mode 100644
index 000000000..fe62d8c0e
--- /dev/null
+++ b/src/gui/widgets/vertcontainer.h
@@ -0,0 +1,52 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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_VERTCONTAINER_H
+#define GUI_VERTCONTAINER_H
+
+#include "gui/widgets/container.h"
+
+#include <guichan/widgetlistener.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+/**
+ * A widget container.
+ *
+ * This container places it's contents veritcally.
+ */
+class VertContainer : public Container, public gcn::WidgetListener
+{
+ public:
+ VertContainer(int spacing);
+ virtual void add(gcn::Widget *widget);
+ virtual void clear();
+ void widgetResized(const gcn::Event &event);
+
+ private:
+ int mSpacing;
+ int mCount;
+};
+
+#endif
diff --git a/src/gui/widgets/whispertab.cpp b/src/gui/widgets/whispertab.cpp
new file mode 100644
index 000000000..f0c347b59
--- /dev/null
+++ b/src/gui/widgets/whispertab.cpp
@@ -0,0 +1,164 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 "whispertab.h"
+
+#include "chatlog.h"
+#include "commandhandler.h"
+#include "localplayer.h"
+#include "log.h"
+
+#include "gui/theme.h"
+
+#include "net/chathandler.h"
+#include "net/net.h"
+
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+WhisperTab::WhisperTab(const std::string &nick) :
+ ChatTab(nick),
+ mNick(nick)
+{
+ setTabColor(&Theme::getThemeColor(Theme::WHISPER));
+}
+
+WhisperTab::~WhisperTab()
+{
+ if (chatWindow)
+ chatWindow->removeWhisper(mNick);
+}
+
+void WhisperTab::handleInput(const std::string &msg)
+{
+// if (msg.empty())
+// {
+// chatLog(_("Cannot send empty chat!"), BY_SERVER, false);
+// return;
+// }
+
+ if (chatWindow)
+ {
+ Net::getChatHandler()->privateMessage(mNick,
+ chatWindow->doReplace(msg));
+ }
+ else
+ {
+ Net::getChatHandler()->privateMessage(mNick, msg);
+ }
+
+ if (player_node)
+ chatLog(player_node->getName(), msg);
+ else
+ chatLog("?", msg);
+}
+
+void WhisperTab::handleCommand(const std::string &msg)
+{
+ if (msg == "close")
+ {
+ delete this;
+ return;
+ }
+
+ std::string::size_type pos = msg.find(' ');
+ std::string type(msg, 0, pos);
+ std::string args(msg, pos == std::string::npos
+ ? msg.size() : pos + 1);
+
+ if (type == "me")
+ {
+ std::string str = strprintf("*%s*", args.c_str());
+ Net::getChatHandler()->privateMessage(mNick, str);
+ if (player_node)
+ chatLog(player_node->getName(), str);
+ else
+ chatLog("?", str);
+ }
+ else
+ {
+ ChatTab::handleCommand(msg);
+ }
+}
+
+void WhisperTab::showHelp()
+{
+ chatLog(_("/ignore > Ignore the other player"));
+ chatLog(_("/unignore > Stop ignoring the other player"));
+ chatLog(_("/close > Close the whisper tab"));
+}
+
+bool WhisperTab::handleCommand(const std::string &type,
+ const std::string &args)
+{
+ if (type == "help")
+ {
+ if (args == "close")
+ {
+ chatLog(_("Command: /close"));
+ chatLog(_("This command closes the current whisper tab."));
+ }
+ else if (args == "ignore")
+ {
+ chatLog(_("Command: /ignore"));
+ chatLog(_("This command ignores the other player regardless of "
+ "current relations."));
+ }
+ else if (args == "unignore")
+ {
+ chatLog(_("Command: /unignore <player>"));
+ chatLog(_("This command stops ignoring the other player if they "
+ "are being ignored."));
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (type == "close")
+ {
+ delete this;
+ if (chatWindow)
+ chatWindow->defaultTab();
+ }
+ else if (type == "ignore")
+ {
+ if (commandHandler)
+ commandHandler->handleIgnore(mNick, this);
+ }
+ else if (type == "unignore")
+ {
+ if (commandHandler)
+ commandHandler->handleUnignore(mNick, this);
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void WhisperTab::saveToLogFile(std::string &msg)
+{
+ if (chatLogger)
+ chatLogger->log(getNick(), msg);
+}
diff --git a/src/gui/widgets/whispertab.h b/src/gui/widgets/whispertab.h
new file mode 100644
index 000000000..89e70695b
--- /dev/null
+++ b/src/gui/widgets/whispertab.h
@@ -0,0 +1,67 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 WHISPERTAB_H
+#define WHISPERTAB_H
+
+#include "chattab.h"
+
+class Channel;
+
+/**
+ * A tab for whispers from a single player.
+ */
+class WhisperTab : public ChatTab
+{
+ public:
+ const std::string &getNick() const { return mNick; }
+
+ void showHelp();
+
+ bool handleCommand(const std::string &type,
+ const std::string &args);
+
+ int getType() const
+ { return ChatTab::TAB_WHISPER; }
+
+ void saveToLogFile(std::string &msg);
+
+ protected:
+ friend class ChatWindow;
+
+ /**
+ * Constructor.
+ *
+ * @param nick the name of the player this tab is whispering to
+ */
+ WhisperTab(const std::string &nick);
+
+ ~WhisperTab();
+
+ void handleInput(const std::string &msg);
+
+ void handleCommand(const std::string &msg);
+
+ private:
+ std::string mNick;
+};
+
+#endif // CHANNELTAB_H
diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp
new file mode 100644
index 000000000..6564a8d3f
--- /dev/null
+++ b/src/gui/widgets/window.cpp
@@ -0,0 +1,924 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/window.h"
+
+#include "client.h"
+#include "configuration.h"
+#include "log.h"
+
+#include "gui/gui.h"
+#include "gui/palette.h"
+#include "gui/theme.h"
+#include "gui/viewport.h"
+
+#include "gui/widgets/layout.h"
+#include "gui/widgets/resizegrip.h"
+#include "gui/widgets/windowcontainer.h"
+
+#include "resources/image.h"
+
+#include <guichan/exception.hpp>
+#include <guichan/focushandler.hpp>
+
+int Window::instances = 0;
+int Window::mouseResize = 0;
+
+Window::Window(const std::string &caption, bool modal, Window *parent,
+ const std::string &skin):
+ gcn::Window(caption),
+ mGrip(0),
+ mParent(parent),
+ mLayout(NULL),
+ mWindowName("window"),
+ mShowTitle(true),
+ mModal(modal),
+ mCloseButton(false),
+ mDefaultVisible(false),
+ mSaveVisible(false),
+ mStickyButton(false),
+ mSticky(false),
+ mMinWinWidth(100),
+ mMinWinHeight(40),
+ mMaxWinWidth(graphics->getWidth()),
+ mMaxWinHeight(graphics->getHeight())
+{
+ logger->log("Window::Window(\"%s\")", caption.c_str());
+
+ if (!windowContainer)
+ throw GCN_EXCEPTION("Window::Window(): no windowContainer set");
+
+ instances++;
+
+ setFrameSize(0);
+ setPadding(3);
+ setTitleBarHeight(20);
+
+ // Loads the skin
+ mSkin = Theme::instance()->load(skin);
+
+ // Add this window to the window container
+ windowContainer->add(this);
+
+ if (mModal)
+ {
+ gui->setCursorType(Gui::CURSOR_POINTER);
+ requestModalFocus();
+ }
+
+ // Windows are invisible by default
+ setVisible(false);
+
+ addWidgetListener(this);
+}
+
+Window::~Window()
+{
+ logger->log("Window::~Window(\"%s\")", getCaption().c_str());
+
+ saveWindowState();
+
+ delete mLayout;
+ mLayout = 0;
+
+ while (!mWidgets.empty())
+ delete mWidgets.front();
+
+// need mWidgets.clean ?
+
+ removeWidgetListener(this);
+
+ instances--;
+
+ if (mSkin)
+ mSkin->instances--;
+}
+
+void Window::setWindowContainer(WindowContainer *wc)
+{
+ windowContainer = wc;
+}
+
+void Window::draw(gcn::Graphics *graphics)
+{
+ if (!mSkin)
+ return;
+
+ Graphics *g = static_cast<Graphics*>(graphics);
+
+ g->drawImageRect(0, 0, getWidth(), getHeight(), mSkin->getBorder());
+
+ // Draw title
+ if (mShowTitle)
+ {
+ g->setColor(Theme::getThemeColor(Theme::TEXT));
+ g->setFont(getFont());
+ g->drawText(getCaption(), 7, 5, gcn::Graphics::LEFT);
+ }
+
+ // Draw Close Button
+ if (mCloseButton && mSkin->getCloseImage())
+ {
+ g->drawImage(mSkin->getCloseImage(),
+ getWidth() - mSkin->getCloseImage()->getWidth() - getPadding(),
+ getPadding());
+ }
+
+ // Draw Sticky Button
+ if (mStickyButton)
+ {
+ Image *button = mSkin->getStickyImage(mSticky);
+ if (button)
+ {
+ int x = getWidth() - button->getWidth() - getPadding();
+ if (mCloseButton && mSkin->getCloseImage())
+ x -= mSkin->getCloseImage()->getWidth();
+
+ g->drawImage(button, x, getPadding());
+ }
+ }
+
+ drawChildren(graphics);
+}
+
+void Window::setContentSize(int width, int height)
+{
+ width = width + 2 * getPadding();
+ height = height + getPadding() + getTitleBarHeight();
+
+ if (getMinWidth() > width)
+ width = getMinWidth();
+ else if (getMaxWidth() < width)
+ width = getMaxWidth();
+ if (getMinHeight() > height)
+ height = getMinHeight();
+ else if (getMaxHeight() < height)
+ height = getMaxHeight();
+
+ setSize(width, height);
+}
+
+void Window::setLocationRelativeTo(gcn::Widget *widget)
+{
+ if (!widget)
+ return;
+
+ int wx, wy;
+ int x, y;
+
+ widget->getAbsolutePosition(wx, wy);
+ getAbsolutePosition(x, y);
+
+ setPosition(getX() + (wx + (widget->getWidth() - getWidth()) / 2 - x),
+ getY() + (wy + (widget->getHeight() - getHeight()) / 2 - y));
+}
+
+void Window::setLocationHorisontallyRelativeTo(gcn::Widget *widget)
+{
+ if (!widget)
+ return;
+
+ int wx, wy;
+ int x, y;
+
+ widget->getAbsolutePosition(wx, wy);
+ getAbsolutePosition(x, y);
+
+ setPosition(getX() + (wx + (widget->getWidth() - getWidth()) / 2 - x), 0);
+}
+
+void Window::setLocationRelativeTo(ImageRect::ImagePosition position,
+ int offsetX, int offsetY)
+{
+ if (position == ImageRect::UPPER_LEFT)
+ {
+ }
+ else if (position == ImageRect::UPPER_CENTER)
+ {
+ offsetX += (graphics->getWidth() - getWidth()) / 2;
+ }
+ else if (position == ImageRect::UPPER_RIGHT)
+ {
+ offsetX += graphics->getWidth() - getWidth();
+ }
+ else if (position == ImageRect::LEFT)
+ {
+ offsetY += (graphics->getHeight() - getHeight()) / 2;
+ }
+ else if (position == ImageRect::CENTER)
+ {
+ offsetX += (graphics->getWidth() - getWidth()) / 2;
+ offsetY += (graphics->getHeight() - getHeight()) / 2;
+ }
+ else if (position == ImageRect::RIGHT)
+ {
+ offsetX += graphics->getWidth() - getWidth();
+ offsetY += (graphics->getHeight() - getHeight()) / 2;
+ }
+ else if (position == ImageRect::LOWER_LEFT)
+ {
+ offsetY += graphics->getHeight() - getHeight();
+ }
+ else if (position == ImageRect::LOWER_CENTER)
+ {
+ offsetX += (graphics->getWidth() - getWidth()) / 2;
+ offsetY += graphics->getHeight() - getHeight();
+ }
+ else if (position == ImageRect::LOWER_RIGHT)
+ {
+ offsetX += graphics->getWidth() - getWidth();
+ offsetY += graphics->getHeight() - getHeight();
+ }
+
+ setPosition(offsetX, offsetY);
+}
+
+void Window::setMinWidth(int width)
+{
+ mMinWinWidth = width > mSkin->getMinWidth() ? width : mSkin->getMinWidth();
+}
+
+void Window::setMinHeight(int height)
+{
+ mMinWinHeight = height > mSkin->getMinHeight() ?
+ height : mSkin->getMinHeight();
+}
+
+void Window::setMaxWidth(int width)
+{
+ mMaxWinWidth = width;
+}
+
+void Window::setMaxHeight(int height)
+{
+ mMaxWinHeight = height;
+}
+
+void Window::setResizable(bool r)
+{
+ if (static_cast<bool>(mGrip) == r)
+ return;
+
+ if (r)
+ {
+ mGrip = new ResizeGrip;
+ mGrip->setX(getWidth() - mGrip->getWidth() - getChildrenArea().x);
+ mGrip->setY(getHeight() - mGrip->getHeight() - getChildrenArea().y);
+ add(mGrip);
+ }
+ else
+ {
+ remove(mGrip);
+ delete mGrip;
+ mGrip = 0;
+ }
+}
+
+void Window::widgetResized(const gcn::Event &event _UNUSED_)
+{
+ const gcn::Rectangle area = getChildrenArea();
+
+ if (mGrip)
+ {
+ mGrip->setPosition(getWidth() - mGrip->getWidth() - area.x,
+ getHeight() - mGrip->getHeight() - area.y);
+ }
+
+ if (mLayout)
+ {
+ int w = area.width;
+ int h = area.height;
+ mLayout->reflow(w, h);
+ }
+}
+
+void Window::widgetHidden(const gcn::Event &event _UNUSED_)
+{
+ if (gui)
+ gui->setCursorType(Gui::CURSOR_POINTER);
+
+ WidgetListIterator it;
+
+ if (!mFocusHandler)
+ return;
+
+ for (it = mWidgets.begin(); it != mWidgets.end(); it++)
+ {
+ if (mFocusHandler->isFocused(*it))
+ mFocusHandler->focusNone();
+ }
+}
+
+void Window::setCloseButton(bool flag)
+{
+ mCloseButton = flag;
+}
+
+bool Window::isResizable() const
+{
+ return mGrip;
+}
+
+void Window::setStickyButton(bool flag)
+{
+ mStickyButton = flag;
+}
+
+void Window::setSticky(bool sticky)
+{
+ mSticky = sticky;
+}
+
+void Window::setVisible(bool visible)
+{
+ setVisible(visible, false);
+}
+
+void Window::setVisible(bool visible, bool forceSticky)
+{
+ if (visible == isVisible())
+ return; // Nothing to do
+
+ // Check if the window is off screen...
+ if (visible)
+ checkIfIsOffScreen();
+
+ gcn::Window::setVisible((!forceSticky && isSticky()) || visible);
+}
+
+void Window::scheduleDelete()
+{
+ windowContainer->scheduleDelete(this);
+}
+
+void Window::mousePressed(gcn::MouseEvent &event)
+{
+ // Let Guichan move window to top and figure out title bar drag
+ gcn::Window::mousePressed(event);
+
+ if (event.getButton() == gcn::MouseEvent::LEFT)
+ {
+ const int x = event.getX();
+ const int y = event.getY();
+
+ // Handle close button
+ if (mCloseButton)
+ {
+ Image *img = mSkin->getCloseImage();
+ if (img)
+ {
+ gcn::Rectangle closeButtonRect(
+ getWidth() - img->getWidth()
+ - getPadding(), getPadding(),
+ img->getWidth(), img->getHeight());
+
+ if (closeButtonRect.isPointInRect(x, y))
+ {
+ mouseResize = 0;
+ mMoved = 0;
+ close();
+ return;
+ }
+ }
+ }
+
+ // Handle sticky button
+ if (mStickyButton)
+ {
+ Image *button = mSkin->getStickyImage(mSticky);
+ if (button)
+ {
+ int rx = getWidth() - button->getWidth() - getPadding();
+ if (mCloseButton)
+ {
+ Image *img = mSkin->getCloseImage();
+ if (img)
+ rx -= img->getWidth();
+ }
+ gcn::Rectangle stickyButtonRect(rx, getPadding(),
+ button->getWidth(), button->getHeight());
+ if (stickyButtonRect.isPointInRect(x, y))
+ {
+ setSticky(!isSticky());
+ mouseResize = 0;
+ mMoved = 0;
+ return;
+ }
+ }
+ }
+
+ // Handle window resizing
+ mouseResize = getResizeHandles(event);
+ mMoved = !mouseResize;
+ }
+}
+
+void Window::close()
+{
+ setVisible(false);
+}
+
+void Window::mouseReleased(gcn::MouseEvent &event _UNUSED_)
+{
+ if (mGrip && mouseResize)
+ {
+ mouseResize = 0;
+ if (gui)
+ gui->setCursorType(Gui::CURSOR_POINTER);
+ }
+
+ // This should be the responsibility of Guichan (and is from 0.8.0 on)
+ mMoved = false;
+}
+
+void Window::mouseExited(gcn::MouseEvent &event _UNUSED_)
+{
+ if (mGrip && !mouseResize && gui)
+ gui->setCursorType(Gui::CURSOR_POINTER);
+}
+
+void Window::mouseMoved(gcn::MouseEvent &event)
+{
+ if (!gui)
+ return;
+
+ int resizeHandles = getResizeHandles(event);
+
+ // Changes the custom mouse cursor based on it's current position.
+ switch (resizeHandles)
+ {
+ case BOTTOM | RIGHT:
+ case TOP | LEFT:
+ gui->setCursorType(Gui::CURSOR_RESIZE_DOWN_RIGHT);
+ break;
+ case TOP | RIGHT:
+ case BOTTOM | LEFT:
+ gui->setCursorType(Gui::CURSOR_RESIZE_DOWN_LEFT);
+ break;
+ case BOTTOM:
+ case TOP:
+ gui->setCursorType(Gui::CURSOR_RESIZE_DOWN);
+ break;
+ case RIGHT:
+ case LEFT:
+ gui->setCursorType(Gui::CURSOR_RESIZE_ACROSS);
+ break;
+ default:
+ gui->setCursorType(Gui::CURSOR_POINTER);
+ }
+
+ if (viewport)
+ viewport->hideBeingPopup();
+}
+
+void Window::mouseDragged(gcn::MouseEvent &event)
+{
+ // Let Guichan handle title bar drag
+ gcn::Window::mouseDragged(event);
+
+ // Keep guichan window inside screen when it may be moved
+ if (isMovable() && mMoved)
+ {
+ int newX = std::max(0, getX());
+ int newY = std::max(0, getY());
+ newX = std::min(graphics->getWidth() - getWidth(), newX);
+ newY = std::min(graphics->getHeight() - getHeight(), newY);
+ setPosition(newX, newY);
+ }
+
+ if (mouseResize && !mMoved)
+ {
+ const int dx = event.getX() - mDragOffsetX;
+ const int dy = event.getY() - mDragOffsetY;
+ gcn::Rectangle newDim = getDimension();
+
+ if (mouseResize & (TOP | BOTTOM))
+ {
+ int newHeight = newDim.height + ((mouseResize & TOP) ? -dy : dy);
+ newDim.height = std::min(mMaxWinHeight,
+ std::max(mMinWinHeight, newHeight));
+
+ if (mouseResize & TOP)
+ newDim.y -= newDim.height - getHeight();
+ }
+
+ if (mouseResize & (LEFT | RIGHT))
+ {
+ int newWidth = newDim.width + ((mouseResize & LEFT) ? -dx : dx);
+ newDim.width = std::min(mMaxWinWidth,
+ std::max(mMinWinWidth, newWidth));
+
+ if (mouseResize & LEFT)
+ newDim.x -= newDim.width - getWidth();
+ }
+
+ // Keep guichan window inside screen (supports resizing any side)
+ if (newDim.x < 0)
+ {
+ newDim.width += newDim.x;
+ newDim.x = 0;
+ }
+ if (newDim.y < 0)
+ {
+ newDim.height += newDim.y;
+ newDim.y = 0;
+ }
+ if (newDim.x + newDim.width > graphics->getWidth())
+ {
+ newDim.width = graphics->getWidth() - newDim.x;
+ }
+ if (newDim.y + newDim.height > graphics->getHeight())
+ {
+ newDim.height = graphics->getHeight() - newDim.y;
+ }
+
+ // Update mouse offset when dragging bottom or right border
+ if (mouseResize & BOTTOM)
+ mDragOffsetY += newDim.height - getHeight();
+
+ if (mouseResize & RIGHT)
+ mDragOffsetX += newDim.width - getWidth();
+
+ // Set the new window and content dimensions
+ setDimension(newDim);
+ }
+}
+
+void Window::setModal(bool modal)
+{
+ if (mModal != modal)
+ {
+ mModal = modal;
+ if (mModal)
+ {
+ if (gui)
+ gui->setCursorType(Gui::CURSOR_POINTER);
+ requestModalFocus();
+ }
+ else
+ {
+ releaseModalFocus();
+ }
+ }
+}
+
+void Window::loadWindowState()
+{
+ const std::string &name = mWindowName;
+ assert(!name.empty());
+
+ setPosition(config.getValueInt(name + "WinX", mDefaultX),
+ config.getValueInt(name + "WinY", mDefaultY));
+
+ if (mSaveVisible)
+ {
+ setVisible(config.getValueBool(name
+ + "Visible", mDefaultVisible));
+ }
+
+ if (mStickyButton)
+ {
+ setSticky(config.getValueBool(name
+ + "Sticky", isSticky()));
+ }
+
+ if (mGrip)
+ {
+ int width = config.getValueInt(name + "WinWidth", mDefaultWidth);
+ int height = config.getValueInt(name + "WinHeight", mDefaultHeight);
+
+ if (getMinWidth() > width)
+ width = getMinWidth();
+ else if (getMaxWidth() < width)
+ width = getMaxWidth();
+ if (getMinHeight() > height)
+ height = getMinHeight();
+ else if (getMaxHeight() < height)
+ height = getMaxHeight();
+
+ setSize(width, height);
+ }
+ else
+ {
+ setSize(mDefaultWidth, mDefaultHeight);
+ }
+
+ // Check if the window is off screen...
+ checkIfIsOffScreen();
+}
+
+void Window::saveWindowState()
+{
+ // Saving X, Y and Width and Height for resizables in the config
+ if (!mWindowName.empty() && mWindowName != "window")
+ {
+ config.setValue(mWindowName + "WinX", getX());
+ config.setValue(mWindowName + "WinY", getY());
+
+ if (mSaveVisible)
+ config.setValue(mWindowName + "Visible", isVisible());
+
+ if (mStickyButton)
+ config.setValue(mWindowName + "Sticky", isSticky());
+
+ if (mGrip)
+ {
+ if (getMinWidth() > getWidth())
+ setWidth(getMinWidth());
+ else if (getMaxWidth() < getWidth())
+ setWidth(getMaxWidth());
+ if (getMinHeight() > getHeight())
+ setHeight(getMinHeight());
+ else if (getMaxHeight() < getHeight())
+ setHeight(getMaxHeight());
+
+ config.setValue(mWindowName + "WinWidth", getWidth());
+ config.setValue(mWindowName + "WinHeight", getHeight());
+ }
+ }
+}
+
+void Window::setDefaultSize(int defaultX, int defaultY,
+ int defaultWidth, int defaultHeight)
+{
+ if (getMinWidth() > defaultWidth)
+ defaultWidth = getMinWidth();
+ else if (getMaxWidth() < defaultWidth)
+ defaultWidth = getMaxWidth();
+ if (getMinHeight() > defaultHeight)
+ defaultHeight = getMinHeight();
+ else if (getMaxHeight() < defaultHeight)
+ defaultHeight = getMaxHeight();
+
+ mDefaultX = defaultX;
+ mDefaultY = defaultY;
+ mDefaultWidth = defaultWidth;
+ mDefaultHeight = defaultHeight;
+}
+
+void Window::setDefaultSize()
+{
+ mDefaultX = getX();
+ mDefaultY = getY();
+ mDefaultWidth = getWidth();
+ mDefaultHeight = getHeight();
+}
+
+void Window::setDefaultSize(int defaultWidth, int defaultHeight,
+ ImageRect::ImagePosition position,
+ int offsetX, int offsetY)
+{
+ int x = 0, y = 0;
+
+ if (position == ImageRect::UPPER_LEFT)
+ {
+ }
+ else if (position == ImageRect::UPPER_CENTER)
+ {
+ x = (graphics->getWidth() - defaultWidth) / 2;
+ }
+ else if (position == ImageRect::UPPER_RIGHT)
+ {
+ x = graphics->getWidth() - defaultWidth;
+ }
+ else if (position == ImageRect::LEFT)
+ {
+ y = (graphics->getHeight() - defaultHeight) / 2;
+ }
+ else if (position == ImageRect::CENTER)
+ {
+ x = (graphics->getWidth() - defaultWidth) / 2;
+ y = (graphics->getHeight() - defaultHeight) / 2;
+ }
+ else if (position == ImageRect::RIGHT)
+ {
+ x = graphics->getWidth() - defaultWidth;
+ y = (graphics->getHeight() - defaultHeight) / 2;
+ }
+ else if (position == ImageRect::LOWER_LEFT)
+ {
+ y = graphics->getHeight() - defaultHeight;
+ }
+ else if (position == ImageRect::LOWER_CENTER)
+ {
+ x = (graphics->getWidth() - defaultWidth) / 2;
+ y = graphics->getHeight() - defaultHeight;
+ }
+ else if (position == ImageRect::LOWER_RIGHT)
+ {
+ x = graphics->getWidth() - defaultWidth;
+ y = graphics->getHeight() - defaultHeight;
+ }
+
+ mDefaultX = x - offsetX;
+ mDefaultY = y - offsetY;
+ mDefaultWidth = defaultWidth;
+ mDefaultHeight = defaultHeight;
+}
+
+void Window::resetToDefaultSize()
+{
+ setPosition(mDefaultX, mDefaultY);
+ setSize(mDefaultWidth, mDefaultHeight);
+ saveWindowState();
+}
+
+int Window::getResizeHandles(gcn::MouseEvent &event)
+{
+ int resizeHandles = 0;
+ const int y = event.getY();
+
+ if (mGrip && (y > static_cast<int>(mTitleBarHeight)
+ || (y < (int)getPadding() && mTitleBarHeight > getPadding())))
+ {
+ const int x = event.getX();
+
+ if (!getWindowArea().isPointInRect(x, y) && event.getSource() == this)
+ {
+ resizeHandles |= (x > getWidth() - resizeBorderWidth) ? RIGHT :
+ (x < resizeBorderWidth) ? LEFT : 0;
+ resizeHandles |= (y > getHeight() - resizeBorderWidth) ? BOTTOM :
+ (y < resizeBorderWidth) ? TOP : 0;
+ }
+
+ if (event.getSource() == mGrip)
+ {
+ mDragOffsetX = x;
+ mDragOffsetY = y;
+ resizeHandles |= BOTTOM | RIGHT;
+ }
+ }
+
+ return resizeHandles;
+}
+
+bool Window::isResizeAllowed(gcn::MouseEvent &event)
+{
+ const int y = event.getY();
+
+ if (mGrip && (y > static_cast<int>(mTitleBarHeight)
+ || y < (int)getPadding()))
+ {
+ const int x = event.getX();
+
+ if (!getWindowArea().isPointInRect(x, y) && event.getSource() == this)
+ return true;
+
+ if (event.getSource() == mGrip)
+ return true;
+ }
+
+ return false;
+}
+
+int Window::getGuiAlpha()
+{
+ float alpha = std::max(Client::getGuiAlpha(),
+ Theme::instance()->getMinimumOpacity());
+ return static_cast<int>(alpha * 255.0f);
+}
+
+Layout &Window::getLayout()
+{
+ if (!mLayout)
+ mLayout = new Layout;
+ return *mLayout;
+}
+
+void Window::clearLayout()
+{
+ clear();
+
+ // Restore the resize grip
+ if (mGrip)
+ add(mGrip);
+
+ // Recreate layout instance when one is present
+ if (mLayout)
+ {
+ delete mLayout;
+ mLayout = new Layout;
+ }
+}
+
+LayoutCell &Window::place(int x, int y, gcn::Widget *wg, int w, int h)
+{
+ add(wg);
+ return getLayout().place(wg, x, y, w, h);
+}
+
+ContainerPlacer Window::getPlacer(int x, int y)
+{
+ return ContainerPlacer(this, &getLayout().at(x, y));
+}
+
+void Window::reflowLayout(int w, int h)
+{
+ if (!mLayout)
+ return;
+
+ mLayout->reflow(w, h);
+ delete mLayout;
+ mLayout = 0;
+ setContentSize(w, h);
+}
+
+void Window::redraw()
+{
+ if (mLayout)
+ {
+ const gcn::Rectangle area = getChildrenArea();
+ int w = area.width;
+ int h = area.height;
+ mLayout->reflow(w, h);
+ }
+}
+
+void Window::center()
+{
+ setLocationRelativeTo(getParent());
+}
+
+void Window::centerHorisontally()
+{
+ setLocationHorisontallyRelativeTo(getParent());
+}
+
+void Window::checkIfIsOffScreen(bool partially, bool entirely)
+{
+ // Move the window onto screen if it has become off screen
+ // For instance, because of resolution change...
+
+ // First of all, don't deal when a window hasn't got
+ // any size initialized yet...
+ if (getWidth() == 0 && getHeight() == 0)
+ return;
+
+ // Made partially the default behaviour
+ if (!partially && !entirely)
+ partially = true;
+
+ // Keep guichan window inside screen (supports resizing any side)
+
+ gcn::Rectangle winDimension = getDimension();
+
+ if (winDimension.x < 0)
+ {
+ winDimension.width += winDimension.x;
+ winDimension.x = 0;
+ }
+ if (winDimension.y < 0)
+ {
+ winDimension.height += winDimension.y;
+ winDimension.y = 0;
+ }
+
+ // Look if the window is partially off-screen limits...
+ if (partially)
+ {
+ if (winDimension.x + winDimension.width > graphics->getWidth())
+ winDimension.x = graphics->getWidth() - winDimension.width;
+
+ if (winDimension.y + winDimension.height > graphics->getHeight())
+ winDimension.y = graphics->getHeight() - winDimension.height;
+
+ setDimension(winDimension);
+ return;
+ }
+
+ if (entirely)
+ {
+ if (winDimension.x > graphics->getWidth())
+ winDimension.x = graphics->getWidth() - winDimension.width;
+
+ if (winDimension.y > graphics->getHeight())
+ winDimension.y = graphics->getHeight() - winDimension.height;
+ }
+ setDimension(winDimension);
+}
+
+gcn::Rectangle Window::getWindowArea()
+{
+ return gcn::Rectangle(getPadding(),
+ getPadding(),
+ getWidth() - getPadding() * 2,
+ getHeight() - getPadding() * 2);
+} \ No newline at end of file
diff --git a/src/gui/widgets/window.h b/src/gui/widgets/window.h
new file mode 100644
index 000000000..09e15d3f4
--- /dev/null
+++ b/src/gui/widgets/window.h
@@ -0,0 +1,441 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 WINDOW_H
+#define WINDOW_H
+
+#include "graphics.h"
+#include "guichanfwd.h"
+
+#include <guichan/widgetlistener.hpp>
+
+#include <guichan/widgets/window.hpp>
+
+#ifdef __GNUC__
+#define _UNUSED_ __attribute__ ((unused))
+#else
+#define _UNUSED_
+#endif
+
+class ContainerPlacer;
+class Layout;
+class LayoutCell;
+class ResizeGrip;
+class Skin;
+class WindowContainer;
+
+/**
+ * A window. This window can be dragged around and has a title bar. Windows are
+ * invisible by default.
+ *
+ * \ingroup GUI
+ */
+class Window : public gcn::Window, gcn::WidgetListener
+{
+ public:
+ /**
+ * Constructor. Initializes the title to the given text and hooks
+ * itself into the window container.
+ *
+ * @param caption The initial window title, "Window" by default.
+ * @param modal Block input to other windows.
+ * @param parent The parent window. This is the window standing above
+ * this one in the window hiearchy. When reordering,
+ * a window will never go below its parent window.
+ * @param skin The location where the window's skin XML can be found.
+ */
+ Window(const std::string &caption = "Window", bool modal = false,
+ Window *parent = NULL, const std::string &skin = "window.xml");
+
+ /**
+ * Destructor. Deletes all the added widgets.
+ */
+ ~Window();
+
+ /**
+ * Sets the window container to be used by new windows.
+ */
+ static void setWindowContainer(WindowContainer *windowContainer);
+
+ /**
+ * Draws the window.
+ */
+ void draw(gcn::Graphics *graphics);
+
+ /**
+ * Sets the size of this window.
+ */
+ void setContentSize(int width, int height);
+
+ /**
+ * Sets the location relative to the given widget.
+ */
+ void setLocationRelativeTo(gcn::Widget *widget);
+
+ /**
+ * Sets the location relative to the given widget (only horisontally)
+ */
+ void setLocationHorisontallyRelativeTo(gcn::Widget *widget);
+
+ /**
+ * Sets the location relative to the given enumerated position.
+ */
+ void setLocationRelativeTo(ImageRect::ImagePosition position,
+ int offsetX = 0, int offsetY = 0);
+
+ /**
+ * Sets whether or not the window can be resized.
+ */
+ void setResizable(bool resize);
+
+ void redraw();
+
+ /**
+ * Called whenever the widget changes size.
+ */
+ void widgetResized(const gcn::Event &event);
+
+ /**
+ * Called whenever the widget is hidden.
+ */
+ virtual void widgetHidden(const gcn::Event& event);
+
+ /**
+ * Sets whether or not the window has a close button.
+ */
+ void setCloseButton(bool flag);
+
+ /**
+ * Returns whether the window can be resized.
+ */
+ bool isResizable() const;
+
+ /**
+ * Sets the minimum width of the window.
+ */
+ void setMinWidth(int width);
+
+ int getMinWidth() const
+ { return mMinWinWidth; }
+
+ /**
+ * Sets the minimum height of the window.
+ */
+ void setMinHeight(int height);
+
+ int getMinHeight() const
+ { return mMinWinHeight; }
+
+ /**
+ * Sets the maximum width of the window.
+ */
+ void setMaxWidth(int width);
+
+ int getMaxWidth() const
+ { return mMaxWinWidth; }
+
+ /**
+ * Sets the minimum height of the window.
+ */
+ void setMaxHeight(int height);
+
+ int getMaxHeight() const
+ { return mMaxWinHeight; }
+
+ /**
+ * Sets flag to show a title or not.
+ */
+ void setShowTitle(bool flag)
+ { mShowTitle = flag; }
+
+ /**
+ * Sets whether or not the window has a sticky button.
+ */
+ void setStickyButton(bool flag);
+
+ /**
+ * Sets whether the window is sticky. A sticky window will not have
+ * its visibility set to false on a general setVisible(false) call.
+ * Use this to set the default before you call loadWindowState().
+ */
+ void setSticky(bool sticky);
+
+ /**
+ * Returns whether the window is sticky.
+ */
+ bool isSticky() const
+ { return mSticky; }
+
+ /**
+ * Overloads window setVisible by Guichan to allow sticky window
+ * handling.
+ */
+ virtual void setVisible(bool visible);
+
+ /**
+ * Overloads window setVisible by Guichan to allow sticky window
+ * handling, or not, if you force the sticky state.
+ */
+ void setVisible(bool visible, bool forceSticky);
+
+ /**
+ * Returns whether the window is visible by default.
+ */
+ bool isDefaultVisible() const
+ { return mDefaultVisible; }
+
+ /**
+ * Sets whether the window is visible by default.
+ */
+ void setDefaultVisible(bool save)
+ { mDefaultVisible = save; }
+
+ /**
+ * Returns whether the window will save it's visibility.
+ */
+ bool willSaveVisible() const
+ { return mSaveVisible; }
+
+ /**
+ * Sets whether the window will save it's visibility.
+ */
+ void setSaveVisible(bool save)
+ { mSaveVisible = save; }
+
+ /**
+ * Returns the parent window.
+ *
+ * @return The parent window or <code>NULL</code> if there is none.
+ */
+ Window *getParentWindow() const
+ { return mParent; }
+
+ /**
+ * Schedule this window for deletion. It will be deleted at the start
+ * of the next logic update.
+ */
+ void scheduleDelete();
+
+ /**
+ * Starts window resizing when appropriate.
+ */
+ void mousePressed(gcn::MouseEvent &event);
+
+ /**
+ * Implements window resizing and makes sure the window is not
+ * dragged/resized outside of the screen.
+ */
+ void mouseDragged(gcn::MouseEvent &event);
+
+ /**
+ * Implements custom cursor image changing context, based on mouse
+ * relative position.
+ */
+ void mouseMoved(gcn::MouseEvent &event);
+
+ /**
+ * When the mouse button has been let go, this ensures that the mouse
+ * custom cursor is restored back to it's standard image.
+ */
+ void mouseReleased(gcn::MouseEvent &event);
+
+ /**
+ * When the mouse leaves the window this ensures that the custom cursor
+ * is restored back to it's standard image.
+ */
+ void mouseExited(gcn::MouseEvent &event);
+
+ /**
+ * Sets the name of the window. This is not the window title.
+ */
+ void setWindowName(const std::string &name)
+ { mWindowName = name; }
+
+ /**
+ * Returns the name of the window. This is not the window title.
+ */
+ const std::string &getWindowName() const
+ { return mWindowName; }
+
+ /**
+ * Reads the position (and the size for resizable windows) in the
+ * configuration based on the given string.
+ * Uses the default values when config values are missing.
+ * Don't forget to set these default values and resizable before
+ * calling this function.
+ */
+ void loadWindowState();
+
+ /**
+ * Saves the window state so that when the window is reloaded, it'll
+ * maintain its previous state and location.
+ */
+ void saveWindowState();
+
+ /**
+ * Set the default win pos and size.
+ * (which can be different of the actual ones.)
+ */
+ void setDefaultSize(int defaultX, int defaultY,
+ int defaultWidth, int defaultHeight);
+
+ /**
+ * Set the default win pos and size to the current ones.
+ */
+ void setDefaultSize();
+
+ /**
+ * Set the default win pos and size.
+ * (which can be different of the actual ones.)
+ * This version of setDefaultSize sets the window's position based
+ * on a relative enumerated position, rather than a coordinate position.
+ */
+ void setDefaultSize(int defaultWidth, int defaultHeight,
+ ImageRect::ImagePosition position,
+ int offsetx = 0, int offsetY = 0);
+
+ /**
+ * Reset the win pos and size to default. Don't forget to set defaults
+ * first.
+ */
+ virtual void resetToDefaultSize();
+
+ /**
+ * Gets the layout handler for this window.
+ */
+ Layout &getLayout();
+
+ /**
+ * Clears the window's layout (useful for redesigning the window). Does
+ * not delete the widgets!
+ */
+ void clearLayout();
+
+ /**
+ * Computes the position of the widgets according to the current
+ * layout. Resizes the window so that the layout fits. Deletes the
+ * layout.
+ * @param w if non-zero, force the window to this width.
+ * @param h if non-zero, force the window to this height.
+ * @note This function is meant to be called with fixed-size windows.
+ */
+ void reflowLayout(int w = 0, int h = 0);
+
+ /**
+ * Adds a widget to the window and sets it at given cell.
+ */
+ LayoutCell &place(int x, int y, gcn::Widget *, int w = 1, int h = 1);
+
+ /**
+ * Returns a proxy for adding widgets in an inner table of the layout.
+ */
+ ContainerPlacer getPlacer(int x, int y);
+
+ /**
+ * Positions the window in the center of it's parent.
+ */
+ void center();
+
+ /**
+ * Positions the window in the horisontal center of it's parent.
+ */
+ void centerHorisontally();
+
+ /**
+ * Overrideable functionality for when the window is to close. This
+ * allows for class implementations to clean up or do certain actions
+ * on window close they couldn't do otherwise.
+ */
+ virtual void close();
+
+ /**
+ * Allows the windows modal status to change
+ */
+ void setModal(bool modal);
+
+ /**
+ * Gets the alpha value used by the window, in a GUIChan usable format.
+ */
+ int getGuiAlpha();
+
+ gcn::Rectangle getWindowArea();
+
+ bool isResizeAllowed(gcn::MouseEvent &event);
+
+ private:
+ enum ResizeHandles
+ {
+ TOP = 0x01,
+ RIGHT = 0x02,
+ BOTTOM = 0x04,
+ LEFT = 0x08
+ };
+
+ /**
+ * Check if the window is off-screen and then move it to be visible
+ * again. This is internally used by loadWindowState
+ * and setVisible(true) members.
+ */
+ void checkIfIsOffScreen(bool partially = true, bool entirely = true);
+
+ /**
+ * Determines if the mouse is in a resize area and returns appropriate
+ * resize handles. Also initializes drag offset in case the resize
+ * grip is used.
+ *
+ * @see ResizeHandles
+ */
+ int getResizeHandles(gcn::MouseEvent &event);
+
+ ResizeGrip *mGrip; /**< Resize grip */
+ Window *mParent; /**< The parent window */
+ Layout *mLayout; /**< Layout handler */
+ std::string mWindowName; /**< Name of the window */
+ bool mShowTitle; /**< Window has a title bar */
+ bool mModal; /**< Window is modal */
+ bool mCloseButton; /**< Window has a close button */
+ bool mDefaultVisible; /**< Window's default visibility */
+ bool mSaveVisible; /**< Window will save visibility */
+ bool mStickyButton; /**< Window has a sticky button */
+ bool mSticky; /**< Window resists hiding*/
+ int mMinWinWidth; /**< Minimum window width */
+ int mMinWinHeight; /**< Minimum window height */
+ int mMaxWinWidth; /**< Maximum window width */
+ int mMaxWinHeight; /**< Maximum window height */
+ int mDefaultX; /**< Default window X position */
+ int mDefaultY; /**< Default window Y position */
+ int mDefaultWidth; /**< Default window width */
+ int mDefaultHeight; /**< Default window height */
+
+ static int mouseResize; /**< Active resize handles */
+ static int instances; /**< Number of Window instances */
+
+ Skin *mSkin; /**< Skin in use by this window */
+
+ /**
+ * The width of the resize border. Is independent of the actual window
+ * border width, and determines mostly the size of the corner area
+ * where two borders are moved at the same time.
+ */
+ static const int resizeBorderWidth = 10;
+};
+
+#endif
diff --git a/src/gui/widgets/windowcontainer.cpp b/src/gui/widgets/windowcontainer.cpp
new file mode 100644
index 000000000..0fff491e4
--- /dev/null
+++ b/src/gui/widgets/windowcontainer.cpp
@@ -0,0 +1,40 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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/widgets/windowcontainer.h"
+
+#include "utils/dtor.h"
+
+WindowContainer *windowContainer = NULL;
+
+void WindowContainer::logic()
+{
+ delete_all(mDeathList);
+ mDeathList.clear();
+
+ gcn::Container::logic();
+}
+
+void WindowContainer::scheduleDelete(gcn::Widget *widget)
+{
+ if (widget)
+ mDeathList.push_back(widget);
+}
diff --git a/src/gui/widgets/windowcontainer.h b/src/gui/widgets/windowcontainer.h
new file mode 100644
index 000000000..2ec65d15a
--- /dev/null
+++ b/src/gui/widgets/windowcontainer.h
@@ -0,0 +1,59 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana 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 WINDOWCONTAINER_H
+#define WINDOWCONTAINER_H
+
+#include "gui/widgets/container.h"
+
+/**
+ * A window container. This container adds functionality for more convenient
+ * widget (windows in particular) destruction.
+ *
+ * \ingroup GUI
+ */
+class WindowContainer : public Container
+{
+ public:
+ /**
+ * Do GUI logic. This functions adds automatic deletion of objects that
+ * volunteered to be deleted.
+ */
+ void logic();
+
+ /**
+ * Schedule a widget for deletion. It will be deleted at the start of
+ * the next logic update.
+ */
+ void scheduleDelete(gcn::Widget *widget);
+
+ private:
+ /**
+ * List of widgets that are scheduled to be deleted.
+ */
+ typedef std::list<gcn::Widget*> Widgets;
+ typedef Widgets::iterator WidgetIterator;
+ Widgets mDeathList;
+};
+
+extern WindowContainer *windowContainer;
+
+#endif