/*
* The Mana World
* Copyright 2007 The Mana World Development Team
*
* This file is part of The Mana World.
*
* The Mana World 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.
*
* The Mana World 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 The Mana World; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id$
*/
#include "layout.h"
void Layout::resizeGrid(int w, int h)
{
bool extW = w && w > (int)mSizes[0].size(),
extH = h && h > (int)mSizes[1].size();
if (!extW && !extH) return;
if (extH)
{
mSizes[1].resize(h, FILL);
mCells.resize(h);
if (!extW) w = mSizes[0].size();
}
if (extW)
{
mSizes[0].resize(w, FILL);
}
for (std::vector< std::vector< Cell > >::iterator
i = mCells.begin(), i_end = mCells.end(); i != i_end; ++i)
{
i->resize(w);
}
}
void Layout::setColWidth(int n, int w)
{
resizeGrid(n + 1, 0);
mSizes[0][n] = w;
}
void Layout::setRowHeight(int n, int h)
{
resizeGrid(0, n + 1);
mSizes[1][n] = h;
}
void Layout::matchColWidth(int n1, int n2)
{
resizeGrid(std::max(n1, n2) + 1, 0);
std::vector< int > widths = compute(0, mW);
int s = std::max(widths[n1], widths[n2]);
mSizes[0][n1] = s;
mSizes[0][n2] = s;
}
Cell &Layout::place(gcn::Widget *widget, int x, int y, int w, int h)
{
resizeGrid(x + w, y + h);
Cell &cell = mCells[y][x];
cell.mWidget = widget;
cell.mExtent[0] = w;
cell.mExtent[1] = h;
cell.mPadding = 0;
cell.mAlign[0] = Cell::FILL;
cell.mAlign[1] = Cell::FILL;
int &cs = mSizes[0][x], &rs = mSizes[1][y];
if (cs == FILL) cs = 0;
if (rs == FILL) rs = 0;
return cell;
}
void Layout::align(int &pos, int &size, int dim,
Cell const &cell, int *sizes) const
{
int size_max = sizes[0] - cell.mPadding * 2;
for (int i = 1; i < cell.mExtent[dim]; ++i)
size_max += sizes[i] + mSpacing;
size = std::min(dim == 0 ? cell.mWidget->getWidth()
: cell.mWidget->getHeight(), size_max);
pos += cell.mPadding;
switch (cell.mAlign[dim])
{
case Cell::LEFT:
return;
case Cell::RIGHT:
pos += size_max - size;
return;
case Cell::CENTER:
pos += (size_max - size) / 2;
return;
case Cell::FILL:
size = size_max;
return;
}
}
std::vector< int > Layout::compute(int dim, int upp) const
{
int gridW = mSizes[0].size(), gridH = mSizes[1].size();
std::vector< int > sizes = mSizes[dim];
// Compute minimum sizes.
for (int gridY = 0; gridY < gridH; ++gridY)
{
for (int gridX = 0; gridX < gridW; ++gridX)
{
Cell const &cell = mCells[gridY][gridX];
if (!cell.mWidget) continue;
if (cell.mExtent[dim] == 1)
{
int s = dim == 0 ? cell.mWidget->getWidth()
: cell.mWidget->getHeight();
int n = dim == 0 ? gridX : gridY;
s += cell.mPadding * 2;
if (s > sizes[n]) sizes[n] = s;
}
}
}
// Compute the FILL sizes.
int nb = sizes.size();
int nbFill = 0;
for (int i = 0; i < nb; ++i)
{
if (mSizes[dim][i] == FILL) ++nbFill;
if (sizes[i] == FILL) sizes[i] = 0;
else upp -= sizes[i];
upp -= mSpacing;
}
upp = upp + mSpacing - mMargin * 2;
if (nbFill == 0) return sizes;
for (int i = 0; i < nb; ++i)
{
if (mSizes[dim][i] != FILL) continue;
int s = upp / nbFill;
sizes[i] += s;
upp -= s;
--nbFill;
}
return sizes;
}
void Layout::reflow(int &nw, int &nh)
{
int gridW = mSizes[0].size(), gridH = mSizes[1].size();
std::vector< int > widths = compute(0, mW);
std::vector< int > heights = compute(1, mH);
int x, y = mY + mMargin;
for (int gridY = 0; gridY < gridH; ++gridY)
{
x = mX + mMargin;
for (int gridX = 0; gridX < gridW; ++gridX)
{
Cell &cell = mCells[gridY][gridX];
if (cell.mWidget)
{
int dx = x, dy = y, dw, dh;
align(dx, dw, 0, cell, &widths[gridX]);
align(dy, dh, 1, cell, &heights[gridY]);
cell.mWidget->setDimension(gcn::Rectangle(dx, dy, dw, dh));
}
x += widths[gridX] + mSpacing;
}
y += heights[gridY] + mSpacing;
}
nw = x - mX - mSpacing + mMargin;
nh = y - mY - mSpacing + mMargin;
}
void Layout::flush()
{
int w, h;
reflow(w, h);
mY += h;
mW = w;
mSizes[0].clear();
mSizes[1].clear();
mCells.clear();
}