diff ext/guichan-0.8.1/src/widgets/dropdown.cpp @ 0:4a0efb7baf70

* Datasets becomes the new trunk and retires after that :-)
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 29 Jun 2008 18:44:17 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/guichan-0.8.1/src/widgets/dropdown.cpp	Sun Jun 29 18:44:17 2008 +0000
@@ -0,0 +1,632 @@
+/*      _______   __   __   __   ______   __   __   _______   __   __
+ *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
+ *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
+ *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
+ *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
+ * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
+ * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
+ *
+ * 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)
+    {
+        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);
+        }
+    }
+}
+