mirror of
https://github.com/Oibaf66/frodo-wii.git
synced 2024-12-02 08:24:52 +01:00
636 lines
18 KiB
C++
636 lines
18 KiB
C++
/* _______ __ __ __ ______ __ __ _______ __ __
|
|
* / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
|
|
* / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
|
|
* / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
|
|
* / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
|
|
* /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
|
|
* \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
|
|
*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|
|
|