TableViewpublic abstract class TableView extends BoxView
Implements View interface for a table, that is composed of an
element structure where the child elements of the element
this view is responsible for represent rows and the child
elements of the row elements are cells. The cell elements can
have an arbitrary element structure under them, which will
be built with the ViewFactory returned by the getViewFactory
method.
TABLE
ROW
CELL
CELL
ROW
CELL
CELL
This is implemented as a hierarchy of boxes, the table itself
is a vertical box, the rows are horizontal boxes, and the cells
are vertical boxes. The cells are allowed to span multiple
columns and rows. By default, the table can be thought of as
being formed over a grid (i.e. somewhat like one would find in
gridbag layout), where table cells can request to span more
than one grid cell. The default horizontal span of table cells
will be based upon this grid, but can be changed by reimplementing
the requested span of the cell (i.e. table cells can have independant
spans if desired). |
Fields Summary |
---|
int[] | columnSpans | int[] | columnOffsets | SizeRequirements[] | columnRequirements | Vector | rows | boolean | gridValid | private static final BitSet | EMPTY |
Constructors Summary |
---|
public TableView(Element elem)Constructs a TableView for the given element.
super(elem, View.Y_AXIS);
rows = new Vector();
gridValid = false;
|
Methods Summary |
---|
void | addFill(int row, int col)Mark a grid location as filled in for a cells overflow.
TableRow rv = getRow(row);
if (rv != null) {
rv.fillColumn(col);
}
| void | calculateColumnRequirements(int axis)Calculate the requirements for each column. The calculation
is done as two passes over the table. The table cells that
occupy a single column are scanned first to determine the
maximum of minimum, preferred, and maximum spans along the
give axis. Table cells that span multiple columns are excluded
from the first pass. A second pass is made to determine if
the cells that span multiple columns are satisfied. If the
column requirements are not satisified, the needs of the
multi-column cell is mixed into the existing column requirements.
The calculation of the multi-column distribution is based upon
the proportions of the existing column requirements and taking
into consideration any constraining maximums.
// pass 1 - single column cells
boolean hasMultiColumn = false;
int nrows = getRowCount();
for (int i = 0; i < nrows; i++) {
TableRow row = getRow(i);
int col = 0;
int ncells = row.getViewCount();
for (int cell = 0; cell < ncells; cell++, col++) {
View cv = row.getView(cell);
for (; row.isFilled(col); col++); // advance to a free column
int rowSpan = getRowsOccupied(cv);
int colSpan = getColumnsOccupied(cv);
if (colSpan == 1) {
checkSingleColumnCell(axis, col, cv);
} else {
hasMultiColumn = true;
col += colSpan - 1;
}
}
}
// pass 2 - multi-column cells
if (hasMultiColumn) {
for (int i = 0; i < nrows; i++) {
TableRow row = getRow(i);
int col = 0;
int ncells = row.getViewCount();
for (int cell = 0; cell < ncells; cell++, col++) {
View cv = row.getView(cell);
for (; row.isFilled(col); col++); // advance to a free column
int colSpan = getColumnsOccupied(cv);
if (colSpan > 1) {
checkMultiColumnCell(axis, col, colSpan, cv);
col += colSpan - 1;
}
}
}
}
/*
if (shouldTrace()) {
System.err.println("calc:");
for (int i = 0; i < columnRequirements.length; i++) {
System.err.println(" " + i + ": " + columnRequirements[i]);
}
}
*/
| protected javax.swing.SizeRequirements | calculateMinorAxisRequirements(int axis, javax.swing.SizeRequirements r)Calculate the requirements for the minor axis. This is called by
the superclass whenever the requirements need to be updated (i.e.
a preferenceChanged was messaged through this view).
This is implemented to calculate the requirements as the sum of the
requirements of the columns.
updateGrid();
// calculate column requirements for each column
calculateColumnRequirements(axis);
// the requirements are the sum of the columns.
if (r == null) {
r = new SizeRequirements();
}
long min = 0;
long pref = 0;
long max = 0;
for (int i = 0; i < columnRequirements.length; i++) {
SizeRequirements req = columnRequirements[i];
min += req.minimum;
pref += req.preferred;
max += req.maximum;
}
r.minimum = (int) min;
r.preferred = (int) pref;
r.maximum = (int) max;
r.alignment = 0;
return r;
| void | checkMultiColumnCell(int axis, int col, int ncols, javax.swing.text.View v)check the requirements of a table cell that spans multiple
columns.
// calculate the totals
long min = 0;
long pref = 0;
long max = 0;
for (int i = 0; i < ncols; i++) {
SizeRequirements req = columnRequirements[col + i];
min += req.minimum;
pref += req.preferred;
max += req.maximum;
}
// check if the minimum size needs adjustment.
int cmin = (int) v.getMinimumSpan(axis);
if (cmin > min) {
/*
* the columns that this cell spans need adjustment to fit
* this table cell.... calculate the adjustments. The
* maximum for each cell is the maximum of the existing
* maximum or the amount needed by the cell.
*/
SizeRequirements[] reqs = new SizeRequirements[ncols];
for (int i = 0; i < ncols; i++) {
SizeRequirements r = reqs[i] = columnRequirements[col + i];
r.maximum = Math.max(r.maximum, (int) v.getMaximumSpan(axis));
}
int[] spans = new int[ncols];
int[] offsets = new int[ncols];
SizeRequirements.calculateTiledPositions(cmin, null, reqs,
offsets, spans);
// apply the adjustments
for (int i = 0; i < ncols; i++) {
SizeRequirements req = reqs[i];
req.minimum = Math.max(spans[i], req.minimum);
req.preferred = Math.max(req.minimum, req.preferred);
req.maximum = Math.max(req.preferred, req.maximum);
}
}
// check if the preferred size needs adjustment.
int cpref = (int) v.getPreferredSpan(axis);
if (cpref > pref) {
/*
* the columns that this cell spans need adjustment to fit
* this table cell.... calculate the adjustments. The
* maximum for each cell is the maximum of the existing
* maximum or the amount needed by the cell.
*/
SizeRequirements[] reqs = new SizeRequirements[ncols];
for (int i = 0; i < ncols; i++) {
SizeRequirements r = reqs[i] = columnRequirements[col + i];
}
int[] spans = new int[ncols];
int[] offsets = new int[ncols];
SizeRequirements.calculateTiledPositions(cpref, null, reqs,
offsets, spans);
// apply the adjustments
for (int i = 0; i < ncols; i++) {
SizeRequirements req = reqs[i];
req.preferred = Math.max(spans[i], req.preferred);
req.maximum = Math.max(req.preferred, req.maximum);
}
}
| void | checkSingleColumnCell(int axis, int col, javax.swing.text.View v)check the requirements of a table cell that spans a single column.
SizeRequirements req = columnRequirements[col];
req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
req.maximum = Math.max((int) v.getMaximumSpan(axis), req.maximum);
| protected javax.swing.text.TableView$TableCell | createTableCell(javax.swing.text.Element elem)
return new TableCell(elem);
| protected javax.swing.text.TableView$TableRow | createTableRow(javax.swing.text.Element elem)Creates a new table row.
return new TableRow(elem);
| protected void | forwardUpdate(javax.swing.event.DocumentEvent$ElementChange ec, javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)
super.forwardUpdate(ec, e, a, f);
// A change in any of the table cells usually effects the whole table,
// so redraw it all!
if (a != null) {
Component c = getContainer();
if (c != null) {
Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
a.getBounds();
c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
}
}
| int | getColumnCount()The number of columns in the table.
return columnSpans.length;
| int | getColumnSpan(int col)Fetches the span (width) of the given column.
This is used by the nested cells to query the
sizes of grid locations outside of themselves.
return columnSpans[col];
| int | getColumnsOccupied(javax.swing.text.View v)Determines the number of columns occupied by
the table cell represented by given element.
// PENDING(prinz) this code should be in the html
// paragraph, but we can't add api to enable it.
AttributeSet a = v.getElement().getAttributes();
String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
if (s != null) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException nfe) {
// fall through to one column
}
}
return 1;
| javax.swing.text.TableView$TableRow | getRow(int row)
if (row < rows.size()) {
return (TableRow) rows.elementAt(row);
}
return null;
| int | getRowCount()The number of rows in the table.
return rows.size();
| int | getRowSpan(int row)Fetches the span (height) of the given row.
View rv = getRow(row);
if (rv != null) {
return (int) rv.getPreferredSpan(Y_AXIS);
}
return 0;
| int | getRowsOccupied(javax.swing.text.View v)Determines the number of rows occupied by
the table cell represented by given element.
// PENDING(prinz) this code should be in the html
// paragraph, but we can't add api to enable it.
AttributeSet a = v.getElement().getAttributes();
String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
if (s != null) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException nfe) {
// fall through to one row
}
}
return 1;
| protected javax.swing.text.View | getViewAtPosition(int pos, java.awt.Rectangle a)Fetches the child view that represents the given position in
the model. This is implemented to walk through the children
looking for a range that contains the given position. In this
view the children do not necessarily have a one to one mapping
with the child elements.
int n = getViewCount();
for (int i = 0; i < n; i++) {
View v = getView(i);
int p0 = v.getStartOffset();
int p1 = v.getEndOffset();
if ((pos >= p0) && (pos < p1)) {
// it's in this view.
if (a != null) {
childAllocation(i, a);
}
return v;
}
}
if (pos == getEndOffset()) {
View v = getView(n - 1);
if (a != null) {
this.childAllocation(n - 1, a);
}
return v;
}
return null;
| void | invalidateGrid()
gridValid = false;
| protected void | layoutColumns(int targetSpan, int[] offsets, int[] spans, javax.swing.SizeRequirements[] reqs)Layout the columns to fit within the given target span.
// allocate using the convenience method on SizeRequirements
SizeRequirements.calculateTiledPositions(targetSpan, null, reqs,
offsets, spans);
| protected void | layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans)Perform layout for the minor axis of the box (i.e. the
axis orthoginal to the axis that it represents). The results
of the layout should be placed in the given arrays which represent
the allocations to the children along the minor axis. This
is called by the superclass whenever the layout needs to be
updated along the minor axis.
This is implemented to call the
layoutColumns method, and then
forward to the superclass to actually carry out the layout
of the tables rows.
// make grid is properly represented
updateGrid();
// all of the row layouts are invalid, so mark them that way
int n = getRowCount();
for (int i = 0; i < n; i++) {
TableRow row = getRow(i);
row.layoutChanged(axis);
}
// calculate column spans
layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
// continue normal layout
super.layoutMinorAxis(targetSpan, axis, offsets, spans);
| public void | replace(int offset, int length, javax.swing.text.View[] views)Change the child views. This is implemented to
provide the superclass behavior and invalidate the
grid so that rows and columns will be recalculated.
super.replace(offset, length, views);
invalidateGrid();
| void | updateGrid()Fill in the grid locations that are placeholders
for multi-column, multi-row, and missing grid
locations.
if (! gridValid) {
// determine which views are table rows and clear out
// grid points marked filled.
rows.removeAllElements();
int n = getViewCount();
for (int i = 0; i < n; i++) {
View v = getView(i);
if (v instanceof TableRow) {
rows.addElement(v);
TableRow rv = (TableRow) v;
rv.clearFilledColumns();
rv.setRow(i);
}
}
int maxColumns = 0;
int nrows = rows.size();
for (int row = 0; row < nrows; row++) {
TableRow rv = getRow(row);
int col = 0;
for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
View cv = rv.getView(cell);
// advance to a free column
for (; rv.isFilled(col); col++);
int rowSpan = getRowsOccupied(cv);
int colSpan = getColumnsOccupied(cv);
if ((colSpan > 1) || (rowSpan > 1)) {
// fill in the overflow entries for this cell
int rowLimit = row + rowSpan;
int colLimit = col + colSpan;
for (int i = row; i < rowLimit; i++) {
for (int j = col; j < colLimit; j++) {
if (i != row || j != col) {
addFill(i, j);
}
}
}
if (colSpan > 1) {
col += colSpan - 1;
}
}
}
maxColumns = Math.max(maxColumns, col);
}
// setup the column layout/requirements
columnSpans = new int[maxColumns];
columnOffsets = new int[maxColumns];
columnRequirements = new SizeRequirements[maxColumns];
for (int i = 0; i < maxColumns; i++) {
columnRequirements[i] = new SizeRequirements();
}
gridValid = true;
}
|
|