| /* TableView.java -- A table view for HTML tables |
| Copyright (C) 2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing.text.html; |
| |
| import java.awt.Graphics; |
| import java.awt.Rectangle; |
| import java.awt.Shape; |
| |
| import gnu.javax.swing.text.html.css.Length; |
| |
| import javax.swing.SizeRequirements; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.text.AttributeSet; |
| import javax.swing.text.Element; |
| import javax.swing.text.StyleConstants; |
| import javax.swing.text.View; |
| import javax.swing.text.ViewFactory; |
| |
| /** |
| * A view implementation that renders HTML tables. |
| * |
| * This is basically a vertical BoxView that contains the rows of the table |
| * and the rows are horizontal BoxViews that contain the actual columns. |
| */ |
| class TableView |
| extends BlockView |
| implements ViewFactory |
| { |
| |
| /** |
| * Represents a single table row. |
| */ |
| class RowView |
| extends BlockView |
| { |
| /** |
| * Has true at column positions where an above row's cell overlaps into |
| * this row. |
| */ |
| boolean[] overlap; |
| |
| /** |
| * Stores the row index of this row. |
| */ |
| int rowIndex; |
| |
| /** |
| * Creates a new RowView. |
| * |
| * @param el the element for the row view |
| */ |
| RowView(Element el) |
| { |
| super(el, X_AXIS); |
| } |
| |
| public void replace(int offset, int len, View[] views) |
| { |
| gridValid = false; |
| super.replace(offset, len, views); |
| } |
| |
| /** |
| * Overridden to make rows not resizable along the Y axis. |
| */ |
| public float getMaximumSpan(int axis) |
| { |
| float span; |
| if (axis == Y_AXIS) |
| span = super.getPreferredSpan(axis); |
| else |
| span = Integer.MAX_VALUE; |
| return span; |
| } |
| |
| public float getMinimumSpan(int axis) |
| { |
| float span; |
| if (axis == X_AXIS) |
| span = totalColumnRequirements.minimum; |
| else |
| span = super.getMinimumSpan(axis); |
| return span; |
| } |
| |
| public float getPreferredSpan(int axis) |
| { |
| float span; |
| if (axis == X_AXIS) |
| span = totalColumnRequirements.preferred; |
| else |
| span = super.getPreferredSpan(axis); |
| return span; |
| } |
| |
| /** |
| * Calculates the overall size requirements for the row along the |
| * major axis. This will be the sum of the column requirements. |
| */ |
| protected SizeRequirements calculateMajorAxisRequirements(int axis, |
| SizeRequirements r) |
| { |
| if (r == null) |
| r = new SizeRequirements(); |
| int adjust = (columnRequirements.length + 1) * cellSpacing; |
| r.minimum = totalColumnRequirements.minimum + adjust; |
| r.preferred = totalColumnRequirements.preferred + adjust; |
| r.maximum = totalColumnRequirements.maximum + adjust; |
| r.alignment = 0.0F; |
| return r; |
| } |
| |
| /** |
| * Lays out the columns in this row. |
| */ |
| protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, |
| int spans[]) |
| { |
| super.layoutMinorAxis(targetSpan, axis, offsets, spans); |
| |
| // Adjust columns that have rowSpan > 1. |
| int numCols = getViewCount(); |
| for (int i = 0; i < numCols; i++) |
| { |
| View v = getView(i); |
| if (v instanceof CellView) |
| { |
| CellView cell = (CellView) v; |
| if (cell.rowSpan > 1) |
| { |
| for (int r = 1; r < cell.rowSpan; r++) |
| { |
| spans[i] += TableView.this.getSpan(axis, rowIndex + r); |
| spans[i] += cellSpacing; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Lays out the columns in this row. |
| */ |
| protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, |
| int spans[]) |
| { |
| updateGrid(); |
| int numCols = offsets.length; |
| int realColumn = 0; |
| int colCount = getViewCount(); |
| for (int i = 0; i < numColumns;) |
| { |
| if (! overlap[i] && realColumn < colCount) |
| { |
| View v = getView(realColumn); |
| if (v instanceof CellView) |
| { |
| CellView cv = (CellView) v; |
| offsets[realColumn] = columnOffsets[i]; |
| spans[realColumn] = 0; |
| for (int j = 0; j < cv.colSpan; j++, i++) |
| { |
| spans[realColumn] += columnSpans[i]; |
| if (j < cv.colSpan - 1) |
| spans[realColumn] += cellSpacing; |
| } |
| } |
| realColumn++; |
| } |
| else |
| { |
| i++; |
| } |
| } |
| } |
| } |
| |
| /** |
| * A view that renders HTML table cells (TD and TH tags). |
| */ |
| class CellView |
| extends BlockView |
| { |
| |
| /** |
| * The number of columns that this view spans. |
| */ |
| int colSpan; |
| |
| /** |
| * The number of rows that this cell spans. |
| */ |
| int rowSpan; |
| |
| /** |
| * Creates a new CellView for the specified element. |
| * |
| * @param el the element for which to create the colspan |
| */ |
| CellView(Element el) |
| { |
| super(el, Y_AXIS); |
| } |
| |
| protected SizeRequirements calculateMajorAxisRequirements(int axis, |
| SizeRequirements r) |
| { |
| r = super.calculateMajorAxisRequirements(axis, r); |
| r.maximum = Integer.MAX_VALUE; |
| return r; |
| } |
| |
| /** |
| * Overridden to fetch the columnSpan attibute. |
| */ |
| protected void setPropertiesFromAttributes() |
| { |
| super.setPropertiesFromAttributes(); |
| colSpan = 1; |
| AttributeSet atts = getAttributes(); |
| Object o = atts.getAttribute(HTML.Attribute.COLSPAN); |
| if (o != null) |
| { |
| try |
| { |
| colSpan = Integer.parseInt(o.toString()); |
| } |
| catch (NumberFormatException ex) |
| { |
| // Couldn't parse the colspan, assume 1. |
| colSpan = 1; |
| } |
| } |
| rowSpan = 1; |
| o = atts.getAttribute(HTML.Attribute.ROWSPAN); |
| if (o != null) |
| { |
| try |
| { |
| rowSpan = Integer.parseInt(o.toString()); |
| } |
| catch (NumberFormatException ex) |
| { |
| // Couldn't parse the colspan, assume 1. |
| rowSpan = 1; |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * The attributes of this view. |
| */ |
| private AttributeSet attributes; |
| |
| /** |
| * The column requirements. |
| * |
| * Package private to avoid accessor methods. |
| */ |
| SizeRequirements[] columnRequirements; |
| |
| /** |
| * The overall requirements across all columns. |
| * |
| * Package private to avoid accessor methods. |
| */ |
| SizeRequirements totalColumnRequirements; |
| |
| /** |
| * The column layout, offsets. |
| * |
| * Package private to avoid accessor methods. |
| */ |
| int[] columnOffsets; |
| |
| /** |
| * The column layout, spans. |
| * |
| * Package private to avoid accessor methods. |
| */ |
| int[] columnSpans; |
| |
| /** |
| * The widths of the columns that have been explicitly specified. |
| */ |
| Length[] columnWidths; |
| |
| /** |
| * The total number of columns. |
| */ |
| int numColumns; |
| |
| /** |
| * The table width. |
| */ |
| private Length width; |
| |
| /** |
| * Indicates if the grid setup is ok. |
| */ |
| boolean gridValid = false; |
| |
| /** |
| * Additional space that is added _between_ table cells. |
| * |
| * This is package private to avoid accessor methods. |
| */ |
| int cellSpacing; |
| |
| /** |
| * A cached Rectangle object for reuse in paint(). |
| */ |
| private Rectangle tmpRect; |
| |
| /** |
| * Creates a new HTML table view for the specified element. |
| * |
| * @param el the element for the table view |
| */ |
| public TableView(Element el) |
| { |
| super(el, Y_AXIS); |
| totalColumnRequirements = new SizeRequirements(); |
| tmpRect = new Rectangle(); |
| } |
| |
| /** |
| * Implementation of the ViewFactory interface for creating the |
| * child views correctly. |
| */ |
| public View create(Element elem) |
| { |
| View view = null; |
| AttributeSet atts = elem.getAttributes(); |
| Object name = atts.getAttribute(StyleConstants.NameAttribute); |
| AttributeSet pAtts = elem.getParentElement().getAttributes(); |
| Object pName = pAtts.getAttribute(StyleConstants.NameAttribute); |
| |
| if (name == HTML.Tag.TR && pName == HTML.Tag.TABLE) |
| view = new RowView(elem); |
| else if ((name == HTML.Tag.TD || name == HTML.Tag.TH) |
| && pName == HTML.Tag.TR) |
| view = new CellView(elem); |
| else if (name == HTML.Tag.CAPTION) |
| view = new ParagraphView(elem); |
| else |
| { |
| // If we haven't mapped the element, then fall back to the standard |
| // view factory. |
| View parent = getParent(); |
| if (parent != null) |
| { |
| ViewFactory vf = parent.getViewFactory(); |
| if (vf != null) |
| view = vf.create(elem); |
| } |
| } |
| return view; |
| } |
| |
| /** |
| * Returns this object as view factory so that we get our TR, TD, TH |
| * and CAPTION subelements created correctly. |
| */ |
| public ViewFactory getViewFactory() |
| { |
| return this; |
| } |
| |
| /** |
| * Returns the attributes of this view. This is overridden to provide |
| * the attributes merged with the CSS stuff. |
| */ |
| public AttributeSet getAttributes() |
| { |
| if (attributes == null) |
| attributes = getStyleSheet().getViewAttributes(this); |
| return attributes; |
| } |
| |
| /** |
| * Returns the stylesheet associated with this view. |
| * |
| * @return the stylesheet associated with this view |
| */ |
| protected StyleSheet getStyleSheet() |
| { |
| HTMLDocument doc = (HTMLDocument) getDocument(); |
| return doc.getStyleSheet(); |
| } |
| |
| /** |
| * Overridden to calculate the size requirements according to the |
| * columns distribution. |
| */ |
| protected SizeRequirements calculateMinorAxisRequirements(int axis, |
| SizeRequirements r) |
| { |
| updateGrid(); |
| calculateColumnRequirements(); |
| |
| // Calculate the horizontal requirements according to the superclass. |
| // This will return the maximum of the row's widths. |
| r = super.calculateMinorAxisRequirements(axis, r); |
| |
| // Try to set the CSS width if it fits. |
| if (width != null) |
| { |
| int w = (int) width.getValue(); |
| if (r.minimum < w) |
| r.minimum = w; |
| } |
| |
| // Adjust requirements when we have cell spacing. |
| int adjust = (columnRequirements.length + 1) * cellSpacing; |
| r.minimum += adjust; |
| r.preferred += adjust; |
| |
| // Apply the alignment. |
| AttributeSet atts = getAttributes(); |
| Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN); |
| r.alignment = 0.0F; |
| if (o != null) |
| { |
| String al = o.toString(); |
| if (al.equals("left")) |
| r.alignment = 0.0F; |
| else if (al.equals("center")) |
| r.alignment = 0.5F; |
| else if (al.equals("right")) |
| r.alignment = 1.0F; |
| } |
| |
| // Make it not resize in the horizontal direction. |
| r.maximum = r.preferred; |
| return r; |
| } |
| |
| /** |
| * Overridden to perform the table layout before calling the super |
| * implementation. |
| */ |
| protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, |
| int[] spans) |
| { |
| updateGrid(); |
| |
| // Mark all rows as invalid along their minor axis to force correct |
| // layout of multi-row cells. |
| int n = getViewCount(); |
| for (int i = 0; i < n; i++) |
| { |
| View row = getView(i); |
| if (row instanceof RowView) |
| ((RowView) row).layoutChanged(axis); |
| } |
| |
| layoutColumns(targetSpan); |
| super.layoutMinorAxis(targetSpan, axis, offsets, spans); |
| } |
| |
| /** |
| * Calculates the size requirements for the columns. |
| */ |
| private void calculateColumnRequirements() |
| { |
| int numRows = getViewCount(); |
| totalColumnRequirements.minimum = 0; |
| totalColumnRequirements.preferred = 0; |
| totalColumnRequirements.maximum = 0; |
| |
| // In this first pass we find out a suitable total width to fit in |
| // all columns of all rows. |
| for (int r = 0; r < numRows; r++) |
| { |
| View rowView = getView(r); |
| int numCols; |
| if (rowView instanceof RowView) |
| numCols = ((RowView) rowView).getViewCount(); |
| else |
| numCols = 0; |
| |
| // We collect the normal (non-relative) column requirements in the |
| // total variable and the relative requirements in the relTotal |
| // variable. In the end we create the maximum of both to get the |
| // real requirements. |
| SizeRequirements total = new SizeRequirements(); |
| SizeRequirements relTotal = new SizeRequirements(); |
| float totalPercent = 0.F; |
| int realCol = 0; |
| for (int c = 0; c < numCols; c++) |
| { |
| View v = rowView.getView(c); |
| if (v instanceof CellView) |
| { |
| CellView cellView = (CellView) v; |
| int colSpan = cellView.colSpan; |
| if (colSpan > 1) |
| { |
| int cellMin = (int) cellView.getMinimumSpan(X_AXIS); |
| int cellPref = (int) cellView.getPreferredSpan(X_AXIS); |
| int cellMax = (int) cellView.getMaximumSpan(X_AXIS); |
| int currentMin = 0; |
| int currentPref = 0; |
| long currentMax = 0; |
| for (int i = 0; i < colSpan; i++) |
| { |
| SizeRequirements req = columnRequirements[realCol]; |
| currentMin += req.minimum; |
| currentPref += req.preferred; |
| currentMax += req.maximum; |
| } |
| int deltaMin = cellMin - currentMin; |
| int deltaPref = cellPref - currentPref; |
| int deltaMax = (int) (cellMax - currentMax); |
| // Distribute delta. |
| for (int i = 0; i < colSpan; i++) |
| { |
| SizeRequirements req = columnRequirements[realCol]; |
| if (deltaMin > 0) |
| req.minimum += deltaMin / colSpan; |
| if (deltaPref > 0) |
| req.preferred += deltaPref / colSpan; |
| if (deltaMax > 0) |
| req.maximum += deltaMax / colSpan; |
| if (columnWidths[realCol] == null |
| || ! columnWidths[realCol].isPercentage()) |
| { |
| total.minimum += req.minimum; |
| total.preferred += req.preferred; |
| total.maximum += req.maximum; |
| } |
| else |
| { |
| relTotal.minimum = |
| Math.max(relTotal.minimum, |
| (int) (req.minimum |
| * columnWidths[realCol].getValue())); |
| relTotal.preferred = |
| Math.max(relTotal.preferred, |
| (int) (req.preferred |
| * columnWidths[realCol].getValue())); |
| relTotal.maximum = |
| Math.max(relTotal.maximum, |
| (int) (req.maximum |
| * columnWidths[realCol].getValue())); |
| totalPercent += columnWidths[realCol].getValue(); |
| } |
| } |
| realCol += colSpan; |
| } |
| else |
| { |
| // Shortcut for colSpan == 1. |
| SizeRequirements req = columnRequirements[realCol]; |
| req.minimum = Math.max(req.minimum, |
| (int) cellView.getMinimumSpan(X_AXIS)); |
| req.preferred = Math.max(req.preferred, |
| (int) cellView.getPreferredSpan(X_AXIS)); |
| req.maximum = Math.max(req.maximum, |
| (int) cellView.getMaximumSpan(X_AXIS)); |
| if (columnWidths[realCol] == null |
| || ! columnWidths[realCol].isPercentage()) |
| { |
| total.minimum += columnRequirements[realCol].minimum; |
| total.preferred += |
| columnRequirements[realCol].preferred; |
| total.maximum += columnRequirements[realCol].maximum; |
| } |
| else |
| { |
| relTotal.minimum = |
| Math.max(relTotal.minimum, |
| (int) (req.minimum |
| / columnWidths[c].getValue())); |
| relTotal.preferred = |
| Math.max(relTotal.preferred, |
| (int) (req.preferred |
| / columnWidths[c].getValue())); |
| relTotal.maximum = |
| Math.max(relTotal.maximum, |
| (int) (req.maximum |
| / columnWidths[c].getValue())); |
| totalPercent += columnWidths[c].getValue(); |
| } |
| realCol += 1; |
| } |
| } |
| } |
| |
| // Update the total requirements as follows: |
| // 1. Multiply the absolute requirements with 1 - totalPercent. This |
| // gives the total requirements based on the wishes of the absolute |
| // cells. |
| // 2. Take the maximum of this value and the total relative |
| // requirements. Now we should have enough space for whatever cell |
| // in this column. |
| // 3. Take the maximum of this value and the previous maximum value. |
| total.minimum *= 1.F / (1.F - totalPercent); |
| total.preferred *= 1.F / (1.F - totalPercent); |
| total.maximum *= 1.F / (1.F - totalPercent); |
| |
| int rowTotalMin = Math.max(total.minimum, relTotal.minimum); |
| int rowTotalPref = Math.max(total.preferred, relTotal.preferred); |
| int rowTotalMax = Math.max(total.maximum, relTotal.maximum); |
| totalColumnRequirements.minimum = |
| Math.max(totalColumnRequirements.minimum, rowTotalMin); |
| totalColumnRequirements.preferred = |
| Math.max(totalColumnRequirements.preferred, rowTotalPref); |
| totalColumnRequirements.maximum = |
| Math.max(totalColumnRequirements.maximum, rowTotalMax); |
| } |
| |
| // Now we know what we want and can fix up the actual relative |
| // column requirements. |
| int numCols = columnRequirements.length; |
| for (int i = 0; i < numCols; i++) |
| { |
| if (columnWidths[i] != null) |
| { |
| columnRequirements[i].minimum = (int) |
| columnWidths[i].getValue(totalColumnRequirements.minimum); |
| columnRequirements[i].preferred = (int) |
| columnWidths[i].getValue(totalColumnRequirements.preferred); |
| columnRequirements[i].maximum = (int) |
| columnWidths[i].getValue(totalColumnRequirements.maximum); |
| } |
| } |
| } |
| |
| /** |
| * Lays out the columns. |
| * |
| * @param targetSpan the target span into which the table is laid out |
| */ |
| private void layoutColumns(int targetSpan) |
| { |
| // Set the spans to the preferred sizes. Determine the space |
| // that we have to adjust the sizes afterwards. |
| long sumPref = 0; |
| int n = columnRequirements.length; |
| for (int i = 0; i < n; i++) |
| { |
| SizeRequirements col = columnRequirements[i]; |
| if (columnWidths[i] != null) |
| columnSpans[i] = (int) columnWidths[i].getValue(targetSpan); |
| else |
| columnSpans[i] = col.preferred; |
| sumPref += columnSpans[i]; |
| } |
| |
| // Try to adjust the spans so that we fill the targetSpan. |
| // For adjustments we have to use the targetSpan minus the cumulated |
| // cell spacings. |
| long diff = targetSpan - (n + 1) * cellSpacing - sumPref; |
| float factor = 0.0F; |
| int[] diffs = null; |
| if (diff != 0) |
| { |
| long total = 0; |
| diffs = new int[n]; |
| for (int i = 0; i < n; i++) |
| { |
| // Only adjust the width if we haven't set a column width here. |
| if (columnWidths[i] == null) |
| { |
| SizeRequirements col = columnRequirements[i]; |
| int span; |
| if (diff < 0) |
| { |
| span = col.minimum; |
| diffs[i] = columnSpans[i] - span; |
| } |
| else |
| { |
| span = col.maximum; |
| diffs[i] = span - columnSpans[i]; |
| } |
| total += span; |
| } |
| else |
| total += columnSpans[i]; |
| } |
| |
| float maxAdjust = Math.abs(total - sumPref); |
| factor = diff / maxAdjust; |
| factor = Math.min(factor, 1.0F); |
| factor = Math.max(factor, -1.0F); |
| } |
| |
| // Actually perform adjustments. |
| int totalOffs = cellSpacing; |
| for (int i = 0; i < n; i++) |
| { |
| columnOffsets[i] = totalOffs; |
| if (diff != 0) |
| { |
| float adjust = factor * diffs[i]; |
| columnSpans[i] += Math.round(adjust); |
| } |
| // Avoid overflow here. |
| totalOffs = (int) Math.min((long) totalOffs + (long) columnSpans[i] |
| + (long) cellSpacing, Integer.MAX_VALUE); |
| } |
| } |
| |
| /** |
| * Updates the arrays that contain the row and column data in response |
| * to a change to the table structure. |
| * |
| * Package private to avoid accessor methods. |
| */ |
| void updateGrid() |
| { |
| if (! gridValid) |
| { |
| AttributeSet atts = getAttributes(); |
| StyleSheet ss = getStyleSheet(); |
| float emBase = ss.getEMBase(atts); |
| float exBase = ss.getEXBase(atts); |
| int maxColumns = 0; |
| int numRows = getViewCount(); |
| for (int r = 0; r < numRows; r++) |
| { |
| View rowView = getView(r); |
| int numCols = 0; |
| if (rowView instanceof RowView) |
| { |
| int numCells = ((RowView) rowView).getViewCount(); |
| for (int i = 0; i < numCells; i++) |
| { |
| View v = rowView.getView(i); |
| if (v instanceof CellView) |
| numCols += ((CellView) v).colSpan; |
| } |
| } |
| maxColumns = Math.max(numCols, maxColumns); |
| } |
| numColumns = maxColumns; |
| columnWidths = new Length[maxColumns]; |
| int[] rowSpans = new int[maxColumns]; |
| for (int r = 0; r < numRows; r++) |
| { |
| View view = getView(r); |
| if (view instanceof RowView) |
| { |
| RowView rowView = (RowView) view; |
| rowView.rowIndex = r; |
| rowView.overlap = new boolean[maxColumns]; |
| int colIndex = 0; |
| int colCount = rowView.getViewCount(); |
| for (int c = 0; c < maxColumns;) |
| { |
| if (rowSpans[c] > 0) |
| { |
| rowSpans[c]--; |
| rowView.overlap[c] = true; |
| c++; |
| } |
| else if (colIndex < colCount) |
| { |
| View v = rowView.getView(colIndex); |
| colIndex++; |
| if (v instanceof CellView) |
| { |
| CellView cv = (CellView) v; |
| Object o = |
| cv.getAttributes().getAttribute(CSS.Attribute.WIDTH); |
| if (o != null && columnWidths[c] == null |
| && o instanceof Length) |
| { |
| columnWidths[c]= (Length) o; |
| columnWidths[c].setFontBases(emBase, exBase); |
| } |
| int rs = cv.rowSpan - 1; |
| for (int col = cv.colSpan - 1; col >= 0; col--) |
| { |
| rowSpans[c] = rs; |
| c++; |
| } |
| } |
| } |
| else |
| { |
| c++; |
| } |
| } |
| } |
| } |
| columnRequirements = new SizeRequirements[maxColumns]; |
| for (int i = 0; i < maxColumns; i++) |
| columnRequirements[i] = new SizeRequirements(); |
| columnOffsets = new int[maxColumns]; |
| columnSpans = new int[maxColumns]; |
| |
| gridValid = true; |
| } |
| } |
| |
| /** |
| * Overridden to restrict the table width to the preferred size. |
| */ |
| public float getMaximumSpan(int axis) |
| { |
| float span; |
| if (axis == X_AXIS) |
| span = super.getPreferredSpan(axis); |
| else |
| span = super.getMaximumSpan(axis); |
| return span; |
| } |
| |
| /** |
| * Overridden to fetch the CSS attributes when view gets connected. |
| */ |
| public void setParent(View parent) |
| { |
| super.setParent(parent); |
| if (parent != null) |
| setPropertiesFromAttributes(); |
| } |
| |
| /** |
| * Fetches CSS and HTML layout attributes. |
| */ |
| protected void setPropertiesFromAttributes() |
| { |
| super.setPropertiesFromAttributes(); |
| |
| // Fetch and parse cell spacing. |
| AttributeSet atts = getAttributes(); |
| StyleSheet ss = getStyleSheet(); |
| float emBase = ss.getEMBase(atts); |
| float exBase = ss.getEXBase(atts); |
| Object o = atts.getAttribute(CSS.Attribute.BORDER_SPACING); |
| if (o != null && o instanceof Length) |
| { |
| Length l = (Length) o; |
| l.setFontBases(emBase, exBase); |
| cellSpacing = (int) l.getValue(); |
| } |
| o = atts.getAttribute(CSS.Attribute.WIDTH); |
| if (o != null && o instanceof Length) |
| { |
| width = (Length) o; |
| width.setFontBases(emBase, exBase); |
| } |
| } |
| |
| /** |
| * Overridden to adjust for cellSpacing. |
| */ |
| protected SizeRequirements calculateMajorAxisRequirements(int axis, |
| SizeRequirements r) |
| { |
| r = super.calculateMajorAxisRequirements(axis, r); |
| int adjust = (getViewCount() + 1) * cellSpacing; |
| r.minimum += adjust; |
| r.preferred += adjust; |
| r.maximum += adjust; |
| return r; |
| } |
| |
| /** |
| * Overridden to adjust for cellSpacing. |
| */ |
| protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, |
| int spans[]) |
| { |
| // Mark all rows as invalid along their minor axis to force correct |
| // layout of multi-row cells. |
| int n = getViewCount(); |
| for (int i = 0; i < n; i++) |
| { |
| View row = getView(i); |
| if (row instanceof RowView) |
| ((RowView) row).layoutChanged(axis); |
| } |
| |
| int adjust = (getViewCount() + 1) * cellSpacing; |
| super.layoutMajorAxis(targetSpan - adjust, axis, offsets, spans); |
| for (int i = 0; i < offsets.length; i++) |
| { |
| offsets[i] += (i + 1) * cellSpacing; |
| } |
| } |
| |
| /** |
| * Overridden to replace view factory with this one. |
| */ |
| public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) |
| { |
| super.insertUpdate(e, a, this); |
| } |
| |
| /** |
| * Overridden to replace view factory with this one. |
| */ |
| public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) |
| { |
| super.removeUpdate(e, a, this); |
| } |
| |
| /** |
| * Overridden to replace view factory with this one. |
| */ |
| public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) |
| { |
| super.changedUpdate(e, a, this); |
| } |
| |
| public void replace(int offset, int len, View[] views) |
| { |
| gridValid = false; |
| super.replace(offset, len, views); |
| } |
| |
| /** |
| * We can't use the super class's paint() method because it might cut |
| * off multi-row children. Instead we trigger painting for all rows |
| * and let the rows sort out what to paint and what not. |
| */ |
| public void paint(Graphics g, Shape a) |
| { |
| Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); |
| painter.paint(g, rect.x, rect.y, rect.width, rect.height, this); |
| int nRows = getViewCount(); |
| Rectangle inside = getInsideAllocation(a); |
| for (int r = 0; r < nRows; r++) |
| { |
| tmpRect.setBounds(inside); |
| childAllocation(r, tmpRect); |
| paintChild(g, tmpRect, r); |
| } |
| } |
| |
| } |