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