blob: db0dc790fc20ef630e36d57927ca127bd9e427ed [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing.text.html;
26
27import java.awt.*;
28import java.util.BitSet;
29import java.util.Vector;
30import java.util.Arrays;
31import javax.swing.SizeRequirements;
32import javax.swing.event.DocumentEvent;
33
34import javax.swing.text.*;
35
36/**
37 * HTML table view.
38 *
39 * @author Timothy Prinzing
40 * @see View
41 */
42/*public*/ class TableView extends BoxView implements ViewFactory {
43
44 /**
45 * Constructs a TableView for the given element.
46 *
47 * @param elem the element that this view is responsible for
48 */
49 public TableView(Element elem) {
50 super(elem, View.Y_AXIS);
51 rows = new Vector();
52 gridValid = false;
53 captionIndex = -1;
54 totalColumnRequirements = new SizeRequirements();
55 }
56
57 /**
58 * Creates a new table row.
59 *
60 * @param elem an element
61 * @return the row
62 */
63 protected RowView createTableRow(Element elem) {
64 // PENDING(prinz) need to add support for some of the other
65 // elements, but for now just ignore anything that is not
66 // a TR.
67 Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
68 if (o == HTML.Tag.TR) {
69 return new RowView(elem);
70 }
71 return null;
72 }
73
74 /**
75 * The number of columns in the table.
76 */
77 public int getColumnCount() {
78 return columnSpans.length;
79 }
80
81 /**
82 * Fetches the span (width) of the given column.
83 * This is used by the nested cells to query the
84 * sizes of grid locations outside of themselves.
85 */
86 public int getColumnSpan(int col) {
87 if (col < columnSpans.length) {
88 return columnSpans[col];
89 }
90 return 0;
91 }
92
93 /**
94 * The number of rows in the table.
95 */
96 public int getRowCount() {
97 return rows.size();
98 }
99
100 /**
101 * Fetch the span of multiple rows. This includes
102 * the border area.
103 */
104 public int getMultiRowSpan(int row0, int row1) {
105 RowView rv0 = getRow(row0);
106 RowView rv1 = getRow(row1);
107 if ((rv0 != null) && (rv1 != null)) {
108 int index0 = rv0.viewIndex;
109 int index1 = rv1.viewIndex;
110 int span = getOffset(Y_AXIS, index1) - getOffset(Y_AXIS, index0) +
111 getSpan(Y_AXIS, index1);
112 return span;
113 }
114 return 0;
115 }
116
117 /**
118 * Fetches the span (height) of the given row.
119 */
120 public int getRowSpan(int row) {
121 RowView rv = getRow(row);
122 if (rv != null) {
123 return getSpan(Y_AXIS, rv.viewIndex);
124 }
125 return 0;
126 }
127
128 RowView getRow(int row) {
129 if (row < rows.size()) {
130 return (RowView) rows.elementAt(row);
131 }
132 return null;
133 }
134
135 protected View getViewAtPoint(int x, int y, Rectangle alloc) {
136 int n = getViewCount();
137 View v = null;
138 Rectangle allocation = new Rectangle();
139 for (int i = 0; i < n; i++) {
140 allocation.setBounds(alloc);
141 childAllocation(i, allocation);
142 v = getView(i);
143 if (v instanceof RowView) {
144 v = ((RowView)v).findViewAtPoint(x, y, allocation);
145 if (v != null) {
146 alloc.setBounds(allocation);
147 return v;
148 }
149 }
150 }
151 return super.getViewAtPoint(x, y, alloc);
152 }
153
154 /**
155 * Determines the number of columns occupied by
156 * the table cell represented by given element.
157 */
158 protected int getColumnsOccupied(View v) {
159 AttributeSet a = v.getElement().getAttributes();
160
161 if (a.isDefined(HTML.Attribute.COLSPAN)) {
162 String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
163 if (s != null) {
164 try {
165 return Integer.parseInt(s);
166 } catch (NumberFormatException nfe) {
167 // fall through to one column
168 }
169 }
170 }
171
172 return 1;
173 }
174
175 /**
176 * Determines the number of rows occupied by
177 * the table cell represented by given element.
178 */
179 protected int getRowsOccupied(View v) {
180 AttributeSet a = v.getElement().getAttributes();
181
182 if (a.isDefined(HTML.Attribute.ROWSPAN)) {
183 String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
184 if (s != null) {
185 try {
186 return Integer.parseInt(s);
187 } catch (NumberFormatException nfe) {
188 // fall through to one row
189 }
190 }
191 }
192
193 return 1;
194 }
195
196 protected void invalidateGrid() {
197 gridValid = false;
198 }
199
200 protected StyleSheet getStyleSheet() {
201 HTMLDocument doc = (HTMLDocument) getDocument();
202 return doc.getStyleSheet();
203 }
204
205 /**
206 * Update the insets, which contain the caption if there
207 * is a caption.
208 */
209 void updateInsets() {
210 short top = (short) painter.getInset(TOP, this);
211 short bottom = (short) painter.getInset(BOTTOM, this);
212 if (captionIndex != -1) {
213 View caption = getView(captionIndex);
214 short h = (short) caption.getPreferredSpan(Y_AXIS);
215 AttributeSet a = caption.getAttributes();
216 Object align = a.getAttribute(CSS.Attribute.CAPTION_SIDE);
217 if ((align != null) && (align.equals("bottom"))) {
218 bottom += h;
219 } else {
220 top += h;
221 }
222 }
223 setInsets(top, (short) painter.getInset(LEFT, this),
224 bottom, (short) painter.getInset(RIGHT, this));
225 }
226
227 /**
228 * Update any cached values that come from attributes.
229 */
230 protected void setPropertiesFromAttributes() {
231 StyleSheet sheet = getStyleSheet();
232 attr = sheet.getViewAttributes(this);
233 painter = sheet.getBoxPainter(attr);
234 if (attr != null) {
235 setInsets((short) painter.getInset(TOP, this),
236 (short) painter.getInset(LEFT, this),
237 (short) painter.getInset(BOTTOM, this),
238 (short) painter.getInset(RIGHT, this));
239
240 CSS.LengthValue lv = (CSS.LengthValue)
241 attr.getAttribute(CSS.Attribute.BORDER_SPACING);
242 if (lv != null) {
243 cellSpacing = (int) lv.getValue();
244 } else {
245 cellSpacing = 0;
246 }
247 lv = (CSS.LengthValue)
248 attr.getAttribute(CSS.Attribute.BORDER_TOP_WIDTH);
249 if (lv != null) {
250 borderWidth = (int) lv.getValue();
251 } else {
252 borderWidth = 0;
253 }
254
255 }
256 }
257
258 /**
259 * Fill in the grid locations that are placeholders
260 * for multi-column, multi-row, and missing grid
261 * locations.
262 */
263 void updateGrid() {
264 if (! gridValid) {
265 relativeCells = false;
266 multiRowCells = false;
267
268 // determine which views are table rows and clear out
269 // grid points marked filled.
270 captionIndex = -1;
271 rows.removeAllElements();
272 int n = getViewCount();
273 for (int i = 0; i < n; i++) {
274 View v = getView(i);
275 if (v instanceof RowView) {
276 rows.addElement(v);
277 RowView rv = (RowView) v;
278 rv.clearFilledColumns();
279 rv.rowIndex = rows.size() - 1;
280 rv.viewIndex = i;
281 } else {
282 Object o = v.getElement().getAttributes().getAttribute(StyleConstants.NameAttribute);
283 if (o instanceof HTML.Tag) {
284 HTML.Tag kind = (HTML.Tag) o;
285 if (kind == HTML.Tag.CAPTION) {
286 captionIndex = i;
287 }
288 }
289 }
290 }
291
292 int maxColumns = 0;
293 int nrows = rows.size();
294 for (int row = 0; row < nrows; row++) {
295 RowView rv = getRow(row);
296 int col = 0;
297 for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
298 View cv = rv.getView(cell);
299 if (! relativeCells) {
300 AttributeSet a = cv.getAttributes();
301 CSS.LengthValue lv = (CSS.LengthValue)
302 a.getAttribute(CSS.Attribute.WIDTH);
303 if ((lv != null) && (lv.isPercentage())) {
304 relativeCells = true;
305 }
306 }
307 // advance to a free column
308 for (; rv.isFilled(col); col++);
309 int rowSpan = getRowsOccupied(cv);
310 if (rowSpan > 1) {
311 multiRowCells = true;
312 }
313 int colSpan = getColumnsOccupied(cv);
314 if ((colSpan > 1) || (rowSpan > 1)) {
315 // fill in the overflow entries for this cell
316 int rowLimit = row + rowSpan;
317 int colLimit = col + colSpan;
318 for (int i = row; i < rowLimit; i++) {
319 for (int j = col; j < colLimit; j++) {
320 if (i != row || j != col) {
321 addFill(i, j);
322 }
323 }
324 }
325 if (colSpan > 1) {
326 col += colSpan - 1;
327 }
328 }
329 }
330 maxColumns = Math.max(maxColumns, col);
331 }
332
333 // setup the column layout/requirements
334 columnSpans = new int[maxColumns];
335 columnOffsets = new int[maxColumns];
336 columnRequirements = new SizeRequirements[maxColumns];
337 for (int i = 0; i < maxColumns; i++) {
338 columnRequirements[i] = new SizeRequirements();
339 columnRequirements[i].maximum = Integer.MAX_VALUE;
340 }
341 gridValid = true;
342 }
343 }
344
345 /**
346 * Mark a grid location as filled in for a cells overflow.
347 */
348 void addFill(int row, int col) {
349 RowView rv = getRow(row);
350 if (rv != null) {
351 rv.fillColumn(col);
352 }
353 }
354
355 /**
356 * Layout the columns to fit within the given target span.
357 *
358 * @param targetSpan the given span for total of all the table
359 * columns
360 * @param reqs the requirements desired for each column. This
361 * is the column maximum of the cells minimum, preferred, and
362 * maximum requested span
363 * @param spans the return value of how much to allocated to
364 * each column
365 * @param offsets the return value of the offset from the
366 * origin for each column
367 * @return the offset from the origin and the span for each column
368 * in the offsets and spans parameters
369 */
370 protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,
371 SizeRequirements[] reqs) {
372 //clean offsets and spans
373 Arrays.fill(offsets, 0);
374 Arrays.fill(spans, 0);
375 colIterator.setLayoutArrays(offsets, spans, targetSpan);
376 CSS.calculateTiledLayout(colIterator, targetSpan);
377 }
378
379 /**
380 * Calculate the requirements for each column. The calculation
381 * is done as two passes over the table. The table cells that
382 * occupy a single column are scanned first to determine the
383 * maximum of minimum, preferred, and maximum spans along the
384 * give axis. Table cells that span multiple columns are excluded
385 * from the first pass. A second pass is made to determine if
386 * the cells that span multiple columns are satisfied. If the
387 * column requirements are not satisified, the needs of the
388 * multi-column cell is mixed into the existing column requirements.
389 * The calculation of the multi-column distribution is based upon
390 * the proportions of the existing column requirements and taking
391 * into consideration any constraining maximums.
392 */
393 void calculateColumnRequirements(int axis) {
394 // clean columnRequirements
395 for (SizeRequirements req : columnRequirements) {
396 req.minimum = 0;
397 req.preferred = 0;
398 req.maximum = Integer.MAX_VALUE;
399 }
400 Container host = getContainer();
401 if (host != null) {
402 if (host instanceof JTextComponent) {
403 skipComments = !((JTextComponent)host).isEditable();
404 } else {
405 skipComments = true;
406 }
407 }
408 // pass 1 - single column cells
409 boolean hasMultiColumn = false;
410 int nrows = getRowCount();
411 for (int i = 0; i < nrows; i++) {
412 RowView row = getRow(i);
413 int col = 0;
414 int ncells = row.getViewCount();
415 for (int cell = 0; cell < ncells; cell++) {
416 View cv = row.getView(cell);
417 if (skipComments && !(cv instanceof CellView)) {
418 continue;
419 }
420 for (; row.isFilled(col); col++); // advance to a free column
421 int rowSpan = getRowsOccupied(cv);
422 int colSpan = getColumnsOccupied(cv);
423 if (colSpan == 1) {
424 checkSingleColumnCell(axis, col, cv);
425 } else {
426 hasMultiColumn = true;
427 col += colSpan - 1;
428 }
429 col++;
430 }
431 }
432
433 // pass 2 - multi-column cells
434 if (hasMultiColumn) {
435 for (int i = 0; i < nrows; i++) {
436 RowView row = getRow(i);
437 int col = 0;
438 int ncells = row.getViewCount();
439 for (int cell = 0; cell < ncells; cell++) {
440 View cv = row.getView(cell);
441 if (skipComments && !(cv instanceof CellView)) {
442 continue;
443 }
444 for (; row.isFilled(col); col++); // advance to a free column
445 int colSpan = getColumnsOccupied(cv);
446 if (colSpan > 1) {
447 checkMultiColumnCell(axis, col, colSpan, cv);
448 col += colSpan - 1;
449 }
450 col++;
451 }
452 }
453 }
454 }
455
456 /**
457 * check the requirements of a table cell that spans a single column.
458 */
459 void checkSingleColumnCell(int axis, int col, View v) {
460 SizeRequirements req = columnRequirements[col];
461 req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
462 req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
463 }
464
465 /**
466 * check the requirements of a table cell that spans multiple
467 * columns.
468 */
469 void checkMultiColumnCell(int axis, int col, int ncols, View v) {
470 // calculate the totals
471 long min = 0;
472 long pref = 0;
473 long max = 0;
474 for (int i = 0; i < ncols; i++) {
475 SizeRequirements req = columnRequirements[col + i];
476 min += req.minimum;
477 pref += req.preferred;
478 max += req.maximum;
479 }
480
481 // check if the minimum size needs adjustment.
482 int cmin = (int) v.getMinimumSpan(axis);
483 if (cmin > min) {
484 /*
485 * the columns that this cell spans need adjustment to fit
486 * this table cell.... calculate the adjustments.
487 */
488 SizeRequirements[] reqs = new SizeRequirements[ncols];
489 for (int i = 0; i < ncols; i++) {
490 reqs[i] = columnRequirements[col + i];
491 }
492 int[] spans = new int[ncols];
493 int[] offsets = new int[ncols];
494 SizeRequirements.calculateTiledPositions(cmin, null, reqs,
495 offsets, spans);
496 // apply the adjustments
497 for (int i = 0; i < ncols; i++) {
498 SizeRequirements req = reqs[i];
499 req.minimum = Math.max(spans[i], req.minimum);
500 req.preferred = Math.max(req.minimum, req.preferred);
501 req.maximum = Math.max(req.preferred, req.maximum);
502 }
503 }
504
505 // check if the preferred size needs adjustment.
506 int cpref = (int) v.getPreferredSpan(axis);
507 if (cpref > pref) {
508 /*
509 * the columns that this cell spans need adjustment to fit
510 * this table cell.... calculate the adjustments.
511 */
512 SizeRequirements[] reqs = new SizeRequirements[ncols];
513 for (int i = 0; i < ncols; i++) {
514 reqs[i] = columnRequirements[col + i];
515 }
516 int[] spans = new int[ncols];
517 int[] offsets = new int[ncols];
518 SizeRequirements.calculateTiledPositions(cpref, null, reqs,
519 offsets, spans);
520 // apply the adjustments
521 for (int i = 0; i < ncols; i++) {
522 SizeRequirements req = reqs[i];
523 req.preferred = Math.max(spans[i], req.preferred);
524 req.maximum = Math.max(req.preferred, req.maximum);
525 }
526 }
527
528 }
529
530 // --- BoxView methods -----------------------------------------
531
532 /**
533 * Calculate the requirements for the minor axis. This is called by
534 * the superclass whenever the requirements need to be updated (i.e.
535 * a preferenceChanged was messaged through this view).
536 * <p>
537 * This is implemented to calculate the requirements as the sum of the
538 * requirements of the columns and then adjust it if the
539 * CSS width or height attribute is specified and applicable to
540 * the axis.
541 */
542 protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
543 updateGrid();
544
545 // calculate column requirements for each column
546 calculateColumnRequirements(axis);
547
548
549 // the requirements are the sum of the columns.
550 if (r == null) {
551 r = new SizeRequirements();
552 }
553 long min = 0;
554 long pref = 0;
555 int n = columnRequirements.length;
556 for (int i = 0; i < n; i++) {
557 SizeRequirements req = columnRequirements[i];
558 min += req.minimum;
559 pref += req.preferred;
560 }
561 int adjust = (n + 1) * cellSpacing + 2 * borderWidth;
562 min += adjust;
563 pref += adjust;
564 r.minimum = (int) min;
565 r.preferred = (int) pref;
566 r.maximum = (int) pref;
567
568
569 AttributeSet attr = getAttributes();
570 CSS.LengthValue cssWidth = (CSS.LengthValue)attr.getAttribute(
571 CSS.Attribute.WIDTH);
572
573 if (BlockView.spanSetFromAttributes(axis, r, cssWidth, null)) {
574 if (r.minimum < (int)min) {
575 // The user has requested a smaller size than is needed to
576 // show the table, override it.
577 r.maximum = r.minimum = r.preferred = (int) min;
578 }
579 }
580 totalColumnRequirements.minimum = r.minimum;
581 totalColumnRequirements.preferred = r.preferred;
582 totalColumnRequirements.maximum = r.maximum;
583
584 // set the alignment
585 Object o = attr.getAttribute(CSS.Attribute.TEXT_ALIGN);
586 if (o != null) {
587 // set horizontal alignment
588 String ta = o.toString();
589 if (ta.equals("left")) {
590 r.alignment = 0;
591 } else if (ta.equals("center")) {
592 r.alignment = 0.5f;
593 } else if (ta.equals("right")) {
594 r.alignment = 1;
595 } else {
596 r.alignment = 0;
597 }
598 } else {
599 r.alignment = 0;
600 }
601
602 return r;
603 }
604
605 /**
606 * Calculate the requirements for the major axis. This is called by
607 * the superclass whenever the requirements need to be updated (i.e.
608 * a preferenceChanged was messaged through this view).
609 * <p>
610 * This is implemented to provide the superclass behavior adjusted for
611 * multi-row table cells.
612 */
613 protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
614 updateInsets();
615 rowIterator.updateAdjustments();
616 r = CSS.calculateTiledRequirements(rowIterator, r);
617 r.maximum = r.preferred;
618 return r;
619 }
620
621 /**
622 * Perform layout for the minor axis of the box (i.e. the
623 * axis orthoginal to the axis that it represents). The results
624 * of the layout should be placed in the given arrays which represent
625 * the allocations to the children along the minor axis. This
626 * is called by the superclass whenever the layout needs to be
627 * updated along the minor axis.
628 * <p>
629 * This is implemented to call the
630 * <a href="#layoutColumns">layoutColumns</a> method, and then
631 * forward to the superclass to actually carry out the layout
632 * of the tables rows.
633 *
634 * @param targetSpan the total span given to the view, which
635 * whould be used to layout the children
636 * @param axis the axis being layed out
637 * @param offsets the offsets from the origin of the view for
638 * each of the child views. This is a return value and is
639 * filled in by the implementation of this method
640 * @param spans the span of each child view; this is a return
641 * value and is filled in by the implementation of this method
642 * @return the offset and span for each child view in the
643 * offsets and spans parameters
644 */
645 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
646 // make grid is properly represented
647 updateGrid();
648
649 // all of the row layouts are invalid, so mark them that way
650 int n = getRowCount();
651 for (int i = 0; i < n; i++) {
652 RowView row = getRow(i);
653 row.layoutChanged(axis);
654 }
655
656 // calculate column spans
657 layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
658
659 // continue normal layout
660 super.layoutMinorAxis(targetSpan, axis, offsets, spans);
661 }
662
663
664 /**
665 * Perform layout for the major axis of the box (i.e. the
666 * axis that it represents). The results
667 * of the layout should be placed in the given arrays which represent
668 * the allocations to the children along the minor axis. This
669 * is called by the superclass whenever the layout needs to be
670 * updated along the minor axis.
671 * <p>
672 * This method is where the layout of the table rows within the
673 * table takes place. This method is implemented to call the use
674 * the RowIterator and the CSS collapsing tile to layout
675 * with border spacing and border collapsing capabilities.
676 *
677 * @param targetSpan the total span given to the view, which
678 * whould be used to layout the children
679 * @param axis the axis being layed out
680 * @param offsets the offsets from the origin of the view for
681 * each of the child views; this is a return value and is
682 * filled in by the implementation of this method
683 * @param spans the span of each child view; this is a return
684 * value and is filled in by the implementation of this method
685 * @return the offset and span for each child view in the
686 * offsets and spans parameters
687 */
688 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
689 rowIterator.setLayoutArrays(offsets, spans);
690 CSS.calculateTiledLayout(rowIterator, targetSpan);
691
692 if (captionIndex != -1) {
693 // place the caption
694 View caption = getView(captionIndex);
695 int h = (int) caption.getPreferredSpan(Y_AXIS);
696 spans[captionIndex] = h;
697 short boxBottom = (short) painter.getInset(BOTTOM, this);
698 if (boxBottom != getBottomInset()) {
699 offsets[captionIndex] = targetSpan + boxBottom;
700 } else {
701 offsets[captionIndex] = - getTopInset();
702 }
703 }
704 }
705
706 /**
707 * Fetches the child view that represents the given position in
708 * the model. This is implemented to walk through the children
709 * looking for a range that contains the given position. In this
710 * view the children do not necessarily have a one to one mapping
711 * with the child elements.
712 *
713 * @param pos the search position >= 0
714 * @param a the allocation to the table on entry, and the
715 * allocation of the view containing the position on exit
716 * @return the view representing the given position, or
717 * null if there isn't one
718 */
719 protected View getViewAtPosition(int pos, Rectangle a) {
720 int n = getViewCount();
721 for (int i = 0; i < n; i++) {
722 View v = getView(i);
723 int p0 = v.getStartOffset();
724 int p1 = v.getEndOffset();
725 if ((pos >= p0) && (pos < p1)) {
726 // it's in this view.
727 if (a != null) {
728 childAllocation(i, a);
729 }
730 return v;
731 }
732 }
733 if (pos == getEndOffset()) {
734 View v = getView(n - 1);
735 if (a != null) {
736 this.childAllocation(n - 1, a);
737 }
738 return v;
739 }
740 return null;
741 }
742
743 // --- View methods ---------------------------------------------
744
745 /**
746 * Fetches the attributes to use when rendering. This is
747 * implemented to multiplex the attributes specified in the
748 * model with a StyleSheet.
749 */
750 public AttributeSet getAttributes() {
751 if (attr == null) {
752 StyleSheet sheet = getStyleSheet();
753 attr = sheet.getViewAttributes(this);
754 }
755 return attr;
756 }
757
758 /**
759 * Renders using the given rendering surface and area on that
760 * surface. This is implemented to delegate to the css box
761 * painter to paint the border and background prior to the
762 * interior. The superclass culls rendering the children
763 * that don't directly intersect the clip and the row may
764 * have cells hanging from a row above in it. The table
765 * does not use the superclass rendering behavior and instead
766 * paints all of the rows and lets the rows cull those
767 * cells not intersecting the clip region.
768 *
769 * @param g the rendering surface to use
770 * @param allocation the allocated region to render into
771 * @see View#paint
772 */
773 public void paint(Graphics g, Shape allocation) {
774 // paint the border
775 Rectangle a = allocation.getBounds();
776 setSize(a.width, a.height);
777 if (captionIndex != -1) {
778 // adjust the border for the caption
779 short top = (short) painter.getInset(TOP, this);
780 short bottom = (short) painter.getInset(BOTTOM, this);
781 if (top != getTopInset()) {
782 int h = getTopInset() - top;
783 a.y += h;
784 a.height -= h;
785 } else {
786 a.height -= getBottomInset() - bottom;
787 }
788 }
789 painter.paint(g, a.x, a.y, a.width, a.height, this);
790 // paint interior
791 int n = getViewCount();
792 for (int i = 0; i < n; i++) {
793 View v = getView(i);
794 v.paint(g, getChildAllocation(i, allocation));
795 }
796 //super.paint(g, a);
797 }
798
799 /**
800 * Establishes the parent view for this view. This is
801 * guaranteed to be called before any other methods if the
802 * parent view is functioning properly.
803 * <p>
804 * This is implemented
805 * to forward to the superclass as well as call the
806 * <a href="#setPropertiesFromAttributes">setPropertiesFromAttributes</a>
807 * method to set the paragraph properties from the css
808 * attributes. The call is made at this time to ensure
809 * the ability to resolve upward through the parents
810 * view attributes.
811 *
812 * @param parent the new parent, or null if the view is
813 * being removed from a parent it was previously added
814 * to
815 */
816 public void setParent(View parent) {
817 super.setParent(parent);
818 if (parent != null) {
819 setPropertiesFromAttributes();
820 }
821 }
822
823 /**
824 * Fetches the ViewFactory implementation that is feeding
825 * the view hierarchy.
826 * This replaces the ViewFactory with an implementation that
827 * calls through to the createTableRow and createTableCell
828 * methods. If the element given to the factory isn't a
829 * table row or cell, the request is delegated to the factory
830 * produced by the superclass behavior.
831 *
832 * @return the factory, null if none
833 */
834 public ViewFactory getViewFactory() {
835 return this;
836 }
837
838 /**
839 * Gives notification that something was inserted into
840 * the document in a location that this view is responsible for.
841 * This replaces the ViewFactory with an implementation that
842 * calls through to the createTableRow and createTableCell
843 * methods. If the element given to the factory isn't a
844 * table row or cell, the request is delegated to the factory
845 * passed as an argument.
846 *
847 * @param e the change information from the associated document
848 * @param a the current allocation of the view
849 * @param f the factory to use to rebuild if the view has children
850 * @see View#insertUpdate
851 */
852 public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
853 super.insertUpdate(e, a, this);
854 }
855
856 /**
857 * Gives notification that something was removed from the document
858 * in a location that this view is responsible for.
859 * This replaces the ViewFactory with an implementation that
860 * calls through to the createTableRow and createTableCell
861 * methods. If the element given to the factory isn't a
862 * table row or cell, the request is delegated to the factory
863 * passed as an argument.
864 *
865 * @param e the change information from the associated document
866 * @param a the current allocation of the view
867 * @param f the factory to use to rebuild if the view has children
868 * @see View#removeUpdate
869 */
870 public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
871 super.removeUpdate(e, a, this);
872 }
873
874 /**
875 * Gives notification from the document that attributes were changed
876 * in a location that this view is responsible for.
877 * This replaces the ViewFactory with an implementation that
878 * calls through to the createTableRow and createTableCell
879 * methods. If the element given to the factory isn't a
880 * table row or cell, the request is delegated to the factory
881 * passed as an argument.
882 *
883 * @param e the change information from the associated document
884 * @param a the current allocation of the view
885 * @param f the factory to use to rebuild if the view has children
886 * @see View#changedUpdate
887 */
888 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
889 super.changedUpdate(e, a, this);
890 }
891
892 protected void forwardUpdate(DocumentEvent.ElementChange ec,
893 DocumentEvent e, Shape a, ViewFactory f) {
894 super.forwardUpdate(ec, e, a, f);
895 // A change in any of the table cells usually effects the whole table,
896 // so redraw it all!
897 if (a != null) {
898 Component c = getContainer();
899 if (c != null) {
900 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
901 a.getBounds();
902 c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
903 }
904 }
905 }
906
907 /**
908 * Change the child views. This is implemented to
909 * provide the superclass behavior and invalidate the
910 * grid so that rows and columns will be recalculated.
911 */
912 public void replace(int offset, int length, View[] views) {
913 super.replace(offset, length, views);
914 invalidateGrid();
915 }
916
917 // --- ViewFactory methods ------------------------------------------
918
919 /**
920 * The table itself acts as a factory for the various
921 * views that actually represent pieces of the table.
922 * All other factory activity is delegated to the factory
923 * returned by the parent of the table.
924 */
925 public View create(Element elem) {
926 Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
927 if (o instanceof HTML.Tag) {
928 HTML.Tag kind = (HTML.Tag) o;
929 if (kind == HTML.Tag.TR) {
930 return createTableRow(elem);
931 } else if ((kind == HTML.Tag.TD) || (kind == HTML.Tag.TH)) {
932 return new CellView(elem);
933 } else if (kind == HTML.Tag.CAPTION) {
934 return new javax.swing.text.html.ParagraphView(elem);
935 }
936 }
937 // default is to delegate to the normal factory
938 View p = getParent();
939 if (p != null) {
940 ViewFactory f = p.getViewFactory();
941 if (f != null) {
942 return f.create(elem);
943 }
944 }
945 return null;
946 }
947
948 // ---- variables ----------------------------------------------------
949
950 private AttributeSet attr;
951 private StyleSheet.BoxPainter painter;
952
953 private int cellSpacing;
954 private int borderWidth;
955
956 /**
957 * The index of the caption view if there is a caption.
958 * This has a value of -1 if there is no caption. The
959 * caption lives in the inset area of the table, and is
960 * updated with each time the grid is recalculated.
961 */
962 private int captionIndex;
963
964 /**
965 * Do any of the table cells contain a relative size
966 * specification? This is updated with each call to
967 * updateGrid(). If this is true, the ColumnIterator
968 * will do extra work to calculate relative cell
969 * specifications.
970 */
971 private boolean relativeCells;
972
973 /**
974 * Do any of the table cells span multiple rows? If
975 * true, the RowRequirementIterator will do additional
976 * work to adjust the requirements of rows spanned by
977 * a single table cell. This is updated with each call to
978 * updateGrid().
979 */
980 private boolean multiRowCells;
981
982 int[] columnSpans;
983 int[] columnOffsets;
984 /**
985 * SizeRequirements for all the columns.
986 */
987 SizeRequirements totalColumnRequirements;
988 SizeRequirements[] columnRequirements;
989
990 RowIterator rowIterator = new RowIterator();
991 ColumnIterator colIterator = new ColumnIterator();
992
993 Vector rows;
994
995 // whether to display comments inside table or not.
996 boolean skipComments = false;
997
998 boolean gridValid;
999 static final private BitSet EMPTY = new BitSet();
1000
1001 class ColumnIterator implements CSS.LayoutIterator {
1002
1003 /**
1004 * Disable percentage adjustments which should only apply
1005 * when calculating layout, not requirements.
1006 */
1007 void disablePercentages() {
1008 percentages = null;
1009 }
1010
1011 /**
1012 * Update percentage adjustments if they are needed.
1013 */
1014 private void updatePercentagesAndAdjustmentWeights(int span) {
1015 adjustmentWeights = new int[columnRequirements.length];
1016 for (int i = 0; i < columnRequirements.length; i++) {
1017 adjustmentWeights[i] = 0;
1018 }
1019 if (relativeCells) {
1020 percentages = new int[columnRequirements.length];
1021 } else {
1022 percentages = null;
1023 }
1024 int nrows = getRowCount();
1025 for (int rowIndex = 0; rowIndex < nrows; rowIndex++) {
1026 RowView row = getRow(rowIndex);
1027 int col = 0;
1028 int ncells = row.getViewCount();
1029 for (int cell = 0; cell < ncells; cell++, col++) {
1030 View cv = row.getView(cell);
1031 for (; row.isFilled(col); col++); // advance to a free column
1032 int rowSpan = getRowsOccupied(cv);
1033 int colSpan = getColumnsOccupied(cv);
1034 AttributeSet a = cv.getAttributes();
1035 CSS.LengthValue lv = (CSS.LengthValue)
1036 a.getAttribute(CSS.Attribute.WIDTH);
1037 if ( lv != null ) {
1038 int len = (int) (lv.getValue(span) / colSpan + 0.5f);
1039 for (int i = 0; i < colSpan; i++) {
1040 if (lv.isPercentage()) {
1041 // add a percentage requirement
1042 percentages[col+i] = Math.max(percentages[col+i], len);
1043 adjustmentWeights[col + i] = Math.max(adjustmentWeights[col + i], WorstAdjustmentWeight);
1044 } else {
1045 adjustmentWeights[col + i] = Math.max(adjustmentWeights[col + i], WorstAdjustmentWeight - 1);
1046 }
1047 }
1048 }
1049 col += colSpan - 1;
1050 }
1051 }
1052 }
1053
1054 /**
1055 * Set the layout arrays to use for holding layout results
1056 */
1057 public void setLayoutArrays(int offsets[], int spans[], int targetSpan) {
1058 this.offsets = offsets;
1059 this.spans = spans;
1060 updatePercentagesAndAdjustmentWeights(targetSpan);
1061 }
1062
1063 // --- RequirementIterator methods -------------------
1064
1065 public int getCount() {
1066 return columnRequirements.length;
1067 }
1068
1069 public void setIndex(int i) {
1070 col = i;
1071 }
1072
1073 public void setOffset(int offs) {
1074 offsets[col] = offs;
1075 }
1076
1077 public int getOffset() {
1078 return offsets[col];
1079 }
1080
1081 public void setSpan(int span) {
1082 spans[col] = span;
1083 }
1084
1085 public int getSpan() {
1086 return spans[col];
1087 }
1088
1089 public float getMinimumSpan(float parentSpan) {
1090 // do not care for percentages, since min span can't
1091 // be less than columnRequirements[col].minimum,
1092 // but can be less than percentage value.
1093 return columnRequirements[col].minimum;
1094 }
1095
1096 public float getPreferredSpan(float parentSpan) {
1097 if ((percentages != null) && (percentages[col] != 0)) {
1098 return Math.max(percentages[col], columnRequirements[col].minimum);
1099 }
1100 return columnRequirements[col].preferred;
1101 }
1102
1103 public float getMaximumSpan(float parentSpan) {
1104 return columnRequirements[col].maximum;
1105 }
1106
1107 public float getBorderWidth() {
1108 return borderWidth;
1109 }
1110
1111
1112 public float getLeadingCollapseSpan() {
1113 return cellSpacing;
1114 }
1115
1116 public float getTrailingCollapseSpan() {
1117 return cellSpacing;
1118 }
1119
1120 public int getAdjustmentWeight() {
1121 return adjustmentWeights[col];
1122 }
1123
1124 /**
1125 * Current column index
1126 */
1127 private int col;
1128
1129 /**
1130 * percentage values (may be null since there
1131 * might not be any).
1132 */
1133 private int[] percentages;
1134
1135 private int[] adjustmentWeights;
1136
1137 private int[] offsets;
1138 private int[] spans;
1139 }
1140
1141 class RowIterator implements CSS.LayoutIterator {
1142
1143 RowIterator() {
1144 }
1145
1146 void updateAdjustments() {
1147 int axis = Y_AXIS;
1148 if (multiRowCells) {
1149 // adjust requirements of multi-row cells
1150 int n = getRowCount();
1151 adjustments = new int[n];
1152 for (int i = 0; i < n; i++) {
1153 RowView rv = getRow(i);
1154 if (rv.multiRowCells == true) {
1155 int ncells = rv.getViewCount();
1156 for (int j = 0; j < ncells; j++) {
1157 View v = rv.getView(j);
1158 int nrows = getRowsOccupied(v);
1159 if (nrows > 1) {
1160 int spanNeeded = (int) v.getPreferredSpan(axis);
1161 adjustMultiRowSpan(spanNeeded, nrows, i);
1162 }
1163 }
1164 }
1165 }
1166 } else {
1167 adjustments = null;
1168 }
1169 }
1170
1171 /**
1172 * Fixup preferences to accomodate a multi-row table cell
1173 * if not already covered by existing preferences. This is
1174 * a no-op if not all of the rows needed (to do this check/fixup)
1175 * have arrived yet.
1176 */
1177 void adjustMultiRowSpan(int spanNeeded, int nrows, int rowIndex) {
1178 if ((rowIndex + nrows) > getCount()) {
1179 // rows are missing (could be a bad rowspan specification)
1180 // or not all the rows have arrived. Do the best we can with
1181 // the current set of rows.
1182 nrows = getCount() - rowIndex;
1183 if (nrows < 1) {
1184 return;
1185 }
1186 }
1187 int span = 0;
1188 for (int i = 0; i < nrows; i++) {
1189 RowView rv = getRow(rowIndex + i);
1190 span += rv.getPreferredSpan(Y_AXIS);
1191 }
1192 if (spanNeeded > span) {
1193 int adjust = (spanNeeded - span);
1194 int rowAdjust = adjust / nrows;
1195 int firstAdjust = rowAdjust + (adjust - (rowAdjust * nrows));
1196 RowView rv = getRow(rowIndex);
1197 adjustments[rowIndex] = Math.max(adjustments[rowIndex],
1198 firstAdjust);
1199 for (int i = 1; i < nrows; i++) {
1200 adjustments[rowIndex + i] = Math.max(
1201 adjustments[rowIndex + i], rowAdjust);
1202 }
1203 }
1204 }
1205
1206 void setLayoutArrays(int[] offsets, int[] spans) {
1207 this.offsets = offsets;
1208 this.spans = spans;
1209 }
1210
1211 // --- RequirementIterator methods -------------------
1212
1213 public void setOffset(int offs) {
1214 RowView rv = getRow(row);
1215 if (rv != null) {
1216 offsets[rv.viewIndex] = offs;
1217 }
1218 }
1219
1220 public int getOffset() {
1221 RowView rv = getRow(row);
1222 if (rv != null) {
1223 return offsets[rv.viewIndex];
1224 }
1225 return 0;
1226 }
1227
1228 public void setSpan(int span) {
1229 RowView rv = getRow(row);
1230 if (rv != null) {
1231 spans[rv.viewIndex] = span;
1232 }
1233 }
1234
1235 public int getSpan() {
1236 RowView rv = getRow(row);
1237 if (rv != null) {
1238 return spans[rv.viewIndex];
1239 }
1240 return 0;
1241 }
1242
1243 public int getCount() {
1244 return rows.size();
1245 }
1246
1247 public void setIndex(int i) {
1248 row = i;
1249 }
1250
1251 public float getMinimumSpan(float parentSpan) {
1252 return getPreferredSpan(parentSpan);
1253 }
1254
1255 public float getPreferredSpan(float parentSpan) {
1256 RowView rv = getRow(row);
1257 if (rv != null) {
1258 int adjust = (adjustments != null) ? adjustments[row] : 0;
1259 return rv.getPreferredSpan(TableView.this.getAxis()) + adjust;
1260 }
1261 return 0;
1262 }
1263
1264 public float getMaximumSpan(float parentSpan) {
1265 return getPreferredSpan(parentSpan);
1266 }
1267
1268 public float getBorderWidth() {
1269 return borderWidth;
1270 }
1271
1272 public float getLeadingCollapseSpan() {
1273 return cellSpacing;
1274 }
1275
1276 public float getTrailingCollapseSpan() {
1277 return cellSpacing;
1278 }
1279
1280 public int getAdjustmentWeight() {
1281 return 0;
1282 }
1283
1284 /**
1285 * Current row index
1286 */
1287 private int row;
1288
1289 /**
1290 * Adjustments to the row requirements to handle multi-row
1291 * table cells.
1292 */
1293 private int[] adjustments;
1294
1295 private int[] offsets;
1296 private int[] spans;
1297 }
1298
1299 /**
1300 * View of a row in a row-centric table.
1301 */
1302 public class RowView extends BoxView {
1303
1304 /**
1305 * Constructs a TableView for the given element.
1306 *
1307 * @param elem the element that this view is responsible for
1308 */
1309 public RowView(Element elem) {
1310 super(elem, View.X_AXIS);
1311 fillColumns = new BitSet();
1312 RowView.this.setPropertiesFromAttributes();
1313 }
1314
1315 void clearFilledColumns() {
1316 fillColumns.and(EMPTY);
1317 }
1318
1319 void fillColumn(int col) {
1320 fillColumns.set(col);
1321 }
1322
1323 boolean isFilled(int col) {
1324 return fillColumns.get(col);
1325 }
1326
1327 /**
1328 * The number of columns present in this row.
1329 */
1330 int getColumnCount() {
1331 int nfill = 0;
1332 int n = fillColumns.size();
1333 for (int i = 0; i < n; i++) {
1334 if (fillColumns.get(i)) {
1335 nfill ++;
1336 }
1337 }
1338 return getViewCount() + nfill;
1339 }
1340
1341 /**
1342 * Fetches the attributes to use when rendering. This is
1343 * implemented to multiplex the attributes specified in the
1344 * model with a StyleSheet.
1345 */
1346 public AttributeSet getAttributes() {
1347 return attr;
1348 }
1349
1350 View findViewAtPoint(int x, int y, Rectangle alloc) {
1351 int n = getViewCount();
1352 for (int i = 0; i < n; i++) {
1353 if (getChildAllocation(i, alloc).contains(x, y)) {
1354 childAllocation(i, alloc);
1355 return getView(i);
1356 }
1357 }
1358 return null;
1359 }
1360
1361 protected StyleSheet getStyleSheet() {
1362 HTMLDocument doc = (HTMLDocument) getDocument();
1363 return doc.getStyleSheet();
1364 }
1365
1366 /**
1367 * This is called by a child to indicate its
1368 * preferred span has changed. This is implemented to
1369 * execute the superclass behavior and well as try to
1370 * determine if a row with a multi-row cell hangs across
1371 * this row. If a multi-row cell covers this row it also
1372 * needs to propagate a preferenceChanged so that it will
1373 * recalculate the multi-row cell.
1374 *
1375 * @param child the child view
1376 * @param width true if the width preference should change
1377 * @param height true if the height preference should change
1378 */
1379 public void preferenceChanged(View child, boolean width, boolean height) {
1380 super.preferenceChanged(child, width, height);
1381 if (TableView.this.multiRowCells && height) {
1382 for (int i = rowIndex - 1; i >= 0; i--) {
1383 RowView rv = TableView.this.getRow(i);
1384 if (rv.multiRowCells) {
1385 rv.preferenceChanged(null, false, true);
1386 break;
1387 }
1388 }
1389 }
1390 }
1391
1392 // The major axis requirements for a row are dictated by the column
1393 // requirements. These methods use the value calculated by
1394 // TableView.
1395 protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
1396 SizeRequirements req = new SizeRequirements();
1397 req.minimum = totalColumnRequirements.minimum;
1398 req.maximum = totalColumnRequirements.maximum;
1399 req.preferred = totalColumnRequirements.preferred;
1400 req.alignment = 0f;
1401 return req;
1402 }
1403
1404 public float getMinimumSpan(int axis) {
1405 float value;
1406
1407 if (axis == View.X_AXIS) {
1408 value = totalColumnRequirements.minimum + getLeftInset() +
1409 getRightInset();
1410 }
1411 else {
1412 value = super.getMinimumSpan(axis);
1413 }
1414 return value;
1415 }
1416
1417 public float getMaximumSpan(int axis) {
1418 float value;
1419
1420 if (axis == View.X_AXIS) {
1421 // We're flexible.
1422 value = (float)Integer.MAX_VALUE;
1423 }
1424 else {
1425 value = super.getMaximumSpan(axis);
1426 }
1427 return value;
1428 }
1429
1430 public float getPreferredSpan(int axis) {
1431 float value;
1432
1433 if (axis == View.X_AXIS) {
1434 value = totalColumnRequirements.preferred + getLeftInset() +
1435 getRightInset();
1436 }
1437 else {
1438 value = super.getPreferredSpan(axis);
1439 }
1440 return value;
1441 }
1442
1443 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1444 super.changedUpdate(e, a, f);
1445 int pos = e.getOffset();
1446 if (pos <= getStartOffset() && (pos + e.getLength()) >=
1447 getEndOffset()) {
1448 RowView.this.setPropertiesFromAttributes();
1449 }
1450 }
1451
1452 /**
1453 * Renders using the given rendering surface and area on that
1454 * surface. This is implemented to delegate to the css box
1455 * painter to paint the border and background prior to the
1456 * interior.
1457 *
1458 * @param g the rendering surface to use
1459 * @param allocation the allocated region to render into
1460 * @see View#paint
1461 */
1462 public void paint(Graphics g, Shape allocation) {
1463 Rectangle a = (Rectangle) allocation;
1464 painter.paint(g, a.x, a.y, a.width, a.height, this);
1465 super.paint(g, a);
1466 }
1467
1468 /**
1469 * Change the child views. This is implemented to
1470 * provide the superclass behavior and invalidate the
1471 * grid so that rows and columns will be recalculated.
1472 */
1473 public void replace(int offset, int length, View[] views) {
1474 super.replace(offset, length, views);
1475 invalidateGrid();
1476 }
1477
1478 /**
1479 * Calculate the height requirements of the table row. The
1480 * requirements of multi-row cells are not considered for this
1481 * calculation. The table itself will check and adjust the row
1482 * requirements for all the rows that have multi-row cells spanning
1483 * them. This method updates the multi-row flag that indicates that
1484 * this row and rows below need additional consideration.
1485 */
1486 protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
1487// return super.calculateMinorAxisRequirements(axis, r);
1488 long min = 0;
1489 long pref = 0;
1490 long max = 0;
1491 multiRowCells = false;
1492 int n = getViewCount();
1493 for (int i = 0; i < n; i++) {
1494 View v = getView(i);
1495 if (getRowsOccupied(v) > 1) {
1496 multiRowCells = true;
1497 max = Math.max((int) v.getMaximumSpan(axis), max);
1498 } else {
1499 min = Math.max((int) v.getMinimumSpan(axis), min);
1500 pref = Math.max((int) v.getPreferredSpan(axis), pref);
1501 max = Math.max((int) v.getMaximumSpan(axis), max);
1502 }
1503 }
1504
1505 if (r == null) {
1506 r = new SizeRequirements();
1507 r.alignment = 0.5f;
1508 }
1509 r.preferred = (int) pref;
1510 r.minimum = (int) min;
1511 r.maximum = (int) max;
1512 return r;
1513 }
1514
1515 /**
1516 * Perform layout for the major axis of the box (i.e. the
1517 * axis that it represents). The results of the layout should
1518 * be placed in the given arrays which represent the allocations
1519 * to the children along the major axis.
1520 * <p>
1521 * This is re-implemented to give each child the span of the column
1522 * width for the table, and to give cells that span multiple columns
1523 * the multi-column span.
1524 *
1525 * @param targetSpan the total span given to the view, which
1526 * whould be used to layout the children
1527 * @param axis the axis being layed out
1528 * @param offsets the offsets from the origin of the view for
1529 * each of the child views; this is a return value and is
1530 * filled in by the implementation of this method
1531 * @param spans the span of each child view; this is a return
1532 * value and is filled in by the implementation of this method
1533 * @return the offset and span for each child view in the
1534 * offsets and spans parameters
1535 */
1536 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
1537 int col = 0;
1538 int ncells = getViewCount();
1539 for (int cell = 0; cell < ncells; cell++) {
1540 View cv = getView(cell);
1541 if (skipComments && !(cv instanceof CellView)) {
1542 continue;
1543 }
1544 for (; isFilled(col); col++); // advance to a free column
1545 int colSpan = getColumnsOccupied(cv);
1546 spans[cell] = columnSpans[col];
1547 offsets[cell] = columnOffsets[col];
1548 if (colSpan > 1) {
1549 int n = columnSpans.length;
1550 for (int j = 1; j < colSpan; j++) {
1551 // Because the table may be only partially formed, some
1552 // of the columns may not yet exist. Therefore we check
1553 // the bounds.
1554 if ((col+j) < n) {
1555 spans[cell] += columnSpans[col+j];
1556 spans[cell] += cellSpacing;
1557 }
1558 }
1559 col += colSpan - 1;
1560 }
1561 col++;
1562 }
1563 }
1564
1565 /**
1566 * Perform layout for the minor axis of the box (i.e. the
1567 * axis orthoginal to the axis that it represents). The results
1568 * of the layout should be placed in the given arrays which represent
1569 * the allocations to the children along the minor axis. This
1570 * is called by the superclass whenever the layout needs to be
1571 * updated along the minor axis.
1572 * <p>
1573 * This is implemented to delegate to the superclass, then adjust
1574 * the span for any cell that spans multiple rows.
1575 *
1576 * @param targetSpan the total span given to the view, which
1577 * whould be used to layout the children
1578 * @param axis the axis being layed out
1579 * @param offsets the offsets from the origin of the view for
1580 * each of the child views; this is a return value and is
1581 * filled in by the implementation of this method
1582 * @param spans the span of each child view; this is a return
1583 * value and is filled in by the implementation of this method
1584 * @return the offset and span for each child view in the
1585 * offsets and spans parameters
1586 */
1587 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
1588 super.layoutMinorAxis(targetSpan, axis, offsets, spans);
1589 int col = 0;
1590 int ncells = getViewCount();
1591 for (int cell = 0; cell < ncells; cell++, col++) {
1592 View cv = getView(cell);
1593 for (; isFilled(col); col++); // advance to a free column
1594 int colSpan = getColumnsOccupied(cv);
1595 int rowSpan = getRowsOccupied(cv);
1596 if (rowSpan > 1) {
1597
1598 int row0 = rowIndex;
1599 int row1 = Math.min(rowIndex + rowSpan - 1, getRowCount()-1);
1600 spans[cell] = getMultiRowSpan(row0, row1);
1601 }
1602 if (colSpan > 1) {
1603 col += colSpan - 1;
1604 }
1605 }
1606 }
1607
1608 /**
1609 * Determines the resizability of the view along the
1610 * given axis. A value of 0 or less is not resizable.
1611 *
1612 * @param axis may be either View.X_AXIS or View.Y_AXIS
1613 * @return the resize weight
1614 * @exception IllegalArgumentException for an invalid axis
1615 */
1616 public int getResizeWeight(int axis) {
1617 return 1;
1618 }
1619
1620 /**
1621 * Fetches the child view that represents the given position in
1622 * the model. This is implemented to walk through the children
1623 * looking for a range that contains the given position. In this
1624 * view the children do not necessarily have a one to one mapping
1625 * with the child elements.
1626 *
1627 * @param pos the search position >= 0
1628 * @param a the allocation to the table on entry, and the
1629 * allocation of the view containing the position on exit
1630 * @return the view representing the given position, or
1631 * null if there isn't one
1632 */
1633 protected View getViewAtPosition(int pos, Rectangle a) {
1634 int n = getViewCount();
1635 for (int i = 0; i < n; i++) {
1636 View v = getView(i);
1637 int p0 = v.getStartOffset();
1638 int p1 = v.getEndOffset();
1639 if ((pos >= p0) && (pos < p1)) {
1640 // it's in this view.
1641 if (a != null) {
1642 childAllocation(i, a);
1643 }
1644 return v;
1645 }
1646 }
1647 if (pos == getEndOffset()) {
1648 View v = getView(n - 1);
1649 if (a != null) {
1650 this.childAllocation(n - 1, a);
1651 }
1652 return v;
1653 }
1654 return null;
1655 }
1656
1657 /**
1658 * Update any cached values that come from attributes.
1659 */
1660 void setPropertiesFromAttributes() {
1661 StyleSheet sheet = getStyleSheet();
1662 attr = sheet.getViewAttributes(this);
1663 painter = sheet.getBoxPainter(attr);
1664 }
1665
1666 private StyleSheet.BoxPainter painter;
1667 private AttributeSet attr;
1668
1669 /** columns filled by multi-column or multi-row cells */
1670 BitSet fillColumns;
1671
1672 /**
1673 * The row index within the overall grid
1674 */
1675 int rowIndex;
1676
1677 /**
1678 * The view index (for row index to view index conversion).
1679 * This is set by the updateGrid method.
1680 */
1681 int viewIndex;
1682
1683 /**
1684 * Does this table row have cells that span multiple rows?
1685 */
1686 boolean multiRowCells;
1687
1688 }
1689
1690 /**
1691 * Default view of an html table cell. This needs to be moved
1692 * somewhere else.
1693 */
1694 class CellView extends BlockView {
1695
1696 /**
1697 * Constructs a TableCell for the given element.
1698 *
1699 * @param elem the element that this view is responsible for
1700 */
1701 public CellView(Element elem) {
1702 super(elem, Y_AXIS);
1703 }
1704
1705 /**
1706 * Perform layout for the major axis of the box (i.e. the
1707 * axis that it represents). The results of the layout should
1708 * be placed in the given arrays which represent the allocations
1709 * to the children along the major axis. This is called by the
1710 * superclass to recalculate the positions of the child views
1711 * when the layout might have changed.
1712 * <p>
1713 * This is implemented to delegate to the superclass to
1714 * tile the children. If the target span is greater than
1715 * was needed, the offsets are adjusted to align the children
1716 * (i.e. position according to the html valign attribute).
1717 *
1718 * @param targetSpan the total span given to the view, which
1719 * whould be used to layout the children
1720 * @param axis the axis being layed out
1721 * @param offsets the offsets from the origin of the view for
1722 * each of the child views; this is a return value and is
1723 * filled in by the implementation of this method
1724 * @param spans the span of each child view; this is a return
1725 * value and is filled in by the implementation of this method
1726 * @return the offset and span for each child view in the
1727 * offsets and spans parameters
1728 */
1729 protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
1730 super.layoutMajorAxis(targetSpan, axis, offsets, spans);
1731 // calculate usage
1732 int used = 0;
1733 int n = spans.length;
1734 for (int i = 0; i < n; i++) {
1735 used += spans[i];
1736 }
1737
1738 // calculate adjustments
1739 int adjust = 0;
1740 if (used < targetSpan) {
1741 // PENDING(prinz) change to use the css alignment.
1742 String valign = (String) getElement().getAttributes().getAttribute(
1743 HTML.Attribute.VALIGN);
1744 if (valign == null) {
1745 AttributeSet rowAttr = getElement().getParentElement().getAttributes();
1746 valign = (String) rowAttr.getAttribute(HTML.Attribute.VALIGN);
1747 }
1748 if ((valign == null) || valign.equals("middle")) {
1749 adjust = (targetSpan - used) / 2;
1750 } else if (valign.equals("bottom")) {
1751 adjust = targetSpan - used;
1752 }
1753 }
1754
1755 // make adjustments.
1756 if (adjust != 0) {
1757 for (int i = 0; i < n; i++) {
1758 offsets[i] += adjust;
1759 }
1760 }
1761 }
1762
1763 /**
1764 * Calculate the requirements needed along the major axis.
1765 * This is called by the superclass whenever the requirements
1766 * need to be updated (i.e. a preferenceChanged was messaged
1767 * through this view).
1768 * <p>
1769 * This is implemented to delegate to the superclass, but
1770 * indicate the maximum size is very large (i.e. the cell
1771 * is willing to expend to occupy the full height of the row).
1772 *
1773 * @param axis the axis being layed out.
1774 * @param r the requirements to fill in. If null, a new one
1775 * should be allocated.
1776 */
1777 protected SizeRequirements calculateMajorAxisRequirements(int axis,
1778 SizeRequirements r) {
1779 SizeRequirements req = super.calculateMajorAxisRequirements(axis, r);
1780 req.maximum = Integer.MAX_VALUE;
1781 return req;
1782 }
1783
1784 @Override
1785 protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
1786 SizeRequirements rv = super.calculateMinorAxisRequirements(axis, r);
1787 //for the cell the minimum should be derived from the child views
1788 //the parent behaviour is to use CSS for that
1789 int n = getViewCount();
1790 int min = 0;
1791 for (int i = 0; i < n; i++) {
1792 View v = getView(i);
1793 min = Math.max((int) v.getMinimumSpan(axis), min);
1794 }
1795 rv.minimum = Math.min(rv.minimum, min);
1796 return rv;
1797 }
1798 }
1799
1800
1801}