All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.codename1.ui.table.TableLayout Maven / Gradle / Ivy

There is a newer version: 7.0.164
Show newest version
/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores
 * CA 94065 USA or visit www.oracle.com if you need additional information or
 * have any questions.
 */
package com.codename1.ui.table;

import com.codename1.io.Log;
import com.codename1.ui.layouts.*;
import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.geom.Dimension;
import com.codename1.ui.plaf.Style;
import java.util.Vector;

/**
 * 

TableLayout is a very elaborate constraint based layout manager that can arrange elements * in rows/columns while defining constraints to control complex behavior such as spanning, alignment/weight * etc.
* Notice that the table layout is in the com.codename1.ui.table package and not in the * layouts package.
* This is due to the fact that TableLayout was originally designed for the * {@link Table} class.

* *

Despite being constraint based the table layout isn't strict about constraints and will implicitly add a * constraint when one is missing. However, unlike grid layout table layout won't implicitly add a row if the * row/column count is incorrect
* E.g this creates a 2x2 table but adds 5 elements. The 5th element won't show:

* * TableLayout 2x2 missing component * *

Table layout supports the ability to grow the last column which can be enabled using the * setGrowHorizontally method. You can also use a shortened terse syntax to construct a table * layout however since the table layout is a constraint based layout you won't be able to utilize its full power * with this syntax. * *

The default usage of the encloseIn below uses the setGrowHorizontally flag.

* * TableLayout that grows the last column * *

The Full Potential

*

To truly appreciate the {@code TableLayout} we need to use the constraint syntax which allows * us to span, align and set width/height for the rows & columns.
* Table layout works with a {@link Constraint} instance that can communicate our intentions into the * layout manager. Such constraints can include more than one attribute e.g. span and height.

* *

Notice that table layout constraints can't be reused for more than one component.
* The constraint class supports the following attributes:

* * * * * * * * * * * * * * * * * * * * * * * * * * *
column The column for the table cell. This defaults to -1 which will just place the component in the next available cell
row Similar to column, defaults to -1 as well
width The column width in percentages, -1 will use the preferred size. -2 for width will take up the rest of the available space
height The row height in percentages, -1 will use the preferred size. -2 for height will take up the rest of the available space
spanHorizontal The cells that should be occupied horizontally defaults to 1 and can't exceed the column count - current offset.
spanVertical Similar to spanHorizontal with the same limitations
horizontalAlign The horizontal alignment of the content within the cell, defaults to the special case -1 value to take up all the cell space can be either `-1`, `Component.LEFT`, `Component.RIGHT` or `Component.CENTER`
verticalAlign Similar to horizontalAlign can be one of `-1`, `Component.TOP`, `Component.BOTTOM` or `Component.CENTER`
* *

Notice that you only need to set `width`/`height` to one cell in a column/row.
* The table layout constraint sample tries to demonstrate some of the unique things you can do with constraints.
* * We go into further details on this in the developer guide * so check that out.

* * TableLayout with complex constraints * * @author Shai Almog */ public class TableLayout extends Layout { /** * Represents the layout constraint for an entry within the table indicating * the desired position/behavior of the component. */ public static class Constraint { private Component parent; private int row = -1; private int column = -1; private int width = defaultColumnWidth; private int height = defaultRowHeight; private int spanHorizontal = 1; private int spanVertical = 1; private int align = -1; private int valign = -1; int actualRow = -1; int actualColumn = -1; /** * {@inheritDoc} */ public String toString() { return "row: " + row + " column: " + column + " width: " + width + " height: " + height + " hspan: " + spanHorizontal + " vspan: " + spanVertical + " align " + align + " valign " + valign; } /** * Sets the cells to span vertically, this number must never be smaller than 1 * * @param span a number larger than 1 */ public void setVerticalSpan(int span) { if(span < 1) { throw new IllegalArgumentException("Illegal span"); } spanVertical = span; } /** * Sets the cells to span vertically, this number must never be smaller than 1 * * @param span a number larger than 1 * @return this */ public Constraint verticalSpan(int span) { setVerticalSpan(span); return this; } /** * Sets the cells to span vertically, this number must never be smaller than 1 * * @param span a number larger than 1 * @return this */ public Constraint vs(int span) { return verticalSpan(span); } /** * Sets the cells to span horizontally, this number must never be smaller than 1 * * @param span a number larger than 1 */ public void setHorizontalSpan(int span) { if(span < 1) { throw new IllegalArgumentException("Illegal span"); } spanHorizontal = span; } /** * Sets the cells to span horizontally, this number must never be smaller than 1 * * @param span a number larger than 1 */ public Constraint horizontalSpan(int span) { setHorizontalSpan(span); return this; } /** * Sets the cells to span horizontally, this number must never be smaller than 1 * * @param span a number larger than 1 */ public Constraint hs(int span) { return horizontalSpan(span); } /** * Sets the column width based on percentage of the parent * * @param width negative number indicates ignoring this member */ public void setWidthPercentage(int width) { this.width = width; } /** * Sets the column width based on percentage of the parent * * @param width negative number indicates ignoring this member */ public Constraint widthPercentage(int width) { this.width = width; return this; } /** * Sets the column width based on percentage of the parent * * @param width negative number indicates ignoring this member */ public Constraint wp(int width) { return widthPercentage(width); } /** * Sets the row height based on percentage of the parent * * @param height negative number indicates ignoring this member */ public void setHeightPercentage(int height) { this.height = height; } /** * Sets the row height based on percentage of the parent * * @param height negative number indicates ignoring this member */ public Constraint heightPercentage(int height) { this.height = height; return this; } /** * Sets the row height based on percentage of the parent * * @param height negative number indicates ignoring this member */ public Constraint hp(int height) { return heightPercentage(height); } /** * Sets the horizontal alignment of the table cell * * @param align Component.LEFT/RIGHT/CENTER */ public void setHorizontalAlign(int align) { this.align = align; } /** * Sets the horizontal alignment of the table cell * * @param align Component.LEFT/RIGHT/CENTER */ public Constraint horizontalAlign(int align) { this.align = align; return this; } /** * Sets the horizontal alignment of the table cell * * @param align Component.LEFT/RIGHT/CENTER */ public Constraint ha(int align) { return horizontalAlign(align); } /** * Sets the vertical alignment of the table cell * * @param valign Component.TOP/BOTTOM/CENTER */ public void setVerticalAlign(int valign) { this.valign = valign; } /** * Sets the vertical alignment of the table cell * * @param valign Component.TOP/BOTTOM/CENTER */ public Constraint verticalAlign(int valign) { this.valign = valign; return this; } /** * Sets the vertical alignment of the table cell * * @param valign Component.TOP/BOTTOM/CENTER */ public Constraint va(int valign) { return verticalAlign(valign); } /** * @return the row */ public int getRow() { return row; } /** * @return the column */ public int getColumn() { return column; } /** * @return the width */ public int getWidthPercentage() { return width; } /** * @return the height */ public int getHeightPercentage() { return height; } /** * @return the spanHorizontal */ public int getHorizontalSpan() { return spanHorizontal; } /** * @return the spanVertical */ public int getVerticalSpan() { return spanVertical; } /** * @return the align */ public int getHorizontalAlign() { return align; } /** * @return the valign */ public int getVerticalAlign() { return valign; } } private int currentRow; private int currentColumn; private static int minimumSizePerColumn = 10; private Constraint[] tablePositions; private int[] columnPositions; private int[] rowPositions; /** * Special case marker SPAN constraint reserving place for other elements */ private static final Constraint H_SPAN_CONSTRAINT = new Constraint(); private static final Constraint V_SPAN_CONSTRAINT = new Constraint(); private static final Constraint VH_SPAN_CONSTRAINT = new Constraint(); private static int defaultColumnWidth = -1; private static int defaultRowHeight = -1; private boolean horizontalSpanningExists; private boolean verticalSpanningExists; private int rows, columns; private boolean growHorizontally; private boolean truncateHorizontally; //whether we should truncate or shrink the table if the prefered width of all elements exceed the available width. default = false = shrink private boolean truncateVertically; /** * A table must declare the amount of rows and columns in advance * * @param rows rows of the table * @param columns columns of the table */ public TableLayout(int rows, int columns) { this.rows = rows; this.columns = columns; tablePositions = new Constraint[rows * columns]; } /** * Get the number of rows * @return number of rows */ public int getRows() { return rows; } /** * Get the number of columns * @return number of columns */ public int getColumns() { return columns; } /** * Returns the component at the given row/column * * @param row the row of the component * @param column the column of the component * @return the component instance */ public Component getComponentAt(int row, int column) { int pos = row * columns + column; if(pos > -1 && pos < tablePositions.length) { Constraint c =tablePositions[pos]; return c != null ? c.parent : null; } return null; } /** * {@inheritDoc} */ public void layoutContainer(Container parent) { try { verticalSpanningExists = false; horizontalSpanningExists = false; // column and row size in pixels Style s = parent.getStyle(); int top = s.getPaddingTop(); int left = s.getPaddingLeft(parent.isRTL()); int bottom = s.getPaddingBottom(); int right = s.getPaddingRight(parent.isRTL()); boolean rtl = parent.isRTL(); //compute columns width and X position int[] columnSizes = new int[columns]; boolean[] modifableColumnSize = new boolean[columns]; boolean[] growingColumnSize = new boolean[columns]; columnPositions = new int[columns]; int pWidth = parent.getLayoutWidth() - parent.getSideGap() - left - right; int cslen = columnSizes.length; int availableReminder = pWidth; int growingWidth = 0; boolean hasGrowingCols = false; int totalWidth = 0; int totalModifyablePixels = 0; for(int iter = 0 ; iter < cslen ; iter++) { int[] psize = getColumnWidthPixels(iter, pWidth); columnSizes[iter] = psize[0]; availableReminder -= columnSizes[iter]; totalWidth += columnSizes[iter]; if( psize[1]<0) { modifableColumnSize[iter] = true; totalModifyablePixels += columnSizes[iter]; } if(psize[1]<-1) { growingColumnSize[iter]=true; hasGrowingCols = true; growingWidth += columnSizes[iter]; } } //If there is some space left and some "auto growing" columns, attribute them the availableReminder space if (hasGrowingCols && availableReminder>0) { for(int iter = 0 ; iter < cslen ; iter++) { if (growingColumnSize[iter]) { int sp = (int) (((float)columnSizes[iter]) / ((float)growingWidth) * availableReminder); columnSizes[iter]+=sp; } } } // For horizontally scrollable tables, if not enough room is available // to correctly display all the components given their preferred width, truncate or shrink the table if(!parent.isScrollableX() && pWidth < totalWidth) { if (truncateHorizontally) { //TODO: see if this is actually necessary to recompute the column size for truncated columns as the drawer should already automatically clip components with pixels out of the drawing boundaries availableReminder = pWidth; for(int iter = 0 ; iter < cslen ; iter++) { columnSizes[iter] = Math.min(columnSizes[iter], Math.max(0, availableReminder)); availableReminder -= columnSizes[iter]; } } else { // try to recalculate the columns width so they are distributed sensibly int totalPixelsToRemove = totalWidth - pWidth; int totalPixelsNecessary = totalModifyablePixels - totalPixelsToRemove; // Go over the modifyable columns and remove the right pixels according to the ratio for(int iter = 0 ; iter < cslen ; iter++) { if(modifableColumnSize[iter]) { columnSizes[iter] = (int)(((float)columnSizes[iter]) / ((float)totalModifyablePixels) * totalPixelsNecessary); } } } } //Compute X position int currentX = left; for(int iter = 0 ; iter < cslen ; iter++) { if(rtl) { currentX += columnSizes[iter]; columnPositions[iter] = pWidth - currentX; } else { columnPositions[iter] = currentX; currentX += columnSizes[iter]; } } //Compute rows height and Y position int[] rowSizes = new int[rows]; boolean[] modifableRowSize = new boolean[rows]; boolean[] growingRowSize = new boolean[rows]; rowPositions = new int[rows]; int pHeight = parent.getLayoutHeight() - parent.getBottomGap() - top - bottom; int rlen = rowSizes.length; availableReminder = pHeight; int growingHeight= 0; boolean hasGrowingRows = false; int totalHeight = 0; totalModifyablePixels = 0; for(int iter = 0 ; iter < rlen ; iter++) { int[] psize = getRowHeightPixels(iter, pHeight); rowSizes[iter] = psize[0]; availableReminder -= rowSizes[iter]; totalHeight += rowSizes[iter]; if(psize[0]<0) { modifableRowSize[iter] = true; totalModifyablePixels += rowSizes[iter]; } if(psize[0]<-1) { growingRowSize[iter] = true; hasGrowingRows = true; growingHeight += rowSizes[iter]; } } //If there is some space left and some "auto growing" rows, attribute them the availableReminder space if (hasGrowingRows && availableReminder>0) { for(int iter = 0 ; iter < rlen ; iter++) { if (growingRowSize[iter]) { int sp = (int) (((float)rowSizes[iter]) / ((float)growingHeight) * availableReminder); rowSizes[iter]+=sp; } } } // For vertically scrollable tables, if not enough room is available // to correctly display all the components given their preferred height, truncate or shrink the table if(!parent.isScrollableY() && pHeight < totalHeight) { if (truncateVertically) { //TODO: see if this is actually necessary to recompute the row size for truncated rows as the drawer should already automatically clip components with pixels out of the drawing boundaries availableReminder = pHeight; for(int iter = 0 ; iter < rlen ; iter++) { rowSizes[iter] = Math.min(rowSizes[iter], Math.max(0, availableReminder)); availableReminder -= rowSizes[iter]; } } else { // try to recalculate the rows height so they are distributed sensibly int totalPixelsToRemove = totalHeight - pHeight; int totalPixelsNecessary = totalModifyablePixels - totalPixelsToRemove; // Go over the modifyable rows and remove the bottom pixels according to the ratio for(int iter = 0 ; iter < rlen ; iter++) { if(modifableRowSize[iter]) { rowSizes[iter] = (int)(((float)rowSizes[iter]) / ((float)totalModifyablePixels) * totalPixelsNecessary); } } } } //Compute Y position int currentY = top; for(int iter = 0 ; iter < rlen ; iter++) { rowPositions[iter] = currentY; currentY += rowSizes[iter]; } //Place each cell component int clen = columnSizes.length; for(int r = 0 ; r < rlen ; r++) { for(int c = 0 ; c < clen ; c++) { Constraint con = tablePositions[r * columns + c]; int conX, conY, conW, conH; if(con != null && con != H_SPAN_CONSTRAINT && con != V_SPAN_CONSTRAINT && con != VH_SPAN_CONSTRAINT) { Style componentStyle = con.parent.getStyle(); int leftMargin = componentStyle.getMarginLeft(parent.isRTL()); int topMargin = componentStyle.getMarginTop(); // conX = left + leftMargin + columnPositions[c]; // bugfix table with padding not drawn correctly // conY = top + topMargin + rowPositions[r]; // bugfix table with padding not drawn correctly conX = leftMargin + columnPositions[c]; conY = topMargin + rowPositions[r]; if(con.spanHorizontal > 1) { horizontalSpanningExists = true; int w = columnSizes[c]; for(int sh = 1 ; sh < con.spanHorizontal ; sh++) { w += columnSizes[Math.min(c + sh, columnSizes.length - 1)]; } // for RTL we need to move the component to the side so spanning will work if(rtl) { int spanEndPos = c + con.spanHorizontal - 1; if (spanEndPos < 0) { spanEndPos = 0; } else if (spanEndPos > clen - 1) { spanEndPos = clen-1; } conX = left + leftMargin + columnPositions[spanEndPos]; } conW = w - leftMargin - componentStyle.getMarginRight(parent.isRTL()); } else { conW = columnSizes[c] - leftMargin - componentStyle.getMarginRight(parent.isRTL()); } if(con.spanVertical > 1) { verticalSpanningExists = true; int h = rowSizes[r]; for(int sv = 1 ; sv < con.spanVertical ; sv++) { h += rowSizes[Math.min(r + sv, rowSizes.length - 1)]; } conH = h - topMargin - componentStyle.getMarginBottom(); } else { conH = rowSizes[r] - topMargin - componentStyle.getMarginBottom(); } placeComponent(rtl, con, conX, conY, conW, conH); } } } } catch(ArrayIndexOutOfBoundsException err) { Log.e(err); } } /** * Returns the position of the given table row. A valid value is only returned after the * layout occurred. * * @param row the row in the table * @return the Y position in pixels or -1 if layout hasn't occured/row is too large etc. */ public int getRowPosition(int row) { if(rowPositions != null && rowPositions.length > row) { return rowPositions[row]; } return -1; } /** * Returns the position of the given table column. A valid value is only returned after the * layout occurred. * * @param col the column in the table * @return the X position in pixels or -1 if layout hasn't occured/column is too large etc. */ public int getColumnPosition(int col) { if(columnPositions != null && columnPositions.length > col) { return columnPositions[col]; } return -1; } /** * Places the component/constraint in the proper alignment within the cell whose bounds are given */ private void placeComponent(boolean rtl, Constraint con, int x, int y, int width, int height) { con.parent.setX(x); con.parent.setY(y); con.parent.setWidth(width); con.parent.setHeight(height); Dimension pref = con.parent.getPreferredSize(); int pWidth = pref.getWidth(); int pHeight = pref.getHeight(); if(pWidth < width) { int d = (width - pWidth); int a = con.align; if(rtl) { switch(a) { case Component.LEFT: a = Component.RIGHT; break; case Component.RIGHT: a = Component.LEFT; break; } } switch(a) { case Component.LEFT: con.parent.setX(x); con.parent.setWidth(width - d); break; case Component.RIGHT: con.parent.setX(x + d); con.parent.setWidth(width - d); break; case Component.CENTER: con.parent.setX(x + d / 2); con.parent.setWidth(width - d); break; } } if(pHeight < height) { int d = (height - pHeight); switch(con.valign) { case Component.TOP: con.parent.setY(y); con.parent.setHeight(height - d); break; case Component.BOTTOM: con.parent.setY(y + d); con.parent.setHeight(height - d); break; case Component.CENTER: con.parent.setY(y + d / 2); con.parent.setHeight(height - d); break; } } } /** * @param column: the column index * @param percentageOf: the table width to take into account to compute percentages constraints. if <0 these constraints are ignored and the max prefered width of the components of this column is returned * @return a size 2 int array with: the prefered width of the column , in pixels, as first element of the array and a constraint code for this column as second element. 0=column width is fixed, -1=column width is modifiable, -2=column width can automatically grow to take all the available space */ private int[] getColumnWidthPixels(int column, int percentageOf) { int current = 0; boolean foundExplicitWidth = false; boolean growable = false; for(int iter = 0 ; iter < rows ; iter++) { Constraint c = tablePositions[iter * columns + column]; //ignore "virtual" cells (i.e. cells that are part of a merge) if(c == null || c == H_SPAN_CONSTRAINT || c == V_SPAN_CONSTRAINT || c == VH_SPAN_CONSTRAINT || c.spanHorizontal > 1) { continue; } // width in percentage of the parent container if(c.width > 0 && percentageOf > -1) { current = Math.max(current, c.width * percentageOf / 100); foundExplicitWidth = true; } else if (!foundExplicitWidth) { // special case, width -2 gives the column the rest of the available space (and growHorizontally=true is the same as setting -2 in the width constraint of a cell from the last column. Kept here for historical reasons) if(c.width == -2 || (growHorizontally && column == columns - 1)) { growable=true; } Style s = c.parent.getStyle(); current = Math.max(current, c.parent.getPreferredW() + s.getMarginLeftNoRTL() + s.getMarginRightNoRTL()); } } return new int[] {current, (foundExplicitWidth?0:(growable?-2:-1))}; } private int[] getRowHeightPixels(int row, int percentageOf) { int current = 0; boolean foundExplicitHeight = false; boolean growable = false; for(int iter = 0 ; iter < columns ; iter++) { Constraint c = tablePositions[row * columns + iter]; if(c == null || c == H_SPAN_CONSTRAINT || c == V_SPAN_CONSTRAINT || c == VH_SPAN_CONSTRAINT || c.spanVertical > 1) { continue; } // height in percentage of the parent container if(c.height > 0 && percentageOf > -1) { current = Math.max(current, c.height * percentageOf / 100); foundExplicitHeight = true; } else if (!foundExplicitHeight) { // special case, height -2 gives the row the possibility to take the rest of the available space -> tag these rows if(c.height == -2) { growable=true; } Style s = c.parent.getStyle(); current = Math.max(current, c.parent.getPreferredH() + s.getMarginTop() + s.getMarginBottom()); } } return new int[] {current, (foundExplicitHeight?0:(growable?-2:-1))}; } /** * {@inheritDoc} */ public Dimension getPreferredSize(Container parent) { Style s = parent.getStyle(); int w = s.getPaddingLeftNoRTL() + s.getPaddingRightNoRTL(); int h = s.getPaddingTop() + s.getPaddingBottom(); for(int iter = 0 ; iter < columns ; iter++) { w += getColumnWidthPixels(iter, -1)[0]; } for(int iter = 0 ; iter < rows ; iter++) { h += getRowHeightPixels(iter, -1)[0]; } return new Dimension(w, h); } /** * Returns the row where the next operation of add will appear * * @return the row where the next operation of add will appear */ public int getNextRow() { return currentRow; } /** * Returns the column where the next operation of add will appear * * @return the column where the next operation of add will appear */ public int getNextColumn() { return currentColumn; } private void shiftCell(int row, int column) { Constraint currentConstraint = tablePositions[row * columns + column]; for(int iter = column + 1 ; iter < columns ; iter++) { if(tablePositions[row * columns + iter] != null) { Constraint tmp = tablePositions[row * columns + iter]; tablePositions[row * columns + iter] = currentConstraint; currentConstraint = tmp; } else { tablePositions[row * columns + iter] = currentConstraint; return; } } for(int rowIter = row + 1 ; rowIter < getRows() ; rowIter++) { for(int colIter = 0 ; colIter < getColumns() ; colIter++) { if(tablePositions[rowIter * columns + colIter] != null) { Constraint tmp = tablePositions[rowIter * columns + colIter]; tablePositions[rowIter * columns + colIter] = currentConstraint; currentConstraint = tmp; } else { tablePositions[rowIter * columns + colIter] = currentConstraint; return; } } } // if we reached this point there aren't enough rows addRow(); } private void addRow() { rows++; Constraint[] newArr = new Constraint[rows * columns]; System.arraycopy(tablePositions, 0, newArr, 0, tablePositions.length); tablePositions = newArr; } /** * {@inheritDoc} */ public void addLayoutComponent(Object value, Component comp, Container c) { Constraint con = null; if(!(value instanceof Constraint)) { con = createConstraint(); } else { con = (Constraint)value; if(con.parent != null) { Constraint con2 = createConstraint(); con2.align = con.align; con2.column = con.column; con2.height = con.height; con2.parent = c; con2.row = con.row; con2.spanHorizontal = con.spanHorizontal; con2.spanVertical = con.spanVertical; con2.valign = con.valign; con2.width = con.width; con = con2; } } con.actualRow = con.row; con.actualColumn = con.column; if(con.actualRow < 0) { con.actualRow = currentRow; } if(con.actualColumn < 0) { con.actualColumn = currentColumn; } con.parent = comp; if(con.actualRow >= rows) { // increase the table row count implicitly addRow(); } if(tablePositions[con.actualRow * columns + con.actualColumn] != null) { if(tablePositions[con.actualRow * columns + con.actualColumn].row != -1 || tablePositions[con.actualRow * columns + con.actualColumn].column != -1) { throw new IllegalArgumentException("Row: " + con.row + " and column: " + con.column + " already occupied"); } // try to reflow the table from this row/column onwards shiftCell(con.actualRow, con.actualColumn); tablePositions[con.actualRow * columns + con.actualColumn] = con; } tablePositions[con.actualRow * columns + con.actualColumn] = con; if(con.spanHorizontal > 1 || con.spanVertical > 1) { for(int sh = 0 ; sh < con.spanHorizontal ; sh++) { for(int sv = 0 ; sv < con.spanVertical ; sv++) { if((sh > 0 || sv > 0) && rows > con.actualRow + sv && columns > con.actualColumn + sh) { if(tablePositions[(con.actualRow + sv) * columns + con.actualColumn + sh] == null) { if(con.spanHorizontal > 1) { if(con.spanVertical > 1) { tablePositions[(con.actualRow + sv) * columns + con.actualColumn + sh] = VH_SPAN_CONSTRAINT; } else { tablePositions[(con.actualRow + sv) * columns + con.actualColumn + sh] = V_SPAN_CONSTRAINT; } } else { tablePositions[(con.actualRow + sv) * columns + con.actualColumn + sh] = H_SPAN_CONSTRAINT; } } } } } } updateRowColumn(); } private void updateRowColumn() { if(currentRow >= rows) { return; } while(tablePositions[currentRow * columns + currentColumn] != null) { currentColumn++; if(currentColumn >= columns) { currentColumn = 0; currentRow++; if(currentRow >= rows) { return; } } } } /** * Returns the spanning for the table cell at the given coordinate * * @param row row in the table * @param column column within the table * @return the amount of spanning 1 for no spanning */ public int getCellHorizontalSpan(int row, int column) { return tablePositions[row * columns + column].spanHorizontal; } /** * Returns the spanning for the table cell at the given coordinate * * @param row row in the table * @param column column within the table * @return the amount of spanning 1 for no spanning */ public int getCellVerticalSpan(int row, int column) { return tablePositions[row * columns + column].spanVertical; } /** * Returns true if the cell at the given position is spanned through vertically * * @param row cell row * @param column cell column * @return true if the cell is a part of a span for another cell */ public boolean isCellSpannedThroughVertically(int row, int column) { return tablePositions[row * columns + column] == V_SPAN_CONSTRAINT || tablePositions[row * columns + column] == VH_SPAN_CONSTRAINT; } /** * Returns true if the cell at the given position is spanned through horizontally * * @param row cell row * @param column cell column * @return true if the cell is a part of a span for another cell */ public boolean isCellSpannedThroughHorizontally(int row, int column) { return tablePositions[row * columns + column] == H_SPAN_CONSTRAINT || tablePositions[row * columns + column] == VH_SPAN_CONSTRAINT; } /** * Indicates whether there is spanning within this layout * * @return true if the layout makes use of spanning */ public boolean hasVerticalSpanning() { return verticalSpanningExists; } /** * Indicates whether there is spanning within this layout * * @return true if the layout makes use of spanning */ public boolean hasHorizontalSpanning() { return horizontalSpanningExists; } /** * {@inheritDoc} */ public void removeLayoutComponent(Component comp) { // reflow the table Vector comps = new Vector(); for(int r = 0 ; r < rows ; r++) { for(int c = 0 ; c < columns ; c++) { if(tablePositions[r * columns + c] != null) { if(tablePositions[r * columns + c].parent != comp) { comps.addElement(tablePositions[r * columns + c]); } else { tablePositions[r * columns + c].parent = null; } } tablePositions[r * columns + c] = null; } } currentRow = 0; currentColumn = 0; int count = comps.size(); for(int iter = 0 ; iter < count ; iter++) { Constraint con = (Constraint)comps.elementAt(iter); if(con == H_SPAN_CONSTRAINT || con == V_SPAN_CONSTRAINT || con == VH_SPAN_CONSTRAINT) { continue; } Component c = con.parent; con.parent = null; addLayoutComponent(con, c, c.getParent()); } } /** * {@inheritDoc} */ public Object getComponentConstraint(Component comp) { for(int r = 0 ; r < rows ; r++) { for(int c = 0 ; c < columns ; c++) { if(tablePositions[r * columns + c] != null && tablePositions[r * columns + c].parent == comp) { return tablePositions[r * columns + c]; } } } return null; } /** * Creates a new Constraint instance to add to the layout * * @return the default constraint */ public Constraint createConstraint() { return new Constraint(); } /** * Creates a new Constraint instance to add to the layout, same as * {@code createConstraint} only shorter syntax * * @return the default constraint */ public Constraint cc() { return new Constraint(); } /** * Creates a new Constraint instance to add to the layout, same as * {@code createConstraint} only shorter syntax * * @param row the row for the table starting with 0 * @param column the column for the table starting with 0 * @return the new constraint */ public Constraint cc(int row, int column) { return createConstraint(row, column); } /** * Creates a new Constraint instance to add to the layout * * @param row the row for the table starting with 0 * @param column the column for the table starting with 0 * @return the new constraint */ public Constraint createConstraint(int row, int column) { Constraint c = createConstraint(); c.row = row; c.column = column; return c; } /** * Sets the minimum size for a column in the table, this is applicable for tables that are * not scrollable on the X axis. This will force the earlier columns to leave room for * the latter columns. * * @param minimumSize the minimum width of the column */ public static void setMinimumSizePerColumn(int minimumSize) { minimumSizePerColumn = minimumSize; } /** * Indicates the minimum size for a column in the table, this is applicable for tables that are * not scrollable on the X axis. This will force the earlier columns to leave room for * the latter columns. * * @return the minimum width of the column */ public static int getMinimumSizePerColumn() { return minimumSizePerColumn; } /** * Indicates the default (in percentage) for the column width, -1 indicates * automatic sizing * * @param w width in percentage */ public static void setDefaultColumnWidth(int w) { defaultColumnWidth = w; } /** * Indicates the default (in percentage) for the column width, -1 indicates * automatic sizing * * @return width in percentage */ public static int getDefaultColumnWidth() { return defaultColumnWidth; } /** * Indicates the default (in percentage) for the row height, -1 indicates * automatic sizing * * @param h height in percentage */ public static void setDefaultRowHeight(int h) { defaultRowHeight = h; } /** * Indicates the default (in percentage) for the row height, -1 indicates * automatic sizing * * @return height in percentage */ public static int getDefaultRowHeight() { return defaultRowHeight; } /** * {@inheritDoc} */ public String toString() { return "TableLayout"; } /** * {@inheritDoc} */ public boolean equals(Object o) { return super.equals(o) && ((TableLayout)o).getRows() == getRows() && ((TableLayout)o).getColumns() == getColumns(); } /** * {@inheritDoc} */ public boolean isConstraintTracking() { return true; } /** * Indicates whether the table layout should grow horizontally to take up available space by stretching the last column * @return the growHorizontally */ public boolean isGrowHorizontally() { return growHorizontally; } /** * Indicates whether the table layout should grow horizontally to take up available space by stretching the last column * @param growHorizontally the growHorizontally to set */ public void setGrowHorizontally(boolean growHorizontally) { this.growHorizontally = growHorizontally; } /** * Indicates whether the table should be truncated if it do not have enough available horizontal space to display all its content. If not, will shrink * @return the truncateHorizontally */ public boolean isTruncateHorizontally() { return truncateHorizontally; } /** * Indicates whether the table should be truncated if it do not have enough available horizontal space to display all its content. If not, will shrink * @param truncateHorizontally the truncateHorizontally to set */ public void setTruncateHorizontally(boolean truncateHorizontally) { this.truncateHorizontally = truncateHorizontally; } /** * Indicates whether the table should be truncated if it do not have enough available vertical space to display all its content. If not, will shrink * @return the truncateVertically */ public boolean isTruncateVertically() { return truncateVertically; } /** * Indicates whether the table should be truncated if it do not have enough available vertical space to display all its content. If not, will shrink * @param truncateVertically the truncateVertically to set */ public void setTruncateVertically(boolean truncateVertically) { this.truncateVertically = truncateVertically; } /** *

Creates a table layout container that grows the last column horizontally, the number of rows is automatically * calculated based on the number of columns. See usage:

* * TableLayout that grows the last column * @param columns the number of columns * @param cmps components to add * @return a newly created table layout container with the components in it */ public static Container encloseIn(int columns, Component... cmps) { return encloseIn(columns, true, cmps); } /** *

Creates a table layout container, the number of rows is automatically calculated based on the number * of columns. See usage:

* * TableLayout that grows the last column * * @param columns the number of columns * @param growHorizontally true to grow the last column to fit available width * @param cmps components to add * @return a newly created table layout container with the components in it */ public static Container encloseIn(int columns, boolean growHorizontally, Component... cmps) { int rows = cmps.length; if(rows % columns > 0) { rows = rows / columns + 1; } else { rows = rows / columns; } TableLayout tl = new TableLayout(rows, columns); tl.setGrowHorizontally(growHorizontally); return Container.encloseIn(tl, cmps); } @Override public boolean overridesTabIndices(Container parent) { return true; } @Override protected Component[] getChildrenInTraversalOrder(Container parent) { int len = tablePositions.length; Component[] out = new Component[len]; for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy