/*
* The Mana World
* Copyright (C) 2004 The Mana World Development Team
*
* This file is part of The Mana World.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <cassert>
#include <guichan/graphics.hpp>
#include <guichan/actionlistener.hpp>
#include "table.h"
class GuiTableActionListener : public gcn::ActionListener
{
public:
GuiTableActionListener(GuiTable *_table, gcn::Widget *_widget, int _row, int _column);
virtual ~GuiTableActionListener(void);
virtual void action(const gcn::ActionEvent& actionEvent);
protected:
GuiTable *mTable;
int mRow;
int mColumn;
gcn::Widget *mWidget;
};
GuiTableActionListener::GuiTableActionListener(GuiTable *table, gcn::Widget *widget, int row, int column) :
mTable(table),
mRow(row),
mColumn(column),
mWidget(widget)
{
if (widget) {
widget->addActionListener(this);
widget->_setParent(table);
}
}
GuiTableActionListener::~GuiTableActionListener(void)
{
if (mWidget) {
mWidget->removeActionListener(this);
mWidget->_setParent(NULL);
}
}
void GuiTableActionListener::action(const gcn::ActionEvent& actionEvent)
{
mTable->setSelected(mRow, mColumn);
mTable->distributeActionEvent();
}
GuiTable::GuiTable(TableModel *initial_model) :
mLinewiseMode(false),
mModel(NULL),
mSelectedRow(0),
mSelectedColumn(0),
mTopWidget(NULL)
{
setModel(initial_model);
addMouseListener(this);
addKeyListener(this);
}
GuiTable::~GuiTable(void)
{
delete mModel;
}
TableModel* GuiTable::getModel(void) const
{
return mModel;
}
void GuiTable::setModel(TableModel *new_model)
{
if (mModel) {
uninstallActionListeners();
mModel->removeListener(this);
}
mModel = new_model;
installActionListeners();
if (new_model) {
new_model->installListener(this);
recomputeDimensions();
}
}
void GuiTable::recomputeDimensions(void)
{
int rows_nr = mModel->getRows();
int columns_nr = mModel->getColumns();
int width = 0;
int height = 0;
if (mSelectedRow >= rows_nr)
mSelectedRow = rows_nr - 1;
if (mSelectedColumn >= columns_nr)
mSelectedColumn = columns_nr - 1;
for (int i = 0; i < columns_nr; i++)
width += getColumnWidth(i);
height = getRowHeight() * rows_nr;
setWidth(width);
setHeight(height);
}
void GuiTable::setSelected(int row, int column)
{
mSelectedColumn = column;
mSelectedRow = row;
}
int GuiTable::getSelectedRow(void)
{
return mSelectedRow;
}
int GuiTable::getSelectedColumn(void)
{
return mSelectedColumn;
}
void GuiTable::setLinewiseSelection(bool linewise)
{
mLinewiseMode = linewise;
}
int GuiTable::getRowHeight(void)
{
if (mModel)
return mModel->getRowHeight() + 1; // border
else
return 0;
}
int GuiTable::getColumnWidth(int i)
{
if (mModel)
return mModel->getColumnWidth(i) + 1; // border
else
return 0;
}
void GuiTable::uninstallActionListeners(void)
{
for (std::vector<GuiTableActionListener *>::const_iterator it = action_listeners.begin(); it != action_listeners.end(); it++)
delete *it;
action_listeners.clear();
}
void GuiTable::installActionListeners(void)
{
if (!mModel)
return;
int rows = mModel->getRows();
int columns = mModel->getColumns();
for (int row = 0; row < rows; ++row)
for (int column = 0; column < columns; ++column) {
gcn::Widget *widget = mModel->getElementAt(row, column);
action_listeners.push_back(new GuiTableActionListener(this, widget,
row, column));
}
_setFocusHandler(_getFocusHandler()); // propagate focus handler to widgets
}
// -- widget ops
void GuiTable::draw(gcn::Graphics* graphics)
{
graphics->setColor(getBackgroundColor());
graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
if (!mModel)
return;
// First, determine how many rows we need to draw, and where we should start.
int first_row = -(getY() / getRowHeight());
if (first_row < 0)
first_row = 0;
int rows_nr = 1 + (getHeight() / getRowHeight()); // May overestimate by one.
int max_rows_nr = mModel->getRows() - first_row; // clip if neccessary:
if (max_rows_nr < rows_nr)
rows_nr = max_rows_nr;
// Now determine the first and last column
// Take the easy way out; these are usually bounded and all visible.
int first_column = 0;
int last_column = mModel->getColumns() - 1;
// Set up everything for drawing
int height = getRowHeight();
int y_offset = first_row * height;
for (int r = first_row; r < first_row + rows_nr; ++r) {
int x_offset = 0;
for (int c = first_column; c <= last_column; ++c) {
gcn::Widget *widget = mModel->getElementAt(r, c);
int width = getColumnWidth(c);
if (widget) {
gcn::Rectangle bounds(x_offset, y_offset, width, height);
if (widget == mTopWidget) {
bounds.height = widget->getHeight();
bounds.width = widget->getWidth();
}
widget->setDimension(bounds);
graphics->pushClipArea(bounds);
widget->draw(graphics);
graphics->popClipArea();
if (!mLinewiseMode
&& c == mSelectedColumn
&& r == mSelectedRow)
graphics->drawRectangle(bounds);
}
x_offset += width;
}
if (mLinewiseMode
&& r == mSelectedRow)
graphics->drawRectangle(gcn::Rectangle(0, y_offset,
x_offset, height));
y_offset += height;
}
if (mTopWidget) {
gcn::Rectangle bounds = mTopWidget->getDimension();
graphics->pushClipArea(bounds);
mTopWidget->draw(graphics);
graphics->popClipArea();
}
}
void GuiTable::logic(void)
{
}
void GuiTable::moveToTop(gcn::Widget *widget)
{
gcn::Widget::moveToTop(widget);
this->mTopWidget = widget;
}
void GuiTable::moveToBottom(gcn::Widget *widget)
{
gcn::Widget::moveToBottom(widget);
if (widget == this->mTopWidget)
this->mTopWidget = NULL;
}
gcn::Rectangle GuiTable::getChildrenArea(void)
{
return gcn::Rectangle(0, 0, getWidth(), getHeight());
}
// -- KeyListener notifications
void GuiTable::keyPressed(gcn::KeyEvent& keyEvent)
{
}
// -- MouseListener notifications
void GuiTable::mousePressed(gcn::MouseEvent& mouseEvent)
{
if (mouseEvent.getButton() == gcn::MouseEvent::LEFT) {
int row = getRowForY(mouseEvent.getY());
int column = getColumnForX(mouseEvent.getX());
if (row > -1 && column > -1) {
mSelectedColumn = column;
mSelectedRow = row;
}
distributeActionEvent();
}
}
void GuiTable::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent)
{
}
void GuiTable::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent)
{
}
void GuiTable::mouseDragged(gcn::MouseEvent& mouseEvent)
{
}
// -- TableModelListener notifications
void GuiTable::modelUpdated(bool completed)
{
if (completed) {
recomputeDimensions();
installActionListeners();
} else { // before the update?
mTopWidget = NULL; // No longer valid in general
uninstallActionListeners();
}
}
gcn::Widget* GuiTable::getWidgetAt(int x, int y)
{
int row = getRowForY(y);
int column = getColumnForX(x);
if (mTopWidget
&& mTopWidget->getDimension().isPointInRect(x, y))
return mTopWidget;
if (row > -1
&& column > -1) {
gcn::Widget *w = mModel->getElementAt(row, column);
if (w && w->isFocusable())
return w;
else
return NULL; // Grab the event locally
} else
return NULL;
}
int GuiTable::getRowForY(int y)
{
int row = y / getRowHeight();
if (row < 0
|| row >= mModel->getRows())
return -1;
else
return row;
}
int GuiTable::getColumnForX(int x)
{
int column;
int delta = 0;
for (column = 0; column < mModel->getColumns(); column++) {
delta += getColumnWidth(column);
if (x <= delta)
break;
}
if (column < 0
|| column >= mModel->getColumns())
return -1;
else
return column;
}
void GuiTable::_setFocusHandler(gcn::FocusHandler* focusHandler)
{
gcn::Widget::_setFocusHandler(focusHandler);
if (mModel)
for (int r = 0; r < mModel->getRows(); ++r)
for (int c = 0; c < mModel->getColumns(); ++c) {
gcn::Widget *w = mModel->getElementAt(r, c);
if (w)
w->_setFocusHandler(focusHandler);
}
}