summaryrefslogtreecommitdiff
path: root/src/guichan/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'src/guichan/widgets')
-rw-r--r--src/guichan/widgets/button.cpp284
-rw-r--r--src/guichan/widgets/button.hpp214
-rw-r--r--src/guichan/widgets/checkbox.cpp189
-rw-r--r--src/guichan/widgets/checkbox.hpp173
-rw-r--r--src/guichan/widgets/container.cpp112
-rw-r--r--src/guichan/widgets/container.hpp162
-rw-r--r--src/guichan/widgets/dropdown.cpp635
-rw-r--r--src/guichan/widgets/dropdown.hpp318
-rw-r--r--src/guichan/widgets/icon.cpp116
-rw-r--r--src/guichan/widgets/icon.hpp118
-rw-r--r--src/guichan/widgets/imagebutton.cpp166
-rw-r--r--src/guichan/widgets/imagebutton.hpp123
-rw-r--r--src/guichan/widgets/label.cpp120
-rw-r--r--src/guichan/widgets/label.hpp133
-rw-r--r--src/guichan/widgets/listbox.cpp350
-rw-r--r--src/guichan/widgets/listbox.hpp253
-rw-r--r--src/guichan/widgets/radiobutton.cpp298
-rw-r--r--src/guichan/widgets/radiobutton.hpp211
-rw-r--r--src/guichan/widgets/scrollarea.cpp1246
-rw-r--r--src/guichan/widgets/scrollarea.hpp590
-rw-r--r--src/guichan/widgets/slider.cpp369
-rw-r--r--src/guichan/widgets/slider.hpp300
-rw-r--r--src/guichan/widgets/tab.cpp181
-rw-r--r--src/guichan/widgets/tab.hpp151
-rw-r--r--src/guichan/widgets/tabbedarea.cpp482
-rw-r--r--src/guichan/widgets/tabbedarea.hpp280
-rw-r--r--src/guichan/widgets/textbox.cpp517
-rw-r--r--src/guichan/widgets/textbox.hpp289
-rw-r--r--src/guichan/widgets/textfield.cpp280
-rw-r--r--src/guichan/widgets/textfield.hpp177
-rw-r--r--src/guichan/widgets/window.cpp308
-rw-r--r--src/guichan/widgets/window.hpp255
32 files changed, 9400 insertions, 0 deletions
diff --git a/src/guichan/widgets/button.cpp b/src/guichan/widgets/button.cpp
new file mode 100644
index 000000000..199f5c96e
--- /dev/null
+++ b/src/guichan/widgets/button.cpp
@@ -0,0 +1,284 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/button.hpp"
+
+#include "guichan/exception.hpp"
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/key.hpp"
+#include "guichan/mouseevent.hpp"
+#include "guichan/mouseinput.hpp"
+
+namespace gcn
+{
+ Button::Button()
+ : mHasMouse(false),
+ mKeyPressed(false),
+ mMousePressed(false),
+ mAlignment(Graphics::CENTER),
+ mSpacing(4)
+ {
+ setFocusable(true);
+ adjustSize();
+ setFrameSize(1);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ addFocusListener(this);
+ }
+
+ Button::Button(const std::string& caption)
+ : mCaption(caption),
+ mHasMouse(false),
+ mKeyPressed(false),
+ mMousePressed(false),
+ mAlignment(Graphics::CENTER),
+ mSpacing(4)
+ {
+ setFocusable(true);
+ adjustSize();
+ setFrameSize(1);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ addFocusListener(this);
+ }
+
+ void Button::setCaption(const std::string& caption)
+ {
+ mCaption = caption;
+ }
+
+ const std::string& Button::getCaption() const
+ {
+ return mCaption;
+ }
+
+ void Button::setAlignment(Graphics::Alignment alignment)
+ {
+ mAlignment = alignment;
+ }
+
+ Graphics::Alignment Button::getAlignment() const
+ {
+ return mAlignment;
+ }
+
+ void Button::setSpacing(unsigned int spacing)
+ {
+ mSpacing = spacing;
+ }
+
+ unsigned int Button::getSpacing() const
+ {
+ return mSpacing;
+ }
+
+ void Button::draw(Graphics* graphics)
+ {
+ Color faceColor = getBaseColor();
+ Color highlightColor, shadowColor;
+ int alpha = getBaseColor().a;
+
+ if (isPressed())
+ {
+ faceColor = faceColor - 0x303030;
+ faceColor.a = alpha;
+ highlightColor = faceColor - 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor + 0x303030;
+ shadowColor.a = alpha;
+ }
+ else
+ {
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+ }
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(1, 1, getDimension().width-1, getHeight() - 1));
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, getWidth() - 1, 0);
+ graphics->drawLine(0, 1, 0, getHeight() - 1);
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(getWidth() - 1, 1, getWidth() - 1, getHeight() - 1);
+ graphics->drawLine(1, getHeight() - 1, getWidth() - 1, getHeight() - 1);
+
+ graphics->setColor(getForegroundColor());
+
+ int textX;
+ int textY = getHeight() / 2 - getFont()->getHeight() / 2;
+
+ switch (getAlignment())
+ {
+ case Graphics::LEFT:
+ textX = mSpacing;
+ break;
+ case Graphics::CENTER:
+ textX = getWidth() / 2;
+ break;
+ case Graphics::RIGHT:
+ textX = getWidth() - mSpacing;
+ break;
+ default:
+ throw GCN_EXCEPTION("Unknown alignment.");
+ }
+
+ graphics->setFont(getFont());
+
+ if (isPressed())
+ {
+ graphics->drawText(getCaption(), textX + 1, textY + 1, getAlignment());
+ }
+ else
+ {
+ graphics->drawText(getCaption(), textX, textY, getAlignment());
+
+ if (isFocused())
+ {
+ graphics->drawRectangle(Rectangle(2, 2, getWidth() - 4,
+ getHeight() - 4));
+ }
+ }
+ }
+
+ void Button::adjustSize()
+ {
+ setWidth(getFont()->getWidth(mCaption) + 2*mSpacing);
+ setHeight(getFont()->getHeight() + 2*mSpacing);
+ }
+
+ bool Button::isPressed() const
+ {
+ if (mMousePressed)
+ {
+ return mHasMouse;
+ }
+ else
+ {
+ return mKeyPressed;
+ }
+ }
+
+ void Button::mousePressed(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ mMousePressed = true;
+ mouseEvent.consume();
+ }
+ }
+
+ void Button::mouseExited(MouseEvent& mouseEvent)
+ {
+ mHasMouse = false;
+ }
+
+ void Button::mouseEntered(MouseEvent& mouseEvent)
+ {
+ mHasMouse = true;
+ }
+
+ void Button::mouseReleased(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getButton() == MouseEvent::LEFT
+ && mMousePressed
+ && mHasMouse)
+ {
+ mMousePressed = false;
+ distributeActionEvent();
+ mouseEvent.consume();
+ }
+ else if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ mMousePressed = false;
+ mouseEvent.consume();
+ }
+ }
+
+ void Button::mouseDragged(MouseEvent& mouseEvent)
+ {
+ mouseEvent.consume();
+ }
+
+ void Button::keyPressed(KeyEvent& keyEvent)
+ {
+ Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::ENTER
+ || key.getValue() == Key::SPACE)
+ {
+ mKeyPressed = true;
+ keyEvent.consume();
+ }
+ }
+
+ void Button::keyReleased(KeyEvent& keyEvent)
+ {
+ Key key = keyEvent.getKey();
+
+ if ((key.getValue() == Key::ENTER
+ || key.getValue() == Key::SPACE)
+ && mKeyPressed)
+ {
+ mKeyPressed = false;
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ }
+
+ void Button::focusLost(const Event& event)
+ {
+ mMousePressed = false;
+ mKeyPressed = false;
+ }
+}
diff --git a/src/guichan/widgets/button.hpp b/src/guichan/widgets/button.hpp
new file mode 100644
index 000000000..cfe097327
--- /dev/null
+++ b/src/guichan/widgets/button.hpp
@@ -0,0 +1,214 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_BUTTON_HPP
+#define GCN_BUTTON_HPP
+
+#include <string>
+
+#include "guichan/focuslistener.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/keylistener.hpp"
+#include "guichan/mouseevent.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+namespace gcn
+{
+ /**
+ * An implementation of a regular clickable button. A button is capable of
+ * displaying a caption.
+ *
+ * If a button is clicked an action event will be sent to all action listener's
+ * of the button.
+ *
+ * @see ImageButton
+ */
+ class GCN_CORE_DECLSPEC Button : public Widget,
+ public MouseListener,
+ public KeyListener,
+ public FocusListener
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ Button();
+
+ /**
+ * Constructor. The button will be automatically resized
+ * to fit the caption.
+ *
+ * @param caption The caption of the button.
+ */
+ Button(const std::string& caption);
+
+ /**
+ * Sets the caption of the button. It's advisable to call
+ * adjustSize after setting of the caption to adjust the
+ * button's size to fit the caption.
+ *
+ * @param caption The caption of the button.
+ * @see getCaption, adjustSize
+ */
+ void setCaption(const std::string& caption);
+
+ /**
+ * Gets the caption of the button.
+ *
+ * @return The caption of the button.
+ */
+ const std::string& getCaption() const;
+
+ /**
+ * Sets the alignment of the caption. The alignment is relative
+ * to the center of the button.
+ *
+ * @param alignment The alignment of the caption.
+ * @see getAlignment, Graphics
+ */
+ void setAlignment(Graphics::Alignment alignment);
+
+ /**
+ * Gets the alignment of the caption.
+ *
+ * @return The alignment of the caption.
+ * @see setAlignment, Graphics
+ */
+ Graphics::Alignment getAlignment() const;
+
+ /**
+ * Sets the spacing between the border of the button and its caption.
+ *
+ * @param spacing The default value for spacing is 4 and can be changed
+ * using this method.
+ * @see getSpacing
+ */
+ void setSpacing(unsigned int spacing);
+
+ /**
+ * Gets the spacing between the border of the button and its caption.
+ *
+ * @return spacing.
+ * @see setSpacing
+ */
+ unsigned int getSpacing() const;
+
+ /**
+ * Adjusts the button's size to fit the caption.
+ */
+ void adjustSize();
+
+
+ //Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+
+ // Inherited from FocusListener
+
+ virtual void focusLost(const Event& event);
+
+
+ // Inherited from MouseListener
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+ virtual void mouseReleased(MouseEvent& mouseEvent);
+
+ virtual void mouseEntered(MouseEvent& mouseEvent);
+
+ virtual void mouseExited(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+ virtual void keyReleased(KeyEvent& keyEvent);
+
+ protected:
+ /**
+ * Checks if the button is pressed. Convenient method to use
+ * when overloading the draw method of the button.
+ *
+ * @return True if the button is pressed, false otherwise.
+ */
+ bool isPressed() const;
+
+ /**
+ * Holds the caption of the button.
+ */
+ std::string mCaption;
+
+ /**
+ * True if the mouse is ontop of the button, false otherwise.
+ */
+ bool mHasMouse;
+
+ /**
+ * True if a key has been pressed, false otherwise.
+ */
+ bool mKeyPressed;
+
+ /**
+ * True if a mouse has been pressed, false otherwise.
+ */
+ bool mMousePressed;
+
+ /**
+ * Holds the alignment of the caption.
+ */
+ Graphics::Alignment mAlignment;
+
+ /**
+ * Holds the spacing between the border and the caption.
+ */
+ unsigned int mSpacing;
+ };
+}
+
+#endif // end GCN_BUTTON_HPP
diff --git a/src/guichan/widgets/checkbox.cpp b/src/guichan/widgets/checkbox.cpp
new file mode 100644
index 000000000..5f0a52612
--- /dev/null
+++ b/src/guichan/widgets/checkbox.cpp
@@ -0,0 +1,189 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/checkbox.hpp"
+
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/key.hpp"
+#include "guichan/mouseinput.hpp"
+
+namespace gcn
+{
+
+ CheckBox::CheckBox()
+ {
+ setSelected(false);
+
+ setFocusable(true);
+ addMouseListener(this);
+ addKeyListener(this);
+ }
+
+ CheckBox::CheckBox(const std::string &caption, bool selected)
+ {
+ setCaption(caption);
+ setSelected(selected);
+
+ setFocusable(true);
+ addMouseListener(this);
+ addKeyListener(this);
+
+ adjustSize();
+ }
+
+ void CheckBox::draw(Graphics* graphics)
+ {
+ drawBox(graphics);
+
+ graphics->setFont(getFont());
+ graphics->setColor(getForegroundColor());
+
+ const int h = getHeight() + getHeight() / 2;
+
+ graphics->drawText(getCaption(), h - 2, 0);
+ }
+
+ void CheckBox::drawBox(Graphics *graphics)
+ {
+ const int h = getHeight() - 2;
+ const int alpha = getBaseColor().a;
+ Color faceColor = getBaseColor();
+ faceColor.a = alpha;
+ Color highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ Color shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(1, 1, h, 1);
+ graphics->drawLine(1, 1, 1, h);
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(h, 1, h, h);
+ graphics->drawLine(1, h, h - 1, h);
+
+ graphics->setColor(getBackgroundColor());
+ graphics->fillRectangle(Rectangle(2, 2, h - 2, h - 2));
+
+ graphics->setColor(getForegroundColor());
+
+ if (isFocused())
+ {
+ graphics->drawRectangle(Rectangle(0, 0, h + 2, h + 2));
+ }
+
+ if (mSelected)
+ {
+ graphics->drawLine(3, 5, 3, h - 2);
+ graphics->drawLine(4, 5, 4, h - 2);
+
+ graphics->drawLine(5, h - 3, h - 2, 4);
+ graphics->drawLine(5, h - 4, h - 4, 5);
+ }
+ }
+
+ bool CheckBox::isSelected() const
+ {
+ return mSelected;
+ }
+
+ void CheckBox::setSelected(bool selected)
+ {
+ mSelected = selected;
+ }
+
+ const std::string &CheckBox::getCaption() const
+ {
+ return mCaption;
+ }
+
+ void CheckBox::setCaption(const std::string& caption)
+ {
+ mCaption = caption;
+ }
+
+ void CheckBox::keyPressed(KeyEvent& keyEvent)
+ {
+ Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::ENTER ||
+ key.getValue() == Key::SPACE)
+ {
+ toggleSelected();
+ keyEvent.consume();
+ }
+ }
+
+ void CheckBox::mouseClicked(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ toggleSelected();
+ }
+ }
+
+ void CheckBox::mouseDragged(MouseEvent& mouseEvent)
+ {
+ mouseEvent.consume();
+ }
+
+ void CheckBox::adjustSize()
+ {
+ int height = getFont()->getHeight();
+
+ setHeight(height);
+ setWidth(getFont()->getWidth(mCaption) + height + height / 2);
+ }
+
+ void CheckBox::toggleSelected()
+ {
+ mSelected = !mSelected;
+ distributeActionEvent();
+ }
+}
+
diff --git a/src/guichan/widgets/checkbox.hpp b/src/guichan/widgets/checkbox.hpp
new file mode 100644
index 000000000..ad43e2896
--- /dev/null
+++ b/src/guichan/widgets/checkbox.hpp
@@ -0,0 +1,173 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_CHECKBOX_HPP
+#define GCN_CHECKBOX_HPP
+
+#include <string>
+
+#include "guichan/keylistener.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+namespace gcn
+{
+ /**
+ * An implementation of a check box where a user can select or deselect
+ * the check box and where the status of the check box is displayed to the user.
+ * A check box is capable of displaying a caption.
+ *
+ * If a check box's state changes an action event will be sent to all action
+ * listeners of the check box.
+ */
+ class GCN_CORE_DECLSPEC CheckBox :
+ public Widget,
+ public MouseListener,
+ public KeyListener
+ {
+ public:
+
+ /**
+ * Contructor.
+ */
+ CheckBox();
+
+ /**
+ * Constructor. The check box will be automatically resized
+ * to fit the caption.
+ *
+ * @param caption The caption of the check box.
+ * @param marked True if the check box is selected, false otherwise.
+ */
+ CheckBox(const std::string &caption, bool selected = false);
+
+ /**
+ * Destructor.
+ */
+ virtual ~CheckBox() { }
+
+ /**
+ * Checks if the check box is selected.
+ *
+ * @return True if the check box is selected, false otherwise.
+ * @see setSelected
+ */
+ bool isSelected() const;
+
+ /**
+ * Sets the check box to be selected or not.
+ *
+ * @param selected True if the check box should be set as selected.
+ * @see isSelected
+ */
+ void setSelected(bool selected);
+
+ /**
+ * Gets the caption of the check box.
+ *
+ * @return The caption of the check box.
+ * @see setCaption
+ */
+ const std::string &getCaption() const;
+
+ /**
+ * Sets the caption of the check box. It's advisable to call
+ * adjustSize after setting of the caption to adjust the
+ * check box's size to fit the caption.
+ *
+ * @param caption The caption of the check box.
+ * @see getCaption, adjustSize
+ */
+ void setCaption(const std::string& caption);
+
+ /**
+ * Adjusts the check box's size to fit the caption.
+ */
+ void adjustSize();
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+
+ // Inherited from MouseListener
+
+ virtual void mouseClicked(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+
+ protected:
+ /**
+ * Draws the box of the check box.
+ *
+ * @param graphics A Graphics object to draw with.
+ */
+ virtual void drawBox(Graphics *graphics);
+
+ /**
+ * Toggles the check box between being selected and
+ * not being selected.
+ */
+ virtual void toggleSelected();
+
+ /**
+ * True if the check box is selected, false otherwise.
+ */
+ bool mSelected;
+
+ /**
+ * Holds the caption of the check box.
+ */
+ std::string mCaption;
+ };
+}
+
+#endif // end GCN_CHECKBOX_HPP
diff --git a/src/guichan/widgets/container.cpp b/src/guichan/widgets/container.cpp
new file mode 100644
index 000000000..c9a845497
--- /dev/null
+++ b/src/guichan/widgets/container.cpp
@@ -0,0 +1,112 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/container.hpp"
+
+#include "guichan/exception.hpp"
+#include "guichan/graphics.hpp"
+
+namespace gcn
+{
+
+ Container::Container()
+ {
+ mOpaque = true;
+ }
+
+ Container::~Container()
+ {
+
+ }
+
+ void Container::draw(Graphics* graphics)
+ {
+ if (isOpaque())
+ {
+ graphics->setColor(getBaseColor());
+ graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
+ }
+
+ drawChildren(graphics);
+ }
+
+ void Container::setOpaque(bool opaque)
+ {
+ mOpaque = opaque;
+ }
+
+ bool Container::isOpaque() const
+ {
+ return mOpaque;
+ }
+
+ void Container::add(Widget* widget)
+ {
+ BasicContainer::add(widget);
+ }
+
+ void Container::add(Widget* widget, int x, int y)
+ {
+ widget->setPosition(x, y);
+ BasicContainer::add(widget);
+ }
+
+ void Container::remove(Widget* widget)
+ {
+ BasicContainer::remove(widget);
+ }
+
+ void Container::clear()
+ {
+ BasicContainer::clear();
+ }
+
+ Widget* Container::findWidgetById(const std::string &id)
+ {
+ return BasicContainer::findWidgetById(id);
+ }
+}
diff --git a/src/guichan/widgets/container.hpp b/src/guichan/widgets/container.hpp
new file mode 100644
index 000000000..4946b1f74
--- /dev/null
+++ b/src/guichan/widgets/container.hpp
@@ -0,0 +1,162 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_CONTAINER_HPP
+#define GCN_CONTAINER_HPP
+
+#include <list>
+
+#include "guichan/basiccontainer.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/platform.hpp"
+
+namespace gcn
+{
+ /**
+ * An implementation of a container able to contain other widgets. A widget's
+ * position in the container is relative to the container itself and not the screen.
+ * A container is the most common widget to use as the Gui's top widget as makes the Gui
+ * able to contain more than one widget.
+ *
+ * @see Gui::setTop
+ */
+ class GCN_CORE_DECLSPEC Container: public BasicContainer
+ {
+ public:
+
+ /**
+ * Constructor. A container is opauqe as default, if you want a
+ * none opaque container call setQpaque(false).
+ *
+ * @see setOpaque, isOpaque
+ */
+ Container();
+
+ /**
+ * Destructor.
+ */
+ virtual ~Container();
+
+ /**
+ * Sets the container to be opaque or not. If the container
+ * is opaque its background will be drawn, if it's not opaque
+ * its background will not be drawn, and thus making the container
+ * completely transparent.
+ *
+ * NOTE: This is not the same as to set visibility. A non visible
+ * container will not itself nor will it draw its content.
+ *
+ * @param opaque True if the container should be opaque, false otherwise.
+ * @see isOpaque
+ */
+ void setOpaque(bool opaque);
+
+ /**
+ * Checks if the container is opaque or not.
+ *
+ * @return True if the container is opaque, false otherwise.
+ * @see setOpaque
+ */
+ bool isOpaque() const;
+
+ /**
+ * Adds a widget to the container.
+ *
+ * @param widget The widget to add.
+ * @see remove, clear
+ */
+ virtual void add(Widget* widget);
+
+ /**
+ * Adds a widget to the container and also specifies the widget's
+ * position in the container. The position is relative to the container
+ * and not relative to the screen.
+ *
+ * @param widget The widget to add.
+ * @param x The x coordinate for the widget.
+ * @param y The y coordinate for the widget.
+ * @see remove, clear
+ */
+ virtual void add(Widget* widget, int x, int y);
+
+ /**
+ * Removes a widget from the Container.
+ *
+ * @param widget The widget to remove.
+ * @throws Exception when the widget has not been added to the
+ * container.
+ * @see add, clear
+ */
+ virtual void remove(Widget* widget);
+
+ /**
+ * Clears the container of all widgets.
+ *
+ * @see add, remove
+ */
+ virtual void clear();
+
+ /**
+ * Finds a widget given an id.
+ *
+ * @param id The id to find a widget by.
+ * @return A widget with a corrosponding id, NULL if no widget
+ * is found.
+ * @see Widget::setId
+ */
+ virtual Widget* findWidgetById(const std::string &id);
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+ protected:
+ /**
+ * True if the container is opaque, false otherwise.
+ */
+ bool mOpaque;
+ };
+}
+
+#endif // end GCN_CONTAINER_HPP
diff --git a/src/guichan/widgets/dropdown.cpp b/src/guichan/widgets/dropdown.cpp
new file mode 100644
index 000000000..ad0a14cf5
--- /dev/null
+++ b/src/guichan/widgets/dropdown.cpp
@@ -0,0 +1,635 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "guichan/widgets/dropdown.hpp"
+
+#include "guichan/exception.hpp"
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/key.hpp"
+#include "guichan/listmodel.hpp"
+#include "guichan/mouseinput.hpp"
+#include "guichan/widgets/listbox.hpp"
+#include "guichan/widgets/scrollarea.hpp"
+
+namespace gcn
+{
+ DropDown::DropDown(ListModel *listModel,
+ ScrollArea *scrollArea,
+ ListBox *listBox)
+ {
+ setWidth(100);
+ setFocusable(true);
+ mDroppedDown = false;
+ mPushed = false;
+ mIsDragged = false;
+
+ setInternalFocusHandler(&mInternalFocusHandler);
+
+ mInternalScrollArea = (scrollArea == NULL);
+ mInternalListBox = (listBox == NULL);
+
+ if (mInternalScrollArea)
+ {
+ mScrollArea = new ScrollArea();
+ }
+ else
+ {
+ mScrollArea = scrollArea;
+ }
+
+ if (mInternalListBox)
+ {
+ mListBox = new ListBox();
+ }
+ else
+ {
+ mListBox = listBox;
+ }
+
+ mScrollArea->setContent(mListBox);
+ add(mScrollArea);
+
+ mListBox->addActionListener(this);
+ mListBox->addSelectionListener(this);
+
+ setListModel(listModel);
+
+ if (mListBox->getSelected() < 0)
+ {
+ mListBox->setSelected(0);
+ }
+
+ addMouseListener(this);
+ addKeyListener(this);
+ addFocusListener(this);
+
+ adjustHeight();
+ }
+
+ DropDown::~DropDown()
+ {
+ if (widgetExists(mListBox))
+ {
+ mListBox->removeActionListener(this);
+ mListBox->removeSelectionListener(this);
+ }
+
+ if (mInternalScrollArea)
+ {
+ delete mScrollArea;
+ }
+
+ if (mInternalListBox)
+ {
+ delete mListBox;
+ }
+
+ setInternalFocusHandler(NULL);
+ }
+
+ void DropDown::draw(Graphics* graphics)
+ {
+ int h;
+
+ if (mDroppedDown)
+ {
+ h = mFoldedUpHeight;
+ }
+ else
+ {
+ h = getHeight();
+ }
+
+ Color faceColor = getBaseColor();
+ Color highlightColor, shadowColor;
+ int alpha = getBaseColor().a;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ // Draw a border.
+ graphics->setColor(shadowColor);
+ graphics->drawLine(0, 0, getWidth() - 1, 0);
+ graphics->drawLine(0, 1, 0, h - 2);
+ graphics->setColor(highlightColor);
+ graphics->drawLine(getWidth() - 1, 1, getWidth() - 1, h - 1);
+ graphics->drawLine(0, h - 1, getWidth() - 1, h - 1);
+
+ // Push a clip area so the other drawings don't need to worry
+ // about the border.
+ graphics->pushClipArea(Rectangle(1, 1, getWidth() - 2, h - 2));
+ const Rectangle currentClipArea = graphics->getCurrentClipArea();
+
+ graphics->setColor(getBackgroundColor());
+ graphics->fillRectangle(Rectangle(0, 0, currentClipArea.width, currentClipArea.height));
+
+ if (isFocused())
+ {
+ graphics->setColor(getSelectionColor());
+ graphics->fillRectangle(Rectangle(0,
+ 0,
+ currentClipArea.width - currentClipArea.height,
+ currentClipArea.height));
+ graphics->setColor(getForegroundColor());
+ }
+
+ if (mListBox->getListModel()
+ && mListBox->getSelected() >= 0)
+ {
+ graphics->setColor(getForegroundColor());
+ graphics->setFont(getFont());
+
+ graphics->drawText(mListBox->getListModel()->getElementAt(mListBox->getSelected()), 1, 0);
+ }
+
+ // Push a clip area before drawing the button.
+ graphics->pushClipArea(Rectangle(currentClipArea.width - currentClipArea.height,
+ 0,
+ currentClipArea.height,
+ currentClipArea.height));
+ drawButton(graphics);
+ graphics->popClipArea();
+ graphics->popClipArea();
+
+ if (mDroppedDown)
+ {
+ // Draw a border around the children.
+ graphics->setColor(shadowColor);
+ graphics->drawRectangle(Rectangle(0,
+ mFoldedUpHeight,
+ getWidth(),
+ getHeight() - mFoldedUpHeight));
+ drawChildren(graphics);
+ }
+ }
+
+ void DropDown::drawButton(Graphics *graphics)
+ {
+ Color faceColor, highlightColor, shadowColor;
+ int offset;
+ int alpha = getBaseColor().a;
+
+ if (mPushed)
+ {
+ faceColor = getBaseColor() - 0x303030;
+ faceColor.a = alpha;
+ highlightColor = faceColor - 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor + 0x303030;
+ shadowColor.a = alpha;
+ offset = 1;
+ }
+ else
+ {
+ faceColor = getBaseColor();
+ faceColor.a = alpha;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+ offset = 0;
+ }
+
+ const Rectangle currentClipArea = graphics->getCurrentClipArea();
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0,
+ 0,
+ currentClipArea.width - 1,
+ 0);
+ graphics->drawLine(0,
+ 1,
+ 0,
+ currentClipArea.height - 1);
+ graphics->setColor(shadowColor);
+ graphics->drawLine(currentClipArea.width - 1,
+ 1,
+ currentClipArea.width - 1,
+ currentClipArea.height - 1);
+ graphics->drawLine(1,
+ currentClipArea.height - 1,
+ currentClipArea.width - 2,
+ currentClipArea.height - 1);
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(1,
+ 1,
+ currentClipArea.width - 2,
+ currentClipArea.height - 2));
+
+ graphics->setColor(getForegroundColor());
+
+ int i;
+ int n = currentClipArea.height / 3;
+ int dx = currentClipArea.height / 2;
+ int dy = (currentClipArea.height * 2) / 3;
+ for (i = 0; i < n; i++)
+ {
+ graphics->drawLine(dx - i + offset,
+ dy - i + offset,
+ dx + i + offset,
+ dy - i + offset);
+ }
+ }
+
+ int DropDown::getSelected() const
+ {
+ return mListBox->getSelected();
+ }
+
+ void DropDown::setSelected(int selected)
+ {
+ if (selected >= 0)
+ {
+ mListBox->setSelected(selected);
+ }
+ }
+
+ void DropDown::keyPressed(KeyEvent& keyEvent)
+ {
+ if (keyEvent.isConsumed())
+ return;
+
+ Key key = keyEvent.getKey();
+
+ if ((key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
+ && !mDroppedDown)
+ {
+ dropDown();
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::UP)
+ {
+ setSelected(getSelected() - 1);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::DOWN)
+ {
+ setSelected(getSelected() + 1);
+ keyEvent.consume();
+ }
+ }
+
+ void DropDown::mousePressed(MouseEvent& mouseEvent)
+ {
+ // If we have a mouse press on the widget.
+ if (0 <= mouseEvent.getY()
+ && mouseEvent.getY() < getHeight()
+ && mouseEvent.getX() >= 0
+ && mouseEvent.getX() < getWidth()
+ && mouseEvent.getButton() == MouseEvent::LEFT
+ && !mDroppedDown
+ && mouseEvent.getSource() == this)
+ {
+ mPushed = true;
+ dropDown();
+ requestModalMouseInputFocus();
+ }
+ // Fold up the listbox if the upper part is clicked after fold down
+ else if (0 <= mouseEvent.getY()
+ && mouseEvent.getY() < mFoldedUpHeight
+ && mouseEvent.getX() >= 0
+ && mouseEvent.getX() < getWidth()
+ && mouseEvent.getButton() == MouseEvent::LEFT
+ && mDroppedDown
+ && mouseEvent.getSource() == this)
+ {
+ mPushed = false;
+ foldUp();
+ releaseModalMouseInputFocus();
+ }
+ // If we have a mouse press outside the widget
+ else if (0 > mouseEvent.getY()
+ || mouseEvent.getY() >= getHeight()
+ || mouseEvent.getX() < 0
+ || mouseEvent.getX() >= getWidth())
+ {
+ mPushed = false;
+ foldUp();
+ }
+ }
+
+ void DropDown::mouseReleased(MouseEvent& mouseEvent)
+ {
+ if (mIsDragged)
+ {
+ mPushed = false;
+ }
+
+ // Released outside of widget. Can happen when we have modal input focus.
+ if ((0 > mouseEvent.getY()
+ || mouseEvent.getY() >= getHeight()
+ || mouseEvent.getX() < 0
+ || mouseEvent.getX() >= getWidth())
+ && mouseEvent.getButton() == MouseEvent::LEFT
+ && isModalMouseInputFocused())
+ {
+ releaseModalMouseInputFocus();
+
+ if (mIsDragged)
+ {
+ foldUp();
+ }
+ }
+ else if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ mPushed = false;
+ }
+
+ mIsDragged = false;
+ }
+
+ void DropDown::mouseDragged(MouseEvent& mouseEvent)
+ {
+ mIsDragged = true;
+
+ mouseEvent.consume();
+ }
+
+ void DropDown::setListModel(ListModel *listModel)
+ {
+ mListBox->setListModel(listModel);
+
+ if (mListBox->getSelected() < 0)
+ {
+ mListBox->setSelected(0);
+ }
+
+ adjustHeight();
+ }
+
+ ListModel *DropDown::getListModel()
+ {
+ return mListBox->getListModel();
+ }
+
+ void DropDown::adjustHeight()
+ {
+ if (mScrollArea == NULL)
+ {
+ throw GCN_EXCEPTION("Scroll area has been deleted.");
+ }
+
+ if (mListBox == NULL)
+ {
+ throw GCN_EXCEPTION("List box has been deleted.");
+ }
+
+ int listBoxHeight = mListBox->getHeight();
+
+ // We add 2 for the border
+ int h2 = getFont()->getHeight() + 2;
+
+ setHeight(h2);
+
+ // The addition/subtraction of 2 compensates for the seperation lines
+ // seperating the selected element view and the scroll area.
+
+ if (mDroppedDown && getParent())
+ {
+ int h = getParent()->getChildrenArea().height - getY();
+
+ if (listBoxHeight > h - h2 - 2)
+ {
+ mScrollArea->setHeight(h - h2 - 2);
+ setHeight(h);
+ }
+ else
+ {
+ setHeight(listBoxHeight + h2 + 2);
+ mScrollArea->setHeight(listBoxHeight);
+ }
+ }
+
+ mScrollArea->setWidth(getWidth());
+ // Resize the ListBox to exactly fit the ScrollArea.
+ mListBox->setWidth(mScrollArea->getChildrenArea().width);
+ mScrollArea->setPosition(0, 0);
+ }
+
+ void DropDown::dropDown()
+ {
+ if (!mDroppedDown)
+ {
+ mDroppedDown = true;
+ mFoldedUpHeight = getHeight();
+ adjustHeight();
+
+ if (getParent())
+ {
+ getParent()->moveToTop(this);
+ }
+ }
+
+ mListBox->requestFocus();
+ }
+
+ void DropDown::foldUp()
+ {
+ if (mDroppedDown)
+ {
+ mDroppedDown = false;
+ adjustHeight();
+ mInternalFocusHandler.focusNone();
+ }
+ }
+
+ void DropDown::focusLost(const Event& event)
+ {
+ foldUp();
+ mInternalFocusHandler.focusNone();
+ }
+
+
+ void DropDown::death(const Event& event)
+ {
+ if (event.getSource() == mScrollArea)
+ {
+ mScrollArea = NULL;
+ }
+
+ BasicContainer::death(event);
+ }
+
+ void DropDown::action(const ActionEvent& actionEvent)
+ {
+ foldUp();
+ releaseModalMouseInputFocus();
+ distributeActionEvent();
+ }
+
+ Rectangle DropDown::getChildrenArea()
+ {
+ if (mDroppedDown)
+ {
+ // Calculate the children area (with the one pixel border in mind)
+ return Rectangle(1,
+ mFoldedUpHeight + 1,
+ getWidth() - 2,
+ getHeight() - mFoldedUpHeight - 2);
+ }
+
+ return Rectangle();
+ }
+
+ void DropDown::setBaseColor(const Color& color)
+ {
+ if (mInternalScrollArea)
+ {
+ mScrollArea->setBaseColor(color);
+ }
+
+ if (mInternalListBox)
+ {
+ mListBox->setBaseColor(color);
+ }
+
+ Widget::setBaseColor(color);
+ }
+
+ void DropDown::setBackgroundColor(const Color& color)
+ {
+ if (mInternalScrollArea)
+ {
+ mScrollArea->setBackgroundColor(color);
+ }
+
+ if (mInternalListBox)
+ {
+ mListBox->setBackgroundColor(color);
+ }
+
+ Widget::setBackgroundColor(color);
+ }
+
+ void DropDown::setForegroundColor(const Color& color)
+ {
+ if (mInternalScrollArea)
+ {
+ mScrollArea->setForegroundColor(color);
+ }
+
+ if (mInternalListBox)
+ {
+ mListBox->setForegroundColor(color);
+ }
+
+ Widget::setForegroundColor(color);
+ }
+
+ void DropDown::setFont(Font *font)
+ {
+ if (mInternalScrollArea)
+ {
+ mScrollArea->setFont(font);
+ }
+
+ if (mInternalListBox)
+ {
+ mListBox->setFont(font);
+ }
+
+ Widget::setFont(font);
+ }
+
+ void DropDown::mouseWheelMovedUp(MouseEvent& mouseEvent)
+ {
+ if (isFocused() && mouseEvent.getSource() == this)
+ {
+ mouseEvent.consume();
+
+ if (mListBox->getSelected() > 0)
+ {
+ mListBox->setSelected(mListBox->getSelected() - 1);
+ }
+ }
+ }
+
+ void DropDown::mouseWheelMovedDown(MouseEvent& mouseEvent)
+ {
+ if (isFocused() && mouseEvent.getSource() == this)
+ {
+ mouseEvent.consume();
+
+ mListBox->setSelected(mListBox->getSelected() + 1);
+ }
+ }
+
+ void DropDown::setSelectionColor(const Color& color)
+ {
+ Widget::setSelectionColor(color);
+
+ if (mInternalListBox)
+ {
+ mListBox->setSelectionColor(color);
+ }
+ }
+
+ void DropDown::valueChanged(const SelectionEvent& event)
+ {
+ distributeValueChangedEvent();
+ }
+
+ void DropDown::addSelectionListener(SelectionListener* selectionListener)
+ {
+ mSelectionListeners.push_back(selectionListener);
+ }
+
+ void DropDown::removeSelectionListener(SelectionListener* selectionListener)
+ {
+ mSelectionListeners.remove(selectionListener);
+ }
+
+ void DropDown::distributeValueChangedEvent()
+ {
+ SelectionListenerIterator iter;
+
+ for (iter = mSelectionListeners.begin(); iter != mSelectionListeners.end(); ++iter)
+ {
+ SelectionEvent event(this);
+ (*iter)->valueChanged(event);
+ }
+ }
+}
+
diff --git a/src/guichan/widgets/dropdown.hpp b/src/guichan/widgets/dropdown.hpp
new file mode 100644
index 000000000..090fff0ce
--- /dev/null
+++ b/src/guichan/widgets/dropdown.hpp
@@ -0,0 +1,318 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_DROPDOWN_HPP
+#define GCN_DROPDOWN_HPP
+
+#include "guichan/actionlistener.hpp"
+#include "guichan/basiccontainer.hpp"
+#include "guichan/deathlistener.hpp"
+#include "guichan/focushandler.hpp"
+#include "guichan/focuslistener.hpp"
+#include "guichan/keylistener.hpp"
+#include "guichan/listmodel.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/selectionlistener.hpp"
+#include "guichan/widgets/listbox.hpp"
+#include "guichan/widgets/scrollarea.hpp"
+
+namespace gcn {
+ /**
+ * An implementation of a drop downable list from which an item can be
+ * selected. The drop down consists of an internal ScrollArea and an
+ * internal ListBox. The drop down also uses an internal FocusHandler to
+ * handle the focus of the internal ScollArea and the internal ListBox. The
+ * scroll area and the list box can be passed to the drop down if a custom
+ * scroll area and or a custom list box is preferable.
+ *
+ * To be able display a list the drop down uses a user provided list model.
+ * A list model can be any class that implements the ListModel interface.
+ *
+ * If an item is selected in the drop down a select event will be sent to
+ * all selection listeners of the drop down. If an item is selected by
+ * using a mouse click or by using the enter or space key an action event
+ * will be sent to all action listeners of the drop down.
+ */
+ class GCN_CORE_DECLSPEC DropDown :
+ public ActionListener,
+ public BasicContainer,
+ public KeyListener,
+ public MouseListener,
+ public FocusListener,
+ public SelectionListener
+ {
+ 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(ListModel *listModel = NULL,
+ ScrollArea *scrollArea = NULL,
+ ListBox *listBox = NULL);
+
+ /**
+ * Destructor.
+ */
+ virtual ~DropDown();
+
+ /**
+ * Gets the selected item as an index in the list model.
+ *
+ * @return the selected item as an index in the list model.
+ * @see setSelected
+ */
+ int getSelected() const;
+
+ /**
+ * Sets the selected item. The selected item is represented by
+ * an index from the list model.
+ *
+ * @param selected the selected item as an index from the list model.
+ * @see getSelected
+ */
+ void setSelected(int selected);
+
+ /**
+ * Sets the list model to use when displaying the list.
+ *
+ * @param listModel the list model to use.
+ * @see getListModel
+ */
+ void setListModel(ListModel *listModel);
+
+ /**
+ * Gets the list model used.
+ *
+ * @return the ListModel used.
+ * @see setListModel
+ */
+ ListModel *getListModel();
+
+ /**
+ * Adjusts the height of the drop down to fit the height of the
+ * drop down's parent's height. It's used to not make the drop down
+ * draw itself outside of it's parent if folded down.
+ */
+ void adjustHeight();
+
+ /**
+ * Adds a selection listener to the drop down. When the selection
+ * changes an event will be sent to all selection listeners of the
+ * drop down.
+ *
+ * If you delete your selection listener, be sure to also remove it
+ * using removeSelectionListener().
+ *
+ * @param selectionListener the selection listener to add.
+ * @since 0.8.0
+ */
+ void addSelectionListener(SelectionListener* selectionListener);
+
+ /**
+ * Removes a selection listener from the drop down.
+ *
+ * @param selectionListener the selection listener to remove.
+ * @since 0.8.0
+ */
+ void removeSelectionListener(SelectionListener* selectionListener);
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+ void setBaseColor(const Color& color);
+
+ void setBackgroundColor(const Color& color);
+
+ void setForegroundColor(const Color& color);
+
+ void setFont(Font *font);
+
+ void setSelectionColor(const Color& color);
+
+
+ // Inherited from BasicContainer
+
+ virtual Rectangle getChildrenArea();
+
+
+ // Inherited from FocusListener
+
+ virtual void focusLost(const Event& event);
+
+
+ // Inherited from ActionListener
+
+ virtual void action(const ActionEvent& actionEvent);
+
+
+ // Inherited from DeathListener
+
+ virtual void death(const Event& event);
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+
+ // Inherited from MouseListener
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+ virtual void mouseReleased(MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedUp(MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedDown(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+
+ // Inherited from SelectionListener
+
+ virtual void valueChanged(const SelectionEvent& event);
+
+ protected:
+ /**
+ * Draws the button of the drop down.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawButton(Graphics *graphics);
+
+ /**
+ * Sets the drop down to be dropped down.
+ */
+ virtual void dropDown();
+
+ /**
+ * Sets the drop down to be folded up.
+ */
+ virtual void foldUp();
+
+ /**
+ * Distributes a value changed event to all selection listeners
+ * of the drop down.
+ *
+ * @since 0.8.0
+ */
+ void distributeValueChangedEvent();
+
+ /**
+ * True if the drop down is dropped down, false otherwise.
+ */
+ bool mDroppedDown;
+
+ /**
+ * True if the drop down has been pushed with the mouse, false
+ * otherwise.
+ */
+ bool mPushed;
+
+ /**
+ * Holds what the height is if the drop down is folded up. Used when
+ * checking if the list of the drop down was clicked or if the upper
+ * part of the drop down was clicked on a mouse click.
+ */
+ int mFoldedUpHeight;
+
+ /**
+ * The scroll area used.
+ */
+ ScrollArea* mScrollArea;
+
+ /**
+ * The list box used.
+ */
+ ListBox* mListBox;
+
+ /**
+ * The internal focus handler used to keep track of focus for the
+ * internal list box.
+ */
+ FocusHandler mInternalFocusHandler;
+
+ /**
+ * True if an internal scroll area is used, false if a scroll area
+ * has been passed to the drop down which the drop down should not
+ * deleted in it's destructor.
+ */
+ bool mInternalScrollArea;
+
+ /**
+ * True if an internal list box is used, false if a list box
+ * has been passed to the drop down which the drop down should not
+ * deleted in it's destructor.
+ */
+ bool mInternalListBox;
+
+ /**
+ * True if the drop down is dragged.
+ */
+ bool mIsDragged;
+
+ /**
+ * Typedef.
+ */
+ typedef std::list<SelectionListener*> SelectionListenerList;
+
+ /**
+ * The selection listener's of the drop down.
+ */
+ SelectionListenerList mSelectionListeners;
+
+ /**
+ * Typedef.
+ */
+ typedef SelectionListenerList::iterator SelectionListenerIterator;
+ };
+}
+
+#endif // end GCN_DROPDOWN_HPP
diff --git a/src/guichan/widgets/icon.cpp b/src/guichan/widgets/icon.cpp
new file mode 100644
index 000000000..687fadeab
--- /dev/null
+++ b/src/guichan/widgets/icon.cpp
@@ -0,0 +1,116 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/icon.hpp"
+
+#include "guichan/graphics.hpp"
+#include "guichan/image.hpp"
+#include "guichan/rectangle.hpp"
+
+namespace gcn
+{
+ Icon::Icon()
+ : mImage(0)
+ , mInternalImage(false)
+ {
+ setSize(0, 0);
+ }
+
+ Icon::Icon(const std::string& filename)
+ : mImage(0),
+ mInternalImage(false)
+ {
+ mImage = Image::load(filename);
+ mInternalImage = true;
+ setSize(mImage->getWidth(),
+ mImage->getHeight());
+ }
+
+ Icon::Icon(const Image* image)
+ : mImage(image),
+ mInternalImage(false)
+ {
+ setSize(mImage->getWidth(),
+ mImage->getHeight());
+ }
+
+ Icon::~Icon()
+ {
+ if (mInternalImage)
+ {
+ delete mImage;
+ }
+ }
+
+ void Icon::setImage(const Image* image)
+ {
+ if (mInternalImage)
+ {
+ delete mImage;
+ }
+
+ mImage = image;
+ mInternalImage = false;
+ setSize(mImage->getWidth(),
+ mImage->getHeight());
+ }
+
+ const Image* Icon::getImage() const
+ {
+ return mImage;
+ }
+
+ void Icon::draw(Graphics* graphics)
+ {
+ if (mImage != NULL)
+ {
+ const int x = (getWidth() - mImage->getWidth()) / 2;
+ const int y = (getHeight() - mImage->getHeight()) / 2;
+ graphics->drawImage(mImage, x, y);
+ }
+ }
+}
diff --git a/src/guichan/widgets/icon.hpp b/src/guichan/widgets/icon.hpp
new file mode 100644
index 000000000..f0f9ff566
--- /dev/null
+++ b/src/guichan/widgets/icon.hpp
@@ -0,0 +1,118 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_ICON_HPP
+#define GCN_ICON_HPP
+
+#include "guichan/image.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+namespace gcn
+{
+ /**
+ * Implements an icon capable of displaying an image.
+ */
+ class GCN_CORE_DECLSPEC Icon: public Widget
+ {
+ public:
+ /**
+ * Default constructor.
+ */
+ Icon();
+
+ /**
+ * Constructor.
+ *
+ * @param filename The filename of the image to display.
+ */
+ Icon(const std::string& filename);
+
+ /**
+ * Constructor.
+ *
+ * @param image The image to display.
+ */
+ Icon(const Image* image);
+
+ /**
+ * Descructor.
+ */
+ virtual ~Icon();
+
+ /**
+ * Sets the image to display. Existing image is freed automatically
+ * if it was loaded internally.
+ *
+ * @param image The image to display.
+ */
+ void setImage(const Image* image);
+
+ /**
+ * Gets the current image.
+ *
+ * @return The current image.
+ */
+ const Image* getImage() const;
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+ protected:
+ /**
+ * The image to display.
+ */
+ const Image* mImage;
+
+ /**
+ * True if the image has been loaded internally, false otherwise.
+ * An image not loaded internally should not be deleted in the
+ * destructor.
+ */
+ bool mInternalImage;
+ };
+}
+
+#endif // end GCN_ICON_HPP
diff --git a/src/guichan/widgets/imagebutton.cpp b/src/guichan/widgets/imagebutton.cpp
new file mode 100644
index 000000000..f09bca30d
--- /dev/null
+++ b/src/guichan/widgets/imagebutton.cpp
@@ -0,0 +1,166 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/imagebutton.hpp"
+
+#include "guichan/graphics.hpp"
+#include "guichan/image.hpp"
+
+namespace gcn
+{
+ ImageButton::ImageButton()
+ : mImage(0),
+ mInternalImage(false)
+ {
+ setWidth(0);
+ setHeight(0);
+ }
+
+ ImageButton::ImageButton(const std::string& filename)
+ : mImage(0),
+ mInternalImage(false)
+ {
+ mImage = Image::load(filename);
+ mInternalImage = true;
+ setWidth(mImage->getWidth() + mImage->getWidth() / 2);
+ setHeight(mImage->getHeight() + mImage->getHeight() / 2);
+ }
+
+ ImageButton::ImageButton(const Image* image)
+ : mImage(image),
+ mInternalImage(false)
+ {
+ setWidth(mImage->getWidth() + mImage->getWidth() / 2);
+ setHeight(mImage->getHeight() + mImage->getHeight() / 2);
+ }
+
+ ImageButton::~ImageButton()
+ {
+ if (mInternalImage)
+ {
+ delete mImage;
+ }
+ }
+
+ void ImageButton::setImage(const Image* image)
+ {
+ if (mInternalImage)
+ {
+ delete mImage;
+ }
+
+ mImage = image;
+ mInternalImage = false;
+ }
+
+ const Image* ImageButton::getImage() const
+ {
+ return mImage;
+ }
+
+ void ImageButton::draw(Graphics* graphics)
+ {
+ gcn::Color faceColor = getBaseColor();
+ gcn::Color highlightColor, shadowColor;
+ int alpha = getBaseColor().a;
+
+ if (isPressed())
+ {
+ faceColor = faceColor - 0x303030;
+ faceColor.a = alpha;
+ highlightColor = faceColor - 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor + 0x303030;
+ shadowColor.a = alpha;
+ }
+ else
+ {
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+ }
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(1,
+ 1,
+ getDimension().width - 1,
+ getHeight() - 1));
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, getWidth() - 1, 0);
+ graphics->drawLine(0, 1, 0, getHeight() - 1);
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(getWidth() - 1, 1, getWidth() - 1, getHeight() - 1);
+ graphics->drawLine(1, getHeight() - 1, getWidth() - 1, getHeight() - 1);
+
+ graphics->setColor(getForegroundColor());
+
+ const int textX = (getWidth() - (mImage ? mImage->getWidth() : 0) ) / 2;
+ const int textY = (getHeight() - (mImage ? mImage->getHeight() : 0) ) / 2;
+
+ if (isPressed())
+ {
+ if(mImage)
+ graphics->drawImage(mImage, textX + 1, textY + 1);
+ }
+ else
+ {
+ if(mImage)
+ graphics->drawImage(mImage, textX, textY);
+
+ if (isFocused())
+ {
+ graphics->drawRectangle(Rectangle(2,
+ 2,
+ getWidth() - 4,
+ getHeight() - 4));
+ }
+ }
+ }
+}
diff --git a/src/guichan/widgets/imagebutton.hpp b/src/guichan/widgets/imagebutton.hpp
new file mode 100644
index 000000000..1ab285ce9
--- /dev/null
+++ b/src/guichan/widgets/imagebutton.hpp
@@ -0,0 +1,123 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_IMAGEBUTTON_HPP
+#define GCN_IMAGEBUTTON_HPP
+
+#include "guichan/platform.hpp"
+#include "guichan/widgets/button.hpp"
+
+namespace gcn
+{
+ class Image;
+
+ /**
+ * An implementation of a regular clickable button. Unlike a normal button an image
+ * button is capable of displaying an image instead of a simple text caption.
+ * Whenever an image button is clicked an action event will be sent to the action
+ * listener's of the image button.
+ *
+ * @see Button
+ */
+ class GCN_CORE_DECLSPEC ImageButton : public gcn::Button
+ {
+ public:
+ /**
+ * Default constructor.
+ */
+ ImageButton();
+
+ /**
+ * Constructor.
+ *
+ * @param filename The filename of the image to display.
+ */
+ ImageButton(const std::string& filename);
+
+ /**
+ * Constructor.
+ *
+ * @param image The image to display.
+ */
+ ImageButton(const Image* image);
+
+ /**
+ * Destructor.
+ */
+ virtual ~ImageButton();
+
+ /**
+ * Sets the image to display. Existing Image is freed automatically,
+ * if it was loaded internally.
+ *
+ * @param image The image to display.
+ */
+ void setImage(const Image* image);
+
+ /**
+ * Gets current image.
+ *
+ * @return The current image.
+ */
+ const Image* getImage() const;
+
+
+ // Inherited from Widget
+
+ void draw(gcn::Graphics* graphics);
+
+ protected:
+ /**
+ * The image to display.
+ */
+ const Image* mImage;
+
+ /**
+ * True if the image has been loaded internally, false otherwise.
+ * An image not loaded internally should not be deleted in the
+ * destructor.
+ */
+ bool mInternalImage;
+ };
+}
+#endif
diff --git a/src/guichan/widgets/label.cpp b/src/guichan/widgets/label.cpp
new file mode 100644
index 000000000..9b11206a6
--- /dev/null
+++ b/src/guichan/widgets/label.cpp
@@ -0,0 +1,120 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/label.hpp"
+
+#include "guichan/exception.hpp"
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+
+namespace gcn
+{
+ Label::Label()
+ {
+ mAlignment = Graphics::LEFT;
+ }
+
+ Label::Label(const std::string& caption)
+ {
+ mCaption = caption;
+ mAlignment = Graphics::LEFT;
+
+ setWidth(getFont()->getWidth(caption));
+ setHeight(getFont()->getHeight());
+ }
+
+ const std::string &Label::getCaption() const
+ {
+ return mCaption;
+ }
+
+ void Label::setCaption(const std::string& caption)
+ {
+ mCaption = caption;
+ }
+
+ void Label::setAlignment(Graphics::Alignment alignment)
+ {
+ mAlignment = alignment;
+ }
+
+ Graphics::Alignment Label::getAlignment() const
+ {
+ return mAlignment;
+ }
+
+ void Label::draw(Graphics* graphics)
+ {
+ int textX;
+ int textY = getHeight() / 2 - getFont()->getHeight() / 2;
+
+ switch (getAlignment())
+ {
+ case Graphics::LEFT:
+ textX = 0;
+ break;
+ case Graphics::CENTER:
+ textX = getWidth() / 2;
+ break;
+ case Graphics::RIGHT:
+ textX = getWidth();
+ break;
+ default:
+ throw GCN_EXCEPTION("Unknown alignment.");
+ }
+
+ graphics->setFont(getFont());
+ graphics->setColor(getForegroundColor());
+ graphics->drawText(getCaption(), textX, textY, getAlignment());
+ }
+
+ void Label::adjustSize()
+ {
+ setWidth(getFont()->getWidth(getCaption()));
+ setHeight(getFont()->getHeight());
+ }
+}
diff --git a/src/guichan/widgets/label.hpp b/src/guichan/widgets/label.hpp
new file mode 100644
index 000000000..4d6ea0006
--- /dev/null
+++ b/src/guichan/widgets/label.hpp
@@ -0,0 +1,133 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_LABEL_HPP
+#define GCN_LABEL_HPP
+
+#include <string>
+
+#include "guichan/graphics.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+namespace gcn
+{
+ /**
+ * Implementation of a label capable of displaying a caption.
+ */
+ class GCN_CORE_DECLSPEC Label: public Widget
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ Label();
+
+ /**
+ * Constructor. The label will be automatically resized
+ * to fit the caption.
+ *
+ * @param caption The caption of the label.
+ */
+ Label(const std::string& caption);
+
+ /**
+ * Gets the caption of the label.
+ *
+ * @return The caption of the label.
+ * @see setCaption
+ */
+ const std::string &getCaption() const;
+
+ /**
+ * Sets the caption of the label. It's advisable to call
+ * adjustSize after setting of the caption to adjust the
+ * label's size to fit the caption.
+ *
+ * @param caption The caption of the label.
+ * @see getCaption, adjustSize
+ */
+ void setCaption(const std::string& caption);
+
+ /**
+ * Sets the alignment of the caption. The alignment is relative
+ * to the center of the label.
+ *
+ * @param alignemnt The alignment of the caption of the label.
+ * @see getAlignment, Graphics
+ */
+ void setAlignment(Graphics::Alignment alignment);
+
+ /**
+ * Gets the alignment of the caption. The alignment is relative to
+ * the center of the label.
+ *
+ * @return The alignment of caption of the label.
+ * @see setAlignmentm Graphics
+ */
+ Graphics::Alignment getAlignment() const;
+
+ /**
+ * Adjusts the label's size to fit the caption.
+ */
+ void adjustSize();
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+ protected:
+ /**
+ * Holds the caption of the label.
+ */
+ std::string mCaption;
+
+ /**
+ * Holds the alignment of the caption.
+ */
+ Graphics::Alignment mAlignment;
+ };
+}
+
+#endif // end GCN_LABEL_HPP
diff --git a/src/guichan/widgets/listbox.cpp b/src/guichan/widgets/listbox.cpp
new file mode 100644
index 000000000..9b20b8538
--- /dev/null
+++ b/src/guichan/widgets/listbox.cpp
@@ -0,0 +1,350 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/listbox.hpp"
+
+#include "guichan/basiccontainer.hpp"
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/key.hpp"
+#include "guichan/listmodel.hpp"
+#include "guichan/mouseinput.hpp"
+#include "guichan/selectionlistener.hpp"
+
+namespace gcn
+{
+ ListBox::ListBox()
+ : mSelected(-1),
+ mListModel(NULL),
+ mWrappingEnabled(false)
+ {
+ setWidth(100);
+ setFocusable(true);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ }
+
+ ListBox::ListBox(ListModel *listModel)
+ : mSelected(-1),
+ mWrappingEnabled(false)
+ {
+ setWidth(100);
+ setListModel(listModel);
+ setFocusable(true);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ }
+
+ void ListBox::draw(Graphics* graphics)
+ {
+ graphics->setColor(getBackgroundColor());
+ graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
+
+ if (mListModel == NULL)
+ {
+ return;
+ }
+
+ graphics->setColor(getForegroundColor());
+ graphics->setFont(getFont());
+
+ // Check the current clip area so we don't draw unnecessary items
+ // that are not visible.
+ const ClipRectangle currentClipArea = graphics->getCurrentClipArea();
+ int rowHeight = getRowHeight();
+
+ // Calculate the number of rows to draw by checking the clip area.
+ // The addition of two makes covers a partial visible row at the top
+ // and a partial visible row at the bottom.
+ int numberOfRows = currentClipArea.height / rowHeight + 2;
+
+ if (numberOfRows > mListModel->getNumberOfElements())
+ {
+ numberOfRows = mListModel->getNumberOfElements();
+ }
+
+ // Calculate which row to start drawing. If the list box
+ // has a negative y coordinate value we should check if
+ // we should drop rows in the begining of the list as
+ // they might not be visible. A negative y value is very
+ // common if the list box for instance resides in a scroll
+ // area and the user has scrolled the list box downwards.
+ int startRow;
+ if (getY() < 0)
+ {
+ startRow = -1 * (getY() / rowHeight);
+ }
+ else
+ {
+ startRow = 0;
+ }
+
+ int i;
+ // The y coordinate where we start to draw the text is
+ // simply the y coordinate multiplied with the font height.
+ int y = rowHeight * startRow;
+ for (i = startRow; i < startRow + numberOfRows; ++i)
+ {
+ if (i == mSelected)
+ {
+ graphics->setColor(getSelectionColor());
+ graphics->fillRectangle(Rectangle(0, y, getWidth(), rowHeight));
+ graphics->setColor(getForegroundColor());
+ }
+
+ // If the row height is greater than the font height we
+ // draw the text with a center vertical alignment.
+ if (rowHeight > getFont()->getHeight())
+ {
+ graphics->drawText(mListModel->getElementAt(i), 1, y + rowHeight / 2 - getFont()->getHeight() / 2);
+ }
+ else
+ {
+ graphics->drawText(mListModel->getElementAt(i), 1, y);
+ }
+
+ y += rowHeight;
+ }
+ }
+
+ void ListBox::logic()
+ {
+ adjustSize();
+ }
+
+ int ListBox::getSelected() const
+ {
+ return mSelected;
+ }
+
+ void ListBox::setSelected(int selected)
+ {
+ if (mListModel == NULL)
+ {
+ mSelected = -1;
+ }
+ else
+ {
+ if (selected < 0)
+ {
+ mSelected = -1;
+ }
+ else if (selected >= mListModel->getNumberOfElements())
+ {
+ mSelected = mListModel->getNumberOfElements() - 1;
+ }
+ else
+ {
+ mSelected = selected;
+ }
+ }
+
+ Rectangle scroll;
+
+ if (mSelected < 0)
+ {
+ scroll.y = 0;
+ }
+ else
+ {
+ scroll.y = getRowHeight() * mSelected;
+ }
+
+ scroll.height = getRowHeight();
+ showPart(scroll);
+
+ distributeValueChangedEvent();
+ }
+
+ void ListBox::keyPressed(KeyEvent& keyEvent)
+ {
+ Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
+ {
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::UP)
+ {
+ setSelected(mSelected - 1);
+
+ if (mSelected == -1)
+ {
+ if (mWrappingEnabled)
+ {
+ setSelected(getListModel()->getNumberOfElements() - 1);
+ }
+ else
+ {
+ setSelected(0);
+ }
+ }
+
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::DOWN)
+ {
+ if (mWrappingEnabled
+ && getSelected() == getListModel()->getNumberOfElements() - 1)
+ {
+ setSelected(0);
+ }
+ else
+ {
+ setSelected(getSelected() + 1);
+ }
+
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::HOME)
+ {
+ setSelected(0);
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::END)
+ {
+ setSelected(getListModel()->getNumberOfElements() - 1);
+ keyEvent.consume();
+ }
+ }
+
+ void ListBox::mousePressed(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ setSelected(mouseEvent.getY() / getRowHeight());
+ distributeActionEvent();
+ }
+ }
+
+ void ListBox::mouseWheelMovedUp(MouseEvent& mouseEvent)
+ {
+ if (isFocused())
+ {
+ if (getSelected() > 0 )
+ {
+ setSelected(getSelected() - 1);
+ }
+
+ mouseEvent.consume();
+ }
+ }
+
+ void ListBox::mouseWheelMovedDown(MouseEvent& mouseEvent)
+ {
+ if (isFocused())
+ {
+ setSelected(getSelected() + 1);
+
+ mouseEvent.consume();
+ }
+ }
+
+ void ListBox::mouseDragged(MouseEvent& mouseEvent)
+ {
+ mouseEvent.consume();
+ }
+
+ void ListBox::setListModel(ListModel *listModel)
+ {
+ mSelected = -1;
+ mListModel = listModel;
+ adjustSize();
+ }
+
+ ListModel* ListBox::getListModel()
+ {
+ return mListModel;
+ }
+
+ void ListBox::adjustSize()
+ {
+ if (mListModel != NULL)
+ {
+ setHeight(getRowHeight() * mListModel->getNumberOfElements());
+ }
+ }
+
+ bool ListBox::isWrappingEnabled() const
+ {
+ return mWrappingEnabled;
+ }
+
+ void ListBox::setWrappingEnabled(bool wrappingEnabled)
+ {
+ mWrappingEnabled = wrappingEnabled;
+ }
+
+ void ListBox::addSelectionListener(SelectionListener* selectionListener)
+ {
+ mSelectionListeners.push_back(selectionListener);
+ }
+
+ void ListBox::removeSelectionListener(SelectionListener* selectionListener)
+ {
+ mSelectionListeners.remove(selectionListener);
+ }
+
+ void ListBox::distributeValueChangedEvent()
+ {
+ SelectionListenerIterator iter;
+
+ for (iter = mSelectionListeners.begin(); iter != mSelectionListeners.end(); ++iter)
+ {
+ SelectionEvent event(this);
+ (*iter)->valueChanged(event);
+ }
+ }
+
+ unsigned int ListBox::getRowHeight() const
+ {
+ return getFont()->getHeight();
+ }
+}
diff --git a/src/guichan/widgets/listbox.hpp b/src/guichan/widgets/listbox.hpp
new file mode 100644
index 000000000..b99f84aaa
--- /dev/null
+++ b/src/guichan/widgets/listbox.hpp
@@ -0,0 +1,253 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_LISTBOX_HPP
+#define GCN_LISTBOX_HPP
+
+#include <list>
+
+#include "guichan/keylistener.hpp"
+#include "guichan/listmodel.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+namespace gcn
+{
+ class SelectionListener;
+
+ /**
+ * An implementation of a list box where an item can be selected.
+ *
+ * To be able display a list the list box uses a user provided list model.
+ * A list model can be any class that implements the ListModel interface.
+ *
+ * If an item is selected in the list box a select event will be sent to
+ * all selection listeners of the list box. If an item is selected by using
+ * a mouse click or by using the enter or space key an action event will be
+ * sent to all action listeners of the list box.
+ */
+ class GCN_CORE_DECLSPEC ListBox :
+ public Widget,
+ public MouseListener,
+ public KeyListener
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ ListBox();
+
+ /**
+ * Constructor.
+ *
+ * @param listModel the list model to use.
+ */
+ ListBox(ListModel *listModel);
+
+ /**
+ * Destructor.
+ */
+ virtual ~ListBox() { }
+
+ /**
+ * Gets the selected item as an index in the list model.
+ *
+ * @return the selected item as an index in the list model.
+ * @see setSelected
+ */
+ int getSelected() const;
+
+ /**
+ * Sets the selected item. The selected item is represented by
+ * an index from the list model.
+ *
+ * @param selected the selected item as an index from the list model.
+ * @see getSelected
+ */
+ void setSelected(int selected);
+
+ /**
+ * Sets the list model to use.
+ *
+ * @param listModel the list model to use.
+ * @see getListModel
+ */
+ void setListModel(ListModel *listModel);
+
+ /**
+ * Gets the list model used.
+ *
+ * @return the list model used.
+ * @see setListModel
+ */
+ ListModel *getListModel();
+
+ /**
+ * Adjusts the size of the list box to fit it's list model.
+ */
+ void adjustSize();
+
+ /**
+ * Checks whether the list box wraps when selecting items with a
+ * keyboard.
+ *
+ * Wrapping means that the selection of items will be wrapped. That is,
+ * if the first item is selected and up is pressed, the last item will
+ * get selected. If the last item is selected and down is pressed, the
+ * first item will get selected.
+ *
+ * @return true if wrapping is enabled, fasle otherwise.
+ * @see setWrappingEnabled
+ */
+ bool isWrappingEnabled() const;
+
+ /**
+ * Sets the list box to wrap or not when selecting items with a
+ * keyboard.
+ *
+ * Wrapping means that the selection of items will be wrapped. That is,
+ * if the first item is selected and up is pressed, the last item will
+ * get selected. If the last item is selected and down is pressed, the
+ * first item will get selected.
+ *
+ * @see isWrappingEnabled
+ */
+ void setWrappingEnabled(bool wrappingEnabled);
+
+ /**
+ * Adds a selection listener to the list box. When the selection
+ * changes an event will be sent to all selection listeners of the
+ * list box.
+ *
+ * If you delete your selection listener, be sure to also remove it
+ * using removeSelectionListener().
+ *
+ * @param selectionListener The selection listener to add.
+ * @since 0.8.0
+ */
+ void addSelectionListener(SelectionListener* selectionListener);
+
+ /**
+ * Removes a selection listener from the list box.
+ *
+ * @param selectionListener The selection listener to remove.
+ * @since 0.8.0
+ */
+ void removeSelectionListener(SelectionListener* selectionListener);
+
+ /**
+ * Gets the height of a row. Should be overridden if another row
+ * height than the font height is preferred.
+ *
+ * @return The height of a row.
+ * @since 0.8.0
+ */
+ virtual unsigned int getRowHeight() const;
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+ virtual void logic();
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+
+ // Inherited from MouseListener
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedUp(MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedDown(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+
+ protected:
+ /**
+ * Distributes a value changed event to all selection listeners
+ * of the list box.
+ *
+ * @since 0.8.0
+ */
+ void distributeValueChangedEvent();
+
+ /**
+ * The selected item as an index in the list model.
+ */
+ int mSelected;
+
+ /**
+ * The list model to use.
+ */
+ ListModel *mListModel;
+
+ /**
+ * True if wrapping is enabled, false otherwise.
+ */
+ bool mWrappingEnabled;
+
+ /**
+ * Typdef.
+ */
+ typedef std::list<SelectionListener*> SelectionListenerList;
+
+ /**
+ * The selection listeners of the list box.
+ */
+ SelectionListenerList mSelectionListeners;
+
+ /**
+ * Typedef.
+ */
+ typedef SelectionListenerList::iterator SelectionListenerIterator;
+ };
+}
+
+#endif // end GCN_LISTBOX_HPP
diff --git a/src/guichan/widgets/radiobutton.cpp b/src/guichan/widgets/radiobutton.cpp
new file mode 100644
index 000000000..31fdec94d
--- /dev/null
+++ b/src/guichan/widgets/radiobutton.cpp
@@ -0,0 +1,298 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/radiobutton.hpp"
+
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/key.hpp"
+#include "guichan/mouseinput.hpp"
+
+namespace gcn
+{
+ RadioButton::GroupMap RadioButton::mGroupMap;
+
+ RadioButton::RadioButton()
+ {
+ setSelected(false);
+
+ setFocusable(true);
+ addMouseListener(this);
+ addKeyListener(this);
+ }
+
+ RadioButton::RadioButton(const std::string &caption,
+ const std::string &group,
+ bool selected)
+ {
+ setCaption(caption);
+ setGroup(group);
+ setSelected(selected);
+
+ setFocusable(true);
+ addMouseListener(this);
+ addKeyListener(this);
+
+ adjustSize();
+ }
+
+ RadioButton::~RadioButton()
+ {
+ // Remove us from the group list
+ setGroup("");
+ }
+
+ void RadioButton::draw(Graphics* graphics)
+ {
+ graphics->pushClipArea(Rectangle(1,
+ 1,
+ getWidth() - 1,
+ getHeight() - 1));
+ drawBox(graphics);
+ graphics->popClipArea();
+
+
+ graphics->setFont(getFont());
+ graphics->setColor(getForegroundColor());
+
+ if (isFocused())
+ {
+ int fh;
+
+ if (getHeight()%2 == 0)
+ {
+ fh = getHeight() - 4;
+ }
+ else
+ {
+ fh = getHeight() - 3;
+ }
+
+ int hh = (fh + 1) / 2;
+
+ graphics->drawLine(0, hh + 1, hh + 1, 0);
+ graphics->drawLine(hh + 2, 1, fh + 2, hh + 1);
+ graphics->drawLine(fh + 1, hh + 2, hh + 1, fh + 2);
+ graphics->drawLine(hh + 1, fh + 2, 1, hh + 2);
+ }
+
+ int h = getHeight() + getHeight() / 2;
+
+ graphics->drawText(getCaption(), h - 2, 0);
+ }
+
+ void RadioButton::drawBox(Graphics *graphics)
+ {
+ int h;
+
+ if (getHeight()%2 == 0)
+ {
+ h = getHeight() - 4;
+ }
+ else
+ {
+ h = getHeight() - 3;
+ }
+
+ int alpha = getBaseColor().a;
+ Color faceColor = getBaseColor();
+ faceColor.a = alpha;
+ Color highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ Color shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ graphics->setColor(getBackgroundColor());
+
+ int i;
+ int hh = (h + 1) / 2;
+
+ for (i = 1; i <= hh; ++i)
+ {
+ graphics->drawLine(hh - i + 1,
+ i,
+ hh + i - 1,
+ i);
+ }
+
+ for (i = 1; i < hh; ++i)
+ {
+ graphics->drawLine(hh - i + 1,
+ h - i,
+ hh + i - 1,
+ h - i);
+ }
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(hh, 0, 0, hh);
+ graphics->drawLine(hh + 1, 1, h - 1, hh - 1);
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(1, hh + 1, hh, h);
+ graphics->drawLine(hh + 1, h - 1, h, hh);
+
+ graphics->setColor(getForegroundColor());
+
+ int hhh = hh - 3;
+ if (mSelected)
+ {
+ for (i = 0; i < hhh; ++i)
+ {
+ graphics->drawLine(hh - i, 4 + i, hh + i, 4 + i);
+ }
+ for (i = 0; i < hhh; ++i)
+ {
+ graphics->drawLine(hh - i, h - 4 - i, hh + i, h - 4 - i);
+ }
+
+ }
+ }
+
+ bool RadioButton::isSelected() const
+ {
+ return mSelected;
+ }
+
+ void RadioButton::setSelected(bool selected)
+ {
+ if (selected && mGroup != "")
+ {
+ GroupIterator iter, iterEnd;
+ iterEnd = mGroupMap.upper_bound(mGroup);
+
+ for (iter = mGroupMap.lower_bound(mGroup);
+ iter != iterEnd;
+ iter++)
+ {
+ if (iter->second->isSelected())
+ {
+ iter->second->setSelected(false);
+ }
+ }
+ }
+
+ mSelected = selected;
+ }
+
+ const std::string &RadioButton::getCaption() const
+ {
+ return mCaption;
+ }
+
+ void RadioButton::setCaption(const std::string caption)
+ {
+ mCaption = caption;
+ }
+
+ void RadioButton::keyPressed(KeyEvent& keyEvent)
+ {
+ Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::ENTER ||
+ key.getValue() == Key::SPACE)
+ {
+ setSelected(true);
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ }
+
+ void RadioButton::mouseClicked(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ setSelected(true);
+ distributeActionEvent();
+ }
+ }
+
+ void RadioButton::mouseDragged(MouseEvent& mouseEvent)
+ {
+ mouseEvent.consume();
+ }
+
+ void RadioButton::setGroup(const std::string &group)
+ {
+ if (mGroup != "")
+ {
+ GroupIterator iter, iterEnd;
+ iterEnd = mGroupMap.upper_bound(mGroup);
+
+ for (iter = mGroupMap.lower_bound(mGroup);
+ iter != iterEnd;
+ iter++)
+ {
+ if (iter->second == this)
+ {
+ mGroupMap.erase(iter);
+ break;
+ }
+ }
+ }
+
+ if (group != "")
+ {
+ mGroupMap.insert(
+ std::pair<std::string, RadioButton *>(group, this));
+ }
+
+ mGroup = group;
+ }
+
+ const std::string &RadioButton::getGroup() const
+ {
+ return mGroup;
+ }
+
+ void RadioButton::adjustSize()
+ {
+ int height = getFont()->getHeight();
+
+ setHeight(height);
+ setWidth(getFont()->getWidth(getCaption()) + height + height/2);
+ }
+}
diff --git a/src/guichan/widgets/radiobutton.hpp b/src/guichan/widgets/radiobutton.hpp
new file mode 100644
index 000000000..0fb5b4123
--- /dev/null
+++ b/src/guichan/widgets/radiobutton.hpp
@@ -0,0 +1,211 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_RADIOBUTTON_HPP
+#define GCN_RADIOBUTTON_HPP
+
+#include <map>
+#include <string>
+
+#include "guichan/keylistener.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+namespace gcn
+{
+ /**
+ * An implementation of a radio button where a user can select or deselect
+ * the radio button and where the status of the radio button is displayed to the user.
+ * A radio button can belong to a group and when a radio button belongs to a
+ * group only one radio button can be selected in the group. A radio button is
+ * capable of displaying a caption.
+ *
+ * If a radio button's state changes an action event will be sent to all action
+ * listeners of the check box.
+ */
+ class GCN_CORE_DECLSPEC RadioButton :
+ public Widget,
+ public MouseListener,
+ public KeyListener
+ {
+ public:
+
+ /**
+ * Constructor.
+ */
+ RadioButton();
+
+ /**
+ * Constructor. The radio button will be automatically resized
+ * to fit the caption.
+ *
+ * @param caption The caption of the radio button.
+ * @param group The group the radio button should belong to.
+ * @param selected True if the radio button should be selected.
+ */
+ RadioButton(const std::string &caption,
+ const std::string &group,
+ bool selected = false);
+
+ /**
+ * Destructor.
+ */
+ virtual ~RadioButton();
+
+ /**
+ * Checks if the radio button is selected.
+ *
+ * @return True if the radio button is selecte, false otherwise.
+ * @see setSelected
+ */
+ bool isSelected() const;
+
+ /**
+ * Sets the radio button to selected or not.
+ *
+ * @param selected True if the radio button should be selected,
+ * false otherwise.
+ * @see isSelected
+ */
+ void setSelected(bool selected);
+
+ /**
+ * Gets the caption of the radio button.
+ *
+ * @return The caption of the radio button.
+ * @see setCaption
+ */
+ const std::string &getCaption() const;
+
+ /**
+ * Sets the caption of the radio button. It's advisable to call
+ * adjustSize after setting of the caption to adjust the
+ * radio button's size to fit the caption.
+ *
+ * @param caption The caption of the radio button.
+ * @see getCaption, adjustSize
+ */
+ void setCaption(const std::string caption);
+
+ /**
+ * Sets the group the radio button should belong to. Note that
+ * a radio button group is unique per application, not per Gui object
+ * as the group is stored in a static map.
+ *
+ * @param group The name of the group.
+ * @see getGroup
+ */
+ void setGroup(const std::string &group);
+
+ /**
+ * Gets the group the radio button belongs to.
+ *
+ * @return The group the radio button belongs to.
+ * @see setGroup
+ */
+ const std::string &getGroup() const;
+
+ /**
+ * Adjusts the radio button's size to fit the caption.
+ */
+ void adjustSize();
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+
+ // Inherited from MouseListener
+
+ virtual void mouseClicked(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+ protected:
+ /**
+ * Draws the box.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawBox(Graphics *graphics);
+
+ /**
+ * True if the radio button is selected, false otherwise.
+ */
+ bool mSelected;
+
+ /**
+ * Holds the caption of the radio button.
+ */
+ std::string mCaption;
+
+ /**
+ * Holds the group of the radio button.
+ */
+ std::string mGroup;
+
+ /**
+ * Typdef.
+ */
+ typedef std::multimap<std::string, RadioButton *> GroupMap;
+
+ /**
+ * Typdef.
+ */
+ typedef GroupMap::iterator GroupIterator;
+
+ /**
+ * Holds all available radio button groups.
+ */
+ static GroupMap mGroupMap;
+ };
+}
+
+#endif // end GCN_RADIOBUTTON_HPP
diff --git a/src/guichan/widgets/scrollarea.cpp b/src/guichan/widgets/scrollarea.cpp
new file mode 100644
index 000000000..8e935fc47
--- /dev/null
+++ b/src/guichan/widgets/scrollarea.cpp
@@ -0,0 +1,1246 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/scrollarea.hpp"
+
+#include "guichan/exception.hpp"
+#include "guichan/graphics.hpp"
+
+namespace gcn
+{
+ ScrollArea::ScrollArea()
+ {
+ mVScroll = 0;
+ mHScroll = 0;
+ mHPolicy = SHOW_AUTO;
+ mVPolicy = SHOW_AUTO;
+ mScrollbarWidth = 12;
+ mUpButtonPressed = false;
+ mDownButtonPressed = false;
+ mLeftButtonPressed = false;
+ mRightButtonPressed = false;
+ mUpButtonScrollAmount = 10;
+ mDownButtonScrollAmount = 10;
+ mLeftButtonScrollAmount = 10;
+ mRightButtonScrollAmount = 10;
+ mIsVerticalMarkerDragged = false;
+ mIsHorizontalMarkerDragged =false;
+ mOpaque = true;
+
+ addMouseListener(this);
+ }
+
+ ScrollArea::ScrollArea(Widget *content)
+ {
+ mVScroll = 0;
+ mHScroll = 0;
+ mHPolicy = SHOW_AUTO;
+ mVPolicy = SHOW_AUTO;
+ mScrollbarWidth = 12;
+ mUpButtonPressed = false;
+ mDownButtonPressed = false;
+ mLeftButtonPressed = false;
+ mRightButtonPressed = false;
+ mUpButtonScrollAmount = 10;
+ mDownButtonScrollAmount = 10;
+ mLeftButtonScrollAmount = 10;
+ mRightButtonScrollAmount = 10;
+ mIsVerticalMarkerDragged = false;
+ mIsHorizontalMarkerDragged =false;
+ mOpaque = true;
+
+ setContent(content);
+ addMouseListener(this);
+ }
+
+ ScrollArea::ScrollArea(Widget *content,
+ ScrollPolicy hPolicy,
+ ScrollPolicy vPolicy)
+ {
+ mVScroll = 0;
+ mHScroll = 0;
+ mHPolicy = hPolicy;
+ mVPolicy = vPolicy;
+ mScrollbarWidth = 12;
+ mUpButtonPressed = false;
+ mDownButtonPressed = false;
+ mLeftButtonPressed = false;
+ mRightButtonPressed = false;
+ mUpButtonScrollAmount = 10;
+ mDownButtonScrollAmount = 10;
+ mLeftButtonScrollAmount = 10;
+ mRightButtonScrollAmount = 10;
+ mIsVerticalMarkerDragged = false;
+ mIsHorizontalMarkerDragged =false;
+ mOpaque = true;
+
+ setContent(content);
+ addMouseListener(this);
+ }
+
+ ScrollArea::~ScrollArea()
+ {
+ setContent(NULL);
+ }
+
+ void ScrollArea::setContent(Widget* widget)
+ {
+ if (widget != NULL)
+ {
+ clear();
+ add(widget);
+ widget->setPosition(0,0);
+ }
+ else
+ {
+ clear();
+ }
+
+ checkPolicies();
+ }
+
+ Widget* ScrollArea::getContent()
+ {
+ if (mWidgets.size() > 0)
+ {
+ return *mWidgets.begin();
+ }
+
+ return NULL;
+ }
+
+ void ScrollArea::setHorizontalScrollPolicy(ScrollPolicy hPolicy)
+ {
+ mHPolicy = hPolicy;
+ checkPolicies();
+ }
+
+ ScrollArea::ScrollPolicy ScrollArea::getHorizontalScrollPolicy() const
+ {
+ return mHPolicy;
+ }
+
+ void ScrollArea::setVerticalScrollPolicy(ScrollPolicy vPolicy)
+ {
+ mVPolicy = vPolicy;
+ checkPolicies();
+ }
+
+ ScrollArea::ScrollPolicy ScrollArea::getVerticalScrollPolicy() const
+ {
+ return mVPolicy;
+ }
+
+ void ScrollArea::setScrollPolicy(ScrollPolicy hPolicy, ScrollPolicy vPolicy)
+ {
+ mHPolicy = hPolicy;
+ mVPolicy = vPolicy;
+ checkPolicies();
+ }
+
+ void ScrollArea::setVerticalScrollAmount(int vScroll)
+ {
+ int max = getVerticalMaxScroll();
+
+ mVScroll = vScroll;
+
+ if (vScroll > max)
+ {
+ mVScroll = max;
+ }
+
+ if (vScroll < 0)
+ {
+ mVScroll = 0;
+ }
+ }
+
+ int ScrollArea::getVerticalScrollAmount() const
+ {
+ return mVScroll;
+ }
+
+ void ScrollArea::setHorizontalScrollAmount(int hScroll)
+ {
+ int max = getHorizontalMaxScroll();
+
+ mHScroll = hScroll;
+
+ if (hScroll > max)
+ {
+ mHScroll = max;
+ }
+ else if (hScroll < 0)
+ {
+ mHScroll = 0;
+ }
+ }
+
+ int ScrollArea::getHorizontalScrollAmount() const
+ {
+ return mHScroll;
+ }
+
+ void ScrollArea::setScrollAmount(int hScroll, int vScroll)
+ {
+ setHorizontalScrollAmount(hScroll);
+ setVerticalScrollAmount(vScroll);
+ }
+
+ int ScrollArea::getHorizontalMaxScroll()
+ {
+ checkPolicies();
+
+ if (getContent() == NULL)
+ {
+ return 0;
+ }
+
+ int value = getContent()->getWidth() - getChildrenArea().width +
+ 2 * getContent()->getFrameSize();
+
+ if (value < 0)
+ {
+ return 0;
+ }
+
+ return value;
+ }
+
+ int ScrollArea::getVerticalMaxScroll()
+ {
+ checkPolicies();
+
+ if (getContent() == NULL)
+ {
+ return 0;
+ }
+
+ int value;
+
+ value = getContent()->getHeight() - getChildrenArea().height +
+ 2 * getContent()->getFrameSize();
+
+ if (value < 0)
+ {
+ return 0;
+ }
+
+ return value;
+ }
+
+ void ScrollArea::setScrollbarWidth(int width)
+ {
+ if (width > 0)
+ {
+ mScrollbarWidth = width;
+ }
+ else
+ {
+ throw GCN_EXCEPTION("Width should be greater then 0.");
+ }
+ }
+
+ int ScrollArea::getScrollbarWidth() const
+ {
+ return mScrollbarWidth;
+ }
+
+ void ScrollArea::mousePressed(MouseEvent& mouseEvent)
+ {
+ int x = mouseEvent.getX();
+ int y = mouseEvent.getY();
+
+ if (getUpButtonDimension().isPointInRect(x, y))
+ {
+ setVerticalScrollAmount(getVerticalScrollAmount()
+ - mUpButtonScrollAmount);
+ mUpButtonPressed = true;
+ }
+ else if (getDownButtonDimension().isPointInRect(x, y))
+ {
+ setVerticalScrollAmount(getVerticalScrollAmount()
+ + mDownButtonScrollAmount);
+ mDownButtonPressed = true;
+ }
+ else if (getLeftButtonDimension().isPointInRect(x, y))
+ {
+ setHorizontalScrollAmount(getHorizontalScrollAmount()
+ - mLeftButtonScrollAmount);
+ mLeftButtonPressed = true;
+ }
+ else if (getRightButtonDimension().isPointInRect(x, y))
+ {
+ setHorizontalScrollAmount(getHorizontalScrollAmount()
+ + mRightButtonScrollAmount);
+ mRightButtonPressed = true;
+ }
+ else if (getVerticalMarkerDimension().isPointInRect(x, y))
+ {
+ mIsHorizontalMarkerDragged = false;
+ mIsVerticalMarkerDragged = true;
+
+ mVerticalMarkerDragOffset = y - getVerticalMarkerDimension().y;
+ }
+ else if (getVerticalBarDimension().isPointInRect(x,y))
+ {
+ if (y < getVerticalMarkerDimension().y)
+ {
+ setVerticalScrollAmount(getVerticalScrollAmount()
+ - (int)(getChildrenArea().height * 0.95));
+ }
+ else
+ {
+ setVerticalScrollAmount(getVerticalScrollAmount()
+ + (int)(getChildrenArea().height * 0.95));
+ }
+ }
+ else if (getHorizontalMarkerDimension().isPointInRect(x, y))
+ {
+ mIsHorizontalMarkerDragged = true;
+ mIsVerticalMarkerDragged = false;
+
+ mHorizontalMarkerDragOffset = x - getHorizontalMarkerDimension().x;
+ }
+ else if (getHorizontalBarDimension().isPointInRect(x,y))
+ {
+ if (x < getHorizontalMarkerDimension().x)
+ {
+ setHorizontalScrollAmount(getHorizontalScrollAmount()
+ - (int)(getChildrenArea().width * 0.95));
+ }
+ else
+ {
+ setHorizontalScrollAmount(getHorizontalScrollAmount()
+ + (int)(getChildrenArea().width * 0.95));
+ }
+ }
+ }
+
+ void ScrollArea::mouseReleased(MouseEvent& mouseEvent)
+ {
+ mUpButtonPressed = false;
+ mDownButtonPressed = false;
+ mLeftButtonPressed = false;
+ mRightButtonPressed = false;
+ mIsHorizontalMarkerDragged = false;
+ mIsVerticalMarkerDragged = false;
+
+ mouseEvent.consume();
+ }
+
+ void ScrollArea::mouseDragged(MouseEvent& mouseEvent)
+ {
+ if (mIsVerticalMarkerDragged)
+ {
+ int pos = mouseEvent.getY() - getVerticalBarDimension().y - mVerticalMarkerDragOffset;
+ int length = getVerticalMarkerDimension().height;
+
+ Rectangle barDim = getVerticalBarDimension();
+
+ if ((barDim.height - length) > 0)
+ {
+ setVerticalScrollAmount((getVerticalMaxScroll() * pos)
+ / (barDim.height - length));
+ }
+ else
+ {
+ setVerticalScrollAmount(0);
+ }
+ }
+
+ if (mIsHorizontalMarkerDragged)
+ {
+ int pos = mouseEvent.getX() - getHorizontalBarDimension().x - mHorizontalMarkerDragOffset;
+ int length = getHorizontalMarkerDimension().width;
+
+ Rectangle barDim = getHorizontalBarDimension();
+
+ if ((barDim.width - length) > 0)
+ {
+ setHorizontalScrollAmount((getHorizontalMaxScroll() * pos)
+ / (barDim.width - length));
+ }
+ else
+ {
+ setHorizontalScrollAmount(0);
+ }
+ }
+
+ mouseEvent.consume();
+ }
+
+ void ScrollArea::draw(Graphics *graphics)
+ {
+ drawBackground(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(Rectangle(getWidth() - mScrollbarWidth,
+ getHeight() - mScrollbarWidth,
+ mScrollbarWidth,
+ mScrollbarWidth));
+ }
+
+ drawChildren(graphics);
+ }
+
+ void ScrollArea::drawHBar(Graphics* graphics)
+ {
+ Rectangle dim = getHorizontalBarDimension();
+
+ graphics->pushClipArea(dim);
+
+ int alpha = getBaseColor().a;
+ Color trackColor = getBaseColor() - 0x101010;
+ trackColor.a = alpha;
+ Color shadowColor = getBaseColor() - 0x303030;
+ shadowColor.a = alpha;
+
+ graphics->setColor(trackColor);
+ graphics->fillRectangle(Rectangle(0, 0, dim.width, dim.height));
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(0, 0, dim.width, 0);
+
+ graphics->popClipArea();
+ }
+
+ void ScrollArea::drawVBar(Graphics* graphics)
+ {
+ Rectangle dim = getVerticalBarDimension();
+
+ graphics->pushClipArea(dim);
+
+ int alpha = getBaseColor().a;
+ Color trackColor = getBaseColor() - 0x101010;
+ trackColor.a = alpha;
+ Color shadowColor = getBaseColor() - 0x303030;
+ shadowColor.a = alpha;
+
+ graphics->setColor(trackColor);
+ graphics->fillRectangle(Rectangle(0, 0, dim.width, dim.height));
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(0, 0, 0, dim.height);
+
+ graphics->popClipArea();
+ }
+
+ void ScrollArea::drawBackground(Graphics *graphics)
+ {
+ if (isOpaque())
+ {
+ graphics->setColor(getBackgroundColor());
+ graphics->fillRectangle(getChildrenArea());
+ }
+ }
+
+ void ScrollArea::drawUpButton(Graphics* graphics)
+ {
+ Rectangle dim = getUpButtonDimension();
+ graphics->pushClipArea(dim);
+
+ Color highlightColor;
+ Color shadowColor;
+ Color faceColor;
+ int offset;
+ int alpha = getBaseColor().a;
+
+ if (mUpButtonPressed)
+ {
+ faceColor = getBaseColor() - 0x303030;
+ faceColor.a = alpha;
+ highlightColor = faceColor - 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = getBaseColor();
+ shadowColor.a = alpha;
+
+ offset = 1;
+ }
+ else
+ {
+ faceColor = getBaseColor();
+ faceColor.a = alpha;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ offset = 0;
+ }
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(0, 0, dim.width, dim.height));
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, dim.width - 1, 0);
+ graphics->drawLine(0, 1, 0, dim.height - 1);
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(dim.width - 1, 0, dim.width - 1, dim.height - 1);
+ graphics->drawLine(1, dim.height - 1, dim.width - 1, dim.height - 1);
+
+ graphics->setColor(getForegroundColor());
+
+ int i;
+ int w = dim.height / 2;
+ int h = w / 2 + 2;
+ for (i = 0; i < w / 2; ++i)
+ {
+ graphics->drawLine(w - i + offset,
+ i + h + offset,
+ w + i + offset,
+ i + h + offset);
+ }
+
+ graphics->popClipArea();
+ }
+
+ void ScrollArea::drawDownButton(Graphics* graphics)
+ {
+ Rectangle dim = getDownButtonDimension();
+ graphics->pushClipArea(dim);
+
+ Color highlightColor;
+ Color shadowColor;
+ Color faceColor;
+ int offset;
+ int alpha = getBaseColor().a;
+
+ if (mDownButtonPressed)
+ {
+ faceColor = getBaseColor() - 0x303030;
+ faceColor.a = alpha;
+ highlightColor = faceColor - 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = getBaseColor();
+ shadowColor.a = alpha;
+
+ offset = 1;
+ }
+ else
+ {
+ faceColor = getBaseColor();
+ faceColor.a = alpha;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ offset = 0;
+ }
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(0, 0, dim.width, dim.height));
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, dim.width - 1, 0);
+ graphics->drawLine(0, 1, 0, dim.height - 1);
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(dim.width - 1, 0, dim.width - 1, dim.height - 1);
+ graphics->drawLine(1, dim.height - 1, dim.width - 1, dim.height - 1);
+
+ graphics->setColor(getForegroundColor());
+
+ int i;
+ int w = dim.height / 2;
+ int h = w + 1;
+ for (i = 0; i < w / 2; ++i)
+ {
+ graphics->drawLine(w - i + offset,
+ -i + h + offset,
+ w + i + offset,
+ -i + h + offset);
+ }
+
+ graphics->popClipArea();
+ }
+
+ void ScrollArea::drawLeftButton(Graphics* graphics)
+ {
+ Rectangle dim = getLeftButtonDimension();
+ graphics->pushClipArea(dim);
+
+ Color highlightColor;
+ Color shadowColor;
+ Color faceColor;
+ int offset;
+ int alpha = getBaseColor().a;
+
+ if (mLeftButtonPressed)
+ {
+ faceColor = getBaseColor() - 0x303030;
+ faceColor.a = alpha;
+ highlightColor = faceColor - 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = getBaseColor();
+ shadowColor.a = alpha;
+
+ offset = 1;
+ }
+ else
+ {
+ faceColor = getBaseColor();
+ faceColor.a = alpha;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ offset = 0;
+ }
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(0, 0, dim.width, dim.height));
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, dim.width - 1, 0);
+ graphics->drawLine(0, 1, 0, dim.height - 1);
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(dim.width - 1, 0, dim.width - 1, dim.height - 1);
+ graphics->drawLine(1, dim.height - 1, dim.width - 1, dim.height - 1);
+
+ graphics->setColor(getForegroundColor());
+
+ int i;
+ int w = dim.width / 2;
+ int h = w - 2;
+ for (i = 0; i < w / 2; ++i)
+ {
+ graphics->drawLine(i + h + offset,
+ w - i + offset,
+ i + h + offset,
+ w + i + offset);
+ }
+
+ graphics->popClipArea();
+ }
+
+ void ScrollArea::drawRightButton(Graphics* graphics)
+ {
+ Rectangle dim = getRightButtonDimension();
+ graphics->pushClipArea(dim);
+
+ Color highlightColor;
+ Color shadowColor;
+ Color faceColor;
+ int offset;
+ int alpha = getBaseColor().a;
+
+ if (mRightButtonPressed)
+ {
+ faceColor = getBaseColor() - 0x303030;
+ faceColor.a = alpha;
+ highlightColor = faceColor - 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = getBaseColor();
+ shadowColor.a = alpha;
+
+ offset = 1;
+ }
+ else
+ {
+ faceColor = getBaseColor();
+ faceColor.a = alpha;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ offset = 0;
+ }
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(0, 0, dim.width, dim.height));
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, dim.width - 1, 0);
+ graphics->drawLine(0, 1, 0, dim.height - 1);
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(dim.width - 1, 0, dim.width - 1, dim.height - 1);
+ graphics->drawLine(1, dim.height - 1, dim.width - 1, dim.height - 1);
+
+ graphics->setColor(getForegroundColor());
+
+ int i;
+ int w = dim.width / 2;
+ int h = w + 1;
+ for (i = 0; i < w / 2; ++i)
+ {
+ graphics->drawLine(-i + h + offset,
+ w - i + offset,
+ -i + h + offset,
+ w + i + offset);
+ }
+
+ graphics->popClipArea();
+ }
+
+ void ScrollArea::drawVMarker(Graphics* graphics)
+ {
+ Rectangle dim = getVerticalMarkerDimension();
+ graphics->pushClipArea(dim);
+
+ int alpha = getBaseColor().a;
+ Color faceColor = getBaseColor();
+ faceColor.a = alpha;
+ Color highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ Color shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(1, 1, dim.width - 1, dim.height - 1));
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, dim.width - 1, 0);
+ graphics->drawLine(0, 1, 0, dim.height - 1);
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(1, dim.height - 1, dim.width - 1, dim.height - 1);
+ graphics->drawLine(dim.width - 1, 0, dim.width - 1, dim.height - 1);
+
+ graphics->popClipArea();
+ }
+
+ void ScrollArea::drawHMarker(Graphics* graphics)
+ {
+ Rectangle dim = getHorizontalMarkerDimension();
+ graphics->pushClipArea(dim);
+
+ int alpha = getBaseColor().a;
+ Color faceColor = getBaseColor();
+ faceColor.a = alpha;
+ Color highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ Color shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ graphics->setColor(faceColor);
+ graphics->fillRectangle(Rectangle(1, 1, dim.width - 1, dim.height - 1));
+
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, dim.width - 1, 0);
+ graphics->drawLine(0, 1, 0, dim.height - 1);
+
+ graphics->setColor(shadowColor);
+ graphics->drawLine(1, dim.height - 1, dim.width - 1, dim.height - 1);
+ graphics->drawLine(dim.width - 1, 0, dim.width - 1, dim.height - 1);
+
+ graphics->popClipArea();
+ }
+
+ void ScrollArea::logic()
+ {
+ checkPolicies();
+
+ setVerticalScrollAmount(getVerticalScrollAmount());
+ setHorizontalScrollAmount(getHorizontalScrollAmount());
+
+ if (getContent() != NULL)
+ {
+ getContent()->setPosition(-mHScroll + getContent()->getFrameSize(),
+ -mVScroll + getContent()->getFrameSize());
+ getContent()->logic();
+ }
+ }
+
+ void ScrollArea::checkPolicies()
+ {
+ int w = getWidth();
+ int h = getHeight();
+
+ mHBarVisible = false;
+ mVBarVisible = false;
+
+
+ if (!getContent())
+ {
+ mHBarVisible = (mHPolicy == SHOW_ALWAYS);
+ mVBarVisible = (mVPolicy == SHOW_ALWAYS);
+ return;
+ }
+
+ if (mHPolicy == SHOW_AUTO &&
+ mVPolicy == SHOW_AUTO)
+ {
+ if (getContent()->getWidth() <= w
+ && getContent()->getHeight() <= h)
+ {
+ mHBarVisible = false;
+ mVBarVisible = false;
+ }
+
+ if (getContent()->getWidth() > w)
+ {
+ mHBarVisible = true;
+ }
+
+ if ((getContent()->getHeight() > h)
+ || (mHBarVisible && getContent()->getHeight() > h - mScrollbarWidth))
+ {
+ mVBarVisible = true;
+ }
+
+ if (mVBarVisible && getContent()->getWidth() > w - mScrollbarWidth)
+ {
+ mHBarVisible = true;
+ }
+
+ return;
+ }
+
+ switch (mHPolicy)
+ {
+ case SHOW_NEVER:
+ mHBarVisible = false;
+ break;
+
+ case SHOW_ALWAYS:
+ mHBarVisible = true;
+ break;
+
+ case SHOW_AUTO:
+ if (mVPolicy == SHOW_NEVER)
+ {
+ mHBarVisible = getContent()->getWidth() > w;
+ }
+ else // (mVPolicy == SHOW_ALWAYS)
+ {
+ mHBarVisible = getContent()->getWidth() > w - mScrollbarWidth;
+ }
+ break;
+
+ default:
+ throw GCN_EXCEPTION("Horizontal scroll policy invalid.");
+ }
+
+ switch (mVPolicy)
+ {
+ case SHOW_NEVER:
+ mVBarVisible = false;
+ break;
+
+ case SHOW_ALWAYS:
+ mVBarVisible = true;
+ break;
+
+ case SHOW_AUTO:
+ if (mHPolicy == SHOW_NEVER)
+ {
+ mVBarVisible = getContent()->getHeight() > h;
+ }
+ else // (mHPolicy == SHOW_ALWAYS)
+ {
+ mVBarVisible = getContent()->getHeight() > h - mScrollbarWidth;
+ }
+ break;
+ default:
+ throw GCN_EXCEPTION("Vertical scroll policy invalid.");
+ }
+ }
+
+ Rectangle ScrollArea::getUpButtonDimension()
+ {
+ if (!mVBarVisible)
+ {
+ return Rectangle(0, 0, 0, 0);
+ }
+
+ return Rectangle(getWidth() - mScrollbarWidth,
+ 0,
+ mScrollbarWidth,
+ mScrollbarWidth);
+ }
+
+ Rectangle ScrollArea::getDownButtonDimension()
+ {
+ if (!mVBarVisible)
+ {
+ return Rectangle(0, 0, 0, 0);
+ }
+
+ if (mVBarVisible && mHBarVisible)
+ {
+ return Rectangle(getWidth() - mScrollbarWidth,
+ getHeight() - mScrollbarWidth*2,
+ mScrollbarWidth,
+ mScrollbarWidth);
+ }
+
+ return Rectangle(getWidth() - mScrollbarWidth,
+ getHeight() - mScrollbarWidth,
+ mScrollbarWidth,
+ mScrollbarWidth);
+ }
+
+ Rectangle ScrollArea::getLeftButtonDimension()
+ {
+ if (!mHBarVisible)
+ {
+ return Rectangle(0, 0, 0, 0);
+ }
+
+ return Rectangle(0,
+ getHeight() - mScrollbarWidth,
+ mScrollbarWidth,
+ mScrollbarWidth);
+ }
+
+ Rectangle ScrollArea::getRightButtonDimension()
+ {
+ if (!mHBarVisible)
+ {
+ return Rectangle(0, 0, 0, 0);
+ }
+
+ if (mVBarVisible && mHBarVisible)
+ {
+ return Rectangle(getWidth() - mScrollbarWidth*2,
+ getHeight() - mScrollbarWidth,
+ mScrollbarWidth,
+ mScrollbarWidth);
+ }
+
+ return Rectangle(getWidth() - mScrollbarWidth,
+ getHeight() - mScrollbarWidth,
+ mScrollbarWidth,
+ mScrollbarWidth);
+ }
+
+ Rectangle ScrollArea::getChildrenArea()
+ {
+ Rectangle area = Rectangle(0,
+ 0,
+ mVBarVisible ? getWidth() - mScrollbarWidth : getWidth(),
+ mHBarVisible ? getHeight() - mScrollbarWidth : getHeight());
+
+ if (area.width < 0 || area.height < 0)
+ return Rectangle();
+
+ return area;
+ }
+
+ Rectangle ScrollArea::getVerticalBarDimension()
+ {
+ if (!mVBarVisible)
+ {
+ return Rectangle(0, 0, 0, 0);
+ }
+
+ if (mHBarVisible)
+ {
+ return Rectangle(getWidth() - mScrollbarWidth,
+ getUpButtonDimension().height,
+ mScrollbarWidth,
+ getHeight()
+ - getUpButtonDimension().height
+ - getDownButtonDimension().height
+ - mScrollbarWidth);
+ }
+
+ return Rectangle(getWidth() - mScrollbarWidth,
+ getUpButtonDimension().height,
+ mScrollbarWidth,
+ getHeight()
+ - getUpButtonDimension().height
+ - getDownButtonDimension().height);
+ }
+
+ Rectangle ScrollArea::getHorizontalBarDimension()
+ {
+ if (!mHBarVisible)
+ {
+ return Rectangle(0, 0, 0, 0);
+ }
+
+ if (mVBarVisible)
+ {
+ return Rectangle(getLeftButtonDimension().width,
+ getHeight() - mScrollbarWidth,
+ getWidth()
+ - getLeftButtonDimension().width
+ - getRightButtonDimension().width
+ - mScrollbarWidth,
+ mScrollbarWidth);
+ }
+
+ return Rectangle(getLeftButtonDimension().width,
+ getHeight() - mScrollbarWidth,
+ getWidth()
+ - getLeftButtonDimension().width
+ - getRightButtonDimension().width,
+ mScrollbarWidth);
+ }
+
+ Rectangle ScrollArea::getVerticalMarkerDimension()
+ {
+ if (!mVBarVisible)
+ {
+ return Rectangle(0, 0, 0, 0);
+ }
+
+ int length, pos;
+ Rectangle barDim = getVerticalBarDimension();
+
+ if (getContent() && getContent()->getHeight() != 0)
+ {
+ length = (barDim.height * getChildrenArea().height)
+ / getContent()->getHeight();
+ }
+ else
+ {
+ length = barDim.height;
+ }
+
+ if (length < mScrollbarWidth)
+ {
+ length = mScrollbarWidth;
+ }
+
+ if (length > barDim.height)
+ {
+ length = barDim.height;
+ }
+
+ if (getVerticalMaxScroll() != 0)
+ {
+ pos = ((barDim.height - length) * getVerticalScrollAmount())
+ / getVerticalMaxScroll();
+ }
+ else
+ {
+ pos = 0;
+ }
+
+ return Rectangle(barDim.x, barDim.y + pos, mScrollbarWidth, length);
+ }
+
+ Rectangle ScrollArea::getHorizontalMarkerDimension()
+ {
+ if (!mHBarVisible)
+ {
+ return Rectangle(0, 0, 0, 0);
+ }
+
+ int length, pos;
+ Rectangle barDim = getHorizontalBarDimension();
+
+ if (getContent() && getContent()->getWidth() != 0)
+ {
+ length = (barDim.width * getChildrenArea().width)
+ / getContent()->getWidth();
+ }
+ else
+ {
+ length = barDim.width;
+ }
+
+ if (length < mScrollbarWidth)
+ {
+ length = mScrollbarWidth;
+ }
+
+ if (length > barDim.width)
+ {
+ length = barDim.width;
+ }
+
+ if (getHorizontalMaxScroll() != 0)
+ {
+ pos = ((barDim.width - length) * getHorizontalScrollAmount())
+ / getHorizontalMaxScroll();
+ }
+ else
+ {
+ pos = 0;
+ }
+
+ return Rectangle(barDim.x + pos, barDim.y, length, mScrollbarWidth);
+ }
+
+ void ScrollArea::showWidgetPart(Widget* widget, Rectangle area)
+ {
+ if (widget != getContent())
+ {
+ throw GCN_EXCEPTION("Widget not content widget");
+ }
+
+ BasicContainer::showWidgetPart(widget, area);
+
+ setHorizontalScrollAmount(getContent()->getFrameSize() - getContent()->getX());
+ setVerticalScrollAmount(getContent()->getFrameSize() - getContent()->getY());
+ }
+
+ Widget *ScrollArea::getWidgetAt(int x, int y)
+ {
+ if (getChildrenArea().isPointInRect(x, y))
+ {
+ return getContent();
+ }
+
+ return NULL;
+ }
+
+ void ScrollArea::mouseWheelMovedUp(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.isConsumed())
+ {
+ return;
+ }
+
+ setVerticalScrollAmount(getVerticalScrollAmount() - getChildrenArea().height / 8);
+
+ mouseEvent.consume();
+ }
+
+ void ScrollArea::mouseWheelMovedDown(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.isConsumed())
+ {
+ return;
+ }
+
+ setVerticalScrollAmount(getVerticalScrollAmount() + getChildrenArea().height / 8);
+
+ mouseEvent.consume();
+ }
+
+ void ScrollArea::setWidth(int width)
+ {
+ Widget::setWidth(width);
+ checkPolicies();
+ }
+
+ void ScrollArea::setHeight(int height)
+ {
+ Widget::setHeight(height);
+ checkPolicies();
+ }
+
+ void ScrollArea::setDimension(const Rectangle& dimension)
+ {
+ Widget::setDimension(dimension);
+ checkPolicies();
+ }
+
+ void ScrollArea::setLeftButtonScrollAmount(int amount)
+ {
+ mLeftButtonScrollAmount = amount;
+ }
+
+ void ScrollArea::setRightButtonScrollAmount(int amount)
+ {
+ mRightButtonScrollAmount = amount;
+ }
+
+ void ScrollArea::setUpButtonScrollAmount(int amount)
+ {
+ mUpButtonScrollAmount = amount;
+ }
+
+ void ScrollArea::setDownButtonScrollAmount(int amount)
+ {
+ mDownButtonScrollAmount = amount;
+ }
+
+ int ScrollArea::getLeftButtonScrollAmount() const
+ {
+ return mLeftButtonScrollAmount;
+ }
+
+ int ScrollArea::getRightButtonScrollAmount() const
+ {
+ return mRightButtonScrollAmount;
+ }
+
+ int ScrollArea::getUpButtonScrollAmount() const
+ {
+ return mUpButtonScrollAmount;
+ }
+
+ int ScrollArea::getDownButtonScrollAmount() const
+ {
+ return mDownButtonScrollAmount;
+ }
+
+ void ScrollArea::setOpaque(bool opaque)
+ {
+ mOpaque = opaque;
+ }
+
+
+ bool ScrollArea::isOpaque() const
+ {
+ return mOpaque;
+ }
+}
+
+/*
+ * Wow! This is a looooong source file.
+ */
diff --git a/src/guichan/widgets/scrollarea.hpp b/src/guichan/widgets/scrollarea.hpp
new file mode 100644
index 000000000..0b2ccad92
--- /dev/null
+++ b/src/guichan/widgets/scrollarea.hpp
@@ -0,0 +1,590 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_SCROLLAREA_HPP
+#define GCN_SCROLLAREA_HPP
+
+#include <string>
+
+#include "guichan/basiccontainer.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+
+namespace gcn
+{
+ /**
+ * Implementation if a scrollable area used to view widgets larger than the scroll area.
+ * A scroll area can be customized to always show scroll bars or to show them only when
+ * necessary.
+ */
+ class GCN_CORE_DECLSPEC ScrollArea:
+ public BasicContainer,
+ public MouseListener
+ {
+ public:
+
+ /**
+ * Scrollpolicies for the horizontal and vertical scrollbar.
+ * The policies are:
+ *
+ * SHOW_ALWAYS - Always show the scrollbars no matter what.
+ * SHOW_NEVER - Never show the scrollbars no matter waht.
+ * SHOW_AUTO - Show the scrollbars only when needed. That is if the
+ * content grows larger then the ScrollArea.
+ */
+ enum ScrollPolicy
+ {
+ SHOW_ALWAYS = 0,
+ SHOW_NEVER,
+ SHOW_AUTO
+ };
+
+ /**
+ * Constructor.
+ */
+ ScrollArea();
+
+ /**
+ * Constructor.
+ *
+ * @param content The content of the scroll area.
+ */
+ ScrollArea(Widget *content);
+
+ /**
+ * Constructor.
+ *
+ * @param content The content of the scroll area.
+ * @param hPolicy The policy for the horizontal scrollbar. See enum with
+ * policies.
+ * @param vPolicy The policy for the vertical scrollbar. See enum with
+ * policies.
+ */
+ ScrollArea(Widget *content,
+ ScrollPolicy hPolicy,
+ ScrollPolicy vPolicy);
+
+ /**
+ * Destructor.
+ */
+ virtual ~ScrollArea();
+
+ /**
+ * Sets the content.
+ *
+ * @param widget The content of the scroll area.
+ */
+ void setContent(Widget* widget);
+
+ /**
+ * Gets the content.
+ *
+ * @return The content of the scroll area.
+ */
+ Widget* getContent();
+
+ /**
+ * Sets the horizontal scrollbar policy. See enum with policies.
+ *
+ * @param hPolicy The policy for the horizontal scrollbar.
+ * @see getHorizontalScrollPolicy
+ */
+ void setHorizontalScrollPolicy(ScrollPolicy hPolicy);
+
+ /**
+ * Gets the horizontal scrollbar policy. See enum with policies.
+ *
+ * @return The policy for the horizontal scrollbar policy.
+ * @see setHorizontalScrollPolicy, setScrollPolicy
+ */
+ ScrollPolicy getHorizontalScrollPolicy() const;
+
+ /**
+ * Sets the vertical scrollbar policy. See enum with policies.
+ *
+ * @param vPolicy The policy for the vertical scrollbar.
+ * @see getVerticalScrollPolicy
+ */
+ void setVerticalScrollPolicy(ScrollPolicy vPolicy);
+
+ /**
+ * Gets the vertical scrollbar policy. See enum with policies.
+ *
+ * @return The policy for the vertical scrollbar.
+ * @see setVerticalScrollPolicy, setScrollPolicy
+ */
+ ScrollPolicy getVerticalScrollPolicy() const;
+
+ /**
+ * Sets the horizontal and vertical scrollbar policy.
+ *
+ * @param hPolicy The policy for the horizontal scrollbar.
+ * @param vPolicy The policy for the vertical scrollbar.
+ * @see getVerticalScrollPolicy, getHorizontalScrollPolicy
+ */
+ void setScrollPolicy(ScrollPolicy hPolicy, ScrollPolicy vPolicy);
+
+ /**
+ * Sets the amount to scroll vertically.
+ *
+ * @param vScroll The amount to scroll.
+ * @see getVerticalScrollAmount
+ */
+ void setVerticalScrollAmount(int vScroll);
+
+ /**
+ * Gets the amount that is scrolled vertically.
+ *
+ * @return The scroll amount on vertical scroll.
+ * @see setVerticalScrollAmount, setScrollAmount
+ */
+ int getVerticalScrollAmount() const;
+
+ /**
+ * Sets the amount to scroll horizontally.
+ *
+ * @param hScroll The amount to scroll.
+ * @see getHorizontalScrollAmount
+ */
+ void setHorizontalScrollAmount(int hScroll);
+
+ /**
+ * Gets the amount that is scrolled horizontally.
+ *
+ * @return The scroll amount on horizontal scroll.
+ * @see setHorizontalScrollAmount, setScrollAmount
+ */
+ int getHorizontalScrollAmount() const;
+
+ /**
+ * Sets the amount to scroll horizontally and vertically.
+ *
+ * @param hScroll The amount to scroll on horizontal scroll.
+ * @param vScroll The amount to scroll on vertical scroll.
+ * @see getHorizontalScrollAmount, getVerticalScrollAmount
+ */
+ void setScrollAmount(int hScroll, int vScroll);
+
+ /**
+ * Gets the maximum amount of horizontal scroll.
+ *
+ * @return The horizontal max scroll.
+ */
+ int getHorizontalMaxScroll();
+
+ /**
+ * Gets the maximum amount of vertical scroll.
+ *
+ * @return The vertical max scroll.
+ */
+ int getVerticalMaxScroll();
+
+ /**
+ * Sets the width of the scroll bars.
+ *
+ * @param width The width of the scroll bars.
+ * @see getScrollbarWidth
+ */
+ void setScrollbarWidth(int width);
+
+ /**
+ * Gets the width of the scroll bars.
+ *
+ * @return the width of the ScrollBar.
+ * @see setScrollbarWidth
+ */
+ int getScrollbarWidth() const;
+
+ /**
+ * Sets the amount to scroll in pixels when the left scroll button is
+ * pushed.
+ *
+ * @param amount The amount to scroll in pixels.
+ * @see getLeftButtonScrollAmount
+ */
+ void setLeftButtonScrollAmount(int amount);
+
+ /**
+ * Sets the amount to scroll in pixels when the right scroll button is
+ * pushed.
+ *
+ * @param amount The amount to scroll in pixels.
+ * @see getRightButtonScrollAmount
+ */
+ void setRightButtonScrollAmount(int amount);
+
+ /**
+ * Sets the amount to scroll in pixels when the up scroll button is
+ * pushed.
+ *
+ * @param amount The amount to scroll in pixels.
+ * @see getUpButtonScrollAmount
+ */
+ void setUpButtonScrollAmount(int amount);
+
+ /**
+ * Sets the amount to scroll in pixels when the down scroll button is
+ * pushed.
+ *
+ * @param amount The amount to scroll in pixels.
+ * @see getDownButtonScrollAmount
+ */
+ void setDownButtonScrollAmount(int amount);
+
+ /**
+ * Gets the amount to scroll in pixels when the left scroll button is
+ * pushed.
+ *
+ * @return The amount to scroll in pixels.
+ * @see setLeftButtonScrollAmount
+ */
+ int getLeftButtonScrollAmount() const;
+
+ /**
+ * Gets the amount to scroll in pixels when the right scroll button is
+ * pushed.
+ *
+ * @return The amount to scroll in pixels.
+ * @see setRightButtonScrollAmount
+ */
+ int getRightButtonScrollAmount() const;
+
+ /**
+ * Gets the amount to scroll in pixels when the up scroll button is
+ * pushed.
+ *
+ * @return The amount to scroll in pixels.
+ * @see setUpButtonScrollAmount
+ */
+ int getUpButtonScrollAmount() const;
+
+ /**
+ * Gets the amount to scroll in pixels when the down scroll button is
+ * pushed.
+ *
+ * @return The amount to scroll in pixels.
+ * @see setDownButtonScrollAmount
+ */
+ int getDownButtonScrollAmount() const;
+
+ /**
+ * Sets the scroll area to be opaque, that is sets the scoll area
+ * to display its background.
+ *
+ * @param opaque True if the scoll area should be opaque, false otherwise.
+ */
+ void setOpaque(bool opaque);
+
+ /**
+ * Checks if the scroll area is opaque, that is if the scroll area
+ * displays its background.
+ *
+ * @return True if the scroll area is opaque, false otherwise.
+ */
+ bool isOpaque() const;
+
+
+ // Inherited from BasicContainer
+
+ virtual void showWidgetPart(Widget* widget, Rectangle area);
+
+ virtual Rectangle getChildrenArea();
+
+ virtual Widget *getWidgetAt(int x, int y);
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics *graphics);
+
+ virtual void logic();
+
+ void setWidth(int width);
+
+ void setHeight(int height);
+
+ void setDimension(const Rectangle& dimension);
+
+
+ // Inherited from MouseListener
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+ virtual void mouseReleased(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedUp(MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedDown(MouseEvent& mouseEvent);
+
+ protected:
+ /**
+ * Draws the background of the scroll area, that is
+ * the area behind the content.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawBackground(Graphics *graphics);
+
+ /**
+ * Draws the up button.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawUpButton(Graphics *graphics);
+
+ /**
+ * Draws the down button.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawDownButton(Graphics *graphics);
+
+ /**
+ * Draws the left button.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawLeftButton(Graphics *graphics);
+
+ /**
+ * Draws the right button.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawRightButton(Graphics *graphics);
+
+ /**
+ * Draws the vertical scroll bar.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawVBar(Graphics* graphics);
+
+ /**
+ * Draws the horizontal scroll bar.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawHBar(Graphics* graphics);
+
+ /**
+ * Draws the vertical marker.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawVMarker(Graphics* graphics);
+
+ /**
+ * Draws the horizontal marker.
+ *
+ * @param graphics a Graphics object to draw with.
+ */
+ virtual void drawHMarker(Graphics* graphics);
+
+ /**
+ * Checks the policies for the scroll bars.
+ */
+ virtual void checkPolicies();
+
+ /**
+ * Gets the up button dimension.
+ *
+ * @return the dimension of the up button.
+ */
+ Rectangle getUpButtonDimension();
+
+ /**
+ * Gets the down button dimension.
+ *
+ * @return the dimension of the down button.
+ */
+ Rectangle getDownButtonDimension();
+
+ /**
+ * Gets the left button dimension.
+ *
+ * @return the dimension of the left button.
+ */
+ Rectangle getLeftButtonDimension();
+
+ /**
+ * Gets the right button dimension.
+ *
+ * @return the dimension of the right button.
+ */
+ Rectangle getRightButtonDimension();
+
+ /**
+ * Gets the vertical scrollbar dimension.
+ *
+ * @return the dimension of the vertical scrollbar.
+ */
+ Rectangle getVerticalBarDimension();
+
+ /**
+ * Gets the horizontal scrollbar dimension.
+ *
+ * @return the dimension of the horizontal scrollbar.
+ */
+ Rectangle getHorizontalBarDimension();
+
+ /**
+ * Gets the vertical marker dimension.
+ *
+ * @return the dimension of the vertical marker.
+ */
+ Rectangle getVerticalMarkerDimension();
+
+ /**
+ * Gets the horizontal marker dimension.
+ *
+ * @return the dimension of the horizontal marker.
+ */
+ Rectangle getHorizontalMarkerDimension();
+
+ /**
+ * Holds the vertical scroll amount.
+ */
+ int mVScroll;
+
+ /**
+ * Holds the horizontal scroll amount.
+ */
+ int mHScroll;
+
+ /**
+ * Holds the width of the scroll bars.
+ */
+ int mScrollbarWidth;
+
+ /**
+ * Holds the horizontal scroll bar policy.
+ */
+ ScrollPolicy mHPolicy;
+
+ /**
+ * Holds the vertical scroll bar policy.
+ */
+ ScrollPolicy mVPolicy;
+
+ /**
+ * True if the vertical scroll bar is visible, false otherwise.
+ */
+ bool mVBarVisible;
+
+ /**
+ * True if the horizontal scroll bar is visible, false otherwise.
+ */
+ bool mHBarVisible;
+
+ /**
+ * True if the up button is pressed, false otherwise.
+ */
+ bool mUpButtonPressed;
+
+ /**
+ * True if the down button is pressed, false otherwise.
+ */
+ bool mDownButtonPressed;
+
+ /**
+ * True if the left button is pressed, false otherwise.
+ */
+ bool mLeftButtonPressed;
+
+ /**
+ * True if the right button is pressed, false otherwise.
+ */
+ bool mRightButtonPressed;
+
+ /**
+ * Holds the up button scroll amount.
+ */
+ int mUpButtonScrollAmount;
+
+ /**
+ * Holds the down button scroll amount.
+ */
+ int mDownButtonScrollAmount;
+
+ /**
+ * Holds the left button scroll amount.
+ */
+ int mLeftButtonScrollAmount;
+
+ /**
+ * Holds the right button scroll amount.
+ */
+ int mRightButtonScrollAmount;
+
+ /**
+ * True if the vertical marked is dragged.
+ */
+ bool mIsVerticalMarkerDragged;
+
+ /**
+ * True if the horizontal marked is dragged.
+ */
+ bool mIsHorizontalMarkerDragged;
+
+ /**
+ * Holds the horizontal markers drag offset.
+ */
+ int mHorizontalMarkerDragOffset;
+
+ /**
+ * Holds the vertical markers drag offset.
+ */
+ int mVerticalMarkerDragOffset;
+
+ /**
+ * True if the scroll area should be opaque (that is
+ * display its background), false otherwise.
+ */
+ bool mOpaque;
+ };
+}
+
+#endif // end GCN_SCROLLAREA_HPP
diff --git a/src/guichan/widgets/slider.cpp b/src/guichan/widgets/slider.cpp
new file mode 100644
index 000000000..a32209124
--- /dev/null
+++ b/src/guichan/widgets/slider.cpp
@@ -0,0 +1,369 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/slider.hpp"
+
+#include "guichan/graphics.hpp"
+#include "guichan/key.hpp"
+#include "guichan/mouseinput.hpp"
+
+namespace gcn
+{
+ Slider::Slider(double scaleEnd)
+ {
+ mDragged = false;
+
+ mScaleStart = 0;
+ mScaleEnd = scaleEnd;
+
+ setFocusable(true);
+ setFrameSize(1);
+ setOrientation(HORIZONTAL);
+ setValue(0);
+ setStepLength(scaleEnd / 10);
+ setMarkerLength(10);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ }
+
+ Slider::Slider(double scaleStart, double scaleEnd)
+ {
+ mDragged = false;
+
+ mScaleStart = scaleStart;
+ mScaleEnd = scaleEnd;
+
+ setFocusable(true);
+ setFrameSize(1);
+ setOrientation(HORIZONTAL);
+ setValue(scaleStart);
+ setStepLength((scaleEnd - scaleStart)/ 10);
+ setMarkerLength(10);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ }
+
+ void Slider::setScale(double scaleStart, double scaleEnd)
+ {
+ mScaleStart = scaleStart;
+ mScaleEnd = scaleEnd;
+ }
+
+ double Slider::getScaleStart() const
+ {
+ return mScaleStart;
+ }
+
+ void Slider::setScaleStart(double scaleStart)
+ {
+ mScaleStart = scaleStart;
+ }
+
+ double Slider::getScaleEnd() const
+ {
+ return mScaleEnd;
+ }
+
+ void Slider::setScaleEnd(double scaleEnd)
+ {
+ mScaleEnd = scaleEnd;
+ }
+
+ void Slider::draw(gcn::Graphics* graphics)
+ {
+ Color shadowColor = getBaseColor() - 0x101010;
+ int alpha = getBaseColor().a;
+ shadowColor.a = alpha;
+
+ graphics->setColor(shadowColor);
+ graphics->fillRectangle(gcn::Rectangle(0,0,getWidth(),getHeight()));
+
+ drawMarker(graphics);
+ }
+
+ void Slider::drawMarker(gcn::Graphics* graphics)
+ {
+ gcn::Color faceColor = getBaseColor();
+ Color highlightColor, shadowColor;
+ int alpha = getBaseColor().a;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ graphics->setColor(faceColor);
+
+ if (getOrientation() == HORIZONTAL)
+ {
+ int v = getMarkerPosition();
+ graphics->fillRectangle(gcn::Rectangle(v + 1, 1, getMarkerLength() - 2, getHeight() - 2));
+ graphics->setColor(highlightColor);
+ graphics->drawLine(v, 0, v + getMarkerLength() - 1,0);
+ graphics->drawLine(v, 0, v, getHeight() - 1);
+ graphics->setColor(shadowColor);
+ graphics->drawLine(v + getMarkerLength() - 1, 1, v + getMarkerLength() - 1, getHeight() - 1);
+ graphics->drawLine(v + 1, getHeight() - 1, v + getMarkerLength() - 1, getHeight() - 1);
+
+ if (isFocused())
+ {
+ graphics->setColor(getForegroundColor());
+ graphics->drawRectangle(Rectangle(v + 2, 2, getMarkerLength() - 4, getHeight() - 4));
+ }
+ }
+ else
+ {
+ int v = (getHeight() - getMarkerLength()) - getMarkerPosition();
+ graphics->fillRectangle(gcn::Rectangle(1, v + 1, getWidth() - 2, getMarkerLength() - 2));
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, v, 0, v + getMarkerLength() - 1);
+ graphics->drawLine(0, v, getWidth() - 1, v);
+ graphics->setColor(shadowColor);
+ graphics->drawLine(1, v + getMarkerLength() - 1, getWidth() - 1, v + getMarkerLength() - 1);
+ graphics->drawLine(getWidth() - 1, v + 1, getWidth() - 1, v + getMarkerLength() - 1);
+
+ if (isFocused())
+ {
+ graphics->setColor(getForegroundColor());
+ graphics->drawRectangle(Rectangle(2, v + 2, getWidth() - 4, getMarkerLength() - 4));
+ }
+ }
+ }
+
+ void Slider::mousePressed(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getButton() == gcn::MouseEvent::LEFT
+ && mouseEvent.getX() >= 0
+ && mouseEvent.getX() <= getWidth()
+ && mouseEvent.getY() >= 0
+ && mouseEvent.getY() <= getHeight())
+ {
+ if (getOrientation() == HORIZONTAL)
+ {
+ setValue(markerPositionToValue(mouseEvent.getX() - getMarkerLength() / 2));
+ }
+ else
+ {
+ setValue(markerPositionToValue(getHeight() - mouseEvent.getY() - getMarkerLength() / 2));
+ }
+
+ distributeActionEvent();
+ }
+ }
+
+ void Slider::mouseDragged(MouseEvent& mouseEvent)
+ {
+ if (getOrientation() == HORIZONTAL)
+ {
+ setValue(markerPositionToValue(mouseEvent.getX() - getMarkerLength() / 2));
+ }
+ else
+ {
+ setValue(markerPositionToValue(getHeight() - mouseEvent.getY() - getMarkerLength() / 2));
+ }
+
+ distributeActionEvent();
+
+ mouseEvent.consume();
+ }
+
+ void Slider::setValue(double value)
+ {
+ if (value > getScaleEnd())
+ {
+ mValue = getScaleEnd();
+ return;
+ }
+
+ if (value < getScaleStart())
+ {
+ mValue = getScaleStart();
+ return;
+ }
+
+ mValue = value;
+ }
+
+ double Slider::getValue() const
+ {
+ return mValue;
+ }
+
+ int Slider::getMarkerLength() const
+ {
+ return mMarkerLength;
+ }
+
+ void Slider::setMarkerLength(int length)
+ {
+ mMarkerLength = length;
+ }
+
+ void Slider::keyPressed(KeyEvent& keyEvent)
+ {
+ Key key = keyEvent.getKey();
+
+ if (getOrientation() == HORIZONTAL)
+ {
+ if (key.getValue() == Key::RIGHT)
+ {
+ setValue(getValue() + getStepLength());
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::LEFT)
+ {
+ setValue(getValue() - getStepLength());
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ }
+ else
+ {
+ if (key.getValue() == Key::UP)
+ {
+ setValue(getValue() + getStepLength());
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ else if (key.getValue() == Key::DOWN)
+ {
+ setValue(getValue() - getStepLength());
+ distributeActionEvent();
+ keyEvent.consume();
+ }
+ }
+ }
+
+ void Slider::setOrientation(Slider::Orientation orientation)
+ {
+ mOrientation = orientation;
+ }
+
+ Slider::Orientation Slider::getOrientation() const
+ {
+ return mOrientation;
+ }
+
+ double Slider::markerPositionToValue(int v) const
+ {
+ int w;
+ if (getOrientation() == HORIZONTAL)
+ {
+ w = getWidth();
+ }
+ else
+ {
+ w = getHeight();
+ }
+
+ double pos = v / ((double)w - getMarkerLength());
+ return (1.0 - pos) * getScaleStart() + pos * getScaleEnd();
+
+ }
+
+ int Slider::valueToMarkerPosition(double value) const
+ {
+ int v;
+ if (getOrientation() == HORIZONTAL)
+ {
+ v = getWidth();
+ }
+ else
+ {
+ v = getHeight();
+ }
+
+ int w = (int)((v - getMarkerLength())
+ * (value - getScaleStart())
+ / (getScaleEnd() - getScaleStart()));
+
+ if (w < 0)
+ {
+ return 0;
+ }
+
+ if (w > v - getMarkerLength())
+ {
+ return v - getMarkerLength();
+ }
+
+ return w;
+ }
+
+ void Slider::setStepLength(double length)
+ {
+ mStepLength = length;
+ }
+
+ double Slider::getStepLength() const
+ {
+ return mStepLength;
+ }
+
+ int Slider::getMarkerPosition() const
+ {
+ return valueToMarkerPosition(getValue());
+ }
+
+ void Slider::mouseWheelMovedUp(MouseEvent& mouseEvent)
+ {
+ setValue(getValue() + getStepLength());
+ distributeActionEvent();
+
+ mouseEvent.consume();
+ }
+
+ void Slider::mouseWheelMovedDown(MouseEvent& mouseEvent)
+ {
+ setValue(getValue() - getStepLength());
+ distributeActionEvent();
+
+ mouseEvent.consume();
+ }
+}
diff --git a/src/guichan/widgets/slider.hpp b/src/guichan/widgets/slider.hpp
new file mode 100644
index 000000000..eddf02c50
--- /dev/null
+++ b/src/guichan/widgets/slider.hpp
@@ -0,0 +1,300 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_SLIDER_HPP
+#define GCN_SLIDER_HPP
+
+#include "guichan/keylistener.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+namespace gcn
+{
+ /**
+ * An implementation of a slider where a user can select different values by
+ * sliding between a start value and an end value of a scale.
+ *
+ * If the selected value is changed an action event will be sent to all
+ * action listeners of the slider.
+ */
+ class GCN_CORE_DECLSPEC Slider :
+ public Widget,
+ public MouseListener,
+ public KeyListener
+ {
+ public:
+
+ /**
+ * Draw orientations for the slider. A slider can be drawn vertically or
+ * horizontally.
+ */
+ enum Orientation
+ {
+ HORIZONTAL = 0,
+ VERTICAL
+ };
+
+ /**
+ * Constructor. The default start value of the slider scale is zero.
+ *
+ * @param scaleEnd The end value of the slider scale.
+ */
+ Slider(double scaleEnd = 1.0);
+
+ /**
+ * Constructor.
+ *
+ * @param scaleStart The start value of the slider scale.
+ * @param scaleEnd The end value of the slider scale.
+ */
+ Slider(double scaleStart, double scaleEnd);
+
+ /**
+ * Destructor.
+ */
+ virtual ~Slider() { }
+
+ /**
+ * Sets the scale of the slider.
+ *
+ * @param scaleStart The start value of the scale.
+ * @param scaleEnd tThe end of value the scale.
+ * @see getScaleStart, getScaleEnd
+ */
+ void setScale(double scaleStart, double scaleEnd);
+
+ /**
+ * Gets the start value of the scale.
+ *
+ * @return The start value of the scale.
+ * @see setScaleStart, setScale
+ */
+ double getScaleStart() const;
+
+ /**
+ * Sets the start value of the scale.
+ *
+ * @param scaleStart The start value of the scale.
+ * @see getScaleStart
+ */
+ void setScaleStart(double scaleStart);
+
+ /**
+ * Gets the end value of the scale.
+ *
+ * @return The end value of the scale.
+ * @see setScaleEnd, setScale
+ */
+ double getScaleEnd() const;
+
+ /**
+ * Sets the end value of the scale.
+ *
+ * @param scaleEnd The end value of the scale.
+ * @see getScaleEnd
+ */
+ void setScaleEnd(double scaleEnd);
+
+ /**
+ * Gets the current selected value.
+ *
+ * @return The current selected value.
+ * @see setValue
+ */
+ double getValue() const;
+
+ /**
+ * Sets the current selected value.
+ *
+ * @param value The current selected value.
+ * @see getValue
+ */
+ void setValue(double value);
+
+ /**
+ * Sets the length of the marker.
+ *
+ * @param length The length for the marker.
+ * @see getMarkerLength
+ */
+ void setMarkerLength(int length);
+
+ /**
+ * Gets the length of the marker.
+ *
+ * @return The length of the marker.
+ * @see setMarkerLength
+ */
+ int getMarkerLength() const;
+
+ /**
+ * Sets the orientation of the slider. A slider can be drawn vertically
+ * or horizontally.
+ *
+ * @param orientation The orientation of the slider.
+ * @see getOrientation
+ */
+ void setOrientation(Orientation orientation);
+
+ /**
+ * Gets the orientation of the slider. A slider can be drawn vertically
+ * or horizontally.
+ *
+ * @return The orientation of the slider.
+ * @see setOrientation
+ */
+ Orientation getOrientation() const;
+
+ /**
+ * Sets the step length. The step length is used when the keys LEFT
+ * and RIGHT are pressed to step in the scale.
+ *
+ * @param length The step length.
+ * @see getStepLength
+ */
+ void setStepLength(double length);
+
+ /**
+ * Gets the step length. The step length is used when the keys LEFT
+ * and RIGHT are pressed to step in the scale.
+ *
+ * @return the step length.
+ * @see setStepLength
+ */
+ double getStepLength() const;
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+
+ // Inherited from MouseListener.
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedUp(MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedDown(MouseEvent& mouseEvent);
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+ protected:
+ /**
+ * Draws the marker.
+ *
+ * @param graphics A graphics object to draw with.
+ */
+ virtual void drawMarker(gcn::Graphics* graphics);
+
+ /**
+ * Converts a marker position to a value in the scale.
+ *
+ * @param position The position to convert.
+ * @return A scale value corresponding to the position.
+ * @see valueToMarkerPosition
+ */
+ virtual double markerPositionToValue(int position) const;
+
+ /**
+ * Converts a value to a marker position.
+ *
+ * @param value The value to convert.
+ * @return A marker position corresponding to the value.
+ * @see markerPositionToValue
+ */
+ virtual int valueToMarkerPosition(double value) const;
+
+ /**
+ * Gets the marker position of the current selected value.
+ *
+ * @return The marker position of the current selected value.
+ */
+ virtual int getMarkerPosition() const;
+
+ /**
+ * True if the slider is dragged, false otherwise.
+ */
+ bool mDragged;
+
+ /**
+ * Holds the current selected value.
+ */
+ double mValue;
+
+ /**
+ * Holds the step length. The step length is used when the keys LEFT
+ * and RIGHT are pressed to step in the scale.
+ */
+ double mStepLength;
+
+ /**
+ * Holds the length of the marker.
+ */
+ int mMarkerLength;
+
+ /**
+ * Holds the start value of the scale.
+ */
+ double mScaleStart;
+
+ /**
+ * Holds the end value of the scale.
+ */
+ double mScaleEnd;
+
+ /**
+ * Holds the orientation of the slider. A slider can be drawn
+ * vertically or horizontally.
+ */
+ Orientation mOrientation;
+ };
+}
+
+#endif // end GCN_SLIDER_HPP
diff --git a/src/guichan/widgets/tab.cpp b/src/guichan/widgets/tab.cpp
new file mode 100644
index 000000000..8ba122b26
--- /dev/null
+++ b/src/guichan/widgets/tab.cpp
@@ -0,0 +1,181 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/tab.hpp"
+
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/widgets/button.hpp"
+#include "guichan/widgets/label.hpp"
+#include "guichan/widgets/tabbedarea.hpp"
+
+namespace gcn
+{
+ Tab::Tab()
+ :mHasMouse(false),
+ mTabbedArea(NULL)
+ {
+ mLabel = new Label();
+ mLabel->setPosition(4, 4);
+ add(mLabel);
+
+ addMouseListener(this);
+ }
+
+ Tab::~Tab()
+ {
+ delete mLabel;
+ }
+
+ void Tab::adjustSize()
+ {
+ setSize(mLabel->getWidth() + 8,
+ mLabel->getHeight() + 8);
+
+ if (mTabbedArea != NULL)
+ {
+ mTabbedArea->adjustTabPositions();
+ }
+ }
+
+ void Tab::setTabbedArea(TabbedArea* tabbedArea)
+ {
+ mTabbedArea = tabbedArea;
+ }
+
+ TabbedArea* Tab::getTabbedArea()
+ {
+ return mTabbedArea;
+ }
+
+ void Tab::setCaption(const std::string& caption)
+ {
+ mLabel->setCaption(caption);
+ mLabel->adjustSize();
+ adjustSize();
+ }
+
+ const std::string& Tab::getCaption() const
+ {
+ return mLabel->getCaption();
+ }
+
+ void Tab::draw(Graphics *graphics)
+ {
+ const Color &faceColor = getBaseColor();
+ const int alpha = getBaseColor().a;
+ Color highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ Color shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ Color borderColor;
+ Color baseColor;
+
+ if ((mTabbedArea != NULL && mTabbedArea->isTabSelected(this))
+ || mHasMouse)
+ {
+ // Draw a border.
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0, 0, getWidth() - 1, 0);
+ graphics->drawLine(0, 1, 0, getHeight() - 1);
+ graphics->setColor(shadowColor);
+ graphics->drawLine(getWidth() - 1, 1, getWidth() - 1, getHeight() - 1);
+
+ borderColor = highlightColor;
+ baseColor = getBaseColor();
+ }
+ else
+ {
+ // Draw a border.
+ graphics->setColor(shadowColor);
+ graphics->drawLine(0, 0, getWidth() - 1, 0);
+ graphics->drawLine(0, 1, 0, getHeight() - 1);
+ graphics->drawLine(getWidth() - 1, 1, getWidth() - 1, getHeight() - 1);
+
+ baseColor = getBaseColor() - 0x151515;
+ baseColor.a = alpha;
+ }
+
+ // Push a clip area so the other drawings don't need to worry
+ // about the border.
+ graphics->pushClipArea(Rectangle(1, 1, getWidth() - 2, getHeight() - 1));
+ const Rectangle currentClipArea = graphics->getCurrentClipArea();
+
+ graphics->setColor(baseColor);
+ graphics->fillRectangle(Rectangle(0,
+ 0,
+ currentClipArea.width,
+ currentClipArea.height));
+
+ drawChildren(graphics);
+
+ if (mTabbedArea != NULL
+ && mTabbedArea->isFocused()
+ && mTabbedArea->isTabSelected(this))
+ {
+ graphics->setColor(Color(0x000000));
+ graphics->drawRectangle(Rectangle(2,
+ 2,
+ currentClipArea.width - 4,
+ currentClipArea.height - 4));
+ }
+
+ graphics->popClipArea();
+ }
+
+ void Tab::mouseEntered(MouseEvent& mouseEvent)
+ {
+ mHasMouse = true;
+ }
+
+ void Tab::mouseExited(MouseEvent& mouseEvent)
+ {
+ mHasMouse = false;
+ }
+}
+
diff --git a/src/guichan/widgets/tab.hpp b/src/guichan/widgets/tab.hpp
new file mode 100644
index 000000000..bf89afccb
--- /dev/null
+++ b/src/guichan/widgets/tab.hpp
@@ -0,0 +1,151 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_TAB_HPP
+#define GCN_TAB_HPP
+
+#include <map>
+#include <string>
+
+#include "guichan/basiccontainer.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+
+namespace gcn
+{
+ class Label;
+ class TabbedArea;
+
+ /**
+ * An implementation of a simple tab to be used in a tabbed area.
+ *
+ * @see TabbedArea
+ * @since 0.8.0
+ */
+ class GCN_CORE_DECLSPEC Tab:
+ public BasicContainer,
+ public MouseListener
+ {
+ public:
+
+ /**
+ * Constructor.
+ */
+ Tab();
+
+ /**
+ * Destructor.
+ */
+ virtual ~Tab();
+
+ /**
+ * Adjusts the size of the tab to fit the caption. If this tab was
+ * added to a TabbedArea, it will also adjust the tab positions.
+ */
+ void adjustSize();
+
+ /**
+ * Sets the tabbed area the tab should be a part of.
+ *
+ * @param tabbedArea The tabbed area the tab should be a part of.
+ * @see getTabbedArea
+ */
+ void setTabbedArea(TabbedArea* tabbedArea);
+
+ /**
+ * Gets the tabbed are the tab is a part of.
+ *
+ * @return The tabbed are the tab is a part of.
+ * @see setTabbedArea
+ */
+ TabbedArea* getTabbedArea();
+
+ /**
+ * Sets the caption of the tab. It's advisable to call
+ * adjustSize after setting the caption to make the tab
+ * fit the caption.
+ *
+ * @param caption The caption of the tab.
+ * @see getCaption, adjustSize
+ */
+ void setCaption(const std::string& caption);
+
+ /**
+ * Gets the caption of the tab.
+ *
+ * @return The caption of the tab.
+ * @see setCaption
+ */
+ const std::string& getCaption() const;
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics *graphics);
+
+
+ // Inherited from MouseListener
+
+ virtual void mouseEntered(MouseEvent& mouseEvent);
+
+ virtual void mouseExited(MouseEvent& mouseEvent);
+
+ protected:
+ /**
+ * Holds the label of the tab.
+ */
+ Label* mLabel;
+
+ /**
+ * True if the tab has the mouse, false otherwise.
+ */
+ bool mHasMouse;
+
+ /**
+ * Holds the tabbed area the tab is a part of.
+ */
+ TabbedArea* mTabbedArea;
+ };
+}
+
+#endif // end GCN_TABBEDAREA_HPP
diff --git a/src/guichan/widgets/tabbedarea.cpp b/src/guichan/widgets/tabbedarea.cpp
new file mode 100644
index 000000000..e07d14c4d
--- /dev/null
+++ b/src/guichan/widgets/tabbedarea.cpp
@@ -0,0 +1,482 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/tabbedarea.hpp"
+
+#include "guichan/exception.hpp"
+#include "guichan/focushandler.hpp"
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+
+#include "guichan/widgets/container.hpp"
+#include "guichan/widgets/tab.hpp"
+
+#include <algorithm>
+
+namespace gcn
+{
+ TabbedArea::TabbedArea()
+ :mSelectedTab(NULL),
+ mOpaque(false)
+ {
+ setFocusable(true);
+ addKeyListener(this);
+ addMouseListener(this);
+
+ mTabContainer = new Container();
+ mTabContainer->setOpaque(false);
+ mWidgetContainer = new Container();
+
+ add(mTabContainer);
+ add(mWidgetContainer);
+ }
+
+ TabbedArea::~TabbedArea()
+ {
+ remove(mTabContainer);
+ remove(mWidgetContainer);
+
+ delete mTabContainer;
+ delete mWidgetContainer;
+
+ for (unsigned int i = 0; i < mTabsToDelete.size(); i++)
+ {
+ delete mTabsToDelete[i];
+ }
+ }
+
+ void TabbedArea::addTab(const std::string& caption, Widget* widget)
+ {
+ Tab* tab = new Tab();
+ tab->setCaption(caption);
+ mTabsToDelete.push_back(tab);
+
+ addTab(tab, widget);
+ }
+
+ void TabbedArea::addTab(Tab* tab, Widget* widget)
+ {
+ tab->setTabbedArea(this);
+ tab->addActionListener(this);
+
+ mTabContainer->add(tab);
+ mTabs.push_back(std::pair<Tab*, Widget*>(tab, widget));
+
+ if (mSelectedTab == NULL)
+ {
+ setSelectedTab(tab);
+ }
+
+ adjustTabPositions();
+ adjustSize();
+ }
+
+ void TabbedArea::removeTabWithIndex(unsigned int index)
+ {
+ if (index >= mTabs.size())
+ {
+ throw GCN_EXCEPTION("No such tab index.");
+ }
+
+ removeTab(mTabs[index].first);
+ }
+
+ void TabbedArea::removeTab(Tab* tab)
+ {
+ int tabIndexToBeSelected = - 1;
+
+ if (tab == mSelectedTab)
+ {
+ int index = getSelectedTabIndex();
+
+ if (index == (int)mTabs.size() - 1
+ && mTabs.size() >= 2)
+ {
+ tabIndexToBeSelected = index--;
+ }
+ else if (index == (int)mTabs.size() - 1
+ && mTabs.size() == 1)
+ {
+ tabIndexToBeSelected = -1;
+ }
+ else
+ {
+ tabIndexToBeSelected = index;
+ }
+ }
+
+ std::vector<std::pair<Tab*, Widget*> >::iterator iter;
+ for (iter = mTabs.begin(); iter != mTabs.end(); iter++)
+ {
+ if (iter->first == tab)
+ {
+ mTabContainer->remove(tab);
+ mTabs.erase(iter);
+ break;
+ }
+ }
+
+ std::vector<Tab*>::iterator iter2;
+ for (iter2 = mTabsToDelete.begin(); iter2 != mTabsToDelete.end(); iter2++)
+ {
+ if (*iter2 == tab)
+ {
+ mTabsToDelete.erase(iter2);
+ delete tab;
+ break;
+ }
+ }
+
+ if (tabIndexToBeSelected == -1)
+ {
+ mSelectedTab = NULL;
+ mWidgetContainer->clear();
+ }
+ else
+ {
+ setSelectedTab(tabIndexToBeSelected);
+ }
+
+ adjustSize();
+ adjustTabPositions();
+ }
+
+ bool TabbedArea::isTabSelected(unsigned int index) const
+ {
+ if (index >= mTabs.size())
+ {
+ throw GCN_EXCEPTION("No such tab index.");
+ }
+
+ return mSelectedTab == mTabs[index].first;
+ }
+
+ bool TabbedArea::isTabSelected(Tab* tab)
+ {
+ return mSelectedTab == tab;
+ }
+
+ void TabbedArea::setSelectedTab(unsigned int index)
+ {
+ if (index >= mTabs.size())
+ {
+ throw GCN_EXCEPTION("No such tab index.");
+ }
+
+ setSelectedTab(mTabs[index].first);
+ }
+
+ void TabbedArea::setSelectedTab(Tab* tab)
+ {
+ unsigned int i;
+ for (i = 0; i < mTabs.size(); i++)
+ {
+ if (mTabs[i].first == mSelectedTab)
+ {
+ mWidgetContainer->remove(mTabs[i].second);
+ }
+ }
+
+ for (i = 0; i < mTabs.size(); i++)
+ {
+ if (mTabs[i].first == tab)
+ {
+ mSelectedTab = tab;
+ mWidgetContainer->add(mTabs[i].second);
+ }
+ }
+ }
+
+ int TabbedArea::getSelectedTabIndex() const
+ {
+ unsigned int i;
+ for (i = 0; i < mTabs.size(); i++)
+ {
+ if (mTabs[i].first == mSelectedTab)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ Tab* TabbedArea::getSelectedTab()
+ {
+ return mSelectedTab;
+ }
+
+ void TabbedArea::setOpaque(bool opaque)
+ {
+ mOpaque = opaque;
+ }
+
+ bool TabbedArea::isOpaque() const
+ {
+ return mOpaque;
+ }
+
+ void TabbedArea::draw(Graphics *graphics)
+ {
+ const Color &faceColor = getBaseColor();
+ const int alpha = getBaseColor().a;
+ Color highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ Color shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ // Draw a border.
+ graphics->setColor(highlightColor);
+ graphics->drawLine(0,
+ mTabContainer->getHeight(),
+ 0,
+ getHeight() - 2);
+ graphics->setColor(shadowColor);
+ graphics->drawLine(getWidth() - 1,
+ mTabContainer->getHeight() + 1,
+ getWidth() - 1,
+ getHeight() - 1);
+ graphics->drawLine(1,
+ getHeight() - 1,
+ getWidth() - 1,
+ getHeight() - 1);
+
+ if (isOpaque())
+ {
+ graphics->setColor(getBaseColor());
+ graphics->fillRectangle(Rectangle(1, 1,
+ getWidth() - 2,
+ getHeight() - 2));
+ }
+
+ // Draw a line underneath the tabs.
+ graphics->setColor(highlightColor);
+ graphics->drawLine(1,
+ mTabContainer->getHeight(),
+ getWidth() - 1,
+ mTabContainer->getHeight());
+
+ // If a tab is selected, remove the line right underneath
+ // the selected tab.
+ if (mSelectedTab != NULL)
+ {
+ graphics->setColor(getBaseColor());
+ graphics->drawLine(mSelectedTab->getX() + 1,
+ mTabContainer->getHeight(),
+ mSelectedTab->getX() + mSelectedTab->getWidth() - 2,
+ mTabContainer->getHeight());
+
+ }
+
+ drawChildren(graphics);
+ }
+
+ void TabbedArea::logic()
+ {
+ }
+
+ void TabbedArea::adjustSize()
+ {
+ int maxTabHeight = 0;
+
+ for (unsigned int i = 0; i < mTabs.size(); i++)
+ {
+ if (mTabs[i].first->getHeight() > maxTabHeight)
+ {
+ maxTabHeight = mTabs[i].first->getHeight();
+ }
+ }
+
+ mTabContainer->setSize(getWidth() - 2,
+ maxTabHeight);
+
+ mWidgetContainer->setPosition(1, maxTabHeight + 1);
+ mWidgetContainer->setSize(getWidth() - 2,
+ getHeight() - maxTabHeight - 2);
+ }
+
+ void TabbedArea::adjustTabPositions()
+ {
+ int maxTabHeight = 0;
+ unsigned int i;
+ for (i = 0; i < mTabs.size(); i++)
+ {
+ if (mTabs[i].first->getHeight() > maxTabHeight)
+ {
+ maxTabHeight = mTabs[i].first->getHeight();
+ }
+ }
+
+ int x = 0;
+ for (i = 0; i < mTabs.size(); i++)
+ {
+ Tab* tab = mTabs[i].first;
+ tab->setPosition(x, maxTabHeight - tab->getHeight());
+ x += tab->getWidth();
+ }
+ }
+
+ void TabbedArea::setWidth(int width)
+ {
+ Widget::setWidth(width);
+ adjustSize();
+ }
+
+ void TabbedArea::setHeight(int height)
+ {
+ Widget::setHeight(height);
+ adjustSize();
+ }
+
+ void TabbedArea::setSize(int width, int height)
+ {
+ Widget::setSize(width, height);
+ adjustSize();
+ }
+
+ void TabbedArea::setDimension(const Rectangle& dimension)
+ {
+ Widget::setDimension(dimension);
+ adjustSize();
+ }
+
+ void TabbedArea::keyPressed(KeyEvent& keyEvent)
+ {
+ if (keyEvent.isConsumed() || !isFocused())
+ {
+ return;
+ }
+
+ if (keyEvent.getKey().getValue() == Key::LEFT)
+ {
+ int index = getSelectedTabIndex();
+ index--;
+
+ if (index < 0)
+ {
+ return;
+ }
+ else
+ {
+ setSelectedTab(mTabs[index].first);
+ }
+
+ keyEvent.consume();
+ }
+ else if (keyEvent.getKey().getValue() == Key::RIGHT)
+ {
+ int index = getSelectedTabIndex();
+ index++;
+
+ if (index >= (int)mTabs.size())
+ {
+ return;
+ }
+ else
+ {
+ setSelectedTab(mTabs[index].first);
+ }
+
+ keyEvent.consume();
+ }
+ }
+
+
+ void TabbedArea::mousePressed(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.isConsumed())
+ {
+ return;
+ }
+
+ if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ Widget* widget = mTabContainer->getWidgetAt(mouseEvent.getX(), mouseEvent.getY());
+ Tab* tab = dynamic_cast<Tab*>(widget);
+
+ if (tab != NULL)
+ {
+ setSelectedTab(tab);
+ }
+ }
+
+ // Request focus only if the source of the event
+ // is not focusble. If the source of the event
+ // is focused we don't want to steal the focus.
+ if (!mouseEvent.getSource()->isFocusable())
+ {
+ requestFocus();
+ }
+ }
+
+ void TabbedArea::death(const Event& event)
+ {
+ Tab* tab = dynamic_cast<Tab*>(event.getSource());
+
+ if (tab != NULL)
+ {
+ removeTab(tab);
+ }
+ else
+ {
+ BasicContainer::death(event);
+ }
+ }
+
+ void TabbedArea::action(const ActionEvent& actionEvent)
+ {
+ Widget* source = actionEvent.getSource();
+ Tab* tab = dynamic_cast<Tab*>(source);
+
+ if (tab == NULL)
+ {
+ throw GCN_EXCEPTION("Received an action from a widget that's not a tab!");
+ }
+
+ setSelectedTab(tab);
+ }
+}
diff --git a/src/guichan/widgets/tabbedarea.hpp b/src/guichan/widgets/tabbedarea.hpp
new file mode 100644
index 000000000..843bbde81
--- /dev/null
+++ b/src/guichan/widgets/tabbedarea.hpp
@@ -0,0 +1,280 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_TABBEDAREA_HPP
+#define GCN_TABBEDAREA_HPP
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "guichan/actionlistener.hpp"
+#include "guichan/basiccontainer.hpp"
+#include "guichan/keylistener.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+
+namespace gcn
+{
+ class Container;
+ class Tab;
+
+ /**
+ * An implementation of a tabbed area where a user can display a widget by
+ * selecting a tab.
+ *
+ * @since 0.8.0
+ */
+ class GCN_CORE_DECLSPEC TabbedArea:
+ public ActionListener,
+ public BasicContainer,
+ public KeyListener,
+ public MouseListener
+ {
+ friend class Tab;
+
+ public:
+ /**
+ * Constructor.
+ */
+ TabbedArea();
+
+ /**
+ * Destructor.
+ */
+ virtual ~TabbedArea();
+
+ /**
+ * Sets the tabbed area to be opaque or not. If the tabbed area is
+ * opaque its background will be drawn, if it's not opaque its
+ * background will not be drawn. By default, a tabbed area is not
+ * opaque.
+ *
+ * The tabbed area's background is normally only visible behind the
+ * tabs, since the container holding the tab contents is opaque by
+ * default.
+ *
+ * @param opaque True if the tabbed area should be opaque, false
+ * otherwise.
+ * @see isOpaque
+ */
+ void setOpaque(bool opaque);
+
+ /**
+ * Checks if the tabbed area is opaque or not.
+ *
+ * @return true if the tabbed area is opaque, false otherwise.
+ * @see setOpaque
+ */
+ bool isOpaque() const;
+
+ /**
+ * Adds a tab to the tabbed area. The newly created tab will be
+ * automatically deleted by the tabbed area when it is removed.
+ *
+ * @param caption The caption of the tab to add.
+ * @param widget The widget to view when the tab is selected.
+ * @see removeTab, removeTabWithIndex
+ */
+ virtual void addTab(const std::string& caption, Widget* widget);
+
+ /**
+ * Adds a tab to the tabbed area. The tab will not be deleted by the
+ * tabbed area when it is removed.
+ *
+ * @param tab The tab widget for the tab.
+ * @param widget The widget to view when the tab is selected.
+ * @see removeTab, removeTabWithIndex
+ */
+ virtual void addTab(Tab* tab, Widget* widget);
+
+ /**
+ * Removes a tab from the tabbed area.
+ *
+ * @param index The index of the tab to remove.
+ * @see addTab
+ */
+ virtual void removeTabWithIndex(unsigned int index);
+
+ /**
+ * Removes a tab from the tabbed area.
+ *
+ * @param index The tab to remove.
+ * @see addTab
+ */
+ virtual void removeTab(Tab* tab);
+
+ /**
+ * Checks if a tab given an index is selected or not.
+ *
+ * @param index The index of the tab to check.
+ * @return True if the tab is selected, false otherwise.
+ * @see setSelectedTab
+ */
+ virtual bool isTabSelected(unsigned int index) const;
+
+ /**
+ * Checks if a tab is selected or not.
+ *
+ * @param index The tab to check.
+ * @return True if the tab is selected, false otherwise.
+ * @see setSelectedTab
+ */
+ virtual bool isTabSelected(Tab* tab);
+
+ /**
+ * Sets a tab given an index to be selected.
+ *
+ * @param index The index of the tab to be selected.
+ * @see isTabSelected, getSelectedTab
+ */
+ virtual void setSelectedTab(unsigned int index);
+
+ /**
+ * Sets a tab to be selected or not.
+ *
+ * @param index The tab to be selected.
+ * @see isTabSelected, getSelectedTab
+ */
+ virtual void setSelectedTab(Tab* tab);
+
+ /**
+ * Gets the index of the selected tab.
+ *
+ * @return The undex of the selected tab.
+ * If no tab is selected -1 will be returned.
+ * @see isTabSelected, setSelectedTab
+ */
+ virtual int getSelectedTabIndex() const;
+
+ /**
+ * Gets the selected tab.
+ *
+ * @return The selected tab.
+ * @see isTabSelected, setSelectedTab
+ */
+ Tab* getSelectedTab();
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics *graphics);
+
+ virtual void logic();
+
+ void setWidth(int width);
+
+ void setHeight(int height);
+
+ void setSize(int width, int height);
+
+ void setDimension(const Rectangle& dimension);
+
+
+ // Inherited from ActionListener
+
+ void action(const ActionEvent& actionEvent);
+
+
+ // Inherited from DeathListener
+
+ virtual void death(const Event& event);
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+
+ // Inherited from MouseListener
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+
+ protected:
+ /**
+ * Adjusts the size of the tab container and the widget container.
+ */
+ void adjustSize();
+
+ /**
+ * Adjusts the positions of the tabs.
+ */
+ void adjustTabPositions();
+
+ /**
+ * Holds the selected tab.
+ */
+ Tab* mSelectedTab;
+
+ /**
+ * Holds the container for the tabs.
+ */
+ Container* mTabContainer;
+
+ /**
+ * Holds the container for the widgets.
+ */
+ Container* mWidgetContainer;
+
+ /**
+ * Holds a vector of tabs to delete in the destructor.
+ * A tab that is to be deleted is a tab that has been
+ * internally created by the tabbed area.
+ */
+ std::vector<Tab*> mTabsToDelete;
+
+ /**
+ * A map between a tab and a widget to display when the
+ * tab is selected.
+ */
+ std::vector<std::pair<Tab*, Widget*> > mTabs;
+
+ /**
+ * True if the tabbed area is opaque, false otherwise.
+ */
+ bool mOpaque;
+ };
+}
+
+#endif // end GCN_TABBEDAREA_HPP
diff --git a/src/guichan/widgets/textbox.cpp b/src/guichan/widgets/textbox.cpp
new file mode 100644
index 000000000..c57296f9c
--- /dev/null
+++ b/src/guichan/widgets/textbox.cpp
@@ -0,0 +1,517 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/textbox.hpp"
+
+#include "guichan/basiccontainer.hpp"
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/key.hpp"
+#include "guichan/mouseinput.hpp"
+
+namespace gcn
+{
+ TextBox::TextBox()
+ {
+ mCaretColumn = 0;
+ mCaretRow = 0;
+ mEditable = true;
+ mOpaque = true;
+
+ setText("");
+
+ setFocusable(true);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ adjustSize();
+ }
+
+ TextBox::TextBox(const std::string& text)
+ {
+ mCaretColumn = 0;
+ mCaretRow = 0;
+ mEditable = true;
+ mOpaque = true;
+
+ setText(text);
+
+ setFocusable(true);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ adjustSize();
+ }
+
+ void TextBox::setText(const std::string& text)
+ {
+ mCaretColumn = 0;
+ mCaretRow = 0;
+
+ mTextRows.clear();
+
+ std::string::size_type pos, lastPos = 0;
+ int length;
+ do
+ {
+ pos = text.find("\n", lastPos);
+
+ if (pos != std::string::npos)
+ {
+ length = pos - lastPos;
+ }
+ else
+ {
+ length = text.size() - lastPos;
+ }
+ std::string sub = text.substr(lastPos, length);
+ mTextRows.push_back(sub);
+ lastPos = pos + 1;
+
+ } while (pos != std::string::npos);
+
+ adjustSize();
+ }
+
+ void TextBox::draw(Graphics* graphics)
+ {
+ unsigned int i;
+
+ if (mOpaque)
+ {
+ graphics->setColor(getBackgroundColor());
+ graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
+ }
+
+ if (isFocused() && isEditable())
+ {
+ drawCaret(graphics, getFont()->getWidth(mTextRows[mCaretRow].substr(0, mCaretColumn)), mCaretRow * getFont()->getHeight());
+ }
+
+ graphics->setColor(getForegroundColor());
+ graphics->setFont(getFont());
+
+ for (i = 0; i < mTextRows.size(); i++)
+ {
+ // Move the text one pixel so we can have a caret before a letter.
+ graphics->drawText(mTextRows[i], 1, i * getFont()->getHeight());
+ }
+ }
+
+ void TextBox::drawCaret(Graphics* graphics, int x, int y)
+ {
+ graphics->setColor(getForegroundColor());
+ graphics->drawLine(x, getFont()->getHeight() + y, x, y);
+ }
+
+ void TextBox::mousePressed(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ mCaretRow = mouseEvent.getY() / getFont()->getHeight();
+
+ if (mCaretRow >= (int)mTextRows.size())
+ {
+ mCaretRow = mTextRows.size() - 1;
+ }
+
+ mCaretColumn = getFont()->getStringIndexAt(mTextRows[mCaretRow], mouseEvent.getX());
+ }
+ }
+
+ void TextBox::mouseDragged(MouseEvent& mouseEvent)
+ {
+ mouseEvent.consume();
+ }
+
+ void TextBox::keyPressed(KeyEvent& keyEvent)
+ {
+ Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::LEFT)
+ {
+ --mCaretColumn;
+ if (mCaretColumn < 0)
+ {
+ --mCaretRow;
+
+ if (mCaretRow < 0)
+ {
+ mCaretRow = 0;
+ mCaretColumn = 0;
+ }
+ else
+ {
+ mCaretColumn = mTextRows[mCaretRow].size();
+ }
+ }
+ }
+
+ else if (key.getValue() == Key::RIGHT)
+ {
+ ++mCaretColumn;
+ if (mCaretColumn > (int)mTextRows[mCaretRow].size())
+ {
+ ++mCaretRow;
+
+ if (mCaretRow >= (int)mTextRows.size())
+ {
+ mCaretRow = mTextRows.size() - 1;
+ if (mCaretRow < 0)
+ {
+ mCaretRow = 0;
+ }
+
+ mCaretColumn = mTextRows[mCaretRow].size();
+ }
+ else
+ {
+ mCaretColumn = 0;
+ }
+ }
+ }
+
+ else if (key.getValue() == Key::DOWN)
+ {
+ setCaretRow(mCaretRow + 1);
+ }
+
+ else if (key.getValue() == Key::UP)
+ {
+ setCaretRow(mCaretRow - 1);
+ }
+
+ else if (key.getValue() == Key::HOME)
+ {
+ mCaretColumn = 0;
+ }
+
+ else if (key.getValue() == Key::END)
+ {
+ mCaretColumn = mTextRows[mCaretRow].size();
+ }
+
+ else if (key.getValue() == Key::ENTER && mEditable)
+ {
+ mTextRows.insert(mTextRows.begin() + mCaretRow + 1,
+ mTextRows[mCaretRow].substr(mCaretColumn, mTextRows[mCaretRow].size() - mCaretColumn));
+ mTextRows[mCaretRow].resize(mCaretColumn);
+ ++mCaretRow;
+ mCaretColumn = 0;
+ }
+
+ else if (key.getValue() == Key::BACKSPACE
+ && mCaretColumn != 0
+ && mEditable)
+ {
+ mTextRows[mCaretRow].erase(mCaretColumn - 1, 1);
+ --mCaretColumn;
+ }
+
+ else if (key.getValue() == Key::BACKSPACE
+ && mCaretColumn == 0
+ && mCaretRow != 0
+ && mEditable)
+ {
+ mCaretColumn = mTextRows[mCaretRow - 1].size();
+ mTextRows[mCaretRow - 1] += mTextRows[mCaretRow];
+ mTextRows.erase(mTextRows.begin() + mCaretRow);
+ --mCaretRow;
+ }
+
+ else if (key.getValue() == Key::DELETE
+ && mCaretColumn < (int)mTextRows[mCaretRow].size()
+ && mEditable)
+ {
+ mTextRows[mCaretRow].erase(mCaretColumn, 1);
+ }
+
+ else if (key.getValue() == Key::DELETE
+ && mCaretColumn == (int)mTextRows[mCaretRow].size()
+ && mCaretRow < ((int)mTextRows.size() - 1)
+ && mEditable)
+ {
+ mTextRows[mCaretRow] += mTextRows[mCaretRow + 1];
+ mTextRows.erase(mTextRows.begin() + mCaretRow + 1);
+ }
+
+ else if(key.getValue() == Key::PAGE_UP)
+ {
+ Widget* par = getParent();
+
+ if (par != NULL)
+ {
+ int rowsPerPage = par->getChildrenArea().height / getFont()->getHeight();
+ mCaretRow -= rowsPerPage;
+
+ if (mCaretRow < 0)
+ {
+ mCaretRow = 0;
+ }
+ }
+ }
+
+ else if(key.getValue() == Key::PAGE_DOWN)
+ {
+ Widget* par = getParent();
+
+ if (par != NULL)
+ {
+ int rowsPerPage = par->getChildrenArea().height / getFont()->getHeight();
+ mCaretRow += rowsPerPage;
+
+ if (mCaretRow >= (int)mTextRows.size())
+ {
+ mCaretRow = mTextRows.size() - 1;
+ }
+ }
+ }
+
+ else if(key.getValue() == Key::TAB
+ && mEditable)
+ {
+ mTextRows[mCaretRow].insert(mCaretColumn,std::string(" "));
+ mCaretColumn += 4;
+ }
+
+ else if (key.isCharacter()
+ && mEditable)
+ {
+ mTextRows[mCaretRow].insert(mCaretColumn,std::string(1,(char)key.getValue()));
+ ++mCaretColumn;
+ }
+
+ adjustSize();
+ scrollToCaret();
+
+ keyEvent.consume();
+ }
+
+ void TextBox::adjustSize()
+ {
+ unsigned int i;
+ int width = 0;
+ for (i = 0; i < mTextRows.size(); ++i)
+ {
+ int w = getFont()->getWidth(mTextRows[i]);
+ if (width < w)
+ {
+ width = w;
+ }
+ }
+
+ setWidth(width + 1);
+ setHeight(getFont()->getHeight() * mTextRows.size());
+ }
+
+ void TextBox::setCaretPosition(unsigned int position)
+ {
+ int row;
+
+ for (row = 0; row < (int)mTextRows.size(); row++)
+ {
+ if (position <= mTextRows[row].size())
+ {
+ mCaretRow = row;
+ mCaretColumn = position;
+ return; // we are done
+ }
+ else
+ {
+ position--;
+ }
+ }
+
+ // position beyond end of text
+ mCaretRow = mTextRows.size() - 1;
+ mCaretColumn = mTextRows[mCaretRow].size();
+ }
+
+ unsigned int TextBox::getCaretPosition() const
+ {
+ int pos = 0, row;
+
+ for (row = 0; row < mCaretRow; row++)
+ {
+ pos += mTextRows[row].size();
+ }
+
+ return pos + mCaretColumn;
+ }
+
+ void TextBox::setCaretRowColumn(int row, int column)
+ {
+ setCaretRow(row);
+ setCaretColumn(column);
+ }
+
+ void TextBox::setCaretRow(int row)
+ {
+ mCaretRow = row;
+
+ if (mCaretRow >= (int)mTextRows.size())
+ {
+ mCaretRow = mTextRows.size() - 1;
+ }
+
+ if (mCaretRow < 0)
+ {
+ mCaretRow = 0;
+ }
+
+ setCaretColumn(mCaretColumn);
+ }
+
+ unsigned int TextBox::getCaretRow() const
+ {
+ return mCaretRow;
+ }
+
+ void TextBox::setCaretColumn(int column)
+ {
+ mCaretColumn = column;
+
+ if (mCaretColumn > (int)mTextRows[mCaretRow].size())
+ {
+ mCaretColumn = mTextRows[mCaretRow].size();
+ }
+
+ if (mCaretColumn < 0)
+ {
+ mCaretColumn = 0;
+ }
+ }
+
+ unsigned int TextBox::getCaretColumn() const
+ {
+ return mCaretColumn;
+ }
+
+ const std::string& TextBox::getTextRow(int row) const
+ {
+ return mTextRows[row];
+ }
+
+ void TextBox::setTextRow(int row, const std::string& text)
+ {
+ mTextRows[row] = text;
+
+ if (mCaretRow == row)
+ {
+ setCaretColumn(mCaretColumn);
+ }
+
+ adjustSize();
+ }
+
+ unsigned int TextBox::getNumberOfRows() const
+ {
+ return mTextRows.size();
+ }
+
+ std::string TextBox::getText() const
+ {
+ if (mTextRows.size() == 0)
+ {
+ return std::string("");
+ }
+
+ int i;
+ std::string text;
+
+ for (i = 0; i < (int)mTextRows.size() - 1; ++i)
+ {
+ text = text + mTextRows[i] + "\n";
+ }
+
+ text = text + mTextRows[i];
+
+ return text;
+ }
+
+ void TextBox::fontChanged()
+ {
+ adjustSize();
+ }
+
+ void TextBox::scrollToCaret()
+ {
+ Rectangle scroll;
+ scroll.x = getFont()->getWidth(mTextRows[mCaretRow].substr(0, mCaretColumn));
+ scroll.y = getFont()->getHeight() * mCaretRow;
+ scroll.width = getFont()->getWidth(" ");
+ scroll.height = getFont()->getHeight() + 2; // add 2 for some extra space
+
+ showPart(scroll);
+ }
+
+ void TextBox::setEditable(bool editable)
+ {
+ mEditable = editable;
+ }
+
+ bool TextBox::isEditable() const
+ {
+ return mEditable;
+ }
+
+ void TextBox::addRow(const std::string row)
+ {
+ mTextRows.push_back(row);
+ adjustSize();
+ }
+
+ bool TextBox::isOpaque()
+ {
+ return mOpaque;
+ }
+
+ void TextBox::setOpaque(bool opaque)
+ {
+ mOpaque = opaque;
+ }
+}
diff --git a/src/guichan/widgets/textbox.hpp b/src/guichan/widgets/textbox.hpp
new file mode 100644
index 000000000..b00dcef20
--- /dev/null
+++ b/src/guichan/widgets/textbox.hpp
@@ -0,0 +1,289 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_TEXTBOX_HPP
+#define GCN_TEXTBOX_HPP
+
+#include <ctime>
+#include <string>
+#include <vector>
+
+#include "guichan/keylistener.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+namespace gcn
+{
+ /**
+ * An implementation of a text box where a user can enter text that contains of many lines.
+ */
+ class GCN_CORE_DECLSPEC TextBox:
+ public Widget,
+ public MouseListener,
+ public KeyListener
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ TextBox();
+
+ /**
+ * Constructor.
+ *
+ * @param text The default text of the text box.
+ */
+ TextBox(const std::string& text);
+
+ /**
+ * Sets the text of the text box.
+ *
+ * @param text The text of the text box.
+ * @see getText
+ */
+ void setText(const std::string& text);
+
+ /**
+ * Gets the text of the text box.
+ *
+ * @return The text of the text box.
+ * @see setText
+ */
+ std::string getText() const;
+
+ /**
+ * Gets a certain row from the text.
+ *
+ * @param row The number of the row to get from the text.
+ * @return A row from the text of the text box.
+ * @see setTextRow
+ */
+ const std::string& getTextRow(int row) const;
+
+ /**
+ * Sets the text of a certain row of the text.
+ *
+ * @param row The number of the row to set in the text.
+ * @param text The text to set in the given row number.
+ * @see getTextRow
+ */
+ void setTextRow(int row, const std::string& text);
+
+ /**
+ * Gets the number of rows in the text.
+ *
+ * @return The number of rows in the text.
+ */
+ unsigned int getNumberOfRows() const;
+
+ /**
+ * Gets the caret position in the text.
+ *
+ * @return The caret position in the text.
+ * @see setCaretPosition
+ */
+ unsigned int getCaretPosition() const;
+
+ /**
+ * Sets the position of the caret in the text.
+ *
+ * @param position the positon of the caret.
+ * @see getCaretPosition
+ */
+ void setCaretPosition(unsigned int position);
+
+ /**
+ * Gets the row number where the caret is currently located.
+ *
+ * @return The row number where the caret is currently located.
+ * @see setCaretRow
+ */
+ unsigned int getCaretRow() const;
+
+ /**
+ * Sets the row where the caret should be currently located.
+ *
+ * @param The row where the caret should be currently located.
+ * @see getCaretRow
+ */
+ void setCaretRow(int row);
+
+ /**
+ * Gets the column where the caret is currently located.
+ *
+ * @return The column where the caret is currently located.
+ * @see setCaretColumn
+ */
+ unsigned int getCaretColumn() const;
+
+ /**
+ * Sets the column where the caret should be currently located.
+ *
+ * @param The column where the caret should be currently located.
+ * @see getCaretColumn
+ */
+ void setCaretColumn(int column);
+
+ /**
+ * Sets the row and the column where the caret should be curretly
+ * located.
+ *
+ * @param row The row where the caret should be currently located.
+ * @param column The column where the caret should be currently located.
+ * @see getCaretRow, getCaretColumn
+ */
+ void setCaretRowColumn(int row, int column);
+
+ /**
+ * Scrolls the text to the caret if the text box is in a scroll area.
+ *
+ * @see ScrollArea
+ */
+ virtual void scrollToCaret();
+
+ /**
+ * Checks if the text box is editable.
+ *
+ * @return True it the text box is editable, false otherwise.
+ * @see setEditable
+ */
+ bool isEditable() const;
+
+ /**
+ * Sets the text box to be editable or not.
+ *
+ * @param editable True if the text box should be editable, false otherwise.
+ */
+ void setEditable(bool editable);
+
+ /**
+ * Adds a row of text to the end of the text.
+ *
+ * @param row The row to add.
+ */
+ virtual void addRow(const std::string row);
+
+ /**
+ * Checks if the text box is opaque. An opaque text box will draw
+ * it's background and it's text. A non opaque text box only draw it's
+ * text making it transparent.
+ *
+ * @return True if the text box is opaque, false otherwise.
+ * @see setOpaque
+ */
+ bool isOpaque();
+
+ /**
+ * Sets the text box to be opaque or not. An opaque text box will draw
+ * it's background and it's text. A non opaque text box only draw it's
+ * text making it transparent.
+ *
+ * @param opaque True if the text box should be opaque, false otherwise.
+ * @see isOpaque
+ */
+ void setOpaque(bool opaque);
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+ virtual void fontChanged();
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+
+ // Inherited from MouseListener
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+ protected:
+ /**
+ * Draws the caret. Overloaded this method if you want to
+ * change the style of the caret.
+ *
+ * @param graphics a Graphics object to draw with.
+ * @param x the x position.
+ * @param y the y position.
+ */
+ virtual void drawCaret(Graphics* graphics, int x, int y);
+
+ /**
+ * Adjusts the text box's size to fit the text.
+ */
+ virtual void adjustSize();
+
+ /**
+ * Holds all the rows of the text.
+ */
+ std::vector<std::string> mTextRows;
+
+ /**
+ * Holds the current column of the caret.
+ */
+ int mCaretColumn;
+
+ /**
+ * Holds the current row of the caret.
+ */
+ int mCaretRow;
+
+ /**
+ * True if the text box is editable, false otherwise.
+ */
+ bool mEditable;
+
+ /**
+ * True if the text box is editable, false otherwise.
+ */
+ bool mOpaque;
+ };
+}
+
+#endif // end GCN_TEXTBOX_HPP
diff --git a/src/guichan/widgets/textfield.cpp b/src/guichan/widgets/textfield.cpp
new file mode 100644
index 000000000..e8fc9c5d6
--- /dev/null
+++ b/src/guichan/widgets/textfield.cpp
@@ -0,0 +1,280 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/textfield.hpp"
+
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/key.hpp"
+#include "guichan/mouseinput.hpp"
+
+namespace gcn
+{
+ TextField::TextField()
+ {
+ mCaretPosition = 0;
+ mXScroll = 0;
+
+ setFocusable(true);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ }
+
+ TextField::TextField(const std::string& text)
+ {
+ mCaretPosition = 0;
+ mXScroll = 0;
+
+ mText = text;
+ adjustSize();
+
+ setFocusable(true);
+
+ addMouseListener(this);
+ addKeyListener(this);
+ }
+
+ void TextField::setText(const std::string& text)
+ {
+ if(text.size() < mCaretPosition )
+ {
+ mCaretPosition = text.size();
+ }
+
+ mText = text;
+ }
+
+ void TextField::draw(Graphics* graphics)
+ {
+ Color faceColor = getBaseColor();
+ Color highlightColor, shadowColor;
+ int alpha = getBaseColor().a;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ // Draw a border.
+ graphics->setColor(shadowColor);
+ graphics->drawLine(0, 0, getWidth() - 1, 0);
+ graphics->drawLine(0, 1, 0, getHeight() - 2);
+ graphics->setColor(highlightColor);
+ graphics->drawLine(getWidth() - 1, 1, getWidth() - 1, getHeight() - 1);
+ graphics->drawLine(0, getHeight() - 1, getWidth() - 1, getHeight() - 1);
+
+ // Push a clip area so the other drawings don't need to worry
+ // about the border.
+ graphics->pushClipArea(Rectangle(1, 1, getWidth() - 2, getHeight() - 2));
+
+ graphics->setColor(getBackgroundColor());
+ graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
+
+ if (isFocused())
+ {
+ graphics->setColor(getSelectionColor());
+ graphics->drawRectangle(Rectangle(0, 0, getWidth() - 2, getHeight() - 2));
+ graphics->drawRectangle(Rectangle(1, 1, getWidth() - 4, getHeight() - 4));
+ }
+
+ if (isFocused())
+ {
+ drawCaret(graphics, getFont()->getWidth(mText.substr(0, mCaretPosition)) - mXScroll);
+ }
+
+ graphics->setColor(getForegroundColor());
+ graphics->setFont(getFont());
+ graphics->drawText(mText, 3 - mXScroll, 1);
+
+ graphics->popClipArea();
+ }
+
+ void TextField::drawCaret(Graphics* graphics, int x)
+ {
+ // Check the current clip area as a clip area with a different
+ // size than the widget might have been pushed (which is the
+ // case in the draw method when we push a clip area after we have
+ // drawn a border).
+ const Rectangle clipArea = graphics->getCurrentClipArea();
+
+ graphics->setColor(getForegroundColor());
+ graphics->drawLine(x, clipArea.height - 2, x, 1);
+ }
+
+ void TextField::mousePressed(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getButton() == MouseEvent::LEFT)
+ {
+ mCaretPosition = getFont()->getStringIndexAt(mText, mouseEvent.getX() + mXScroll);
+ fixScroll();
+ }
+ }
+
+ void TextField::mouseDragged(MouseEvent& mouseEvent)
+ {
+ mouseEvent.consume();
+ }
+
+ void TextField::keyPressed(KeyEvent& keyEvent)
+ {
+ Key key = keyEvent.getKey();
+
+ if (key.getValue() == Key::LEFT && mCaretPosition > 0)
+ {
+ --mCaretPosition;
+ }
+
+ else if (key.getValue() == Key::RIGHT && mCaretPosition < mText.size())
+ {
+ ++mCaretPosition;
+ }
+
+ else if (key.getValue() == Key::DELETE && mCaretPosition < mText.size())
+ {
+ mText.erase(mCaretPosition, 1);
+ }
+
+ else if (key.getValue() == Key::BACKSPACE && mCaretPosition > 0)
+ {
+ mText.erase(mCaretPosition - 1, 1);
+ --mCaretPosition;
+ }
+
+ else if (key.getValue() == Key::ENTER)
+ {
+ distributeActionEvent();
+ }
+
+ else if (key.getValue() == Key::HOME)
+ {
+ mCaretPosition = 0;
+ }
+
+ else if (key.getValue() == Key::END)
+ {
+ mCaretPosition = mText.size();
+ }
+
+ else if (key.isCharacter()
+ && key.getValue() != Key::TAB)
+ {
+ mText.insert(mCaretPosition, std::string(1,(char)key.getValue()));
+ ++mCaretPosition;
+ }
+
+ if (key.getValue() != Key::TAB)
+ {
+ keyEvent.consume();
+ }
+
+ fixScroll();
+ }
+
+ void TextField::adjustSize()
+ {
+ setWidth(getFont()->getWidth(mText) + 7);
+ adjustHeight();
+
+ fixScroll();
+ }
+
+ void TextField::adjustHeight()
+ {
+ setHeight(getFont()->getHeight() + 4);
+ }
+
+ void TextField::fixScroll()
+ {
+ if (isFocused())
+ {
+ int caretX = getFont()->getWidth(mText.substr(0, mCaretPosition));
+
+ if (caretX - mXScroll >= getWidth() - 4)
+ {
+ mXScroll = caretX - getWidth() + 4;
+ }
+ else if (caretX - mXScroll <= 0)
+ {
+ mXScroll = caretX - getWidth() / 2;
+
+ if (mXScroll < 0)
+ {
+ mXScroll = 0;
+ }
+ }
+ }
+ }
+
+ void TextField::setCaretPosition(unsigned int position)
+ {
+ if (position > mText.size())
+ {
+ mCaretPosition = mText.size();
+ }
+ else
+ {
+ mCaretPosition = position;
+ }
+
+ fixScroll();
+ }
+
+ unsigned int TextField::getCaretPosition() const
+ {
+ return mCaretPosition;
+ }
+
+ const std::string& TextField::getText() const
+ {
+ return mText;
+ }
+
+ void TextField::fontChanged()
+ {
+ fixScroll();
+ }
+}
diff --git a/src/guichan/widgets/textfield.hpp b/src/guichan/widgets/textfield.hpp
new file mode 100644
index 000000000..486660a8a
--- /dev/null
+++ b/src/guichan/widgets/textfield.hpp
@@ -0,0 +1,177 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_TEXTFIELD_HPP
+#define GCN_TEXTFIELD_HPP
+
+#include "guichan/keylistener.hpp"
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widget.hpp"
+
+#include <string>
+
+namespace gcn
+{
+ /**
+ * An implementation of a text field where a user can enter a line of text.
+ */
+ class GCN_CORE_DECLSPEC TextField:
+ public Widget,
+ public MouseListener,
+ public KeyListener
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ TextField();
+
+ /**
+ * Constructor. The text field will be automatically resized
+ * to fit the text.
+ *
+ * @param text The default text of the text field.
+ */
+ TextField(const std::string& text);
+
+ /**
+ * Sets the text of the text field.
+ *
+ * @param text The text of the text field.
+ * @see getText
+ */
+ void setText(const std::string& text);
+
+ /**
+ * Gets the text of the text field.
+ *
+ * @return The text of the text field.
+ * @see setText
+ */
+ const std::string& getText() const;
+
+ /**
+ * Adjusts the size of the text field to fit the text.
+ */
+ void adjustSize();
+
+ /**
+ * Adjusts the height of the text field to fit caption.
+ */
+ void adjustHeight();
+
+ /**
+ * Sets the caret position. As there is only one line of text
+ * in a text field the position is the caret's x coordinate.
+ *
+ * @param position The caret position.
+ * @see getCaretPosition
+ */
+ void setCaretPosition(unsigned int position);
+
+ /**
+ * Gets the caret position. As there is only one line of text
+ * in a text field the position is the caret's x coordinate.
+ *
+ * @return The caret position.
+ * @see setCaretPosition
+ */
+ unsigned int getCaretPosition() const;
+
+
+ // Inherited from Widget
+
+ virtual void fontChanged();
+
+ virtual void draw(Graphics* graphics);
+
+
+ // Inherited from MouseListener
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+
+ // Inherited from KeyListener
+
+ virtual void keyPressed(KeyEvent& keyEvent);
+
+ protected:
+ /**
+ * Draws the caret. Overloaded this method if you want to
+ * change the style of the caret.
+ *
+ * @param graphics the Graphics object to draw with.
+ * @param x the caret's x-position.
+ */
+ virtual void drawCaret(Graphics* graphics, int x);
+
+ /**
+ * Scrolls the text horizontally so that the caret shows if needed.
+ * The method is used any time a user types in the text field so the
+ * caret always will be shown.
+ */
+ void fixScroll();
+
+ /**
+ * Holds the text of the text box.
+ */
+ std::string mText;
+
+ /**
+ * Holds the caret position.
+ */
+ unsigned int mCaretPosition;
+
+ /**
+ * Holds the amount scrolled in x. If a user types more characters than
+ * the text field can display, due to the text field being to small, the
+ * text needs to scroll in order to show the last type character.
+ */
+ int mXScroll;
+ };
+}
+
+#endif // end GCN_TEXTFIELD_HPP
diff --git a/src/guichan/widgets/window.cpp b/src/guichan/widgets/window.cpp
new file mode 100644
index 000000000..2ac4d553b
--- /dev/null
+++ b/src/guichan/widgets/window.cpp
@@ -0,0 +1,308 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * For comments regarding functions please see the header file.
+ */
+
+#include "guichan/widgets/window.hpp"
+
+#include "guichan/exception.hpp"
+#include "guichan/font.hpp"
+#include "guichan/graphics.hpp"
+#include "guichan/mouseinput.hpp"
+
+namespace gcn
+{
+ Window::Window()
+ :mMoved(false)
+ {
+ setFrameSize(1);
+ setPadding(2);
+ setTitleBarHeight(16);
+ setAlignment(Graphics::CENTER);
+ addMouseListener(this);
+ setMovable(true);
+ setOpaque(true);
+ }
+
+ Window::Window(const std::string& caption)
+ :mMoved(false)
+ {
+ setCaption(caption);
+ setFrameSize(1);
+ setPadding(2);
+ setTitleBarHeight(16);
+ setAlignment(Graphics::CENTER);
+ addMouseListener(this);
+ setMovable(true);
+ setOpaque(true);
+ }
+
+ Window::~Window()
+ {
+ }
+
+ void Window::setPadding(unsigned int padding)
+ {
+ mPadding = padding;
+ }
+
+ unsigned int Window::getPadding() const
+ {
+ return mPadding;
+ }
+
+ void Window::setTitleBarHeight(unsigned int height)
+ {
+ mTitleBarHeight = height;
+ }
+
+ unsigned int Window::getTitleBarHeight()
+ {
+ return mTitleBarHeight;
+ }
+
+ void Window::setCaption(const std::string& caption)
+ {
+ mCaption = caption;
+ }
+
+ const std::string& Window::getCaption() const
+ {
+ return mCaption;
+ }
+
+ void Window::setAlignment(Graphics::Alignment alignment)
+ {
+ mAlignment = alignment;
+ }
+
+ Graphics::Alignment Window::getAlignment() const
+ {
+ return mAlignment;
+ }
+
+ void Window::draw(Graphics* graphics)
+ {
+ const Color &faceColor = getBaseColor();
+ Color highlightColor, shadowColor;
+ const int alpha = getBaseColor().a;
+ highlightColor = faceColor + 0x303030;
+ highlightColor.a = alpha;
+ shadowColor = faceColor - 0x303030;
+ shadowColor.a = alpha;
+
+ Rectangle d = getChildrenArea();
+
+ // Fill the background around the content
+ graphics->setColor(faceColor);
+ // Fill top
+ graphics->fillRectangle(Rectangle(0,0,getWidth(),d.y - 1));
+ // Fill left
+ graphics->fillRectangle(Rectangle(0,d.y - 1, d.x - 1, getHeight() - d.y + 1));
+ // Fill right
+ graphics->fillRectangle(Rectangle(d.x + d.width + 1,
+ d.y - 1,
+ getWidth() - d.x - d.width - 1,
+ getHeight() - d.y + 1));
+ // Fill bottom
+ graphics->fillRectangle(Rectangle(d.x - 1,
+ d.y + d.height + 1,
+ d.width + 2,
+ getHeight() - d.height - d.y - 1));
+
+ if (isOpaque())
+ {
+ graphics->fillRectangle(d);
+ }
+
+ // Construct a rectangle one pixel bigger than the content
+ d.x -= 1;
+ d.y -= 1;
+ d.width += 2;
+ d.height += 2;
+
+ // Draw a border around the content
+ graphics->setColor(shadowColor);
+ // Top line
+ graphics->drawLine(d.x,
+ d.y,
+ d.x + d.width - 2,
+ d.y);
+
+ // Left line
+ graphics->drawLine(d.x,
+ d.y + 1,
+ d.x,
+ d.y + d.height - 1);
+
+ graphics->setColor(highlightColor);
+ // Right line
+ graphics->drawLine(d.x + d.width - 1,
+ d.y,
+ d.x + d.width - 1,
+ d.y + d.height - 2);
+ // Bottom line
+ graphics->drawLine(d.x + 1,
+ d.y + d.height - 1,
+ d.x + d.width - 1,
+ d.y + d.height - 1);
+
+ drawChildren(graphics);
+
+ int textX;
+ int textY;
+
+ textY = ((int)getTitleBarHeight() - getFont()->getHeight()) / 2;
+
+ switch (getAlignment())
+ {
+ case Graphics::LEFT:
+ textX = 4;
+ break;
+ case Graphics::CENTER:
+ textX = getWidth() / 2;
+ break;
+ case Graphics::RIGHT:
+ textX = getWidth() - 4;
+ break;
+ default:
+ throw GCN_EXCEPTION("Unknown alignment.");
+ }
+
+ graphics->setColor(getForegroundColor());
+ graphics->setFont(getFont());
+ graphics->pushClipArea(Rectangle(0, 0, getWidth(), getTitleBarHeight() - 1));
+ graphics->drawText(getCaption(), textX, textY, getAlignment());
+ graphics->popClipArea();
+ }
+
+ void Window::mousePressed(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.getSource() != this)
+ {
+ return;
+ }
+
+ if (getParent() != NULL)
+ {
+ getParent()->moveToTop(this);
+ }
+
+ mDragOffsetX = mouseEvent.getX();
+ mDragOffsetY = mouseEvent.getY();
+
+ mMoved = mouseEvent.getY() <= (int)mTitleBarHeight;
+ }
+
+ void Window::mouseReleased(MouseEvent& mouseEvent)
+ {
+ mMoved = false;
+ }
+
+ void Window::mouseDragged(MouseEvent& mouseEvent)
+ {
+ if (mouseEvent.isConsumed() || mouseEvent.getSource() != this)
+ {
+ return;
+ }
+
+ if (isMovable() && mMoved)
+ {
+ setPosition(mouseEvent.getX() - mDragOffsetX + getX(),
+ mouseEvent.getY() - mDragOffsetY + getY());
+ }
+
+ mouseEvent.consume();
+ }
+
+ Rectangle Window::getChildrenArea()
+ {
+ return Rectangle(getPadding(),
+ getTitleBarHeight(),
+ getWidth() - getPadding() * 2,
+ getHeight() - getPadding() - getTitleBarHeight());
+ }
+
+ void Window::setMovable(bool movable)
+ {
+ mMovable = movable;
+ }
+
+ bool Window::isMovable() const
+ {
+ return mMovable;
+ }
+
+ void Window::setOpaque(bool opaque)
+ {
+ mOpaque = opaque;
+ }
+
+ bool Window::isOpaque()
+ {
+ return mOpaque;
+ }
+
+ void Window::resizeToContent()
+ {
+ WidgetListIterator it;
+
+ int w = 0, h = 0;
+ for (it = mWidgets.begin(); it != mWidgets.end(); it++)
+ {
+ if ((*it)->getX() + (*it)->getWidth() > w)
+ {
+ w = (*it)->getX() + (*it)->getWidth();
+ }
+
+ if ((*it)->getY() + (*it)->getHeight() > h)
+ {
+ h = (*it)->getY() + (*it)->getHeight();
+ }
+ }
+
+ setSize(w + 2* getPadding(), h + getPadding() + getTitleBarHeight());
+ }
+}
diff --git a/src/guichan/widgets/window.hpp b/src/guichan/widgets/window.hpp
new file mode 100644
index 000000000..373efd164
--- /dev/null
+++ b/src/guichan/widgets/window.hpp
@@ -0,0 +1,255 @@
+/* _______ __ __ __ ______ __ __ _______ __ __
+ * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
+ * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
+ * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
+ *
+ *
+ * Per Larsson a.k.a finalman
+ * Olof Naessén a.k.a jansem/yakslem
+ *
+ * Visit: http://guichan.sourceforge.net
+ *
+ * License: (BSD)
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of Guichan nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCN_WINDOW_HPP
+#define GCN_WINDOW_HPP
+
+#include <string>
+
+#include "guichan/mouselistener.hpp"
+#include "guichan/platform.hpp"
+#include "guichan/widgets/container.hpp"
+
+namespace gcn
+{
+ /**
+ * An implementation of a movable window that can contain other widgets.
+ */
+ class GCN_CORE_DECLSPEC Window : public Container,
+ public MouseListener
+ {
+ public:
+ /**
+ * Constructor.
+ */
+ Window();
+
+ /**
+ * Constructor. The window will be automatically resized in height
+ * to fit the caption.
+ *
+ * @param caption the caption of the window.
+ */
+ Window(const std::string& caption);
+
+ /**
+ * Destructor.
+ */
+ virtual ~Window();
+
+ /**
+ * Sets the caption of the window.
+ *
+ * @param caption The caption of the window.
+ * @see getCaption
+ */
+ void setCaption(const std::string& caption);
+
+ /**
+ * Gets the caption of the window.
+ *
+ * @return the caption of the window.
+ * @see setCaption
+ */
+ const std::string& getCaption() const;
+
+ /**
+ * Sets the alignment of the caption.
+ *
+ * @param alignment The alignment of the caption.
+ * @see getAlignment, Graphics
+ */
+ void setAlignment(Graphics::Alignment alignment);
+
+ /**
+ * Gets the alignment of the caption.
+ *
+ * @return The alignment of caption.
+ * @see setAlignment, Graphics
+ */
+ Graphics::Alignment getAlignment() const;
+
+ /**
+ * Sets the padding of the window. The padding is the distance between the
+ * window border and the content.
+ *
+ * @param padding The padding of the window.
+ * @see getPadding
+ */
+ void setPadding(unsigned int padding);
+
+ /**
+ * Gets the padding of the window. The padding is the distance between the
+ * window border and the content.
+ *
+ * @return The padding of the window.
+ * @see setPadding
+ */
+ unsigned int getPadding() const;
+
+ /**
+ * Sets the title bar height.
+ *
+ * @param height The title height value.
+ * @see getTitleBarHeight
+ */
+ void setTitleBarHeight(unsigned int height);
+
+ /**
+ * Gets the title bar height.
+ *
+ * @return The title bar height.
+ * @see setTitleBarHeight
+ */
+ unsigned int getTitleBarHeight();
+
+ /**
+ * Sets the window to be moveble or not.
+ *
+ * @param movable True if the window should be movable, false otherwise.
+ * @see isMovable
+ */
+ void setMovable(bool movable);
+
+ /**
+ * Checks if the window is movable.
+ *
+ * @return True if the window is movable, false otherwise.
+ * @see setMovable
+ */
+ bool isMovable() const;
+
+ /**
+ * Sets the window to be opaque or not. An opaque window will draw it's background
+ * and it's content. A non opaque window will only draw it's content.
+ *
+ * @param opaque True if the window should be opaque, false otherwise.
+ * @see isOpaque
+ */
+ void setOpaque(bool opaque);
+
+ /**
+ * Checks if the window is opaque.
+ *
+ * @return True if the window is opaque, false otherwise.
+ * @see setOpaque
+ */
+ bool isOpaque();
+
+ /**
+ * Resizes the window to fit the content.
+ */
+ virtual void resizeToContent();
+
+
+ // Inherited from BasicContainer
+
+ virtual Rectangle getChildrenArea();
+
+
+ // Inherited from Widget
+
+ virtual void draw(Graphics* graphics);
+
+
+ // Inherited from MouseListener
+
+ virtual void mousePressed(MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(MouseEvent& mouseEvent);
+
+ virtual void mouseReleased(MouseEvent& mouseEvent);
+
+ protected:
+ /**
+ * Holds the caption of the window.
+ */
+ std::string mCaption;
+
+ /**
+ * Holds the alignment of the caption.
+ */
+ Graphics::Alignment mAlignment;
+
+ /**
+ * Holds the padding of the window.
+ */
+ unsigned int mPadding;
+
+ /**
+ * Holds the title bar height of the window.
+ */
+ unsigned int mTitleBarHeight;
+
+ /**
+ * True if the window is movable, false otherwise.
+ */
+ bool mMovable;
+
+ /**
+ * True if the window is opaque, false otherwise.
+ */
+ bool mOpaque;
+
+ /**
+ * Holds a drag offset as an x coordinate where the drag of the window
+ * started if the window is being dragged. It's used to move the window
+ * correctly when dragged.
+ */
+ int mDragOffsetX;
+
+ /**
+ * Holds a drag offset as an y coordinate where the drag of the window
+ * started if the window is being dragged. It's used to move the window
+ * correctly when dragged.
+ */
+ int mDragOffsetY;
+
+ /**
+ * True if the window is being moved, false otherwise.
+ */
+ bool mMoved;
+ };
+}
+
+#endif // end GCN_WINDOW_HPP