0001 /*
0002 * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025 package javax.swing.text.html;
0026
0027 import java.awt.*;
0028 import java.util.BitSet;
0029 import java.util.Vector;
0030 import java.util.Arrays;
0031 import javax.swing.SizeRequirements;
0032 import javax.swing.event.DocumentEvent;
0033
0034 import javax.swing.text.*;
0035
0036 /**
0037 * HTML table view.
0038 *
0039 * @author Timothy Prinzing
0040 * @version 1.47 05/05/07
0041 * @see View
0042 */
0043 /*public*/class TableView extends BoxView implements ViewFactory {
0044
0045 /**
0046 * Constructs a TableView for the given element.
0047 *
0048 * @param elem the element that this view is responsible for
0049 */
0050 public TableView(Element elem) {
0051 super (elem, View.Y_AXIS);
0052 rows = new Vector();
0053 gridValid = false;
0054 captionIndex = -1;
0055 totalColumnRequirements = new SizeRequirements();
0056 }
0057
0058 /**
0059 * Creates a new table row.
0060 *
0061 * @param elem an element
0062 * @return the row
0063 */
0064 protected RowView createTableRow(Element elem) {
0065 // PENDING(prinz) need to add support for some of the other
0066 // elements, but for now just ignore anything that is not
0067 // a TR.
0068 Object o = elem.getAttributes().getAttribute(
0069 StyleConstants.NameAttribute);
0070 if (o == HTML.Tag.TR) {
0071 return new RowView(elem);
0072 }
0073 return null;
0074 }
0075
0076 /**
0077 * The number of columns in the table.
0078 */
0079 public int getColumnCount() {
0080 return columnSpans.length;
0081 }
0082
0083 /**
0084 * Fetches the span (width) of the given column.
0085 * This is used by the nested cells to query the
0086 * sizes of grid locations outside of themselves.
0087 */
0088 public int getColumnSpan(int col) {
0089 if (col < columnSpans.length) {
0090 return columnSpans[col];
0091 }
0092 return 0;
0093 }
0094
0095 /**
0096 * The number of rows in the table.
0097 */
0098 public int getRowCount() {
0099 return rows.size();
0100 }
0101
0102 /**
0103 * Fetch the span of multiple rows. This includes
0104 * the border area.
0105 */
0106 public int getMultiRowSpan(int row0, int row1) {
0107 RowView rv0 = getRow(row0);
0108 RowView rv1 = getRow(row1);
0109 if ((rv0 != null) && (rv1 != null)) {
0110 int index0 = rv0.viewIndex;
0111 int index1 = rv1.viewIndex;
0112 int span = getOffset(Y_AXIS, index1)
0113 - getOffset(Y_AXIS, index0)
0114 + getSpan(Y_AXIS, index1);
0115 return span;
0116 }
0117 return 0;
0118 }
0119
0120 /**
0121 * Fetches the span (height) of the given row.
0122 */
0123 public int getRowSpan(int row) {
0124 RowView rv = getRow(row);
0125 if (rv != null) {
0126 return getSpan(Y_AXIS, rv.viewIndex);
0127 }
0128 return 0;
0129 }
0130
0131 RowView getRow(int row) {
0132 if (row < rows.size()) {
0133 return (RowView) rows.elementAt(row);
0134 }
0135 return null;
0136 }
0137
0138 protected View getViewAtPoint(int x, int y, Rectangle alloc) {
0139 int n = getViewCount();
0140 View v = null;
0141 Rectangle allocation = new Rectangle();
0142 for (int i = 0; i < n; i++) {
0143 allocation.setBounds(alloc);
0144 childAllocation(i, allocation);
0145 v = getView(i);
0146 if (v instanceof RowView) {
0147 v = ((RowView) v).findViewAtPoint(x, y, allocation);
0148 if (v != null) {
0149 alloc.setBounds(allocation);
0150 return v;
0151 }
0152 }
0153 }
0154 return super .getViewAtPoint(x, y, alloc);
0155 }
0156
0157 /**
0158 * Determines the number of columns occupied by
0159 * the table cell represented by given element.
0160 */
0161 protected int getColumnsOccupied(View v) {
0162 AttributeSet a = v.getElement().getAttributes();
0163
0164 if (a.isDefined(HTML.Attribute.COLSPAN)) {
0165 String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
0166 if (s != null) {
0167 try {
0168 return Integer.parseInt(s);
0169 } catch (NumberFormatException nfe) {
0170 // fall through to one column
0171 }
0172 }
0173 }
0174
0175 return 1;
0176 }
0177
0178 /**
0179 * Determines the number of rows occupied by
0180 * the table cell represented by given element.
0181 */
0182 protected int getRowsOccupied(View v) {
0183 AttributeSet a = v.getElement().getAttributes();
0184
0185 if (a.isDefined(HTML.Attribute.ROWSPAN)) {
0186 String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
0187 if (s != null) {
0188 try {
0189 return Integer.parseInt(s);
0190 } catch (NumberFormatException nfe) {
0191 // fall through to one row
0192 }
0193 }
0194 }
0195
0196 return 1;
0197 }
0198
0199 protected void invalidateGrid() {
0200 gridValid = false;
0201 }
0202
0203 protected StyleSheet getStyleSheet() {
0204 HTMLDocument doc = (HTMLDocument) getDocument();
0205 return doc.getStyleSheet();
0206 }
0207
0208 /**
0209 * Update the insets, which contain the caption if there
0210 * is a caption.
0211 */
0212 void updateInsets() {
0213 short top = (short) painter.getInset(TOP, this );
0214 short bottom = (short) painter.getInset(BOTTOM, this );
0215 if (captionIndex != -1) {
0216 View caption = getView(captionIndex);
0217 short h = (short) caption.getPreferredSpan(Y_AXIS);
0218 AttributeSet a = caption.getAttributes();
0219 Object align = a.getAttribute(CSS.Attribute.CAPTION_SIDE);
0220 if ((align != null) && (align.equals("bottom"))) {
0221 bottom += h;
0222 } else {
0223 top += h;
0224 }
0225 }
0226 setInsets(top, (short) painter.getInset(LEFT, this ), bottom,
0227 (short) painter.getInset(RIGHT, this ));
0228 }
0229
0230 /**
0231 * Update any cached values that come from attributes.
0232 */
0233 protected void setPropertiesFromAttributes() {
0234 StyleSheet sheet = getStyleSheet();
0235 attr = sheet.getViewAttributes(this );
0236 painter = sheet.getBoxPainter(attr);
0237 if (attr != null) {
0238 setInsets((short) painter.getInset(TOP, this ),
0239 (short) painter.getInset(LEFT, this ),
0240 (short) painter.getInset(BOTTOM, this ),
0241 (short) painter.getInset(RIGHT, this ));
0242
0243 CSS.LengthValue lv = (CSS.LengthValue) attr
0244 .getAttribute(CSS.Attribute.BORDER_SPACING);
0245 if (lv != null) {
0246 cellSpacing = (int) lv.getValue();
0247 } else {
0248 cellSpacing = 0;
0249 }
0250 lv = (CSS.LengthValue) attr
0251 .getAttribute(CSS.Attribute.BORDER_TOP_WIDTH);
0252 if (lv != null) {
0253 borderWidth = (int) lv.getValue();
0254 } else {
0255 borderWidth = 0;
0256 }
0257
0258 }
0259 }
0260
0261 /**
0262 * Fill in the grid locations that are placeholders
0263 * for multi-column, multi-row, and missing grid
0264 * locations.
0265 */
0266 void updateGrid() {
0267 if (!gridValid) {
0268 relativeCells = false;
0269 multiRowCells = false;
0270
0271 // determine which views are table rows and clear out
0272 // grid points marked filled.
0273 captionIndex = -1;
0274 rows.removeAllElements();
0275 int n = getViewCount();
0276 for (int i = 0; i < n; i++) {
0277 View v = getView(i);
0278 if (v instanceof RowView) {
0279 rows.addElement(v);
0280 RowView rv = (RowView) v;
0281 rv.clearFilledColumns();
0282 rv.rowIndex = rows.size() - 1;
0283 rv.viewIndex = i;
0284 } else {
0285 Object o = v.getElement().getAttributes()
0286 .getAttribute(StyleConstants.NameAttribute);
0287 if (o instanceof HTML.Tag) {
0288 HTML.Tag kind = (HTML.Tag) o;
0289 if (kind == HTML.Tag.CAPTION) {
0290 captionIndex = i;
0291 }
0292 }
0293 }
0294 }
0295
0296 int maxColumns = 0;
0297 int nrows = rows.size();
0298 for (int row = 0; row < nrows; row++) {
0299 RowView rv = getRow(row);
0300 int col = 0;
0301 for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
0302 View cv = rv.getView(cell);
0303 if (!relativeCells) {
0304 AttributeSet a = cv.getAttributes();
0305 CSS.LengthValue lv = (CSS.LengthValue) a
0306 .getAttribute(CSS.Attribute.WIDTH);
0307 if ((lv != null) && (lv.isPercentage())) {
0308 relativeCells = true;
0309 }
0310 }
0311 // advance to a free column
0312 for (; rv.isFilled(col); col++)
0313 ;
0314 int rowSpan = getRowsOccupied(cv);
0315 if (rowSpan > 1) {
0316 multiRowCells = true;
0317 }
0318 int colSpan = getColumnsOccupied(cv);
0319 if ((colSpan > 1) || (rowSpan > 1)) {
0320 // fill in the overflow entries for this cell
0321 int rowLimit = row + rowSpan;
0322 int colLimit = col + colSpan;
0323 for (int i = row; i < rowLimit; i++) {
0324 for (int j = col; j < colLimit; j++) {
0325 if (i != row || j != col) {
0326 addFill(i, j);
0327 }
0328 }
0329 }
0330 if (colSpan > 1) {
0331 col += colSpan - 1;
0332 }
0333 }
0334 }
0335 maxColumns = Math.max(maxColumns, col);
0336 }
0337
0338 // setup the column layout/requirements
0339 columnSpans = new int[maxColumns];
0340 columnOffsets = new int[maxColumns];
0341 columnRequirements = new SizeRequirements[maxColumns];
0342 for (int i = 0; i < maxColumns; i++) {
0343 columnRequirements[i] = new SizeRequirements();
0344 columnRequirements[i].maximum = Integer.MAX_VALUE;
0345 }
0346 gridValid = true;
0347 }
0348 }
0349
0350 /**
0351 * Mark a grid location as filled in for a cells overflow.
0352 */
0353 void addFill(int row, int col) {
0354 RowView rv = getRow(row);
0355 if (rv != null) {
0356 rv.fillColumn(col);
0357 }
0358 }
0359
0360 /**
0361 * Layout the columns to fit within the given target span.
0362 *
0363 * @param targetSpan the given span for total of all the table
0364 * columns
0365 * @param reqs the requirements desired for each column. This
0366 * is the column maximum of the cells minimum, preferred, and
0367 * maximum requested span
0368 * @param spans the return value of how much to allocated to
0369 * each column
0370 * @param offsets the return value of the offset from the
0371 * origin for each column
0372 * @return the offset from the origin and the span for each column
0373 * in the offsets and spans parameters
0374 */
0375 protected void layoutColumns(int targetSpan, int[] offsets,
0376 int[] spans, SizeRequirements[] reqs) {
0377 //clean offsets and spans
0378 Arrays.fill(offsets, 0);
0379 Arrays.fill(spans, 0);
0380 colIterator.setLayoutArrays(offsets, spans, targetSpan);
0381 CSS.calculateTiledLayout(colIterator, targetSpan);
0382 }
0383
0384 /**
0385 * Calculate the requirements for each column. The calculation
0386 * is done as two passes over the table. The table cells that
0387 * occupy a single column are scanned first to determine the
0388 * maximum of minimum, preferred, and maximum spans along the
0389 * give axis. Table cells that span multiple columns are excluded
0390 * from the first pass. A second pass is made to determine if
0391 * the cells that span multiple columns are satisfied. If the
0392 * column requirements are not satisified, the needs of the
0393 * multi-column cell is mixed into the existing column requirements.
0394 * The calculation of the multi-column distribution is based upon
0395 * the proportions of the existing column requirements and taking
0396 * into consideration any constraining maximums.
0397 */
0398 void calculateColumnRequirements(int axis) {
0399 // clean columnRequirements
0400 for (SizeRequirements req : columnRequirements) {
0401 req.minimum = 0;
0402 req.preferred = 0;
0403 req.maximum = Integer.MAX_VALUE;
0404 }
0405 Container host = getContainer();
0406 if (host != null) {
0407 if (host instanceof JTextComponent) {
0408 skipComments = !((JTextComponent) host).isEditable();
0409 } else {
0410 skipComments = true;
0411 }
0412 }
0413 // pass 1 - single column cells
0414 boolean hasMultiColumn = false;
0415 int nrows = getRowCount();
0416 for (int i = 0; i < nrows; i++) {
0417 RowView row = getRow(i);
0418 int col = 0;
0419 int ncells = row.getViewCount();
0420 for (int cell = 0; cell < ncells; cell++) {
0421 View cv = row.getView(cell);
0422 if (skipComments && !(cv instanceof CellView)) {
0423 continue;
0424 }
0425 for (; row.isFilled(col); col++)
0426 ; // advance to a free column
0427 int rowSpan = getRowsOccupied(cv);
0428 int colSpan = getColumnsOccupied(cv);
0429 if (colSpan == 1) {
0430 checkSingleColumnCell(axis, col, cv);
0431 } else {
0432 hasMultiColumn = true;
0433 col += colSpan - 1;
0434 }
0435 col++;
0436 }
0437 }
0438
0439 // pass 2 - multi-column cells
0440 if (hasMultiColumn) {
0441 for (int i = 0; i < nrows; i++) {
0442 RowView row = getRow(i);
0443 int col = 0;
0444 int ncells = row.getViewCount();
0445 for (int cell = 0; cell < ncells; cell++) {
0446 View cv = row.getView(cell);
0447 if (skipComments && !(cv instanceof CellView)) {
0448 continue;
0449 }
0450 for (; row.isFilled(col); col++)
0451 ; // advance to a free column
0452 int colSpan = getColumnsOccupied(cv);
0453 if (colSpan > 1) {
0454 checkMultiColumnCell(axis, col, colSpan, cv);
0455 col += colSpan - 1;
0456 }
0457 col++;
0458 }
0459 }
0460 }
0461 }
0462
0463 /**
0464 * check the requirements of a table cell that spans a single column.
0465 */
0466 void checkSingleColumnCell(int axis, int col, View v) {
0467 SizeRequirements req = columnRequirements[col];
0468 req.minimum = Math.max((int) v.getMinimumSpan(axis),
0469 req.minimum);
0470 req.preferred = Math.max((int) v.getPreferredSpan(axis),
0471 req.preferred);
0472 }
0473
0474 /**
0475 * check the requirements of a table cell that spans multiple
0476 * columns.
0477 */
0478 void checkMultiColumnCell(int axis, int col, int ncols, View v) {
0479 // calculate the totals
0480 long min = 0;
0481 long pref = 0;
0482 long max = 0;
0483 for (int i = 0; i < ncols; i++) {
0484 SizeRequirements req = columnRequirements[col + i];
0485 min += req.minimum;
0486 pref += req.preferred;
0487 max += req.maximum;
0488 }
0489
0490 // check if the minimum size needs adjustment.
0491 int cmin = (int) v.getMinimumSpan(axis);
0492 if (cmin > min) {
0493 /*
0494 * the columns that this cell spans need adjustment to fit
0495 * this table cell.... calculate the adjustments.
0496 */
0497 SizeRequirements[] reqs = new SizeRequirements[ncols];
0498 for (int i = 0; i < ncols; i++) {
0499 reqs[i] = columnRequirements[col + i];
0500 }
0501 int[] spans = new int[ncols];
0502 int[] offsets = new int[ncols];
0503 SizeRequirements.calculateTiledPositions(cmin, null, reqs,
0504 offsets, spans);
0505 // apply the adjustments
0506 for (int i = 0; i < ncols; i++) {
0507 SizeRequirements req = reqs[i];
0508 req.minimum = Math.max(spans[i], req.minimum);
0509 req.preferred = Math.max(req.minimum, req.preferred);
0510 req.maximum = Math.max(req.preferred, req.maximum);
0511 }
0512 }
0513
0514 // check if the preferred size needs adjustment.
0515 int cpref = (int) v.getPreferredSpan(axis);
0516 if (cpref > pref) {
0517 /*
0518 * the columns that this cell spans need adjustment to fit
0519 * this table cell.... calculate the adjustments.
0520 */
0521 SizeRequirements[] reqs = new SizeRequirements[ncols];
0522 for (int i = 0; i < ncols; i++) {
0523 reqs[i] = columnRequirements[col + i];
0524 }
0525 int[] spans = new int[ncols];
0526 int[] offsets = new int[ncols];
0527 SizeRequirements.calculateTiledPositions(cpref, null, reqs,
0528 offsets, spans);
0529 // apply the adjustments
0530 for (int i = 0; i < ncols; i++) {
0531 SizeRequirements req = reqs[i];
0532 req.preferred = Math.max(spans[i], req.preferred);
0533 req.maximum = Math.max(req.preferred, req.maximum);
0534 }
0535 }
0536
0537 }
0538
0539 // --- BoxView methods -----------------------------------------
0540
0541 /**
0542 * Calculate the requirements for the minor axis. This is called by
0543 * the superclass whenever the requirements need to be updated (i.e.
0544 * a preferenceChanged was messaged through this view).
0545 * <p>
0546 * This is implemented to calculate the requirements as the sum of the
0547 * requirements of the columns and then adjust it if the
0548 * CSS width or height attribute is specified and applicable to
0549 * the axis.
0550 */
0551 protected SizeRequirements calculateMinorAxisRequirements(int axis,
0552 SizeRequirements r) {
0553 updateGrid();
0554
0555 // calculate column requirements for each column
0556 calculateColumnRequirements(axis);
0557
0558 // the requirements are the sum of the columns.
0559 if (r == null) {
0560 r = new SizeRequirements();
0561 }
0562 long min = 0;
0563 long pref = 0;
0564 int n = columnRequirements.length;
0565 for (int i = 0; i < n; i++) {
0566 SizeRequirements req = columnRequirements[i];
0567 min += req.minimum;
0568 pref += req.preferred;
0569 }
0570 int adjust = (n + 1) * cellSpacing + 2 * borderWidth;
0571 min += adjust;
0572 pref += adjust;
0573 r.minimum = (int) min;
0574 r.preferred = (int) pref;
0575 r.maximum = (int) pref;
0576
0577 AttributeSet attr = getAttributes();
0578 CSS.LengthValue cssWidth = (CSS.LengthValue) attr
0579 .getAttribute(CSS.Attribute.WIDTH);
0580
0581 if (BlockView.spanSetFromAttributes(axis, r, cssWidth, null)) {
0582 if (r.minimum < (int) min) {
0583 // The user has requested a smaller size than is needed to
0584 // show the table, override it.
0585 r.maximum = r.minimum = r.preferred = (int) min;
0586 }
0587 }
0588 totalColumnRequirements.minimum = r.minimum;
0589 totalColumnRequirements.preferred = r.preferred;
0590 totalColumnRequirements.maximum = r.maximum;
0591
0592 // set the alignment
0593 Object o = attr.getAttribute(CSS.Attribute.TEXT_ALIGN);
0594 if (o != null) {
0595 // set horizontal alignment
0596 String ta = o.toString();
0597 if (ta.equals("left")) {
0598 r.alignment = 0;
0599 } else if (ta.equals("center")) {
0600 r.alignment = 0.5f;
0601 } else if (ta.equals("right")) {
0602 r.alignment = 1;
0603 } else {
0604 r.alignment = 0;
0605 }
0606 } else {
0607 r.alignment = 0;
0608 }
0609
0610 return r;
0611 }
0612
0613 /**
0614 * Calculate the requirements for the major axis. This is called by
0615 * the superclass whenever the requirements need to be updated (i.e.
0616 * a preferenceChanged was messaged through this view).
0617 * <p>
0618 * This is implemented to provide the superclass behavior adjusted for
0619 * multi-row table cells.
0620 */
0621 protected SizeRequirements calculateMajorAxisRequirements(int axis,
0622 SizeRequirements r) {
0623 updateInsets();
0624 rowIterator.updateAdjustments();
0625 r = CSS.calculateTiledRequirements(rowIterator, r);
0626 r.maximum = r.preferred;
0627 return r;
0628 }
0629
0630 /**
0631 * Perform layout for the minor axis of the box (i.e. the
0632 * axis orthoginal to the axis that it represents). The results
0633 * of the layout should be placed in the given arrays which represent
0634 * the allocations to the children along the minor axis. This
0635 * is called by the superclass whenever the layout needs to be
0636 * updated along the minor axis.
0637 * <p>
0638 * This is implemented to call the
0639 * <a href="#layoutColumns">layoutColumns</a> method, and then
0640 * forward to the superclass to actually carry out the layout
0641 * of the tables rows.
0642 *
0643 * @param targetSpan the total span given to the view, which
0644 * whould be used to layout the children
0645 * @param axis the axis being layed out
0646 * @param offsets the offsets from the origin of the view for
0647 * each of the child views. This is a return value and is
0648 * filled in by the implementation of this method
0649 * @param spans the span of each child view; this is a return
0650 * value and is filled in by the implementation of this method
0651 * @return the offset and span for each child view in the
0652 * offsets and spans parameters
0653 */
0654 protected void layoutMinorAxis(int targetSpan, int axis,
0655 int[] offsets, int[] spans) {
0656 // make grid is properly represented
0657 updateGrid();
0658
0659 // all of the row layouts are invalid, so mark them that way
0660 int n = getRowCount();
0661 for (int i = 0; i < n; i++) {
0662 RowView row = getRow(i);
0663 row.layoutChanged(axis);
0664 }
0665
0666 // calculate column spans
0667 layoutColumns(targetSpan, columnOffsets, columnSpans,
0668 columnRequirements);
0669
0670 // continue normal layout
0671 super .layoutMinorAxis(targetSpan, axis, offsets, spans);
0672 }
0673
0674 /**
0675 * Perform layout for the major axis of the box (i.e. the
0676 * axis that it represents). The results
0677 * of the layout should be placed in the given arrays which represent
0678 * the allocations to the children along the minor axis. This
0679 * is called by the superclass whenever the layout needs to be
0680 * updated along the minor axis.
0681 * <p>
0682 * This method is where the layout of the table rows within the
0683 * table takes place. This method is implemented to call the use
0684 * the RowIterator and the CSS collapsing tile to layout
0685 * with border spacing and border collapsing capabilities.
0686 *
0687 * @param targetSpan the total span given to the view, which
0688 * whould be used to layout the children
0689 * @param axis the axis being layed out
0690 * @param offsets the offsets from the origin of the view for
0691 * each of the child views; this is a return value and is
0692 * filled in by the implementation of this method
0693 * @param spans the span of each child view; this is a return
0694 * value and is filled in by the implementation of this method
0695 * @return the offset and span for each child view in the
0696 * offsets and spans parameters
0697 */
0698 protected void layoutMajorAxis(int targetSpan, int axis,
0699 int[] offsets, int[] spans) {
0700 rowIterator.setLayoutArrays(offsets, spans);
0701 CSS.calculateTiledLayout(rowIterator, targetSpan);
0702
0703 if (captionIndex != -1) {
0704 // place the caption
0705 View caption = getView(captionIndex);
0706 int h = (int) caption.getPreferredSpan(Y_AXIS);
0707 spans[captionIndex] = h;
0708 short boxBottom = (short) painter.getInset(BOTTOM, this );
0709 if (boxBottom != getBottomInset()) {
0710 offsets[captionIndex] = targetSpan + boxBottom;
0711 } else {
0712 offsets[captionIndex] = -getTopInset();
0713 }
0714 }
0715 }
0716
0717 /**
0718 * Fetches the child view that represents the given position in
0719 * the model. This is implemented to walk through the children
0720 * looking for a range that contains the given position. In this
0721 * view the children do not necessarily have a one to one mapping
0722 * with the child elements.
0723 *
0724 * @param pos the search position >= 0
0725 * @param a the allocation to the table on entry, and the
0726 * allocation of the view containing the position on exit
0727 * @return the view representing the given position, or
0728 * null if there isn't one
0729 */
0730 protected View getViewAtPosition(int pos, Rectangle a) {
0731 int n = getViewCount();
0732 for (int i = 0; i < n; i++) {
0733 View v = getView(i);
0734 int p0 = v.getStartOffset();
0735 int p1 = v.getEndOffset();
0736 if ((pos >= p0) && (pos < p1)) {
0737 // it's in this view.
0738 if (a != null) {
0739 childAllocation(i, a);
0740 }
0741 return v;
0742 }
0743 }
0744 if (pos == getEndOffset()) {
0745 View v = getView(n - 1);
0746 if (a != null) {
0747 this .childAllocation(n - 1, a);
0748 }
0749 return v;
0750 }
0751 return null;
0752 }
0753
0754 // --- View methods ---------------------------------------------
0755
0756 /**
0757 * Fetches the attributes to use when rendering. This is
0758 * implemented to multiplex the attributes specified in the
0759 * model with a StyleSheet.
0760 */
0761 public AttributeSet getAttributes() {
0762 if (attr == null) {
0763 StyleSheet sheet = getStyleSheet();
0764 attr = sheet.getViewAttributes(this );
0765 }
0766 return attr;
0767 }
0768
0769 /**
0770 * Renders using the given rendering surface and area on that
0771 * surface. This is implemented to delegate to the css box
0772 * painter to paint the border and background prior to the
0773 * interior. The superclass culls rendering the children
0774 * that don't directly intersect the clip and the row may
0775 * have cells hanging from a row above in it. The table
0776 * does not use the superclass rendering behavior and instead
0777 * paints all of the rows and lets the rows cull those
0778 * cells not intersecting the clip region.
0779 *
0780 * @param g the rendering surface to use
0781 * @param allocation the allocated region to render into
0782 * @see View#paint
0783 */
0784 public void paint(Graphics g, Shape allocation) {
0785 // paint the border
0786 Rectangle a = allocation.getBounds();
0787 setSize(a.width, a.height);
0788 if (captionIndex != -1) {
0789 // adjust the border for the caption
0790 short top = (short) painter.getInset(TOP, this );
0791 short bottom = (short) painter.getInset(BOTTOM, this );
0792 if (top != getTopInset()) {
0793 int h = getTopInset() - top;
0794 a.y += h;
0795 a.height -= h;
0796 } else {
0797 a.height -= getBottomInset() - bottom;
0798 }
0799 }
0800 painter.paint(g, a.x, a.y, a.width, a.height, this );
0801 // paint interior
0802 int n = getViewCount();
0803 for (int i = 0; i < n; i++) {
0804 View v = getView(i);
0805 v.paint(g, getChildAllocation(i, allocation));
0806 }
0807 //super.paint(g, a);
0808 }
0809
0810 /**
0811 * Establishes the parent view for this view. This is
0812 * guaranteed to be called before any other methods if the
0813 * parent view is functioning properly.
0814 * <p>
0815 * This is implemented
0816 * to forward to the superclass as well as call the
0817 * <a href="#setPropertiesFromAttributes">setPropertiesFromAttributes</a>
0818 * method to set the paragraph properties from the css
0819 * attributes. The call is made at this time to ensure
0820 * the ability to resolve upward through the parents
0821 * view attributes.
0822 *
0823 * @param parent the new parent, or null if the view is
0824 * being removed from a parent it was previously added
0825 * to
0826 */
0827 public void setParent(View parent) {
0828 super .setParent(parent);
0829 if (parent != null) {
0830 setPropertiesFromAttributes();
0831 }
0832 }
0833
0834 /**
0835 * Fetches the ViewFactory implementation that is feeding
0836 * the view hierarchy.
0837 * This replaces the ViewFactory with an implementation that
0838 * calls through to the createTableRow and createTableCell
0839 * methods. If the element given to the factory isn't a
0840 * table row or cell, the request is delegated to the factory
0841 * produced by the superclass behavior.
0842 *
0843 * @return the factory, null if none
0844 */
0845 public ViewFactory getViewFactory() {
0846 return this ;
0847 }
0848
0849 /**
0850 * Gives notification that something was inserted into
0851 * the document in a location that this view is responsible for.
0852 * This replaces the ViewFactory with an implementation that
0853 * calls through to the createTableRow and createTableCell
0854 * methods. If the element given to the factory isn't a
0855 * table row or cell, the request is delegated to the factory
0856 * passed as an argument.
0857 *
0858 * @param e the change information from the associated document
0859 * @param a the current allocation of the view
0860 * @param f the factory to use to rebuild if the view has children
0861 * @see View#insertUpdate
0862 */
0863 public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
0864 super .insertUpdate(e, a, this );
0865 }
0866
0867 /**
0868 * Gives notification that something was removed from the document
0869 * in a location that this view is responsible for.
0870 * This replaces the ViewFactory with an implementation that
0871 * calls through to the createTableRow and createTableCell
0872 * methods. If the element given to the factory isn't a
0873 * table row or cell, the request is delegated to the factory
0874 * passed as an argument.
0875 *
0876 * @param e the change information from the associated document
0877 * @param a the current allocation of the view
0878 * @param f the factory to use to rebuild if the view has children
0879 * @see View#removeUpdate
0880 */
0881 public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
0882 super .removeUpdate(e, a, this );
0883 }
0884
0885 /**
0886 * Gives notification from the document that attributes were changed
0887 * in a location that this view is responsible for.
0888 * This replaces the ViewFactory with an implementation that
0889 * calls through to the createTableRow and createTableCell
0890 * methods. If the element given to the factory isn't a
0891 * table row or cell, the request is delegated to the factory
0892 * passed as an argument.
0893 *
0894 * @param e the change information from the associated document
0895 * @param a the current allocation of the view
0896 * @param f the factory to use to rebuild if the view has children
0897 * @see View#changedUpdate
0898 */
0899 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
0900 super .changedUpdate(e, a, this );
0901 }
0902
0903 protected void forwardUpdate(DocumentEvent.ElementChange ec,
0904 DocumentEvent e, Shape a, ViewFactory f) {
0905 super .forwardUpdate(ec, e, a, f);
0906 // A change in any of the table cells usually effects the whole table,
0907 // so redraw it all!
0908 if (a != null) {
0909 Component c = getContainer();
0910 if (c != null) {
0911 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a
0912 : a.getBounds();
0913 c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
0914 }
0915 }
0916 }
0917
0918 /**
0919 * Change the child views. This is implemented to
0920 * provide the superclass behavior and invalidate the
0921 * grid so that rows and columns will be recalculated.
0922 */
0923 public void replace(int offset, int length, View[] views) {
0924 super .replace(offset, length, views);
0925 invalidateGrid();
0926 }
0927
0928 // --- ViewFactory methods ------------------------------------------
0929
0930 /**
0931 * The table itself acts as a factory for the various
0932 * views that actually represent pieces of the table.
0933 * All other factory activity is delegated to the factory
0934 * returned by the parent of the table.
0935 */
0936 public View create(Element elem) {
0937 Object o = elem.getAttributes().getAttribute(
0938 StyleConstants.NameAttribute);
0939 if (o instanceof HTML.Tag) {
0940 HTML.Tag kind = (HTML.Tag) o;
0941 if (kind == HTML.Tag.TR) {
0942 return createTableRow(elem);
0943 } else if ((kind == HTML.Tag.TD) || (kind == HTML.Tag.TH)) {
0944 return new CellView(elem);
0945 } else if (kind == HTML.Tag.CAPTION) {
0946 return new javax.swing.text.html.ParagraphView(elem);
0947 }
0948 }
0949 // default is to delegate to the normal factory
0950 View p = getParent();
0951 if (p != null) {
0952 ViewFactory f = p.getViewFactory();
0953 if (f != null) {
0954 return f.create(elem);
0955 }
0956 }
0957 return null;
0958 }
0959
0960 // ---- variables ----------------------------------------------------
0961
0962 private AttributeSet attr;
0963 private StyleSheet.BoxPainter painter;
0964
0965 private int cellSpacing;
0966 private int borderWidth;
0967
0968 /**
0969 * The index of the caption view if there is a caption.
0970 * This has a value of -1 if there is no caption. The
0971 * caption lives in the inset area of the table, and is
0972 * updated with each time the grid is recalculated.
0973 */
0974 private int captionIndex;
0975
0976 /**
0977 * Do any of the table cells contain a relative size
0978 * specification? This is updated with each call to
0979 * updateGrid(). If this is true, the ColumnIterator
0980 * will do extra work to calculate relative cell
0981 * specifications.
0982 */
0983 private boolean relativeCells;
0984
0985 /**
0986 * Do any of the table cells span multiple rows? If
0987 * true, the RowRequirementIterator will do additional
0988 * work to adjust the requirements of rows spanned by
0989 * a single table cell. This is updated with each call to
0990 * updateGrid().
0991 */
0992 private boolean multiRowCells;
0993
0994 int[] columnSpans;
0995 int[] columnOffsets;
0996 /**
0997 * SizeRequirements for all the columns.
0998 */
0999 SizeRequirements totalColumnRequirements;
1000 SizeRequirements[] columnRequirements;
1001
1002 RowIterator rowIterator = new RowIterator();
1003 ColumnIterator colIterator = new ColumnIterator();
1004
1005 Vector rows;
1006
1007 // whether to display comments inside table or not.
1008 boolean skipComments = false;
1009
1010 boolean gridValid;
1011 static final private BitSet EMPTY = new BitSet();
1012
1013 class ColumnIterator implements CSS.LayoutIterator {
1014
1015 /**
1016 * Disable percentage adjustments which should only apply
1017 * when calculating layout, not requirements.
1018 */
1019 void disablePercentages() {
1020 percentages = null;
1021 }
1022
1023 /**
1024 * Update percentage adjustments if they are needed.
1025 */
1026 private void updatePercentagesAndAdjustmentWeights(int span) {
1027 adjustmentWeights = new int[columnRequirements.length];
1028 for (int i = 0; i < columnRequirements.length; i++) {
1029 adjustmentWeights[i] = 0;
1030 }
1031 if (relativeCells) {
1032 percentages = new int[columnRequirements.length];
1033 } else {
1034 percentages = null;
1035 }
1036 int nrows = getRowCount();
1037 for (int rowIndex = 0; rowIndex < nrows; rowIndex++) {
1038 RowView row = getRow(rowIndex);
1039 int col = 0;
1040 int ncells = row.getViewCount();
1041 for (int cell = 0; cell < ncells; cell++, col++) {
1042 View cv = row.getView(cell);
1043 for (; row.isFilled(col); col++)
1044 ; // advance to a free column
1045 int rowSpan = getRowsOccupied(cv);
1046 int colSpan = getColumnsOccupied(cv);
1047 AttributeSet a = cv.getAttributes();
1048 CSS.LengthValue lv = (CSS.LengthValue) a
1049 .getAttribute(CSS.Attribute.WIDTH);
1050 if (lv != null) {
1051 int len = (int) (lv.getValue(span) / colSpan + 0.5f);
1052 for (int i = 0; i < colSpan; i++) {
1053 if (lv.isPercentage()) {
1054 // add a percentage requirement
1055 percentages[col + i] = Math.max(
1056 percentages[col + i], len);
1057 adjustmentWeights[col + i] = Math.max(
1058 adjustmentWeights[col + i],
1059 WorstAdjustmentWeight);
1060 } else {
1061 adjustmentWeights[col + i] = Math.max(
1062 adjustmentWeights[col + i],
1063 WorstAdjustmentWeight - 1);
1064 }
1065 }
1066 }
1067 col += colSpan - 1;
1068 }
1069 }
1070 }
1071
1072 /**
1073 * Set the layout arrays to use for holding layout results
1074 */
1075 public void setLayoutArrays(int offsets[], int spans[],
1076 int targetSpan) {
1077 this .offsets = offsets;
1078 this .spans = spans;
1079 updatePercentagesAndAdjustmentWeights(targetSpan);
1080 }
1081
1082 // --- RequirementIterator methods -------------------
1083
1084 public int getCount() {
1085 return columnRequirements.length;
1086 }
1087
1088 public void setIndex(int i) {
1089 col = i;
1090 }
1091
1092 public void setOffset(int offs) {
1093 offsets[col] = offs;
1094 }
1095
1096 public int getOffset() {
1097 return offsets[col];
1098 }
1099
1100 public void setSpan(int span) {
1101 spans[col] = span;
1102 }
1103
1104 public int getSpan() {
1105 return spans[col];
1106 }
1107
1108 public float getMinimumSpan(float parentSpan) {
1109 // do not care for percentages, since min span can't
1110 // be less than columnRequirements[col].minimum,
1111 // but can be less than percentage value.
1112 return columnRequirements[col].minimum;
1113 }
1114
1115 public float getPreferredSpan(float parentSpan) {
1116 if ((percentages != null) && (percentages[col] != 0)) {
1117 return Math.max(percentages[col],
1118 columnRequirements[col].minimum);
1119 }
1120 return columnRequirements[col].preferred;
1121 }
1122
1123 public float getMaximumSpan(float parentSpan) {
1124 return columnRequirements[col].maximum;
1125 }
1126
1127 public float getBorderWidth() {
1128 return borderWidth;
1129 }
1130
1131 public float getLeadingCollapseSpan() {
1132 return cellSpacing;
1133 }
1134
1135 public float getTrailingCollapseSpan() {
1136 return cellSpacing;
1137 }
1138
1139 public int getAdjustmentWeight() {
1140 return adjustmentWeights[col];
1141 }
1142
1143 /**
1144 * Current column index
1145 */
1146 private int col;
1147
1148 /**
1149 * percentage values (may be null since there
1150 * might not be any).
1151 */
1152 private int[] percentages;
1153
1154 private int[] adjustmentWeights;
1155
1156 private int[] offsets;
1157 private int[] spans;
1158 }
1159
1160 class RowIterator implements CSS.LayoutIterator {
1161
1162 RowIterator() {
1163 }
1164
1165 void updateAdjustments() {
1166 int axis = Y_AXIS;
1167 if (multiRowCells) {
1168 // adjust requirements of multi-row cells
1169 int n = getRowCount();
1170 adjustments = new int[n];
1171 for (int i = 0; i < n; i++) {
1172 RowView rv = getRow(i);
1173 if (rv.multiRowCells == true) {
1174 int ncells = rv.getViewCount();
1175 for (int j = 0; j < ncells; j++) {
1176 View v = rv.getView(j);
1177 int nrows = getRowsOccupied(v);
1178 if (nrows > 1) {
1179 int spanNeeded = (int) v
1180 .getPreferredSpan(axis);
1181 adjustMultiRowSpan(spanNeeded, nrows, i);
1182 }
1183 }
1184 }
1185 }
1186 } else {
1187 adjustments = null;
1188 }
1189 }
1190
1191 /**
1192 * Fixup preferences to accomodate a multi-row table cell
1193 * if not already covered by existing preferences. This is
1194 * a no-op if not all of the rows needed (to do this check/fixup)
1195 * have arrived yet.
1196 */
1197 void adjustMultiRowSpan(int spanNeeded, int nrows, int rowIndex) {
1198 if ((rowIndex + nrows) > getCount()) {
1199 // rows are missing (could be a bad rowspan specification)
1200 // or not all the rows have arrived. Do the best we can with
1201 // the current set of rows.
1202 nrows = getCount() - rowIndex;
1203 if (nrows < 1) {
1204 return;
1205 }
1206 }
1207 int span = 0;
1208 for (int i = 0; i < nrows; i++) {
1209 RowView rv = getRow(rowIndex + i);
1210 span += rv.getPreferredSpan(Y_AXIS);
1211 }
1212 if (spanNeeded > span) {
1213 int adjust = (spanNeeded - span);
1214 int rowAdjust = adjust / nrows;
1215 int firstAdjust = rowAdjust
1216 + (adjust - (rowAdjust * nrows));
1217 RowView rv = getRow(rowIndex);
1218 adjustments[rowIndex] = Math.max(adjustments[rowIndex],
1219 firstAdjust);
1220 for (int i = 1; i < nrows; i++) {
1221 adjustments[rowIndex + i] = Math.max(
1222 adjustments[rowIndex + i], rowAdjust);
1223 }
1224 }
1225 }
1226
1227 void setLayoutArrays(int[] offsets, int[] spans) {
1228 this .offsets = offsets;
1229 this .spans = spans;
1230 }
1231
1232 // --- RequirementIterator methods -------------------
1233
1234 public void setOffset(int offs) {
1235 RowView rv = getRow(row);
1236 if (rv != null) {
1237 offsets[rv.viewIndex] = offs;
1238 }
1239 }
1240
1241 public int getOffset() {
1242 RowView rv = getRow(row);
1243 if (rv != null) {
1244 return offsets[rv.viewIndex];
1245 }
1246 return 0;
1247 }
1248
1249 public void setSpan(int span) {
1250 RowView rv = getRow(row);
1251 if (rv != null) {
1252 spans[rv.viewIndex] = span;
1253 }
1254 }
1255
1256 public int getSpan() {
1257 RowView rv = getRow(row);
1258 if (rv != null) {
1259 return spans[rv.viewIndex];
1260 }
1261 return 0;
1262 }
1263
1264 public int getCount() {
1265 return rows.size();
1266 }
1267
1268 public void setIndex(int i) {
1269 row = i;
1270 }
1271
1272 public float getMinimumSpan(float parentSpan) {
1273 return getPreferredSpan(parentSpan);
1274 }
1275
1276 public float getPreferredSpan(float parentSpan) {
1277 RowView rv = getRow(row);
1278 if (rv != null) {
1279 int adjust = (adjustments != null) ? adjustments[row]
1280 : 0;
1281 return rv.getPreferredSpan(TableView.this .getAxis())
1282 + adjust;
1283 }
1284 return 0;
1285 }
1286
1287 public float getMaximumSpan(float parentSpan) {
1288 return getPreferredSpan(parentSpan);
1289 }
1290
1291 public float getBorderWidth() {
1292 return borderWidth;
1293 }
1294
1295 public float getLeadingCollapseSpan() {
1296 return cellSpacing;
1297 }
1298
1299 public float getTrailingCollapseSpan() {
1300 return cellSpacing;
1301 }
1302
1303 public int getAdjustmentWeight() {
1304 return 0;
1305 }
1306
1307 /**
1308 * Current row index
1309 */
1310 private int row;
1311
1312 /**
1313 * Adjustments to the row requirements to handle multi-row
1314 * table cells.
1315 */
1316 private int[] adjustments;
1317
1318 private int[] offsets;
1319 private int[] spans;
1320 }
1321
1322 /**
1323 * View of a row in a row-centric table.
1324 */
1325 public class RowView extends BoxView {
1326
1327 /**
1328 * Constructs a TableView for the given element.
1329 *
1330 * @param elem the element that this view is responsible for
1331 */
1332 public RowView(Element elem) {
1333 super (elem, View.X_AXIS);
1334 fillColumns = new BitSet();
1335 RowView.this .setPropertiesFromAttributes();
1336 }
1337
1338 void clearFilledColumns() {
1339 fillColumns.and(EMPTY);
1340 }
1341
1342 void fillColumn(int col) {
1343 fillColumns.set(col);
1344 }
1345
1346 boolean isFilled(int col) {
1347 return fillColumns.get(col);
1348 }
1349
1350 /**
1351 * The number of columns present in this row.
1352 */
1353 int getColumnCount() {
1354 int nfill = 0;
1355 int n = fillColumns.size();
1356 for (int i = 0; i < n; i++) {
1357 if (fillColumns.get(i)) {
1358 nfill++;
1359 }
1360 }
1361 return getViewCount() + nfill;
1362 }
1363
1364 /**
1365 * Fetches the attributes to use when rendering. This is
1366 * implemented to multiplex the attributes specified in the
1367 * model with a StyleSheet.
1368 */
1369 public AttributeSet getAttributes() {
1370 return attr;
1371 }
1372
1373 View findViewAtPoint(int x, int y, Rectangle alloc) {
1374 int n = getViewCount();
1375 for (int i = 0; i < n; i++) {
1376 if (getChildAllocation(i, alloc).contains(x, y)) {
1377 childAllocation(i, alloc);
1378 return getView(i);
1379 }
1380 }
1381 return null;
1382 }
1383
1384 protected StyleSheet getStyleSheet() {
1385 HTMLDocument doc = (HTMLDocument) getDocument();
1386 return doc.getStyleSheet();
1387 }
1388
1389 /**
1390 * This is called by a child to indicate its
1391 * preferred span has changed. This is implemented to
1392 * execute the superclass behavior and well as try to
1393 * determine if a row with a multi-row cell hangs across
1394 * this row. If a multi-row cell covers this row it also
1395 * needs to propagate a preferenceChanged so that it will
1396 * recalculate the multi-row cell.
1397 *
1398 * @param child the child view
1399 * @param width true if the width preference should change
1400 * @param height true if the height preference should change
1401 */
1402 public void preferenceChanged(View child, boolean width,
1403 boolean height) {
1404 super .preferenceChanged(child, width, height);
1405 if (TableView.this .multiRowCells && height) {
1406 for (int i = rowIndex - 1; i >= 0; i--) {
1407 RowView rv = TableView.this .getRow(i);
1408 if (rv.multiRowCells) {
1409 rv.preferenceChanged(null, false, true);
1410 break;
1411 }
1412 }
1413 }
1414 }
1415
1416 // The major axis requirements for a row are dictated by the column
1417 // requirements. These methods use the value calculated by
1418 // TableView.
1419 protected SizeRequirements calculateMajorAxisRequirements(
1420 int axis, SizeRequirements r) {
1421 SizeRequirements req = new SizeRequirements();
1422 req.minimum = totalColumnRequirements.minimum;
1423 req.maximum = totalColumnRequirements.maximum;
1424 req.preferred = totalColumnRequirements.preferred;
1425 req.alignment = 0f;
1426 return req;
1427 }
1428
1429 public float getMinimumSpan(int axis) {
1430 float value;
1431
1432 if (axis == View.X_AXIS) {
1433 value = totalColumnRequirements.minimum
1434 + getLeftInset() + getRightInset();
1435 } else {
1436 value = super .getMinimumSpan(axis);
1437 }
1438 return value;
1439 }
1440
1441 public float getMaximumSpan(int axis) {
1442 float value;
1443
1444 if (axis == View.X_AXIS) {
1445 // We're flexible.
1446 value = (float) Integer.MAX_VALUE;
1447 } else {
1448 value = super .getMaximumSpan(axis);
1449 }
1450 return value;
1451 }
1452
1453 public float getPreferredSpan(int axis) {
1454 float value;
1455
1456 if (axis == View.X_AXIS) {
1457 value = totalColumnRequirements.preferred
1458 + getLeftInset() + getRightInset();
1459 } else {
1460 value = super .getPreferredSpan(axis);
1461 }
1462 return value;
1463 }
1464
1465 public void changedUpdate(DocumentEvent e, Shape a,
1466 ViewFactory f) {
1467 super .changedUpdate(e, a, f);
1468 int pos = e.getOffset();
1469 if (pos <= getStartOffset()
1470 && (pos + e.getLength()) >= getEndOffset()) {
1471 RowView.this .setPropertiesFromAttributes();
1472 }
1473 }
1474
1475 /**
1476 * Renders using the given rendering surface and area on that
1477 * surface. This is implemented to delegate to the css box
1478 * painter to paint the border and background prior to the
1479 * interior.
1480 *
1481 * @param g the rendering surface to use
1482 * @param allocation the allocated region to render into
1483 * @see View#paint
1484 */
1485 public void paint(Graphics g, Shape allocation) {
1486 Rectangle a = (Rectangle) allocation;
1487 painter.paint(g, a.x, a.y, a.width, a.height, this );
1488 super .paint(g, a);
1489 }
1490
1491 /**
1492 * Change the child views. This is implemented to
1493 * provide the superclass behavior and invalidate the
1494 * grid so that rows and columns will be recalculated.
1495 */
1496 public void replace(int offset, int length, View[] views) {
1497 super .replace(offset, length, views);
1498 invalidateGrid();
1499 }
1500
1501 /**
1502 * Calculate the height requirements of the table row. The
1503 * requirements of multi-row cells are not considered for this
1504 * calculation. The table itself will check and adjust the row
1505 * requirements for all the rows that have multi-row cells spanning
1506 * them. This method updates the multi-row flag that indicates that
1507 * this row and rows below need additional consideration.
1508 */
1509 protected SizeRequirements calculateMinorAxisRequirements(
1510 int axis, SizeRequirements r) {
1511 // return super.calculateMinorAxisRequirements(axis, r);
1512 long min = 0;
1513 long pref = 0;
1514 long max = 0;
1515 multiRowCells = false;
1516 int n = getViewCount();
1517 for (int i = 0; i < n; i++) {
1518 View v = getView(i);
1519 if (getRowsOccupied(v) > 1) {
1520 multiRowCells = true;
1521 max = Math.max((int) v.getMaximumSpan(axis), max);
1522 } else {
1523 min = Math.max((int) v.getMinimumSpan(axis), min);
1524 pref = Math.max((int) v.getPreferredSpan(axis),
1525 pref);
1526 max = Math.max((int) v.getMaximumSpan(axis), max);
1527 }
1528 }
1529
1530 if (r == null) {
1531 r = new SizeRequirements();
1532 r.alignment = 0.5f;
1533 }
1534 r.preferred = (int) pref;
1535 r.minimum = (int) min;
1536 r.maximum = (int) max;
1537 return r;
1538 }
1539
1540 /**
1541 * Perform layout for the major axis of the box (i.e. the
1542 * axis that it represents). The results of the layout should
1543 * be placed in the given arrays which represent the allocations
1544 * to the children along the major axis.
1545 * <p>
1546 * This is re-implemented to give each child the span of the column
1547 * width for the table, and to give cells that span multiple columns
1548 * the multi-column span.
1549 *
1550 * @param targetSpan the total span given to the view, which
1551 * whould be used to layout the children
1552 * @param axis the axis being layed out
1553 * @param offsets the offsets from the origin of the view for
1554 * each of the child views; this is a return value and is
1555 * filled in by the implementation of this method
1556 * @param spans the span of each child view; this is a return
1557 * value and is filled in by the implementation of this method
1558 * @return the offset and span for each child view in the
1559 * offsets and spans parameters
1560 */
1561 protected void layoutMajorAxis(int targetSpan, int axis,
1562 int[] offsets, int[] spans) {
1563 int col = 0;
1564 int ncells = getViewCount();
1565 for (int cell = 0; cell < ncells; cell++) {
1566 View cv = getView(cell);
1567 if (skipComments && !(cv instanceof CellView)) {
1568 continue;
1569 }
1570 for (; isFilled(col); col++)
1571 ; // advance to a free column
1572 int colSpan = getColumnsOccupied(cv);
1573 spans[cell] = columnSpans[col];
1574 offsets[cell] = columnOffsets[col];
1575 if (colSpan > 1) {
1576 int n = columnSpans.length;
1577 for (int j = 1; j < colSpan; j++) {
1578 // Because the table may be only partially formed, some
1579 // of the columns may not yet exist. Therefore we check
1580 // the bounds.
1581 if ((col + j) < n) {
1582 spans[cell] += columnSpans[col + j];
1583 spans[cell] += cellSpacing;
1584 }
1585 }
1586 col += colSpan - 1;
1587 }
1588 col++;
1589 }
1590 }
1591
1592 /**
1593 * Perform layout for the minor axis of the box (i.e. the
1594 * axis orthoginal to the axis that it represents). The results
1595 * of the layout should be placed in the given arrays which represent
1596 * the allocations to the children along the minor axis. This
1597 * is called by the superclass whenever the layout needs to be
1598 * updated along the minor axis.
1599 * <p>
1600 * This is implemented to delegate to the superclass, then adjust
1601 * the span for any cell that spans multiple rows.
1602 *
1603 * @param targetSpan the total span given to the view, which
1604 * whould be used to layout the children
1605 * @param axis the axis being layed out
1606 * @param offsets the offsets from the origin of the view for
1607 * each of the child views; this is a return value and is
1608 * filled in by the implementation of this method
1609 * @param spans the span of each child view; this is a return
1610 * value and is filled in by the implementation of this method
1611 * @return the offset and span for each child view in the
1612 * offsets and spans parameters
1613 */
1614 protected void layoutMinorAxis(int targetSpan, int axis,
1615 int[] offsets, int[] spans) {
1616 super .layoutMinorAxis(targetSpan, axis, offsets, spans);
1617 int col = 0;
1618 int ncells = getViewCount();
1619 for (int cell = 0; cell < ncells; cell++, col++) {
1620 View cv = getView(cell);
1621 for (; isFilled(col); col++)
1622 ; // advance to a free column
1623 int colSpan = getColumnsOccupied(cv);
1624 int rowSpan = getRowsOccupied(cv);
1625 if (rowSpan > 1) {
1626
1627 int row0 = rowIndex;
1628 int row1 = Math.min(rowIndex + rowSpan - 1,
1629 getRowCount() - 1);
1630 spans[cell] = getMultiRowSpan(row0, row1);
1631 }
1632 if (colSpan > 1) {
1633 col += colSpan - 1;
1634 }
1635 }
1636 }
1637
1638 /**
1639 * Determines the resizability of the view along the
1640 * given axis. A value of 0 or less is not resizable.
1641 *
1642 * @param axis may be either View.X_AXIS or View.Y_AXIS
1643 * @return the resize weight
1644 * @exception IllegalArgumentException for an invalid axis
1645 */
1646 public int getResizeWeight(int axis) {
1647 return 1;
1648 }
1649
1650 /**
1651 * Fetches the child view that represents the given position in
1652 * the model. This is implemented to walk through the children
1653 * looking for a range that contains the given position. In this
1654 * view the children do not necessarily have a one to one mapping
1655 * with the child elements.
1656 *
1657 * @param pos the search position >= 0
1658 * @param a the allocation to the table on entry, and the
1659 * allocation of the view containing the position on exit
1660 * @return the view representing the given position, or
1661 * null if there isn't one
1662 */
1663 protected View getViewAtPosition(int pos, Rectangle a) {
1664 int n = getViewCount();
1665 for (int i = 0; i < n; i++) {
1666 View v = getView(i);
1667 int p0 = v.getStartOffset();
1668 int p1 = v.getEndOffset();
1669 if ((pos >= p0) && (pos < p1)) {
1670 // it's in this view.
1671 if (a != null) {
1672 childAllocation(i, a);
1673 }
1674 return v;
1675 }
1676 }
1677 if (pos == getEndOffset()) {
1678 View v = getView(n - 1);
1679 if (a != null) {
1680 this .childAllocation(n - 1, a);
1681 }
1682 return v;
1683 }
1684 return null;
1685 }
1686
1687 /**
1688 * Update any cached values that come from attributes.
1689 */
1690 void setPropertiesFromAttributes() {
1691 StyleSheet sheet = getStyleSheet();
1692 attr = sheet.getViewAttributes(this );
1693 painter = sheet.getBoxPainter(attr);
1694 }
1695
1696 private StyleSheet.BoxPainter painter;
1697 private AttributeSet attr;
1698
1699 /** columns filled by multi-column or multi-row cells */
1700 BitSet fillColumns;
1701
1702 /**
1703 * The row index within the overall grid
1704 */
1705 int rowIndex;
1706
1707 /**
1708 * The view index (for row index to view index conversion).
1709 * This is set by the updateGrid method.
1710 */
1711 int viewIndex;
1712
1713 /**
1714 * Does this table row have cells that span multiple rows?
1715 */
1716 boolean multiRowCells;
1717
1718 }
1719
1720 /**
1721 * Default view of an html table cell. This needs to be moved
1722 * somewhere else.
1723 */
1724 class CellView extends BlockView {
1725
1726 /**
1727 * Constructs a TableCell for the given element.
1728 *
1729 * @param elem the element that this view is responsible for
1730 */
1731 public CellView(Element elem) {
1732 super (elem, Y_AXIS);
1733 }
1734
1735 /**
1736 * Perform layout for the major axis of the box (i.e. the
1737 * axis that it represents). The results of the layout should
1738 * be placed in the given arrays which represent the allocations
1739 * to the children along the major axis. This is called by the
1740 * superclass to recalculate the positions of the child views
1741 * when the layout might have changed.
1742 * <p>
1743 * This is implemented to delegate to the superclass to
1744 * tile the children. If the target span is greater than
1745 * was needed, the offsets are adjusted to align the children
1746 * (i.e. position according to the html valign attribute).
1747 *
1748 * @param targetSpan the total span given to the view, which
1749 * whould be used to layout the children
1750 * @param axis the axis being layed out
1751 * @param offsets the offsets from the origin of the view for
1752 * each of the child views; this is a return value and is
1753 * filled in by the implementation of this method
1754 * @param spans the span of each child view; this is a return
1755 * value and is filled in by the implementation of this method
1756 * @return the offset and span for each child view in the
1757 * offsets and spans parameters
1758 */
1759 protected void layoutMajorAxis(int targetSpan, int axis,
1760 int[] offsets, int[] spans) {
1761 super .layoutMajorAxis(targetSpan, axis, offsets, spans);
1762 // calculate usage
1763 int used = 0;
1764 int n = spans.length;
1765 for (int i = 0; i < n; i++) {
1766 used += spans[i];
1767 }
1768
1769 // calculate adjustments
1770 int adjust = 0;
1771 if (used < targetSpan) {
1772 // PENDING(prinz) change to use the css alignment.
1773 String valign = (String) getElement().getAttributes()
1774 .getAttribute(HTML.Attribute.VALIGN);
1775 if (valign == null) {
1776 AttributeSet rowAttr = getElement()
1777 .getParentElement().getAttributes();
1778 valign = (String) rowAttr
1779 .getAttribute(HTML.Attribute.VALIGN);
1780 }
1781 if ((valign == null) || valign.equals("middle")) {
1782 adjust = (targetSpan - used) / 2;
1783 } else if (valign.equals("bottom")) {
1784 adjust = targetSpan - used;
1785 }
1786 }
1787
1788 // make adjustments.
1789 if (adjust != 0) {
1790 for (int i = 0; i < n; i++) {
1791 offsets[i] += adjust;
1792 }
1793 }
1794 }
1795
1796 /**
1797 * Calculate the requirements needed along the major axis.
1798 * This is called by the superclass whenever the requirements
1799 * need to be updated (i.e. a preferenceChanged was messaged
1800 * through this view).
1801 * <p>
1802 * This is implemented to delegate to the superclass, but
1803 * indicate the maximum size is very large (i.e. the cell
1804 * is willing to expend to occupy the full height of the row).
1805 *
1806 * @param axis the axis being layed out.
1807 * @param r the requirements to fill in. If null, a new one
1808 * should be allocated.
1809 */
1810 protected SizeRequirements calculateMajorAxisRequirements(
1811 int axis, SizeRequirements r) {
1812 SizeRequirements req = super
1813 .calculateMajorAxisRequirements(axis, r);
1814 req.maximum = Integer.MAX_VALUE;
1815 return req;
1816 }
1817
1818 @Override
1819 protected SizeRequirements calculateMinorAxisRequirements(
1820 int axis, SizeRequirements r) {
1821 SizeRequirements rv = super .calculateMinorAxisRequirements(
1822 axis, r);
1823 //for the cell the minimum should be derived from the child views
1824 //the parent behaviour is to use CSS for that
1825 int n = getViewCount();
1826 int min = 0;
1827 for (int i = 0; i < n; i++) {
1828 View v = getView(i);
1829 min = Math.max((int) v.getMinimumSpan(axis), min);
1830 }
1831 rv.minimum = Math.min(rv.minimum, min);
1832 return rv;
1833 }
1834 }
1835
1836 }
|