diff options
author | Andrei Karas <akaras@inbox.ru> | 2011-01-02 01:48:38 +0200 |
---|---|---|
committer | Andrei Karas <akaras@inbox.ru> | 2011-01-02 02:41:24 +0200 |
commit | 3eeae12c498d1a4dbe969462d2ba841f77ee3ccb (patch) | |
tree | ff8eab35e732bc0749fc11677c8873a7b3a58704 /src/gui/widgets | |
download | manaverse-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.gz manaverse-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.bz2 manaverse-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.xz manaverse-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')
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 |