1/*
2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * 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<RowView>();
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 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;
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                // Default cell spacing equals 2
246                cellSpacing = 2;
247            }
248            lv = (CSS.LengthValue)
249                    attr.getAttribute(CSS.Attribute.BORDER_TOP_WIDTH);
250            if (lv != null) {
251                    borderWidth = (int) lv.getValue();
252            } else {
253                    borderWidth = 0;
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((RowView) 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 orthogonal 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     *  would 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     *  would 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<RowView> rows;
994
995    // whether to display comments inside table or not.
996    boolean skipComments = false;
997
998    boolean gridValid;
999    private static final 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 accommodate 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         *  would 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 orthogonal 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         *  would 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         *  would 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}
1802