1 /*
2  * Copyright (c) 1997, 2017, 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  */
25 package javax.swing.text;
26 
27 import java.awt.*;
28 import java.util.BitSet;
29 import java.util.Vector;
30 import javax.swing.SizeRequirements;
31 import javax.swing.event.DocumentEvent;
32 
33 import javax.swing.text.html.HTML;
34 
35 /**
36  * <p>
37  * Implements View interface for a table, that is composed of an
38  * element structure where the child elements of the element
39  * this view is responsible for represent rows and the child
40  * elements of the row elements are cells.  The cell elements can
41  * have an arbitrary element structure under them, which will
42  * be built with the ViewFactory returned by the getViewFactory
43  * method.
44  * <pre>
45  *
46  * &nbsp;  TABLE
47  * &nbsp;    ROW
48  * &nbsp;      CELL
49  * &nbsp;      CELL
50  * &nbsp;    ROW
51  * &nbsp;      CELL
52  * &nbsp;      CELL
53  *
54  * </pre>
55  * <p>
56  * This is implemented as a hierarchy of boxes, the table itself
57  * is a vertical box, the rows are horizontal boxes, and the cells
58  * are vertical boxes.  The cells are allowed to span multiple
59  * columns and rows.  By default, the table can be thought of as
60  * being formed over a grid (i.e. somewhat like one would find in
61  * gridbag layout), where table cells can request to span more
62  * than one grid cell.  The default horizontal span of table cells
63  * will be based upon this grid, but can be changed by reimplementing
64  * the requested span of the cell (i.e. table cells can have independent
65  * spans if desired).
66  *
67  * @author  Timothy Prinzing
68  * @see     View
69  */
70 public abstract class TableView extends BoxView {
71 
72     /**
73      * Constructs a TableView for the given element.
74      *
75      * @param elem the element that this view is responsible for
76      */
TableView(Element elem)77     public TableView(Element elem) {
78         super(elem, View.Y_AXIS);
79         rows = new Vector<TableRow>();
80         gridValid = false;
81         totalColumnRequirements = new SizeRequirements();
82     }
83 
84     /**
85      * Creates a new table row.
86      *
87      * @param elem an element
88      * @return the row
89      */
createTableRow(Element elem)90     protected TableRow createTableRow(Element elem) {
91         return new TableRow(elem);
92     }
93 
94     /**
95      * @deprecated Table cells can now be any arbitrary
96      * View implementation and should be produced by the
97      * ViewFactory rather than the table.
98      *
99      * @param elem an element
100      * @return the cell
101      */
102     @Deprecated
createTableCell(Element elem)103     protected TableCell createTableCell(Element elem) {
104         return new TableCell(elem);
105     }
106 
107     /**
108      * The number of columns in the table.
109      */
getColumnCount()110     int getColumnCount() {
111         return columnSpans.length;
112     }
113 
114     /**
115      * Fetches the span (width) of the given column.
116      * This is used by the nested cells to query the
117      * sizes of grid locations outside of themselves.
118      */
getColumnSpan(int col)119     int getColumnSpan(int col) {
120         return columnSpans[col];
121     }
122 
123     /**
124      * The number of rows in the table.
125      */
getRowCount()126     int getRowCount() {
127         return rows.size();
128     }
129 
130     /**
131      * Fetches the span (height) of the given row.
132      */
getRowSpan(int row)133     int getRowSpan(int row) {
134         View rv = getRow(row);
135         if (rv != null) {
136             return (int) rv.getPreferredSpan(Y_AXIS);
137         }
138         return 0;
139     }
140 
getRow(int row)141     TableRow getRow(int row) {
142         if (row < rows.size()) {
143             return rows.elementAt(row);
144         }
145         return null;
146     }
147 
148     /**
149      * Determines the number of columns occupied by
150      * the table cell represented by given element.
151      */
getColumnsOccupied(View v)152     /*protected*/ int getColumnsOccupied(View v) {
153         // PENDING(prinz) this code should be in the html
154         // paragraph, but we can't add api to enable it.
155         AttributeSet a = v.getElement().getAttributes();
156         String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
157         if (s != null) {
158             try {
159                 return Integer.parseInt(s);
160             } catch (NumberFormatException nfe) {
161                 // fall through to one column
162             }
163         }
164 
165         return 1;
166     }
167 
168     /**
169      * Determines the number of rows occupied by
170      * the table cell represented by given element.
171      */
getRowsOccupied(View v)172     /*protected*/ int getRowsOccupied(View v) {
173         // PENDING(prinz) this code should be in the html
174         // paragraph, but we can't add api to enable it.
175         AttributeSet a = v.getElement().getAttributes();
176         String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
177         if (s != null) {
178             try {
179                 return Integer.parseInt(s);
180             } catch (NumberFormatException nfe) {
181                 // fall through to one row
182             }
183         }
184 
185         return 1;
186     }
187 
invalidateGrid()188     /*protected*/ void invalidateGrid() {
189         gridValid = false;
190     }
191 
forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e, Shape a, ViewFactory f)192     protected void forwardUpdate(DocumentEvent.ElementChange ec,
193                                      DocumentEvent e, Shape a, ViewFactory f) {
194         super.forwardUpdate(ec, e, a, f);
195         // A change in any of the table cells usually effects the whole table,
196         // so redraw it all!
197         if (a != null) {
198             Component c = getContainer();
199             if (c != null) {
200                 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
201                                    a.getBounds();
202                 c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
203             }
204         }
205     }
206 
207     /**
208      * Change the child views.  This is implemented to
209      * provide the superclass behavior and invalidate the
210      * grid so that rows and columns will be recalculated.
211      */
replace(int offset, int length, View[] views)212     public void replace(int offset, int length, View[] views) {
213         super.replace(offset, length, views);
214         invalidateGrid();
215     }
216 
217     /**
218      * Fill in the grid locations that are placeholders
219      * for multi-column, multi-row, and missing grid
220      * locations.
221      */
updateGrid()222     void updateGrid() {
223         if (! gridValid) {
224             // determine which views are table rows and clear out
225             // grid points marked filled.
226             rows.removeAllElements();
227             int n = getViewCount();
228             for (int i = 0; i < n; i++) {
229                 View v = getView(i);
230                 if (v instanceof TableRow) {
231                     rows.addElement((TableRow) v);
232                     TableRow rv = (TableRow) v;
233                     rv.clearFilledColumns();
234                     rv.setRow(i);
235                 }
236             }
237 
238             int maxColumns = 0;
239             int nrows = rows.size();
240             for (int row = 0; row < nrows; row++) {
241                 TableRow rv = getRow(row);
242                 int col = 0;
243                 for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
244                     View cv = rv.getView(cell);
245                     // advance to a free column
246                     for (; rv.isFilled(col); col++);
247                     int rowSpan = getRowsOccupied(cv);
248                     int colSpan = getColumnsOccupied(cv);
249                     if ((colSpan > 1) || (rowSpan > 1)) {
250                         // fill in the overflow entries for this cell
251                         int rowLimit = row + rowSpan;
252                         int colLimit = col + colSpan;
253                         for (int i = row; i < rowLimit; i++) {
254                             for (int j = col; j < colLimit; j++) {
255                                 if (i != row || j != col) {
256                                     addFill(i, j);
257                                 }
258                             }
259                         }
260                         if (colSpan > 1) {
261                             col += colSpan - 1;
262                         }
263                     }
264                 }
265                 maxColumns = Math.max(maxColumns, col);
266             }
267 
268             // setup the column layout/requirements
269             columnSpans = new int[maxColumns];
270             columnOffsets = new int[maxColumns];
271             columnRequirements = new SizeRequirements[maxColumns];
272             for (int i = 0; i < maxColumns; i++) {
273                 columnRequirements[i] = new SizeRequirements();
274             }
275             gridValid = true;
276         }
277     }
278 
279     /**
280      * Mark a grid location as filled in for a cells overflow.
281      */
addFill(int row, int col)282     void addFill(int row, int col) {
283         TableRow rv = getRow(row);
284         if (rv != null) {
285             rv.fillColumn(col);
286         }
287     }
288 
289     /**
290      * Lays out the columns to fit within the given target span.
291      * Returns the results through {@code offsets} and {@code spans}.
292      *
293      * @param targetSpan the given span for total of all the table
294      *  columns
295      * @param reqs the requirements desired for each column.  This
296      *  is the column maximum of the cells minimum, preferred, and
297      *  maximum requested span
298      * @param spans the return value of how much to allocated to
299      *  each column
300      * @param offsets the return value of the offset from the
301      *  origin for each column
302      */
layoutColumns(int targetSpan, int[] offsets, int[] spans, SizeRequirements[] reqs)303     protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,
304                                  SizeRequirements[] reqs) {
305         // allocate using the convenience method on SizeRequirements
306         SizeRequirements.calculateTiledPositions(targetSpan, null, reqs,
307                                                  offsets, spans);
308     }
309 
310     /**
311      * Perform layout for the minor axis of the box (i.e. the
312      * axis orthogonal to the axis that it represents).  The results
313      * of the layout should be placed in the given arrays which represent
314      * the allocations to the children along the minor axis.  This
315      * is called by the superclass whenever the layout needs to be
316      * updated along the minor axis.
317      * <p>
318      * This is implemented to call the
319      * {@link #layoutColumns layoutColumns} method, and then
320      * forward to the superclass to actually carry out the layout
321      * of the tables rows.
322      *
323      * @param targetSpan the total span given to the view, which
324      *  would be used to layout the children.
325      * @param axis the axis being layed out.
326      * @param offsets the offsets from the origin of the view for
327      *  each of the child views.  This is a return value and is
328      *  filled in by the implementation of this method.
329      * @param spans the span of each child view.  This is a return
330      *  value and is filled in by the implementation of this method.
331      */
layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans)332     protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
333         // make grid is properly represented
334         updateGrid();
335 
336         // all of the row layouts are invalid, so mark them that way
337         int n = getRowCount();
338         for (int i = 0; i < n; i++) {
339             TableRow row = getRow(i);
340             row.layoutChanged(axis);
341         }
342 
343         // calculate column spans
344         layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
345 
346         // continue normal layout
347         super.layoutMinorAxis(targetSpan, axis, offsets, spans);
348     }
349 
350     /**
351      * Calculate the requirements for the minor axis.  This is called by
352      * the superclass whenever the requirements need to be updated (i.e.
353      * a preferenceChanged was messaged through this view).
354      * <p>
355      * This is implemented to calculate the requirements as the sum of the
356      * requirements of the columns.
357      */
calculateMinorAxisRequirements(int axis, SizeRequirements r)358     protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
359         updateGrid();
360 
361         // calculate column requirements for each column
362         calculateColumnRequirements(axis);
363 
364 
365         // the requirements are the sum of the columns.
366         if (r == null) {
367             r = new SizeRequirements();
368         }
369         long min = 0;
370         long pref = 0;
371         long max = 0;
372         for (SizeRequirements req : columnRequirements) {
373             min += req.minimum;
374             pref += req.preferred;
375             max += req.maximum;
376         }
377         r.minimum = (int) min;
378         r.preferred = (int) pref;
379         r.maximum = (int) max;
380         r.alignment = 0;
381 
382         totalColumnRequirements.minimum = r.minimum;
383         totalColumnRequirements.preferred = r.preferred;
384         totalColumnRequirements.maximum = r.maximum;
385 
386         return r;
387     }
388 
389     /*
390     boolean shouldTrace() {
391         AttributeSet a = getElement().getAttributes();
392         Object o = a.getAttribute(HTML.Attribute.ID);
393         if ((o != null) && o.equals("debug")) {
394             return true;
395         }
396         return false;
397     }
398     */
399 
400     /**
401      * Calculate the requirements for each column.  The calculation
402      * is done as two passes over the table.  The table cells that
403      * occupy a single column are scanned first to determine the
404      * maximum of minimum, preferred, and maximum spans along the
405      * give axis.  Table cells that span multiple columns are excluded
406      * from the first pass.  A second pass is made to determine if
407      * the cells that span multiple columns are satisfied.  If the
408      * column requirements are not satisified, the needs of the
409      * multi-column cell is mixed into the existing column requirements.
410      * The calculation of the multi-column distribution is based upon
411      * the proportions of the existing column requirements and taking
412      * into consideration any constraining maximums.
413      */
calculateColumnRequirements(int axis)414     void calculateColumnRequirements(int axis) {
415 
416         for (SizeRequirements req : columnRequirements) {
417             req.minimum = 0;
418             req.preferred = 0;
419             req.maximum = Integer.MAX_VALUE;
420         }
421 
422         // pass 1 - single column cells
423         boolean hasMultiColumn = false;
424         int nrows = getRowCount();
425         for (int i = 0; i < nrows; i++) {
426             TableRow row = getRow(i);
427             int col = 0;
428             int ncells = row.getViewCount();
429             for (int cell = 0; cell < ncells; cell++, col++) {
430                 View cv = row.getView(cell);
431                 for (; row.isFilled(col); col++); // advance to a free column
432                 int rowSpan = getRowsOccupied(cv);
433                 int colSpan = getColumnsOccupied(cv);
434                 if (colSpan == 1) {
435                     checkSingleColumnCell(axis, col, cv);
436                 } else {
437                     hasMultiColumn = true;
438                     col += colSpan - 1;
439                 }
440             }
441         }
442 
443         // pass 2 - multi-column cells
444         if (hasMultiColumn) {
445             for (int i = 0; i < nrows; i++) {
446                 TableRow row = getRow(i);
447                 int col = 0;
448                 int ncells = row.getViewCount();
449                 for (int cell = 0; cell < ncells; cell++, col++) {
450                     View cv = row.getView(cell);
451                     for (; row.isFilled(col); col++); // advance to a free column
452                     int colSpan = getColumnsOccupied(cv);
453                     if (colSpan > 1) {
454                         checkMultiColumnCell(axis, col, colSpan, cv);
455                         col += colSpan - 1;
456                     }
457                 }
458             }
459         }
460 
461         /*
462         if (shouldTrace()) {
463             System.err.println("calc:");
464             for (int i = 0; i < columnRequirements.length; i++) {
465                 System.err.println(" " + i + ": " + columnRequirements[i]);
466             }
467         }
468         */
469     }
470 
471     /**
472      * check the requirements of a table cell that spans a single column.
473      */
checkSingleColumnCell(int axis, int col, View v)474     void checkSingleColumnCell(int axis, int col, View v) {
475         SizeRequirements req = columnRequirements[col];
476         req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
477         req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
478         req.maximum = Math.max((int) v.getMaximumSpan(axis), req.maximum);
479     }
480 
481     /**
482      * check the requirements of a table cell that spans multiple
483      * columns.
484      */
checkMultiColumnCell(int axis, int col, int ncols, View v)485     void checkMultiColumnCell(int axis, int col, int ncols, View v) {
486         // calculate the totals
487         long min = 0;
488         long pref = 0;
489         long max = 0;
490         for (int i = 0; i < ncols; i++) {
491             SizeRequirements req = columnRequirements[col + i];
492             min += req.minimum;
493             pref += req.preferred;
494             max += req.maximum;
495         }
496 
497         // check if the minimum size needs adjustment.
498         int cmin = (int) v.getMinimumSpan(axis);
499         if (cmin > min) {
500             /*
501              * the columns that this cell spans need adjustment to fit
502              * this table cell.... calculate the adjustments.  The
503              * maximum for each cell is the maximum of the existing
504              * maximum or the amount needed by the cell.
505              */
506             SizeRequirements[] reqs = new SizeRequirements[ncols];
507             for (int i = 0; i < ncols; i++) {
508                 SizeRequirements r = reqs[i] = columnRequirements[col + i];
509                 r.maximum = Math.max(r.maximum, (int) v.getMaximumSpan(axis));
510             }
511             int[] spans = new int[ncols];
512             int[] offsets = new int[ncols];
513             SizeRequirements.calculateTiledPositions(cmin, null, reqs,
514                                                      offsets, spans);
515             // apply the adjustments
516             for (int i = 0; i < ncols; i++) {
517                 SizeRequirements req = reqs[i];
518                 req.minimum = Math.max(spans[i], req.minimum);
519                 req.preferred = Math.max(req.minimum, req.preferred);
520                 req.maximum = Math.max(req.preferred, req.maximum);
521             }
522         }
523 
524         // check if the preferred size needs adjustment.
525         int cpref = (int) v.getPreferredSpan(axis);
526         if (cpref > pref) {
527             /*
528              * the columns that this cell spans need adjustment to fit
529              * this table cell.... calculate the adjustments.  The
530              * maximum for each cell is the maximum of the existing
531              * maximum or the amount needed by the cell.
532              */
533             SizeRequirements[] reqs = new SizeRequirements[ncols];
534             for (int i = 0; i < ncols; i++) {
535                 SizeRequirements r = reqs[i] = columnRequirements[col + i];
536             }
537             int[] spans = new int[ncols];
538             int[] offsets = new int[ncols];
539             SizeRequirements.calculateTiledPositions(cpref, null, reqs,
540                                                      offsets, spans);
541             // apply the adjustments
542             for (int i = 0; i < ncols; i++) {
543                 SizeRequirements req = reqs[i];
544                 req.preferred = Math.max(spans[i], req.preferred);
545                 req.maximum = Math.max(req.preferred, req.maximum);
546             }
547         }
548 
549     }
550 
551     /**
552      * Fetches the child view that represents the given position in
553      * the model.  This is implemented to walk through the children
554      * looking for a range that contains the given position.  In this
555      * view the children do not necessarily have a one to one mapping
556      * with the child elements.
557      *
558      * @param pos  the search position &gt;= 0
559      * @param a  the allocation to the table on entry, and the
560      *   allocation of the view containing the position on exit
561      * @return  the view representing the given position, or
562      *   <code>null</code> if there isn't one
563      */
getViewAtPosition(int pos, Rectangle a)564     protected View getViewAtPosition(int pos, Rectangle a) {
565         int n = getViewCount();
566         for (int i = 0; i < n; i++) {
567             View v = getView(i);
568             int p0 = v.getStartOffset();
569             int p1 = v.getEndOffset();
570             if ((pos >= p0) && (pos < p1)) {
571                 // it's in this view.
572                 if (a != null) {
573                     childAllocation(i, a);
574                 }
575                 return v;
576             }
577         }
578         if (pos == getEndOffset()) {
579             View v = getView(n - 1);
580             if (a != null) {
581                 this.childAllocation(n - 1, a);
582             }
583             return v;
584         }
585         return null;
586     }
587 
588     // ---- variables ----------------------------------------------------
589 
590     int[] columnSpans;
591     int[] columnOffsets;
592 
593     SizeRequirements totalColumnRequirements;
594 
595     SizeRequirements[] columnRequirements;
596     Vector<TableRow> rows;
597     boolean gridValid;
598     private static final BitSet EMPTY = new BitSet();
599 
600     /**
601      * View of a row in a row-centric table.
602      */
603     public class TableRow extends BoxView {
604 
605         /**
606          * Constructs a TableView for the given element.
607          *
608          * @param elem the element that this view is responsible for
609          * @since 1.4
610          */
TableRow(Element elem)611         public TableRow(Element elem) {
612             super(elem, View.X_AXIS);
613             fillColumns = new BitSet();
614         }
615 
clearFilledColumns()616         void clearFilledColumns() {
617             fillColumns.and(EMPTY);
618         }
619 
fillColumn(int col)620         void fillColumn(int col) {
621             fillColumns.set(col);
622         }
623 
isFilled(int col)624         boolean isFilled(int col) {
625             return fillColumns.get(col);
626         }
627 
628         /** get location in the overall set of rows */
getRow()629         int getRow() {
630             return row;
631         }
632 
633         /**
634          * set location in the overall set of rows, this is
635          * set by the TableView.updateGrid() method.
636          */
setRow(int row)637         void setRow(int row) {
638             this.row = row;
639         }
640 
641         /**
642          * The number of columns present in this row.
643          */
getColumnCount()644         int getColumnCount() {
645             int nfill = 0;
646             int n = fillColumns.size();
647             for (int i = 0; i < n; i++) {
648                 if (fillColumns.get(i)) {
649                     nfill ++;
650                 }
651             }
652             return getViewCount() + nfill;
653         }
654 
655         /**
656          * Change the child views.  This is implemented to
657          * provide the superclass behavior and invalidate the
658          * grid so that rows and columns will be recalculated.
659          */
replace(int offset, int length, View[] views)660         public void replace(int offset, int length, View[] views) {
661             super.replace(offset, length, views);
662             invalidateGrid();
663         }
664 
665         @Override
calculateMajorAxisRequirements(int axis, SizeRequirements r)666         protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
667             SizeRequirements req = new SizeRequirements();
668             req.minimum = totalColumnRequirements.minimum;
669             req.maximum = totalColumnRequirements.maximum;
670             req.preferred = totalColumnRequirements.preferred;
671             req.alignment = 0f;
672             return req;
673         }
674 
675         @Override
getMinimumSpan(int axis)676         public float getMinimumSpan(int axis) {
677             float value;
678 
679             if (axis == View.X_AXIS) {
680                 value = totalColumnRequirements.minimum + getLeftInset() + getRightInset();
681             } else {
682                 value = super.getMinimumSpan(axis);
683             }
684             return value;
685         }
686 
687         @Override
getMaximumSpan(int axis)688         public float getMaximumSpan(int axis) {
689             float value;
690 
691             if (axis == View.X_AXIS) {
692                 // We're flexible.
693                 value = (float) Integer.MAX_VALUE;
694             } else {
695                 value = super.getMaximumSpan(axis);
696             }
697             return value;
698         }
699 
700         @Override
getPreferredSpan(int axis)701         public float getPreferredSpan(int axis) {
702             float value;
703 
704             if (axis == View.X_AXIS) {
705                 value = totalColumnRequirements.preferred + getLeftInset() + getRightInset();
706             } else {
707                 value = super.getPreferredSpan(axis);
708             }
709             return value;
710         }
711 
712         /**
713          * Perform layout for the major axis of the box (i.e. the
714          * axis that it represents).  The results of the layout should
715          * be placed in the given arrays which represent the allocations
716          * to the children along the major axis.
717          * <p>
718          * This is re-implemented to give each child the span of the column
719          * width for the table, and to give cells that span multiple columns
720          * the multi-column span.
721          *
722          * @param targetSpan the total span given to the view, which
723          *  would be used to layout the children.
724          * @param axis the axis being layed out.
725          * @param offsets the offsets from the origin of the view for
726          *  each of the child views.  This is a return value and is
727          *  filled in by the implementation of this method.
728          * @param spans the span of each child view.  This is a return
729          *  value and is filled in by the implementation of this method.
730          */
layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans)731         protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
732             int col = 0;
733             int ncells = getViewCount();
734             for (int cell = 0; cell < ncells; cell++, col++) {
735                 View cv = getView(cell);
736                 for (; isFilled(col); col++); // advance to a free column
737                 int colSpan = getColumnsOccupied(cv);
738                 spans[cell] = columnSpans[col];
739                 offsets[cell] = columnOffsets[col];
740                 if (colSpan > 1) {
741                     int n = columnSpans.length;
742                     for (int j = 1; j < colSpan; j++) {
743                         // Because the table may be only partially formed, some
744                         // of the columns may not yet exist.  Therefore we check
745                         // the bounds.
746                         if ((col+j) < n) {
747                             spans[cell] += columnSpans[col+j];
748                         }
749                     }
750                     col += colSpan - 1;
751                 }
752             }
753         }
754 
755         /**
756          * Perform layout for the minor axis of the box (i.e. the
757          * axis orthogonal to the axis that it represents).  The results
758          * of the layout should be placed in the given arrays which represent
759          * the allocations to the children along the minor axis.  This
760          * is called by the superclass whenever the layout needs to be
761          * updated along the minor axis.
762          * <p>
763          * This is implemented to delegate to the superclass, then adjust
764          * the span for any cell that spans multiple rows.
765          *
766          * @param targetSpan the total span given to the view, which
767          *  would be used to layout the children.
768          * @param axis the axis being layed out.
769          * @param offsets the offsets from the origin of the view for
770          *  each of the child views.  This is a return value and is
771          *  filled in by the implementation of this method.
772          * @param spans the span of each child view.  This is a return
773          *  value and is filled in by the implementation of this method.
774          */
layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans)775         protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
776             super.layoutMinorAxis(targetSpan, axis, offsets, spans);
777             int col = 0;
778             int ncells = getViewCount();
779             for (int cell = 0; cell < ncells; cell++, col++) {
780                 View cv = getView(cell);
781                 for (; isFilled(col); col++); // advance to a free column
782                 int colSpan = getColumnsOccupied(cv);
783                 int rowSpan = getRowsOccupied(cv);
784                 if (rowSpan > 1) {
785                     for (int j = 1; j < rowSpan; j++) {
786                         // test bounds of each row because it may not exist
787                         // either because of error or because the table isn't
788                         // fully loaded yet.
789                         int row = getRow() + j;
790                         if (row < TableView.this.getViewCount()) {
791                             int span = TableView.this.getSpan(Y_AXIS, getRow()+j);
792                             spans[cell] += span;
793                         }
794                     }
795                 }
796                 if (colSpan > 1) {
797                     col += colSpan - 1;
798                 }
799             }
800         }
801 
802         /**
803          * Determines the resizability of the view along the
804          * given axis.  A value of 0 or less is not resizable.
805          *
806          * @param axis may be either View.X_AXIS or View.Y_AXIS
807          * @return the resize weight
808          * @exception IllegalArgumentException for an invalid axis
809          */
getResizeWeight(int axis)810         public int getResizeWeight(int axis) {
811             return 1;
812         }
813 
814         /**
815          * Fetches the child view that represents the given position in
816          * the model.  This is implemented to walk through the children
817          * looking for a range that contains the given position.  In this
818          * view the children do not necessarily have a one to one mapping
819          * with the child elements.
820          *
821          * @param pos  the search position &gt;= 0
822          * @param a  the allocation to the table on entry, and the
823          *   allocation of the view containing the position on exit
824          * @return  the view representing the given position, or
825          *   <code>null</code> if there isn't one
826          */
getViewAtPosition(int pos, Rectangle a)827         protected View getViewAtPosition(int pos, Rectangle a) {
828             int n = getViewCount();
829             for (int i = 0; i < n; i++) {
830                 View v = getView(i);
831                 int p0 = v.getStartOffset();
832                 int p1 = v.getEndOffset();
833                 if ((pos >= p0) && (pos < p1)) {
834                     // it's in this view.
835                     if (a != null) {
836                         childAllocation(i, a);
837                     }
838                     return v;
839                 }
840             }
841             if (pos == getEndOffset()) {
842                 View v = getView(n - 1);
843                 if (a != null) {
844                     this.childAllocation(n - 1, a);
845                 }
846                 return v;
847             }
848             return null;
849         }
850 
851         /** columns filled by multi-column or multi-row cells */
852         BitSet fillColumns;
853         /** the row within the overall grid */
854         int row;
855     }
856 
857     /**
858      * @deprecated  A table cell can now be any View implementation.
859      */
860     @Deprecated
861     public class TableCell extends BoxView implements GridCell {
862 
863         /**
864          * Constructs a TableCell for the given element.
865          *
866          * @param elem the element that this view is responsible for
867          * @since 1.4
868          */
TableCell(Element elem)869         public TableCell(Element elem) {
870             super(elem, View.Y_AXIS);
871         }
872 
873         // --- GridCell methods -------------------------------------
874 
875         /**
876          * Gets the number of columns this cell spans (e.g. the
877          * grid width).
878          *
879          * @return the number of columns
880          */
getColumnCount()881         public int getColumnCount() {
882             return 1;
883         }
884 
885         /**
886          * Gets the number of rows this cell spans (that is, the
887          * grid height).
888          *
889          * @return the number of rows
890          */
getRowCount()891         public int getRowCount() {
892             return 1;
893         }
894 
895 
896         /**
897          * Sets the grid location.
898          *
899          * @param row the row &gt;= 0
900          * @param col the column &gt;= 0
901          */
setGridLocation(int row, int col)902         public void setGridLocation(int row, int col) {
903             this.row = row;
904             this.col = col;
905         }
906 
907         /**
908          * Gets the row of the grid location
909          */
getGridRow()910         public int getGridRow() {
911             return row;
912         }
913 
914         /**
915          * Gets the column of the grid location
916          */
getGridColumn()917         public int getGridColumn() {
918             return col;
919         }
920 
921         int row;
922         int col;
923     }
924 
925     /**
926      * <em>
927      * THIS IS NO LONGER USED, AND WILL BE REMOVED IN THE
928      * NEXT RELEASE.  THE JCK SIGNATURE TEST THINKS THIS INTERFACE
929      * SHOULD EXIST
930      * </em>
931      */
932     interface GridCell {
933 
934         /**
935          * Sets the grid location.
936          *
937          * @param row the row &gt;= 0
938          * @param col the column &gt;= 0
939          */
setGridLocation(int row, int col)940         public void setGridLocation(int row, int col);
941 
942         /**
943          * Gets the row of the grid location
944          */
getGridRow()945         public int getGridRow();
946 
947         /**
948          * Gets the column of the grid location
949          */
getGridColumn()950         public int getGridColumn();
951 
952         /**
953          * Gets the number of columns this cell spans (e.g. the
954          * grid width).
955          *
956          * @return the number of columns
957          */
getColumnCount()958         public int getColumnCount();
959 
960         /**
961          * Gets the number of rows this cell spans (that is, the
962          * grid height).
963          *
964          * @return the number of rows
965          */
getRowCount()966         public int getRowCount();
967 
968     }
969 
970 }
971