1/*
2 * Copyright 2012 AndroidPlot.com
3 *
4 *    Licensed under the Apache License, Version 2.0 (the "License");
5 *    you may not use this file except in compliance with the License.
6 *    You may obtain a copy of the License at
7 *
8 *        http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *    Unless required by applicable law or agreed to in writing, software
11 *    distributed under the License is distributed on an "AS IS" BASIS,
12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *    See the License for the specific language governing permissions and
14 *    limitations under the License.
15 */
16
17package com.androidplot.ui;
18
19import android.graphics.RectF;
20
21import java.util.Iterator;
22
23/**
24 * Encapsulates the visual aspects of a table; number of rows and columns
25 * and the height and width in pixels of each element within the table.
26 * There is no support (yet) for variable size cells within a table;  all
27 * cells within a table share the same dimensions.
28 *
29 * The DynamicTableModel provides an Iterator implementation which returns a RectF
30 * of each subsequent cell, based on the order of the plot.  Tables with
31 * an order of COLUMN_MAJOR are traversed left to right column by column until
32 * the end of the row is reached, then proceeding to the next row.
33 * Tables with an order of ROW_MAJOR are traversed top to bottom row by row
34 * until the end of the row is reached, then proceeding to the next column.
35 */
36public class DynamicTableModel extends TableModel {
37
38    //private float cellWidth;
39    //private float cellHeight;
40    //private TableSizingMethod rowSizingMethod;
41    //private TableSizingMethod columnSizingMethod;
42
43
44    private int numRows;
45    private int numColumns;
46
47    private Float cellWidth;
48    private Float cellHeight;
49
50    private CellSizingMethod rowSizingMethod;
51    private CellSizingMethod columnSizingMethod;
52
53    /**
54     * Convenience method.  Sets order to ROW_MAJOR.
55     * @param numColumns
56     * @param numRows
57     */
58    public DynamicTableModel(int numColumns, int numRows) {
59        this(numColumns, numRows, TableOrder.ROW_MAJOR);
60
61    }
62
63    public DynamicTableModel(int numColumns, int numRows, TableOrder order) {
64        super(order);
65        this.numColumns = numColumns;
66        //this.cellWidth = cellWidth;
67        //this.rowSizingMethod = rowSizingMethod;
68        this.numRows = numRows;
69        //this.cellHeight = cellHeight;
70        //this.columnSizingMethod = columnSizingMethod;
71        //this.order = order;
72    }
73
74    /*public DynamicTableModel(Number colVal, CellSizingMethod colSzMethod, Number rowVal, CellSizingMethod rowSzMethod, TableOrder order) {
75        if(colVal == null || rowVal == null) {
76            throw new NullPointerException();
77        }
78        columnSizingMethod = colSzMethod;
79        switch(columnSizingMethod) {
80            case FILL:
81                numColumns = colVal.intValue();
82                break;
83            case FIXED:
84                cellWidth = colVal.floatValue();
85                break;
86        }
87        rowSzMethod = rowSzMethod;
88    }*/
89
90    @Override
91    public TableModelIterator getIterator(RectF tableRect, int totalElements) {
92        return new TableModelIterator(this, tableRect, totalElements);
93    }
94
95    /**
96     * Calculates the dimensions of a single element of this table with
97     * tableRect representing the overall dimensions of the table.
98     * @param tableRect Dimensions/position of the table
99     * @return a RectF representing the first (top-left) element in
100     * the tableRect passed in.
101     */
102    public RectF getCellRect(RectF tableRect, int numElements) {
103        RectF cellRect = new RectF();
104        cellRect.left = tableRect.left;
105        cellRect.top = tableRect.top;
106        //cellRect.bottom = getElementHeightPix(tableRect);
107        cellRect.bottom = tableRect.top + calculateCellSize(tableRect, TableModel.Axis.ROW, numElements);
108        //cellRect.right = getElementWidthPix(tableRect);
109        cellRect.right = tableRect.left + calculateCellSize(tableRect, TableModel.Axis.COLUMN, numElements);
110        return cellRect;
111    }
112
113    /**
114     * Figure out the size of a single cell across the specified axis.
115     * @param tableRect
116     * @param axis
117     * @param numElementsInTable
118     * @return
119     */
120    private float calculateCellSize(RectF tableRect,
121                                    Axis axis,
122                                    int numElementsInTable) {
123        //float elementSizeInPix = 0;
124        int axisElements = 0;
125
126        float axisSizePix = 0;
127        switch (axis) {
128            case ROW:
129                //elementSizeInPix = cellHeight;
130                axisElements = numRows;
131                axisSizePix = tableRect.height();
132                break;
133            case COLUMN:
134                //elementSizeInPix = cellWidth;
135                axisElements = numColumns;
136                axisSizePix = tableRect.width();
137                break;
138        }
139        //if (elementSizeInPix != 0) {
140        //    return elementSizeInPix;
141        if(axisElements != 0) {
142            return axisSizePix / axisElements;
143        } else {
144            return axisSizePix / numElementsInTable;
145        }
146    }
147
148
149
150    public int getNumRows() {
151        return numRows;
152    }
153
154    public void setNumRows(int numRows) {
155        this.numRows = numRows;
156    }
157
158    public int getNumColumns() {
159        return numColumns;
160    }
161
162    public void setNumColumns(int numColumns) {
163        this.numColumns = numColumns;
164    }
165
166/*    public void setCellWidth(Float cellWidth) {
167        this.cellWidth = cellWidth;
168    }
169
170    public Float getCellWidth() {
171        return cellWidth;
172    }
173
174    public Float getCellHeight() {
175        return cellHeight;
176    }
177
178    public void setCellHeight(Float cellHeight) {
179        this.cellHeight = cellHeight;
180    }*/
181
182    private class TableModelIterator implements Iterator<RectF> {
183        private boolean isOk = true;
184        int lastColumn = 0;                     // most recent column iterated
185        int lastRow = 0;                        // most recent row iterated
186        int lastElement = 0;                    // last element index iterated
187        private DynamicTableModel dynamicTableModel;
188        private RectF tableRect;
189        private RectF lastElementRect;
190        private int totalElements;
191        private TableOrder order;
192
193        private int calculatedNumElements;
194        private int calculatedRows;             // number of rows to be iterated
195        private int calculatedColumns;          // number of columns to be iterated
196
197        public TableModelIterator(DynamicTableModel dynamicTableModel, RectF tableRect, int totalElements) {
198            this.dynamicTableModel = dynamicTableModel;
199            this.tableRect = tableRect;
200            this.totalElements = totalElements;
201            order = dynamicTableModel.getOrder();
202
203            // unlimited columns:
204            if(dynamicTableModel.getNumColumns() == 0 && dynamicTableModel.getNumRows() >= 1) {
205                calculatedRows = dynamicTableModel.getNumRows();
206
207                // round up:
208                calculatedColumns = new Float((totalElements / (float) calculatedRows) + 0.5).intValue();
209            } else if(dynamicTableModel.getNumRows() == 0 && dynamicTableModel.getNumColumns() >= 1) {
210                //order = TableOrder.ROW_MAJOR;
211                calculatedColumns = dynamicTableModel.getNumColumns();
212                calculatedRows = new Float((totalElements / (float) calculatedColumns) + 0.5).intValue();
213            // unlimited rows and columns (impossible) so default a single row with n columns:
214            }else if(dynamicTableModel.getNumColumns() == 0 && dynamicTableModel.getNumRows() == 0) {
215                calculatedRows = 1;
216                calculatedColumns = totalElements;
217            } else {
218                //order = dynamicTableModel.getOrder();
219                calculatedRows = dynamicTableModel.getNumRows();
220                calculatedColumns = dynamicTableModel.getNumColumns();
221            }
222            calculatedNumElements = calculatedRows * calculatedColumns;
223            lastElementRect = dynamicTableModel.getCellRect(tableRect, totalElements);
224        }
225
226        @Override
227        public boolean hasNext() {
228            return isOk && lastElement < calculatedNumElements;
229        }
230
231        @Override
232        public RectF next() {
233            if(!hasNext()) {
234                isOk = false;
235                throw new IndexOutOfBoundsException();
236            }
237
238            if (lastElement == 0) {
239                lastElement++;
240                return lastElementRect;
241            }
242
243            RectF nextElementRect = new RectF(lastElementRect);
244
245            switch (order) {
246                case ROW_MAJOR:
247                    if (dynamicTableModel.getNumColumns() > 0 && lastColumn >= (dynamicTableModel.getNumColumns() - 1)) {
248                        // move to the begining of the next row down:// move to the begining of the next row down:
249                        nextElementRect.offsetTo(tableRect.left, lastElementRect.bottom);
250                        lastColumn = 0;
251                        lastRow++;
252                    } else {
253                        // move to the next column over:
254                        nextElementRect.offsetTo(lastElementRect.right, lastElementRect.top);
255                        lastColumn++;
256                    }
257                    break;
258                case COLUMN_MAJOR:
259                    if (dynamicTableModel.getNumRows() > 0 && lastRow >= (dynamicTableModel.getNumRows() - 1)) {
260                        // move to the top of the next column over:
261                        nextElementRect.offsetTo(lastElementRect.right, tableRect.top);
262                        lastRow = 0;
263                        lastColumn++;
264                    } else {
265                        // move to the next row down:
266                        nextElementRect.offsetTo(lastElementRect.left, lastElementRect.bottom);
267                        lastRow++;
268                    }
269                    break;
270                // unknown/unsupported enum val:
271                default:
272                    isOk = false;
273                    throw new IllegalArgumentException();
274            }
275            lastElement++;
276            lastElementRect = nextElementRect;
277            return nextElementRect;
278
279        }
280
281        @Override
282        public void remove() {
283            throw new UnsupportedOperationException();
284        }
285    }
286}
287