/*
* Copyright 2012 AndroidPlot.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.androidplot.ui;
import android.graphics.RectF;
import java.util.Iterator;
/**
* Encapsulates the visual aspects of a table; number of rows and columns
* and the height and width in pixels of each element within the table.
* There is no support (yet) for variable size cells within a table; all
* cells within a table share the same dimensions.
*
* The DynamicTableModel provides an Iterator implementation which returns a RectF
* of each subsequent cell, based on the order of the plot. Tables with
* an order of COLUMN_MAJOR are traversed left to right column by column until
* the end of the row is reached, then proceeding to the next row.
* Tables with an order of ROW_MAJOR are traversed top to bottom row by row
* until the end of the row is reached, then proceeding to the next column.
*/
public class DynamicTableModel extends TableModel {
//private float cellWidth;
//private float cellHeight;
//private TableSizingMethod rowSizingMethod;
//private TableSizingMethod columnSizingMethod;
private int numRows;
private int numColumns;
private Float cellWidth;
private Float cellHeight;
private CellSizingMethod rowSizingMethod;
private CellSizingMethod columnSizingMethod;
/**
* Convenience method. Sets order to ROW_MAJOR.
* @param numColumns
* @param numRows
*/
public DynamicTableModel(int numColumns, int numRows) {
this(numColumns, numRows, TableOrder.ROW_MAJOR);
}
public DynamicTableModel(int numColumns, int numRows, TableOrder order) {
super(order);
this.numColumns = numColumns;
//this.cellWidth = cellWidth;
//this.rowSizingMethod = rowSizingMethod;
this.numRows = numRows;
//this.cellHeight = cellHeight;
//this.columnSizingMethod = columnSizingMethod;
//this.order = order;
}
/*public DynamicTableModel(Number colVal, CellSizingMethod colSzMethod, Number rowVal, CellSizingMethod rowSzMethod, TableOrder order) {
if(colVal == null || rowVal == null) {
throw new NullPointerException();
}
columnSizingMethod = colSzMethod;
switch(columnSizingMethod) {
case FILL:
numColumns = colVal.intValue();
break;
case FIXED:
cellWidth = colVal.floatValue();
break;
}
rowSzMethod = rowSzMethod;
}*/
@Override
public TableModelIterator getIterator(RectF tableRect, int totalElements) {
return new TableModelIterator(this, tableRect, totalElements);
}
/**
* Calculates the dimensions of a single element of this table with
* tableRect representing the overall dimensions of the table.
* @param tableRect Dimensions/position of the table
* @return a RectF representing the first (top-left) element in
* the tableRect passed in.
*/
public RectF getCellRect(RectF tableRect, int numElements) {
RectF cellRect = new RectF();
cellRect.left = tableRect.left;
cellRect.top = tableRect.top;
//cellRect.bottom = getElementHeightPix(tableRect);
cellRect.bottom = tableRect.top + calculateCellSize(tableRect, TableModel.Axis.ROW, numElements);
//cellRect.right = getElementWidthPix(tableRect);
cellRect.right = tableRect.left + calculateCellSize(tableRect, TableModel.Axis.COLUMN, numElements);
return cellRect;
}
/**
* Figure out the size of a single cell across the specified axis.
* @param tableRect
* @param axis
* @param numElementsInTable
* @return
*/
private float calculateCellSize(RectF tableRect,
Axis axis,
int numElementsInTable) {
//float elementSizeInPix = 0;
int axisElements = 0;
float axisSizePix = 0;
switch (axis) {
case ROW:
//elementSizeInPix = cellHeight;
axisElements = numRows;
axisSizePix = tableRect.height();
break;
case COLUMN:
//elementSizeInPix = cellWidth;
axisElements = numColumns;
axisSizePix = tableRect.width();
break;
}
//if (elementSizeInPix != 0) {
// return elementSizeInPix;
if(axisElements != 0) {
return axisSizePix / axisElements;
} else {
return axisSizePix / numElementsInTable;
}
}
public int getNumRows() {
return numRows;
}
public void setNumRows(int numRows) {
this.numRows = numRows;
}
public int getNumColumns() {
return numColumns;
}
public void setNumColumns(int numColumns) {
this.numColumns = numColumns;
}
/* public void setCellWidth(Float cellWidth) {
this.cellWidth = cellWidth;
}
public Float getCellWidth() {
return cellWidth;
}
public Float getCellHeight() {
return cellHeight;
}
public void setCellHeight(Float cellHeight) {
this.cellHeight = cellHeight;
}*/
private class TableModelIterator implements Iterator<RectF> {
private boolean isOk = true;
int lastColumn = 0; // most recent column iterated
int lastRow = 0; // most recent row iterated
int lastElement = 0; // last element index iterated
private DynamicTableModel dynamicTableModel;
private RectF tableRect;
private RectF lastElementRect;
private int totalElements;
private TableOrder order;
private int calculatedNumElements;
private int calculatedRows; // number of rows to be iterated
private int calculatedColumns; // number of columns to be iterated
public TableModelIterator(DynamicTableModel dynamicTableModel, RectF tableRect, int totalElements) {
this.dynamicTableModel = dynamicTableModel;
this.tableRect = tableRect;
this.totalElements = totalElements;
order = dynamicTableModel.getOrder();
// unlimited columns:
if(dynamicTableModel.getNumColumns() == 0 && dynamicTableModel.getNumRows() >= 1) {
calculatedRows = dynamicTableModel.getNumRows();
// round up:
calculatedColumns = new Float((totalElements / (float) calculatedRows) + 0.5).intValue();
} else if(dynamicTableModel.getNumRows() == 0 && dynamicTableModel.getNumColumns() >= 1) {
//order = TableOrder.ROW_MAJOR;
calculatedColumns = dynamicTableModel.getNumColumns();
calculatedRows = new Float((totalElements / (float) calculatedColumns) + 0.5).intValue();
// unlimited rows and columns (impossible) so default a single row with n columns:
}else if(dynamicTableModel.getNumColumns() == 0 && dynamicTableModel.getNumRows() == 0) {
calculatedRows = 1;
calculatedColumns = totalElements;
} else {
//order = dynamicTableModel.getOrder();
calculatedRows = dynamicTableModel.getNumRows();
calculatedColumns = dynamicTableModel.getNumColumns();
}
calculatedNumElements = calculatedRows * calculatedColumns;
lastElementRect = dynamicTableModel.getCellRect(tableRect, totalElements);
}
@Override
public boolean hasNext() {
return isOk && lastElement < calculatedNumElements;
}
@Override
public RectF next() {
if(!hasNext()) {
isOk = false;
throw new IndexOutOfBoundsException();
}
if (lastElement == 0) {
lastElement++;
return lastElementRect;
}
RectF nextElementRect = new RectF(lastElementRect);
switch (order) {
case ROW_MAJOR:
if (dynamicTableModel.getNumColumns() > 0 && lastColumn >= (dynamicTableModel.getNumColumns() - 1)) {
// move to the begining of the next row down:// move to the begining of the next row down:
nextElementRect.offsetTo(tableRect.left, lastElementRect.bottom);
lastColumn = 0;
lastRow++;
} else {
// move to the next column over:
nextElementRect.offsetTo(lastElementRect.right, lastElementRect.top);
lastColumn++;
}
break;
case COLUMN_MAJOR:
if (dynamicTableModel.getNumRows() > 0 && lastRow >= (dynamicTableModel.getNumRows() - 1)) {
// move to the top of the next column over:
nextElementRect.offsetTo(lastElementRect.right, tableRect.top);
lastRow = 0;
lastColumn++;
} else {
// move to the next row down:
nextElementRect.offsetTo(lastElementRect.left, lastElementRect.bottom);
lastRow++;
}
break;
// unknown/unsupported enum val:
default:
isOk = false;
throw new IllegalArgumentException();
}
lastElement++;
lastElementRect = nextElementRect;
return nextElementRect;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}