/*
* Copyright (c) 1998, 2008, 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 javax.swing.text.html;
import java.awt.*;
import java.util.BitSet;
import java.util.Vector;
import java.util.Arrays;
import javax.swing.SizeRequirements;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
HTML table view.
Author: Timothy Prinzing See Also:
/**
* HTML table view.
*
* @author Timothy Prinzing
* @see View
*/
/*public*/ class TableView extends BoxView implements ViewFactory {
Constructs a TableView for the given element.
Params: - elem – the element that this view is responsible for
/**
* Constructs a TableView for the given element.
*
* @param elem the element that this view is responsible for
*/
public TableView(Element elem) {
super(elem, View.Y_AXIS);
rows = new Vector<RowView>();
gridValid = false;
captionIndex = -1;
totalColumnRequirements = new SizeRequirements();
}
Creates a new table row.
Params: - elem – an element
Returns: the row
/**
* Creates a new table row.
*
* @param elem an element
* @return the row
*/
protected RowView createTableRow(Element elem) {
// PENDING(prinz) need to add support for some of the other
// elements, but for now just ignore anything that is not
// a TR.
Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
if (o == HTML.Tag.TR) {
return new RowView(elem);
}
return null;
}
The number of columns in the table.
/**
* The number of columns in the table.
*/
public int getColumnCount() {
return columnSpans.length;
}
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.
/**
* 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.
*/
public int getColumnSpan(int col) {
if (col < columnSpans.length) {
return columnSpans[col];
}
return 0;
}
The number of rows in the table.
/**
* The number of rows in the table.
*/
public int getRowCount() {
return rows.size();
}
Fetch the span of multiple rows. This includes
the border area.
/**
* Fetch the span of multiple rows. This includes
* the border area.
*/
public int getMultiRowSpan(int row0, int row1) {
RowView rv0 = getRow(row0);
RowView rv1 = getRow(row1);
if ((rv0 != null) && (rv1 != null)) {
int index0 = rv0.viewIndex;
int index1 = rv1.viewIndex;
int span = getOffset(Y_AXIS, index1) - getOffset(Y_AXIS, index0) +
getSpan(Y_AXIS, index1);
return span;
}
return 0;
}
Fetches the span (height) of the given row.
/**
* Fetches the span (height) of the given row.
*/
public int getRowSpan(int row) {
RowView rv = getRow(row);
if (rv != null) {
return getSpan(Y_AXIS, rv.viewIndex);
}
return 0;
}
RowView getRow(int row) {
if (row < rows.size()) {
return rows.elementAt(row);
}
return null;
}
protected View getViewAtPoint(int x, int y, Rectangle alloc) {
int n = getViewCount();
View v;
Rectangle allocation = new Rectangle();
for (int i = 0; i < n; i++) {
allocation.setBounds(alloc);
childAllocation(i, allocation);
v = getView(i);
if (v instanceof RowView) {
v = ((RowView)v).findViewAtPoint(x, y, allocation);
if (v != null) {
alloc.setBounds(allocation);
return v;
}
}
}
return super.getViewAtPoint(x, y, alloc);
}
Determines the number of columns occupied by
the table cell represented by given element.
/**
* Determines the number of columns occupied by
* the table cell represented by given element.
*/
protected int getColumnsOccupied(View v) {
AttributeSet a = v.getElement().getAttributes();
if (a.isDefined(HTML.Attribute.COLSPAN)) {
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;
}
Determines the number of rows occupied by
the table cell represented by given element.
/**
* Determines the number of rows occupied by
* the table cell represented by given element.
*/
protected int getRowsOccupied(View v) {
AttributeSet a = v.getElement().getAttributes();
if (a.isDefined(HTML.Attribute.ROWSPAN)) {
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 void invalidateGrid() {
gridValid = false;
}
protected StyleSheet getStyleSheet() {
HTMLDocument doc = (HTMLDocument) getDocument();
return doc.getStyleSheet();
}
Update the insets, which contain the caption if there
is a caption.
/**
* Update the insets, which contain the caption if there
* is a caption.
*/
void updateInsets() {
short top = (short) painter.getInset(TOP, this);
short bottom = (short) painter.getInset(BOTTOM, this);
if (captionIndex != -1) {
View caption = getView(captionIndex);
short h = (short) caption.getPreferredSpan(Y_AXIS);
AttributeSet a = caption.getAttributes();
Object align = a.getAttribute(CSS.Attribute.CAPTION_SIDE);
if ((align != null) && (align.equals("bottom"))) {
bottom += h;
} else {
top += h;
}
}
setInsets(top, (short) painter.getInset(LEFT, this),
bottom, (short) painter.getInset(RIGHT, this));
}
Update any cached values that come from attributes.
/**
* Update any cached values that come from attributes.
*/
protected void setPropertiesFromAttributes() {
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
painter = sheet.getBoxPainter(attr);
if (attr != null) {
setInsets((short) painter.getInset(TOP, this),
(short) painter.getInset(LEFT, this),
(short) painter.getInset(BOTTOM, this),
(short) painter.getInset(RIGHT, this));
CSS.LengthValue lv = (CSS.LengthValue)
attr.getAttribute(CSS.Attribute.BORDER_SPACING);
if (lv != null) {
cellSpacing = (int) lv.getValue();
} else {
// Default cell spacing equals 2
cellSpacing = 2;
}
lv = (CSS.LengthValue)
attr.getAttribute(CSS.Attribute.BORDER_TOP_WIDTH);
if (lv != null) {
borderWidth = (int) lv.getValue();
} else {
borderWidth = 0;
}
}
}
Fill in the grid locations that are placeholders
for multi-column, multi-row, and missing grid
locations.
/**
* Fill in the grid locations that are placeholders
* for multi-column, multi-row, and missing grid
* locations.
*/
void updateGrid() {
if (! gridValid) {
relativeCells = false;
multiRowCells = false;
// determine which views are table rows and clear out
// grid points marked filled.
captionIndex = -1;
rows.removeAllElements();
int n = getViewCount();
for (int i = 0; i < n; i++) {
View v = getView(i);
if (v instanceof RowView) {
rows.addElement((RowView) v);
RowView rv = (RowView) v;
rv.clearFilledColumns();
rv.rowIndex = rows.size() - 1;
rv.viewIndex = i;
} else {
Object o = v.getElement().getAttributes().getAttribute(StyleConstants.NameAttribute);
if (o instanceof HTML.Tag) {
HTML.Tag kind = (HTML.Tag) o;
if (kind == HTML.Tag.CAPTION) {
captionIndex = i;
}
}
}
}
int maxColumns = 0;
int nrows = rows.size();
for (int row = 0; row < nrows; row++) {
RowView rv = getRow(row);
int col = 0;
for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
View cv = rv.getView(cell);
if (! relativeCells) {
AttributeSet a = cv.getAttributes();
CSS.LengthValue lv = (CSS.LengthValue)
a.getAttribute(CSS.Attribute.WIDTH);
if ((lv != null) && (lv.isPercentage())) {
relativeCells = true;
}
}
// advance to a free column
for (; rv.isFilled(col); col++);
int rowSpan = getRowsOccupied(cv);
if (rowSpan > 1) {
multiRowCells = true;
}
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();
columnRequirements[i].maximum = Integer.MAX_VALUE;
}
gridValid = true;
}
}
Mark a grid location as filled in for a cells overflow.
/**
* Mark a grid location as filled in for a cells overflow.
*/
void addFill(int row, int col) {
RowView rv = getRow(row);
if (rv != null) {
rv.fillColumn(col);
}
}
Layout the columns to fit within the given target span.
Params: - targetSpan – the given span for total of all the table
columns
- reqs – the requirements desired for each column. This
is the column maximum of the cells minimum, preferred, and
maximum requested span
- spans – the return value of how much to allocated to
each column
- offsets – the return value of the offset from the
origin for each column
Returns: the offset from the origin and the span for each column
in the offsets and spans parameters
/**
* Layout the columns to fit within the given target span.
*
* @param targetSpan the given span for total of all the table
* columns
* @param reqs the requirements desired for each column. This
* is the column maximum of the cells minimum, preferred, and
* maximum requested span
* @param spans the return value of how much to allocated to
* each column
* @param offsets the return value of the offset from the
* origin for each column
* @return the offset from the origin and the span for each column
* in the offsets and spans parameters
*/
protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,
SizeRequirements[] reqs) {
//clean offsets and spans
Arrays.fill(offsets, 0);
Arrays.fill(spans, 0);
colIterator.setLayoutArrays(offsets, spans, targetSpan);
CSS.calculateTiledLayout(colIterator, targetSpan);
}
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.
/**
* 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.
*/
void calculateColumnRequirements(int axis) {
// clean columnRequirements
for (SizeRequirements req : columnRequirements) {
req.minimum = 0;
req.preferred = 0;
req.maximum = Integer.MAX_VALUE;
}
Container host = getContainer();
if (host != null) {
if (host instanceof JTextComponent) {
skipComments = !((JTextComponent)host).isEditable();
} else {
skipComments = true;
}
}
// pass 1 - single column cells
boolean hasMultiColumn = false;
int nrows = getRowCount();
for (int i = 0; i < nrows; i++) {
RowView row = getRow(i);
int col = 0;
int ncells = row.getViewCount();
for (int cell = 0; cell < ncells; cell++) {
View cv = row.getView(cell);
if (skipComments && !(cv instanceof CellView)) {
continue;
}
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;
}
col++;
}
}
// pass 2 - multi-column cells
if (hasMultiColumn) {
for (int i = 0; i < nrows; i++) {
RowView row = getRow(i);
int col = 0;
int ncells = row.getViewCount();
for (int cell = 0; cell < ncells; cell++) {
View cv = row.getView(cell);
if (skipComments && !(cv instanceof CellView)) {
continue;
}
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;
}
col++;
}
}
}
}
check the requirements of a table cell that spans a single column.
/**
* check the requirements of a table cell that spans a single column.
*/
void checkSingleColumnCell(int axis, int col, View v) {
SizeRequirements req = columnRequirements[col];
req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
}
check the requirements of a table cell that spans multiple
columns.
/**
* check the requirements of a table cell that spans multiple
* columns.
*/
void checkMultiColumnCell(int axis, int col, int ncols, View v) {
// 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.
*/
SizeRequirements[] reqs = new SizeRequirements[ncols];
for (int i = 0; i < ncols; i++) {
reqs[i] = columnRequirements[col + i];
}
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.
*/
SizeRequirements[] reqs = new SizeRequirements[ncols];
for (int i = 0; i < ncols; i++) {
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);
}
}
}
// --- BoxView methods -----------------------------------------
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 and then adjust it if the
CSS width or height attribute is specified and applicable to
the axis.
/**
* 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).
* <p>
* This is implemented to calculate the requirements as the sum of the
* requirements of the columns and then adjust it if the
* CSS width or height attribute is specified and applicable to
* the axis.
*/
protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
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;
int n = columnRequirements.length;
for (int i = 0; i < n; i++) {
SizeRequirements req = columnRequirements[i];
min += req.minimum;
pref += req.preferred;
}
int adjust = (n + 1) * cellSpacing + 2 * borderWidth;
min += adjust;
pref += adjust;
r.minimum = (int) min;
r.preferred = (int) pref;
r.maximum = (int) pref;
AttributeSet attr = getAttributes();
CSS.LengthValue cssWidth = (CSS.LengthValue)attr.getAttribute(
CSS.Attribute.WIDTH);
if (BlockView.spanSetFromAttributes(axis, r, cssWidth, null)) {
if (r.minimum < (int)min) {
// The user has requested a smaller size than is needed to
// show the table, override it.
r.maximum = r.minimum = r.preferred = (int) min;
}
}
totalColumnRequirements.minimum = r.minimum;
totalColumnRequirements.preferred = r.preferred;
totalColumnRequirements.maximum = r.maximum;
// set the alignment
Object o = attr.getAttribute(CSS.Attribute.TEXT_ALIGN);
if (o != null) {
// set horizontal alignment
String ta = o.toString();
if (ta.equals("left")) {
r.alignment = 0;
} else if (ta.equals("center")) {
r.alignment = 0.5f;
} else if (ta.equals("right")) {
r.alignment = 1;
} else {
r.alignment = 0;
}
} else {
r.alignment = 0;
}
return r;
}
Calculate the requirements for the major 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 provide the superclass behavior adjusted for
multi-row table cells.
/**
* Calculate the requirements for the major axis. This is called by
* the superclass whenever the requirements need to be updated (i.e.
* a preferenceChanged was messaged through this view).
* <p>
* This is implemented to provide the superclass behavior adjusted for
* multi-row table cells.
*/
protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
updateInsets();
rowIterator.updateAdjustments();
r = CSS.calculateTiledRequirements(rowIterator, r);
r.maximum = r.preferred;
return r;
}
Perform layout for the minor axis of the box (i.e. the
axis orthogonal 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.
Params: - targetSpan – the total span given to the view, which
would be used to layout the children
- axis – the axis being layed out
- offsets – the offsets from the origin of the view for
each of the child views. This is a return value and is
filled in by the implementation of this method
- spans – the span of each child view; this is a return
value and is filled in by the implementation of this method
Returns: the offset and span for each child view in the
offsets and spans parameters
/**
* Perform layout for the minor axis of the box (i.e. the
* axis orthogonal 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.
* <p>
* This is implemented to call the
* <a href="#layoutColumns">layoutColumns</a> method, and then
* forward to the superclass to actually carry out the layout
* of the tables rows.
*
* @param targetSpan the total span given to the view, which
* would be used to layout the children
* @param axis the axis being layed out
* @param offsets the offsets from the origin of the view for
* each of the child views. This is a return value and is
* filled in by the implementation of this method
* @param spans the span of each child view; this is a return
* value and is filled in by the implementation of this method
* @return the offset and span for each child view in the
* offsets and spans parameters
*/
protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
// 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++) {
RowView row = getRow(i);
row.layoutChanged(axis);
}
// calculate column spans
layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
// continue normal layout
super.layoutMinorAxis(targetSpan, axis, offsets, spans);
}
Perform layout for the major axis of the box (i.e. 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 method is where the layout of the table rows within the
table takes place. This method is implemented to call the use
the RowIterator and the CSS collapsing tile to layout
with border spacing and border collapsing capabilities.
Params: - targetSpan – the total span given to the view, which
would be used to layout the children
- axis – the axis being layed out
- offsets – the offsets from the origin of the view for
each of the child views; this is a return value and is
filled in by the implementation of this method
- spans – the span of each child view; this is a return
value and is filled in by the implementation of this method
Returns: the offset and span for each child view in the
offsets and spans parameters
/**
* Perform layout for the major axis of the box (i.e. 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.
* <p>
* This method is where the layout of the table rows within the
* table takes place. This method is implemented to call the use
* the RowIterator and the CSS collapsing tile to layout
* with border spacing and border collapsing capabilities.
*
* @param targetSpan the total span given to the view, which
* would be used to layout the children
* @param axis the axis being layed out
* @param offsets the offsets from the origin of the view for
* each of the child views; this is a return value and is
* filled in by the implementation of this method
* @param spans the span of each child view; this is a return
* value and is filled in by the implementation of this method
* @return the offset and span for each child view in the
* offsets and spans parameters
*/
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
rowIterator.setLayoutArrays(offsets, spans);
CSS.calculateTiledLayout(rowIterator, targetSpan);
if (captionIndex != -1) {
// place the caption
View caption = getView(captionIndex);
int h = (int) caption.getPreferredSpan(Y_AXIS);
spans[captionIndex] = h;
short boxBottom = (short) painter.getInset(BOTTOM, this);
if (boxBottom != getBottomInset()) {
offsets[captionIndex] = targetSpan + boxBottom;
} else {
offsets[captionIndex] = - getTopInset();
}
}
}
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.
Params: - pos – the search position >= 0
- a – the allocation to the table on entry, and the
allocation of the view containing the position on exit
Returns: the view representing the given position, or
null if there isn't one
/**
* 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.
*
* @param pos the search position >= 0
* @param a the allocation to the table on entry, and the
* allocation of the view containing the position on exit
* @return the view representing the given position, or
* null if there isn't one
*/
protected View getViewAtPosition(int pos, Rectangle a) {
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;
}
// --- View methods ---------------------------------------------
Fetches the attributes to use when rendering. This is
implemented to multiplex the attributes specified in the
model with a StyleSheet.
/**
* Fetches the attributes to use when rendering. This is
* implemented to multiplex the attributes specified in the
* model with a StyleSheet.
*/
public AttributeSet getAttributes() {
if (attr == null) {
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
}
return attr;
}
Renders using the given rendering surface and area on that
surface. This is implemented to delegate to the css box
painter to paint the border and background prior to the
interior. The superclass culls rendering the children
that don't directly intersect the clip and the row may
have cells hanging from a row above in it. The table
does not use the superclass rendering behavior and instead
paints all of the rows and lets the rows cull those
cells not intersecting the clip region.
Params: - g – the rendering surface to use
- allocation – the allocated region to render into
See Also:
/**
* Renders using the given rendering surface and area on that
* surface. This is implemented to delegate to the css box
* painter to paint the border and background prior to the
* interior. The superclass culls rendering the children
* that don't directly intersect the clip and the row may
* have cells hanging from a row above in it. The table
* does not use the superclass rendering behavior and instead
* paints all of the rows and lets the rows cull those
* cells not intersecting the clip region.
*
* @param g the rendering surface to use
* @param allocation the allocated region to render into
* @see View#paint
*/
public void paint(Graphics g, Shape allocation) {
// paint the border
Rectangle a = allocation.getBounds();
setSize(a.width, a.height);
if (captionIndex != -1) {
// adjust the border for the caption
short top = (short) painter.getInset(TOP, this);
short bottom = (short) painter.getInset(BOTTOM, this);
if (top != getTopInset()) {
int h = getTopInset() - top;
a.y += h;
a.height -= h;
} else {
a.height -= getBottomInset() - bottom;
}
}
painter.paint(g, a.x, a.y, a.width, a.height, this);
// paint interior
int n = getViewCount();
for (int i = 0; i < n; i++) {
View v = getView(i);
v.paint(g, getChildAllocation(i, allocation));
}
//super.paint(g, a);
}
Establishes the parent view for this view. This is
guaranteed to be called before any other methods if the
parent view is functioning properly.
This is implemented
to forward to the superclass as well as call the
setPropertiesFromAttributes
method to set the paragraph properties from the css
attributes. The call is made at this time to ensure
the ability to resolve upward through the parents
view attributes.
Params: - parent – the new parent, or null if the view is
being removed from a parent it was previously added
to
/**
* Establishes the parent view for this view. This is
* guaranteed to be called before any other methods if the
* parent view is functioning properly.
* <p>
* This is implemented
* to forward to the superclass as well as call the
* <a href="#setPropertiesFromAttributes">setPropertiesFromAttributes</a>
* method to set the paragraph properties from the css
* attributes. The call is made at this time to ensure
* the ability to resolve upward through the parents
* view attributes.
*
* @param parent the new parent, or null if the view is
* being removed from a parent it was previously added
* to
*/
public void setParent(View parent) {
super.setParent(parent);
if (parent != null) {
setPropertiesFromAttributes();
}
}
Fetches the ViewFactory implementation that is feeding
the view hierarchy.
This replaces the ViewFactory with an implementation that
calls through to the createTableRow and createTableCell
methods. If the element given to the factory isn't a
table row or cell, the request is delegated to the factory
produced by the superclass behavior.
Returns: the factory, null if none
/**
* Fetches the ViewFactory implementation that is feeding
* the view hierarchy.
* This replaces the ViewFactory with an implementation that
* calls through to the createTableRow and createTableCell
* methods. If the element given to the factory isn't a
* table row or cell, the request is delegated to the factory
* produced by the superclass behavior.
*
* @return the factory, null if none
*/
public ViewFactory getViewFactory() {
return this;
}
Gives notification that something was inserted into
the document in a location that this view is responsible for.
This replaces the ViewFactory with an implementation that
calls through to the createTableRow and createTableCell
methods. If the element given to the factory isn't a
table row or cell, the request is delegated to the factory
passed as an argument.
Params: - e – the change information from the associated document
- a – the current allocation of the view
- f – the factory to use to rebuild if the view has children
See Also:
/**
* Gives notification that something was inserted into
* the document in a location that this view is responsible for.
* This replaces the ViewFactory with an implementation that
* calls through to the createTableRow and createTableCell
* methods. If the element given to the factory isn't a
* table row or cell, the request is delegated to the factory
* passed as an argument.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @see View#insertUpdate
*/
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.insertUpdate(e, a, this);
}
Gives notification that something was removed from the document
in a location that this view is responsible for.
This replaces the ViewFactory with an implementation that
calls through to the createTableRow and createTableCell
methods. If the element given to the factory isn't a
table row or cell, the request is delegated to the factory
passed as an argument.
Params: - e – the change information from the associated document
- a – the current allocation of the view
- f – the factory to use to rebuild if the view has children
See Also:
/**
* Gives notification that something was removed from the document
* in a location that this view is responsible for.
* This replaces the ViewFactory with an implementation that
* calls through to the createTableRow and createTableCell
* methods. If the element given to the factory isn't a
* table row or cell, the request is delegated to the factory
* passed as an argument.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @see View#removeUpdate
*/
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.removeUpdate(e, a, this);
}
Gives notification from the document that attributes were changed
in a location that this view is responsible for.
This replaces the ViewFactory with an implementation that
calls through to the createTableRow and createTableCell
methods. If the element given to the factory isn't a
table row or cell, the request is delegated to the factory
passed as an argument.
Params: - e – the change information from the associated document
- a – the current allocation of the view
- f – the factory to use to rebuild if the view has children
See Also:
/**
* Gives notification from the document that attributes were changed
* in a location that this view is responsible for.
* This replaces the ViewFactory with an implementation that
* calls through to the createTableRow and createTableCell
* methods. If the element given to the factory isn't a
* table row or cell, the request is delegated to the factory
* passed as an argument.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @see View#changedUpdate
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.changedUpdate(e, a, this);
}
protected void forwardUpdate(DocumentEvent.ElementChange ec,
DocumentEvent e, Shape a, 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);
}
}
}
Change the child views. This is implemented to
provide the superclass behavior and invalidate the
grid so that rows and columns will be recalculated.
/**
* Change the child views. This is implemented to
* provide the superclass behavior and invalidate the
* grid so that rows and columns will be recalculated.
*/
public void replace(int offset, int length, View[] views) {
super.replace(offset, length, views);
invalidateGrid();
}
// --- ViewFactory methods ------------------------------------------
The table itself acts as a factory for the various
views that actually represent pieces of the table.
All other factory activity is delegated to the factory
returned by the parent of the table.
/**
* The table itself acts as a factory for the various
* views that actually represent pieces of the table.
* All other factory activity is delegated to the factory
* returned by the parent of the table.
*/
public View create(Element elem) {
Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
if (o instanceof HTML.Tag) {
HTML.Tag kind = (HTML.Tag) o;
if (kind == HTML.Tag.TR) {
return createTableRow(elem);
} else if ((kind == HTML.Tag.TD) || (kind == HTML.Tag.TH)) {
return new CellView(elem);
} else if (kind == HTML.Tag.CAPTION) {
return new javax.swing.text.html.ParagraphView(elem);
}
}
// default is to delegate to the normal factory
View p = getParent();
if (p != null) {
ViewFactory f = p.getViewFactory();
if (f != null) {
return f.create(elem);
}
}
return null;
}
// ---- variables ----------------------------------------------------
private AttributeSet attr;
private StyleSheet.BoxPainter painter;
private int cellSpacing;
private int borderWidth;
The index of the caption view if there is a caption.
This has a value of -1 if there is no caption. The
caption lives in the inset area of the table, and is
updated with each time the grid is recalculated.
/**
* The index of the caption view if there is a caption.
* This has a value of -1 if there is no caption. The
* caption lives in the inset area of the table, and is
* updated with each time the grid is recalculated.
*/
private int captionIndex;
Do any of the table cells contain a relative size
specification? This is updated with each call to
updateGrid(). If this is true, the ColumnIterator
will do extra work to calculate relative cell
specifications.
/**
* Do any of the table cells contain a relative size
* specification? This is updated with each call to
* updateGrid(). If this is true, the ColumnIterator
* will do extra work to calculate relative cell
* specifications.
*/
private boolean relativeCells;
Do any of the table cells span multiple rows? If
true, the RowRequirementIterator will do additional
work to adjust the requirements of rows spanned by
a single table cell. This is updated with each call to
updateGrid().
/**
* Do any of the table cells span multiple rows? If
* true, the RowRequirementIterator will do additional
* work to adjust the requirements of rows spanned by
* a single table cell. This is updated with each call to
* updateGrid().
*/
private boolean multiRowCells;
int[] columnSpans;
int[] columnOffsets;
SizeRequirements for all the columns.
/**
* SizeRequirements for all the columns.
*/
SizeRequirements totalColumnRequirements;
SizeRequirements[] columnRequirements;
RowIterator rowIterator = new RowIterator();
ColumnIterator colIterator = new ColumnIterator();
Vector<RowView> rows;
// whether to display comments inside table or not.
boolean skipComments = false;
boolean gridValid;
static final private BitSet EMPTY = new BitSet();
class ColumnIterator implements CSS.LayoutIterator {
Disable percentage adjustments which should only apply
when calculating layout, not requirements.
/**
* Disable percentage adjustments which should only apply
* when calculating layout, not requirements.
*/
void disablePercentages() {
percentages = null;
}
Update percentage adjustments if they are needed.
/**
* Update percentage adjustments if they are needed.
*/
private void updatePercentagesAndAdjustmentWeights(int span) {
adjustmentWeights = new int[columnRequirements.length];
for (int i = 0; i < columnRequirements.length; i++) {
adjustmentWeights[i] = 0;
}
if (relativeCells) {
percentages = new int[columnRequirements.length];
} else {
percentages = null;
}
int nrows = getRowCount();
for (int rowIndex = 0; rowIndex < nrows; rowIndex++) {
RowView row = getRow(rowIndex);
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);
AttributeSet a = cv.getAttributes();
CSS.LengthValue lv = (CSS.LengthValue)
a.getAttribute(CSS.Attribute.WIDTH);
if ( lv != null ) {
int len = (int) (lv.getValue(span) / colSpan + 0.5f);
for (int i = 0; i < colSpan; i++) {
if (lv.isPercentage()) {
// add a percentage requirement
percentages[col+i] = Math.max(percentages[col+i], len);
adjustmentWeights[col + i] = Math.max(adjustmentWeights[col + i], WorstAdjustmentWeight);
} else {
adjustmentWeights[col + i] = Math.max(adjustmentWeights[col + i], WorstAdjustmentWeight - 1);
}
}
}
col += colSpan - 1;
}
}
}
Set the layout arrays to use for holding layout results
/**
* Set the layout arrays to use for holding layout results
*/
public void setLayoutArrays(int offsets[], int spans[], int targetSpan) {
this.offsets = offsets;
this.spans = spans;
updatePercentagesAndAdjustmentWeights(targetSpan);
}
// --- RequirementIterator methods -------------------
public int getCount() {
return columnRequirements.length;
}
public void setIndex(int i) {
col = i;
}
public void setOffset(int offs) {
offsets[col] = offs;
}
public int getOffset() {
return offsets[col];
}
public void setSpan(int span) {
spans[col] = span;
}
public int getSpan() {
return spans[col];
}
public float getMinimumSpan(float parentSpan) {
// do not care for percentages, since min span can't
// be less than columnRequirements[col].minimum,
// but can be less than percentage value.
return columnRequirements[col].minimum;
}
public float getPreferredSpan(float parentSpan) {
if ((percentages != null) && (percentages[col] != 0)) {
return Math.max(percentages[col], columnRequirements[col].minimum);
}
return columnRequirements[col].preferred;
}
public float getMaximumSpan(float parentSpan) {
return columnRequirements[col].maximum;
}
public float getBorderWidth() {
return borderWidth;
}
public float getLeadingCollapseSpan() {
return cellSpacing;
}
public float getTrailingCollapseSpan() {
return cellSpacing;
}
public int getAdjustmentWeight() {
return adjustmentWeights[col];
}
Current column index
/**
* Current column index
*/
private int col;
percentage values (may be null since there
might not be any).
/**
* percentage values (may be null since there
* might not be any).
*/
private int[] percentages;
private int[] adjustmentWeights;
private int[] offsets;
private int[] spans;
}
class RowIterator implements CSS.LayoutIterator {
RowIterator() {
}
void updateAdjustments() {
int axis = Y_AXIS;
if (multiRowCells) {
// adjust requirements of multi-row cells
int n = getRowCount();
adjustments = new int[n];
for (int i = 0; i < n; i++) {
RowView rv = getRow(i);
if (rv.multiRowCells == true) {
int ncells = rv.getViewCount();
for (int j = 0; j < ncells; j++) {
View v = rv.getView(j);
int nrows = getRowsOccupied(v);
if (nrows > 1) {
int spanNeeded = (int) v.getPreferredSpan(axis);
adjustMultiRowSpan(spanNeeded, nrows, i);
}
}
}
}
} else {
adjustments = null;
}
}
Fixup preferences to accommodate a multi-row table cell
if not already covered by existing preferences. This is
a no-op if not all of the rows needed (to do this check/fixup)
have arrived yet.
/**
* Fixup preferences to accommodate a multi-row table cell
* if not already covered by existing preferences. This is
* a no-op if not all of the rows needed (to do this check/fixup)
* have arrived yet.
*/
void adjustMultiRowSpan(int spanNeeded, int nrows, int rowIndex) {
if ((rowIndex + nrows) > getCount()) {
// rows are missing (could be a bad rowspan specification)
// or not all the rows have arrived. Do the best we can with
// the current set of rows.
nrows = getCount() - rowIndex;
if (nrows < 1) {
return;
}
}
int span = 0;
for (int i = 0; i < nrows; i++) {
RowView rv = getRow(rowIndex + i);
span += rv.getPreferredSpan(Y_AXIS);
}
if (spanNeeded > span) {
int adjust = (spanNeeded - span);
int rowAdjust = adjust / nrows;
int firstAdjust = rowAdjust + (adjust - (rowAdjust * nrows));
RowView rv = getRow(rowIndex);
adjustments[rowIndex] = Math.max(adjustments[rowIndex],
firstAdjust);
for (int i = 1; i < nrows; i++) {
adjustments[rowIndex + i] = Math.max(
adjustments[rowIndex + i], rowAdjust);
}
}
}
void setLayoutArrays(int[] offsets, int[] spans) {
this.offsets = offsets;
this.spans = spans;
}
// --- RequirementIterator methods -------------------
public void setOffset(int offs) {
RowView rv = getRow(row);
if (rv != null) {
offsets[rv.viewIndex] = offs;
}
}
public int getOffset() {
RowView rv = getRow(row);
if (rv != null) {
return offsets[rv.viewIndex];
}
return 0;
}
public void setSpan(int span) {
RowView rv = getRow(row);
if (rv != null) {
spans[rv.viewIndex] = span;
}
}
public int getSpan() {
RowView rv = getRow(row);
if (rv != null) {
return spans[rv.viewIndex];
}
return 0;
}
public int getCount() {
return rows.size();
}
public void setIndex(int i) {
row = i;
}
public float getMinimumSpan(float parentSpan) {
return getPreferredSpan(parentSpan);
}
public float getPreferredSpan(float parentSpan) {
RowView rv = getRow(row);
if (rv != null) {
int adjust = (adjustments != null) ? adjustments[row] : 0;
return rv.getPreferredSpan(TableView.this.getAxis()) + adjust;
}
return 0;
}
public float getMaximumSpan(float parentSpan) {
return getPreferredSpan(parentSpan);
}
public float getBorderWidth() {
return borderWidth;
}
public float getLeadingCollapseSpan() {
return cellSpacing;
}
public float getTrailingCollapseSpan() {
return cellSpacing;
}
public int getAdjustmentWeight() {
return 0;
}
Current row index
/**
* Current row index
*/
private int row;
Adjustments to the row requirements to handle multi-row
table cells.
/**
* Adjustments to the row requirements to handle multi-row
* table cells.
*/
private int[] adjustments;
private int[] offsets;
private int[] spans;
}
View of a row in a row-centric table.
/**
* View of a row in a row-centric table.
*/
public class RowView extends BoxView {
Constructs a TableView for the given element.
Params: - elem – the element that this view is responsible for
/**
* Constructs a TableView for the given element.
*
* @param elem the element that this view is responsible for
*/
public RowView(Element elem) {
super(elem, View.X_AXIS);
fillColumns = new BitSet();
RowView.this.setPropertiesFromAttributes();
}
void clearFilledColumns() {
fillColumns.and(EMPTY);
}
void fillColumn(int col) {
fillColumns.set(col);
}
boolean isFilled(int col) {
return fillColumns.get(col);
}
The number of columns present in this row.
/**
* The number of columns present in this row.
*/
int getColumnCount() {
int nfill = 0;
int n = fillColumns.size();
for (int i = 0; i < n; i++) {
if (fillColumns.get(i)) {
nfill ++;
}
}
return getViewCount() + nfill;
}
Fetches the attributes to use when rendering. This is
implemented to multiplex the attributes specified in the
model with a StyleSheet.
/**
* Fetches the attributes to use when rendering. This is
* implemented to multiplex the attributes specified in the
* model with a StyleSheet.
*/
public AttributeSet getAttributes() {
return attr;
}
View findViewAtPoint(int x, int y, Rectangle alloc) {
int n = getViewCount();
for (int i = 0; i < n; i++) {
if (getChildAllocation(i, alloc).contains(x, y)) {
childAllocation(i, alloc);
return getView(i);
}
}
return null;
}
protected StyleSheet getStyleSheet() {
HTMLDocument doc = (HTMLDocument) getDocument();
return doc.getStyleSheet();
}
This is called by a child to indicate its
preferred span has changed. This is implemented to
execute the superclass behavior and well as try to
determine if a row with a multi-row cell hangs across
this row. If a multi-row cell covers this row it also
needs to propagate a preferenceChanged so that it will
recalculate the multi-row cell.
Params: - child – the child view
- width – true if the width preference should change
- height – true if the height preference should change
/**
* This is called by a child to indicate its
* preferred span has changed. This is implemented to
* execute the superclass behavior and well as try to
* determine if a row with a multi-row cell hangs across
* this row. If a multi-row cell covers this row it also
* needs to propagate a preferenceChanged so that it will
* recalculate the multi-row cell.
*
* @param child the child view
* @param width true if the width preference should change
* @param height true if the height preference should change
*/
public void preferenceChanged(View child, boolean width, boolean height) {
super.preferenceChanged(child, width, height);
if (TableView.this.multiRowCells && height) {
for (int i = rowIndex - 1; i >= 0; i--) {
RowView rv = TableView.this.getRow(i);
if (rv.multiRowCells) {
rv.preferenceChanged(null, false, true);
break;
}
}
}
}
// The major axis requirements for a row are dictated by the column
// requirements. These methods use the value calculated by
// TableView.
protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
SizeRequirements req = new SizeRequirements();
req.minimum = totalColumnRequirements.minimum;
req.maximum = totalColumnRequirements.maximum;
req.preferred = totalColumnRequirements.preferred;
req.alignment = 0f;
return req;
}
public float getMinimumSpan(int axis) {
float value;
if (axis == View.X_AXIS) {
value = totalColumnRequirements.minimum + getLeftInset() +
getRightInset();
}
else {
value = super.getMinimumSpan(axis);
}
return value;
}
public float getMaximumSpan(int axis) {
float value;
if (axis == View.X_AXIS) {
// We're flexible.
value = (float)Integer.MAX_VALUE;
}
else {
value = super.getMaximumSpan(axis);
}
return value;
}
public float getPreferredSpan(int axis) {
float value;
if (axis == View.X_AXIS) {
value = totalColumnRequirements.preferred + getLeftInset() +
getRightInset();
}
else {
value = super.getPreferredSpan(axis);
}
return value;
}
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.changedUpdate(e, a, f);
int pos = e.getOffset();
if (pos <= getStartOffset() && (pos + e.getLength()) >=
getEndOffset()) {
RowView.this.setPropertiesFromAttributes();
}
}
Renders using the given rendering surface and area on that
surface. This is implemented to delegate to the css box
painter to paint the border and background prior to the
interior.
Params: - g – the rendering surface to use
- allocation – the allocated region to render into
See Also:
/**
* Renders using the given rendering surface and area on that
* surface. This is implemented to delegate to the css box
* painter to paint the border and background prior to the
* interior.
*
* @param g the rendering surface to use
* @param allocation the allocated region to render into
* @see View#paint
*/
public void paint(Graphics g, Shape allocation) {
Rectangle a = (Rectangle) allocation;
painter.paint(g, a.x, a.y, a.width, a.height, this);
super.paint(g, a);
}
Change the child views. This is implemented to
provide the superclass behavior and invalidate the
grid so that rows and columns will be recalculated.
/**
* Change the child views. This is implemented to
* provide the superclass behavior and invalidate the
* grid so that rows and columns will be recalculated.
*/
public void replace(int offset, int length, View[] views) {
super.replace(offset, length, views);
invalidateGrid();
}
Calculate the height requirements of the table row. The
requirements of multi-row cells are not considered for this
calculation. The table itself will check and adjust the row
requirements for all the rows that have multi-row cells spanning
them. This method updates the multi-row flag that indicates that
this row and rows below need additional consideration.
/**
* Calculate the height requirements of the table row. The
* requirements of multi-row cells are not considered for this
* calculation. The table itself will check and adjust the row
* requirements for all the rows that have multi-row cells spanning
* them. This method updates the multi-row flag that indicates that
* this row and rows below need additional consideration.
*/
protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
// return super.calculateMinorAxisRequirements(axis, r);
long min = 0;
long pref = 0;
long max = 0;
multiRowCells = false;
int n = getViewCount();
for (int i = 0; i < n; i++) {
View v = getView(i);
if (getRowsOccupied(v) > 1) {
multiRowCells = true;
max = Math.max((int) v.getMaximumSpan(axis), max);
} else {
min = Math.max((int) v.getMinimumSpan(axis), min);
pref = Math.max((int) v.getPreferredSpan(axis), pref);
max = Math.max((int) v.getMaximumSpan(axis), max);
}
}
if (r == null) {
r = new SizeRequirements();
r.alignment = 0.5f;
}
r.preferred = (int) pref;
r.minimum = (int) min;
r.maximum = (int) max;
return r;
}
Perform layout for the major axis of the box (i.e. 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 major axis.
This is re-implemented to give each child the span of the column
width for the table, and to give cells that span multiple columns
the multi-column span.
Params: - targetSpan – the total span given to the view, which
would be used to layout the children
- axis – the axis being layed out
- offsets – the offsets from the origin of the view for
each of the child views; this is a return value and is
filled in by the implementation of this method
- spans – the span of each child view; this is a return
value and is filled in by the implementation of this method
Returns: the offset and span for each child view in the
offsets and spans parameters
/**
* Perform layout for the major axis of the box (i.e. 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 major axis.
* <p>
* This is re-implemented to give each child the span of the column
* width for the table, and to give cells that span multiple columns
* the multi-column span.
*
* @param targetSpan the total span given to the view, which
* would be used to layout the children
* @param axis the axis being layed out
* @param offsets the offsets from the origin of the view for
* each of the child views; this is a return value and is
* filled in by the implementation of this method
* @param spans the span of each child view; this is a return
* value and is filled in by the implementation of this method
* @return the offset and span for each child view in the
* offsets and spans parameters
*/
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
int col = 0;
int ncells = getViewCount();
for (int cell = 0; cell < ncells; cell++) {
View cv = getView(cell);
if (skipComments && !(cv instanceof CellView)) {
continue;
}
for (; isFilled(col); col++); // advance to a free column
int colSpan = getColumnsOccupied(cv);
spans[cell] = columnSpans[col];
offsets[cell] = columnOffsets[col];
if (colSpan > 1) {
int n = columnSpans.length;
for (int j = 1; j < colSpan; j++) {
// Because the table may be only partially formed, some
// of the columns may not yet exist. Therefore we check
// the bounds.
if ((col+j) < n) {
spans[cell] += columnSpans[col+j];
spans[cell] += cellSpacing;
}
}
col += colSpan - 1;
}
col++;
}
}
Perform layout for the minor axis of the box (i.e. the
axis orthogonal 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 delegate to the superclass, then adjust
the span for any cell that spans multiple rows.
Params: - targetSpan – the total span given to the view, which
would be used to layout the children
- axis – the axis being layed out
- offsets – the offsets from the origin of the view for
each of the child views; this is a return value and is
filled in by the implementation of this method
- spans – the span of each child view; this is a return
value and is filled in by the implementation of this method
Returns: the offset and span for each child view in the
offsets and spans parameters
/**
* Perform layout for the minor axis of the box (i.e. the
* axis orthogonal 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.
* <p>
* This is implemented to delegate to the superclass, then adjust
* the span for any cell that spans multiple rows.
*
* @param targetSpan the total span given to the view, which
* would be used to layout the children
* @param axis the axis being layed out
* @param offsets the offsets from the origin of the view for
* each of the child views; this is a return value and is
* filled in by the implementation of this method
* @param spans the span of each child view; this is a return
* value and is filled in by the implementation of this method
* @return the offset and span for each child view in the
* offsets and spans parameters
*/
protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
super.layoutMinorAxis(targetSpan, axis, offsets, spans);
int col = 0;
int ncells = getViewCount();
for (int cell = 0; cell < ncells; cell++, col++) {
View cv = getView(cell);
for (; isFilled(col); col++); // advance to a free column
int colSpan = getColumnsOccupied(cv);
int rowSpan = getRowsOccupied(cv);
if (rowSpan > 1) {
int row0 = rowIndex;
int row1 = Math.min(rowIndex + rowSpan - 1, getRowCount()-1);
spans[cell] = getMultiRowSpan(row0, row1);
}
if (colSpan > 1) {
col += colSpan - 1;
}
}
}
Determines the resizability of the view along the
given axis. A value of 0 or less is not resizable.
Params: - axis – may be either View.X_AXIS or View.Y_AXIS
Throws: - IllegalArgumentException – for an invalid axis
Returns: the resize weight
/**
* Determines the resizability of the view along the
* given axis. A value of 0 or less is not resizable.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @return the resize weight
* @exception IllegalArgumentException for an invalid axis
*/
public int getResizeWeight(int axis) {
return 1;
}
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.
Params: - pos – the search position >= 0
- a – the allocation to the table on entry, and the
allocation of the view containing the position on exit
Returns: the view representing the given position, or
null if there isn't one
/**
* 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.
*
* @param pos the search position >= 0
* @param a the allocation to the table on entry, and the
* allocation of the view containing the position on exit
* @return the view representing the given position, or
* null if there isn't one
*/
protected View getViewAtPosition(int pos, Rectangle a) {
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;
}
Update any cached values that come from attributes.
/**
* Update any cached values that come from attributes.
*/
void setPropertiesFromAttributes() {
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
painter = sheet.getBoxPainter(attr);
}
private StyleSheet.BoxPainter painter;
private AttributeSet attr;
columns filled by multi-column or multi-row cells /** columns filled by multi-column or multi-row cells */
BitSet fillColumns;
The row index within the overall grid
/**
* The row index within the overall grid
*/
int rowIndex;
The view index (for row index to view index conversion).
This is set by the updateGrid method.
/**
* The view index (for row index to view index conversion).
* This is set by the updateGrid method.
*/
int viewIndex;
Does this table row have cells that span multiple rows?
/**
* Does this table row have cells that span multiple rows?
*/
boolean multiRowCells;
}
Default view of an html table cell. This needs to be moved
somewhere else.
/**
* Default view of an html table cell. This needs to be moved
* somewhere else.
*/
class CellView extends BlockView {
Constructs a TableCell for the given element.
Params: - elem – the element that this view is responsible for
/**
* Constructs a TableCell for the given element.
*
* @param elem the element that this view is responsible for
*/
public CellView(Element elem) {
super(elem, Y_AXIS);
}
Perform layout for the major axis of the box (i.e. 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 major axis. This is called by the
superclass to recalculate the positions of the child views
when the layout might have changed.
This is implemented to delegate to the superclass to
tile the children. If the target span is greater than
was needed, the offsets are adjusted to align the children
(i.e. position according to the html valign attribute).
Params: - targetSpan – the total span given to the view, which
would be used to layout the children
- axis – the axis being layed out
- offsets – the offsets from the origin of the view for
each of the child views; this is a return value and is
filled in by the implementation of this method
- spans – the span of each child view; this is a return
value and is filled in by the implementation of this method
Returns: the offset and span for each child view in the
offsets and spans parameters
/**
* Perform layout for the major axis of the box (i.e. 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 major axis. This is called by the
* superclass to recalculate the positions of the child views
* when the layout might have changed.
* <p>
* This is implemented to delegate to the superclass to
* tile the children. If the target span is greater than
* was needed, the offsets are adjusted to align the children
* (i.e. position according to the html valign attribute).
*
* @param targetSpan the total span given to the view, which
* would be used to layout the children
* @param axis the axis being layed out
* @param offsets the offsets from the origin of the view for
* each of the child views; this is a return value and is
* filled in by the implementation of this method
* @param spans the span of each child view; this is a return
* value and is filled in by the implementation of this method
* @return the offset and span for each child view in the
* offsets and spans parameters
*/
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
super.layoutMajorAxis(targetSpan, axis, offsets, spans);
// calculate usage
int used = 0;
int n = spans.length;
for (int i = 0; i < n; i++) {
used += spans[i];
}
// calculate adjustments
int adjust = 0;
if (used < targetSpan) {
// PENDING(prinz) change to use the css alignment.
String valign = (String) getElement().getAttributes().getAttribute(
HTML.Attribute.VALIGN);
if (valign == null) {
AttributeSet rowAttr = getElement().getParentElement().getAttributes();
valign = (String) rowAttr.getAttribute(HTML.Attribute.VALIGN);
}
if ((valign == null) || valign.equals("middle")) {
adjust = (targetSpan - used) / 2;
} else if (valign.equals("bottom")) {
adjust = targetSpan - used;
}
}
// make adjustments.
if (adjust != 0) {
for (int i = 0; i < n; i++) {
offsets[i] += adjust;
}
}
}
Calculate the requirements needed along the major 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 delegate to the superclass, but
indicate the maximum size is very large (i.e. the cell
is willing to expend to occupy the full height of the row).
Params: - axis – the axis being layed out.
- r – the requirements to fill in. If null, a new one
should be allocated.
/**
* Calculate the requirements needed along the major axis.
* This is called by the superclass whenever the requirements
* need to be updated (i.e. a preferenceChanged was messaged
* through this view).
* <p>
* This is implemented to delegate to the superclass, but
* indicate the maximum size is very large (i.e. the cell
* is willing to expend to occupy the full height of the row).
*
* @param axis the axis being layed out.
* @param r the requirements to fill in. If null, a new one
* should be allocated.
*/
protected SizeRequirements calculateMajorAxisRequirements(int axis,
SizeRequirements r) {
SizeRequirements req = super.calculateMajorAxisRequirements(axis, r);
req.maximum = Integer.MAX_VALUE;
return req;
}
@Override
protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
SizeRequirements rv = super.calculateMinorAxisRequirements(axis, r);
//for the cell the minimum should be derived from the child views
//the parent behaviour is to use CSS for that
int n = getViewCount();
int min = 0;
for (int i = 0; i < n; i++) {
View v = getView(i);
min = Math.max((int) v.getMinimumSpan(axis), min);
}
rv.minimum = Math.min(rv.minimum, min);
return rv;
}
}
}