warzone2100/lib/betawidget/table.c

232 lines
5.5 KiB
C

/*
This file is part of Warzone 2100.
Copyright (C) 2008 Freddie Witherden
Copyright (C) 2008 Warzone Resurrection Project
Warzone 2100 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
(at your option) any later version.
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "table.h"
/**
* Child bookkeeping information.
*/
typedef struct
{
/// What row the child is in
int row;
/// What column the child is in
int column;
/// How many rows the child spans
int rowspan;
/// How many columns the child spans
int colspan;
} childPositionInfo;
static tableVtbl vtbl;
const classInfo tableClassInfo =
{
&widgetClassInfo,
"table"
};
table *tableCreate(const char *id)
{
table *instance = malloc(sizeof(table));
if (instance == NULL)
{
return NULL;
}
// Call the constructor
tableInit(instance, id);
// Return the new object
return instance;
}
static void tableInitVtbl(table *self)
{
static bool initialised = false;
if (!initialised)
{
// Copy our parents vtable into ours
vtbl.widgetVtbl = *(WIDGET(self)->vtbl);
// Overload widget's destroy, draw and size methods
vtbl.widgetVtbl.destroy = tableDestroyImpl;
vtbl.widgetVtbl.addChild = tableAddChildDummyImpl;
vtbl.widgetVtbl.doDraw = tableDoDrawImpl;
vtbl.widgetVtbl.doLayout = tableDoLayoutImpl;
vtbl.widgetVtbl.getMinSize = tableGetMinSizeImpl;
vtbl.widgetVtbl.getMaxSize = tableGetMaxSizeImpl;
initialised = true;
}
// Replace our parents vtable with our own
WIDGET(self)->vtbl = &vtbl.widgetVtbl;
// Set our vtable
self->vtbl = &vtbl;
}
void tableInit(table *self, const char *id)
{
// Init our parent
widgetInit(WIDGET(self), id);
// Prepare our vtable
tableInitVtbl(self);
// Set our type
WIDGET(self)->classInfo = &tableClassInfo;
// Prepare our child postional info container
self->childPositions = vectorCreate();
// We have no children and therefore no rows/columns
self->numRows = self->numColumns = 0;
}
void tableDestroyImpl(widget *self)
{
// Destroy child positional info
vectorMapAndDestroy(TABLE(self)->childPositions, free);
// Call our parents destructor
widgetDestroyImpl(self);
}
int tableGetRowCount(table *self)
{
return self->numRows;
}
int tableGetColumnCount(table *self)
{
return self->numColumns;
}
widget *tableGetCell(table *self, int row, int column)
{
int i;
const int numChildren = vectorSize(WIDGET(self)->children);
// Ensure that row and column are valid
assert(row > 0 && row <= tableGetRowCount(self));
assert(column > 0 && column <= tableGetColumnCount(self));
// Search through the list of children until we find one matching
for (i = 0; i < numChildren; i++)
{
// Legal as widget->children and table->childPositions are siblings
const childPositionInfo *pos = vectorAt(self->childPositions, i);
// See if it is a match
if (row >= pos->row && row < pos->row + pos->rowspan
&& column >= pos->column && column < pos->column + pos->colspan)
{
// We've found the widget
return vectorAt(WIDGET(self)->children, i);
}
}
// If the cell is empty, return NULL
return NULL;
}
bool tableAddChildDummyImpl(widget *self, widget *child)
{
assert(!"widgetAddChild can not be called directly on tables; "
"please use tableAddChild instead");
return false;
}
bool tableAddChild(table *self, widget *child, int row, int column)
{
// Map onto tableAddSpannedChild with rowspan and colspan as 1
return tableAddSpannedChild(self, child, row, 1, column, 1);
}
bool tableAddSpannedChild(table *self, widget *child, int row, int rowspan,
int column, int colspan)
{
int i, j;
const int rowCount = tableGetRowCount(self);
const int columnCount = tableGetColumnCount(self);
childPositionInfo *pos;
// First, check the row and column is valid
assert(row > 0 && row <= tableGetRowCount(self) + 1);
assert(column > 0 && column <= tableGetColumnCount(self) + 1);
// Second, check all of the cells spanned are empty
for (i = row; i < row + rowspan - 1 && i < rowCount; i++)
{
for (j = column; j < column + colspan - 1 && j < columnCount; j++)
{
assert(tableGetCell(self, i, j) == NULL);
}
}
// Update the row and column counts
self->numRows = MAX(rowCount, row + rowspan - 1);
self->numColumns = MAX(columnCount, column + colspan - 1);
// Add positional information regarding the child to our list
pos = malloc(sizeof(childPositionInfo));
pos->row = row;
pos->rowspan = rowspan;
pos->column = column;
pos->colspan = colspan;
vectorAdd(self->childPositions, pos);
// Call widgetAddChildImpl, which will add the child and re-do the layout
if (widgetAddChildImpl(WIDGET(self), child))
{
return true;
}
// Problem adding the child; positional information needs to be restored
else
{
vectorRemoveAt(self->childPositions, vectorSize(self->childPositions) - 1);
// Release the memory we malloc'ed earlier
free(pos);
// Restore the row and column counts
self->numRows = rowCount;
self->numColumns = columnCount;
return false;
}
}
void tableDoDrawImpl(widget *self)
{
// NO-OP
(void) self;
}