1 /* TableView.java -- A table view for HTML tables
2    Copyright (C) 2006 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package javax.swing.text.html;
40 
41 import java.awt.Graphics;
42 import java.awt.Rectangle;
43 import java.awt.Shape;
44 
45 import gnu.javax.swing.text.html.css.Length;
46 
47 import javax.swing.SizeRequirements;
48 import javax.swing.event.DocumentEvent;
49 import javax.swing.text.AttributeSet;
50 import javax.swing.text.Element;
51 import javax.swing.text.StyleConstants;
52 import javax.swing.text.View;
53 import javax.swing.text.ViewFactory;
54 
55 /**
56  * A view implementation that renders HTML tables.
57  *
58  * This is basically a vertical BoxView that contains the rows of the table
59  * and the rows are horizontal BoxViews that contain the actual columns.
60  */
61 class TableView
62   extends BlockView
63   implements ViewFactory
64 {
65 
66   /**
67    * Represents a single table row.
68    */
69   class RowView
70     extends BlockView
71   {
72     /**
73      * Has true at column positions where an above row's cell overlaps into
74      * this row.
75      */
76     boolean[] overlap;
77 
78     /**
79      * Stores the row index of this row.
80      */
81     int rowIndex;
82 
83     /**
84      * Creates a new RowView.
85      *
86      * @param el the element for the row view
87      */
RowView(Element el)88     RowView(Element el)
89     {
90       super(el, X_AXIS);
91     }
92 
replace(int offset, int len, View[] views)93     public void replace(int offset, int len, View[] views)
94     {
95       gridValid = false;
96       super.replace(offset, len, views);
97     }
98 
99     /**
100      * Overridden to make rows not resizable along the Y axis.
101      */
getMaximumSpan(int axis)102     public float getMaximumSpan(int axis)
103     {
104       float span;
105       if (axis == Y_AXIS)
106         span = super.getPreferredSpan(axis);
107       else
108         span = Integer.MAX_VALUE;
109       return span;
110     }
111 
getMinimumSpan(int axis)112     public float getMinimumSpan(int axis)
113     {
114       float span;
115       if (axis == X_AXIS)
116         span = totalColumnRequirements.minimum;
117       else
118         span = super.getMinimumSpan(axis);
119       return span;
120     }
121 
getPreferredSpan(int axis)122     public float getPreferredSpan(int axis)
123     {
124       float span;
125       if (axis == X_AXIS)
126         span = totalColumnRequirements.preferred;
127       else
128         span = super.getPreferredSpan(axis);
129       return span;
130     }
131 
132     /**
133      * Calculates the overall size requirements for the row along the
134      * major axis. This will be the sum of the column requirements.
135      */
calculateMajorAxisRequirements(int axis, SizeRequirements r)136     protected SizeRequirements calculateMajorAxisRequirements(int axis,
137                                                             SizeRequirements r)
138     {
139       if (r == null)
140         r = new SizeRequirements();
141       int adjust = (columnRequirements.length + 1) * cellSpacing;
142       r.minimum = totalColumnRequirements.minimum + adjust;
143       r.preferred = totalColumnRequirements.preferred + adjust;
144       r.maximum = totalColumnRequirements.maximum + adjust;
145       r.alignment = 0.0F;
146       return r;
147     }
148 
149     /**
150      * Lays out the columns in this row.
151      */
layoutMinorAxis(int targetSpan, int axis, int[] offsets, int spans[])152     protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
153                                    int spans[])
154     {
155       super.layoutMinorAxis(targetSpan, axis, offsets, spans);
156 
157       // Adjust columns that have rowSpan > 1.
158       int numCols = getViewCount();
159       for (int i = 0; i < numCols; i++)
160         {
161           View v = getView(i);
162           if (v instanceof CellView)
163             {
164               CellView cell = (CellView) v;
165               if (cell.rowSpan > 1)
166                 {
167                   for (int r = 1; r < cell.rowSpan; r++)
168                     {
169                       spans[i] += TableView.this.getSpan(axis, rowIndex + r);
170                       spans[i] += cellSpacing;
171                     }
172                 }
173             }
174         }
175     }
176 
177     /**
178      * Lays out the columns in this row.
179      */
layoutMajorAxis(int targetSpan, int axis, int[] offsets, int spans[])180     protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
181                                    int spans[])
182     {
183       updateGrid();
184       int realColumn = 0;
185       int colCount = getViewCount();
186       for (int i = 0; i < numColumns;)
187         {
188           if (! overlap[i] && realColumn < colCount)
189             {
190               View v = getView(realColumn);
191               if (v instanceof CellView)
192                 {
193                   CellView cv = (CellView) v;
194                   offsets[realColumn] = columnOffsets[i];
195                   spans[realColumn] = 0;
196                   for (int j = 0; j < cv.colSpan; j++, i++)
197                     {
198                       spans[realColumn] += columnSpans[i];
199                       if (j < cv.colSpan - 1)
200                         spans[realColumn] += cellSpacing;
201                     }
202                 }
203               realColumn++;
204             }
205           else
206             {
207               i++;
208             }
209         }
210     }
211   }
212 
213   /**
214    * A view that renders HTML table cells (TD and TH tags).
215    */
216   class CellView
217     extends BlockView
218   {
219 
220     /**
221      * The number of columns that this view spans.
222      */
223     int colSpan;
224 
225     /**
226      * The number of rows that this cell spans.
227      */
228     int rowSpan;
229 
230     /**
231      * Creates a new CellView for the specified element.
232      *
233      * @param el the element for which to create the colspan
234      */
CellView(Element el)235     CellView(Element el)
236     {
237       super(el, Y_AXIS);
238     }
239 
calculateMajorAxisRequirements(int axis, SizeRequirements r)240     protected SizeRequirements calculateMajorAxisRequirements(int axis,
241                                                             SizeRequirements r)
242     {
243       r = super.calculateMajorAxisRequirements(axis, r);
244       r.maximum = Integer.MAX_VALUE;
245       return r;
246     }
247 
248     /**
249      * Overridden to fetch the columnSpan attibute.
250      */
setPropertiesFromAttributes()251     protected void setPropertiesFromAttributes()
252     {
253       super.setPropertiesFromAttributes();
254       colSpan = 1;
255       AttributeSet atts = getAttributes();
256       Object o = atts.getAttribute(HTML.Attribute.COLSPAN);
257       if (o != null)
258         {
259           try
260             {
261               colSpan = Integer.parseInt(o.toString());
262             }
263           catch (NumberFormatException ex)
264             {
265               // Couldn't parse the colspan, assume 1.
266               colSpan = 1;
267             }
268         }
269       rowSpan = 1;
270       o = atts.getAttribute(HTML.Attribute.ROWSPAN);
271       if (o != null)
272         {
273           try
274             {
275               rowSpan = Integer.parseInt(o.toString());
276             }
277           catch (NumberFormatException ex)
278             {
279               // Couldn't parse the colspan, assume 1.
280               rowSpan = 1;
281             }
282         }
283     }
284   }
285 
286 
287   /**
288    * The attributes of this view.
289    */
290   private AttributeSet attributes;
291 
292   /**
293    * The column requirements.
294    *
295    * Package private to avoid accessor methods.
296    */
297   SizeRequirements[] columnRequirements;
298 
299   /**
300    * The overall requirements across all columns.
301    *
302    * Package private to avoid accessor methods.
303    */
304   SizeRequirements totalColumnRequirements;
305 
306   /**
307    * The column layout, offsets.
308    *
309    * Package private to avoid accessor methods.
310    */
311   int[] columnOffsets;
312 
313   /**
314    * The column layout, spans.
315    *
316    * Package private to avoid accessor methods.
317    */
318   int[] columnSpans;
319 
320   /**
321    * The widths of the columns that have been explicitly specified.
322    */
323   Length[] columnWidths;
324 
325   /**
326    * The total number of columns.
327    */
328   int numColumns;
329 
330   /**
331    * The table width.
332    */
333   private Length width;
334 
335   /**
336    * Indicates if the grid setup is ok.
337    */
338   boolean gridValid = false;
339 
340   /**
341    * Additional space that is added _between_ table cells.
342    *
343    * This is package private to avoid accessor methods.
344    */
345   int cellSpacing;
346 
347   /**
348    * A cached Rectangle object for reuse in paint().
349    */
350   private Rectangle tmpRect;
351 
352   /**
353    * Creates a new HTML table view for the specified element.
354    *
355    * @param el the element for the table view
356    */
TableView(Element el)357   public TableView(Element el)
358   {
359     super(el, Y_AXIS);
360     totalColumnRequirements = new SizeRequirements();
361     tmpRect = new Rectangle();
362   }
363 
364   /**
365    * Implementation of the ViewFactory interface for creating the
366    * child views correctly.
367    */
create(Element elem)368   public View create(Element elem)
369   {
370     View view = null;
371     AttributeSet atts = elem.getAttributes();
372     Object name = atts.getAttribute(StyleConstants.NameAttribute);
373     AttributeSet pAtts = elem.getParentElement().getAttributes();
374     Object pName = pAtts.getAttribute(StyleConstants.NameAttribute);
375 
376     if (name == HTML.Tag.TR && pName == HTML.Tag.TABLE)
377       view = new RowView(elem);
378     else if ((name == HTML.Tag.TD || name == HTML.Tag.TH)
379              && pName == HTML.Tag.TR)
380       view = new CellView(elem);
381     else if (name == HTML.Tag.CAPTION)
382       view = new ParagraphView(elem);
383     else
384       {
385         // If we haven't mapped the element, then fall back to the standard
386         // view factory.
387         View parent = getParent();
388         if (parent != null)
389           {
390             ViewFactory vf = parent.getViewFactory();
391             if (vf != null)
392               view = vf.create(elem);
393           }
394       }
395     return view;
396   }
397 
398   /**
399    * Returns this object as view factory so that we get our TR, TD, TH
400    * and CAPTION subelements created correctly.
401    */
getViewFactory()402   public ViewFactory getViewFactory()
403   {
404     return this;
405   }
406 
407   /**
408    * Returns the attributes of this view. This is overridden to provide
409    * the attributes merged with the CSS stuff.
410    */
getAttributes()411   public AttributeSet getAttributes()
412   {
413     if (attributes == null)
414       attributes = getStyleSheet().getViewAttributes(this);
415     return attributes;
416   }
417 
418   /**
419    * Returns the stylesheet associated with this view.
420    *
421    * @return the stylesheet associated with this view
422    */
getStyleSheet()423   protected StyleSheet getStyleSheet()
424   {
425     HTMLDocument doc = (HTMLDocument) getDocument();
426     return doc.getStyleSheet();
427   }
428 
429   /**
430    * Overridden to calculate the size requirements according to the
431    * columns distribution.
432    */
calculateMinorAxisRequirements(int axis, SizeRequirements r)433   protected SizeRequirements calculateMinorAxisRequirements(int axis,
434                                                             SizeRequirements r)
435   {
436     updateGrid();
437     calculateColumnRequirements();
438 
439     // Calculate the horizontal requirements according to the superclass.
440     // This will return the maximum of the row's widths.
441     r = super.calculateMinorAxisRequirements(axis, r);
442 
443     // Try to set the CSS width if it fits.
444     if (width != null)
445       {
446         int w = (int) width.getValue();
447         if (r.minimum < w)
448           r.minimum = w;
449       }
450 
451     // Adjust requirements when we have cell spacing.
452     int adjust = (columnRequirements.length + 1) * cellSpacing;
453     r.minimum += adjust;
454     r.preferred += adjust;
455 
456     // Apply the alignment.
457     AttributeSet atts = getAttributes();
458     Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN);
459     r.alignment = 0.0F;
460     if (o != null)
461       {
462         String al = o.toString();
463         if (al.equals("left"))
464           r.alignment = 0.0F;
465         else if (al.equals("center"))
466           r.alignment = 0.5F;
467         else if (al.equals("right"))
468           r.alignment = 1.0F;
469       }
470 
471     // Make it not resize in the horizontal direction.
472     r.maximum = r.preferred;
473     return r;
474   }
475 
476   /**
477    * Overridden to perform the table layout before calling the super
478    * implementation.
479    */
layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans)480   protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
481                                  int[] spans)
482   {
483     updateGrid();
484 
485     // Mark all rows as invalid along their minor axis to force correct
486     // layout of multi-row cells.
487     int n = getViewCount();
488     for (int i = 0; i < n; i++)
489       {
490         View row = getView(i);
491         if (row instanceof RowView)
492           ((RowView) row).layoutChanged(axis);
493       }
494 
495     layoutColumns(targetSpan);
496     super.layoutMinorAxis(targetSpan, axis, offsets, spans);
497   }
498 
499   /**
500    * Calculates the size requirements for the columns.
501    */
calculateColumnRequirements()502   private void calculateColumnRequirements()
503   {
504     int numRows = getViewCount();
505     totalColumnRequirements.minimum = 0;
506     totalColumnRequirements.preferred = 0;
507     totalColumnRequirements.maximum = 0;
508 
509     // In this first pass we find out a suitable total width to fit in
510     // all columns of all rows.
511     for (int r = 0; r < numRows; r++)
512       {
513         View rowView = getView(r);
514         int numCols;
515         if (rowView instanceof RowView)
516           numCols = ((RowView) rowView).getViewCount();
517         else
518           numCols = 0;
519 
520         // We collect the normal (non-relative) column requirements in the
521         // total variable and the relative requirements in the relTotal
522         // variable. In the end we create the maximum of both to get the
523         // real requirements.
524         SizeRequirements total = new SizeRequirements();
525         SizeRequirements relTotal = new SizeRequirements();
526         float totalPercent = 0.F;
527         int realCol = 0;
528         for (int c = 0; c < numCols; c++)
529           {
530             View v = rowView.getView(c);
531             if (v instanceof CellView)
532               {
533                 CellView cellView = (CellView) v;
534                 int colSpan = cellView.colSpan;
535                 if (colSpan > 1)
536                   {
537                     int cellMin = (int) cellView.getMinimumSpan(X_AXIS);
538                     int cellPref = (int) cellView.getPreferredSpan(X_AXIS);
539                     int cellMax = (int) cellView.getMaximumSpan(X_AXIS);
540                     int currentMin = 0;
541                     int currentPref = 0;
542                     long currentMax = 0;
543                     for (int i = 0; i < colSpan; i++)
544                       {
545                         SizeRequirements req = columnRequirements[realCol];
546                         currentMin += req.minimum;
547                         currentPref += req.preferred;
548                         currentMax += req.maximum;
549                       }
550                     int deltaMin = cellMin - currentMin;
551                     int deltaPref = cellPref - currentPref;
552                     int deltaMax = (int) (cellMax - currentMax);
553                     // Distribute delta.
554                     for (int i = 0; i < colSpan; i++)
555                       {
556                         SizeRequirements req = columnRequirements[realCol];
557                         if (deltaMin > 0)
558                           req.minimum += deltaMin / colSpan;
559                         if (deltaPref > 0)
560                           req.preferred += deltaPref / colSpan;
561                         if (deltaMax > 0)
562                           req.maximum += deltaMax / colSpan;
563                         if (columnWidths[realCol] == null
564                             || ! columnWidths[realCol].isPercentage())
565                           {
566                             total.minimum += req.minimum;
567                             total.preferred += req.preferred;
568                             total.maximum += req.maximum;
569                           }
570                         else
571                           {
572                             relTotal.minimum =
573                               Math.max(relTotal.minimum,
574                                      (int) (req.minimum
575                                           * columnWidths[realCol].getValue()));
576                             relTotal.preferred =
577                               Math.max(relTotal.preferred,
578                                      (int) (req.preferred
579                                           * columnWidths[realCol].getValue()));
580                             relTotal.maximum =
581                               Math.max(relTotal.maximum,
582                                      (int) (req.maximum
583                                           * columnWidths[realCol].getValue()));
584                             totalPercent += columnWidths[realCol].getValue();
585                           }
586                       }
587                     realCol += colSpan;
588                   }
589                 else
590                   {
591                     // Shortcut for colSpan == 1.
592                     SizeRequirements req = columnRequirements[realCol];
593                     req.minimum = Math.max(req.minimum,
594                                         (int) cellView.getMinimumSpan(X_AXIS));
595                     req.preferred = Math.max(req.preferred,
596                                       (int) cellView.getPreferredSpan(X_AXIS));
597                     req.maximum = Math.max(req.maximum,
598                                         (int) cellView.getMaximumSpan(X_AXIS));
599                     if (columnWidths[realCol] == null
600                         || ! columnWidths[realCol].isPercentage())
601                       {
602                         total.minimum += columnRequirements[realCol].minimum;
603                         total.preferred +=
604                           columnRequirements[realCol].preferred;
605                         total.maximum += columnRequirements[realCol].maximum;
606                       }
607                     else
608                       {
609                         relTotal.minimum =
610                           Math.max(relTotal.minimum,
611                                  (int) (req.minimum
612                                         / columnWidths[c].getValue()));
613                         relTotal.preferred =
614                           Math.max(relTotal.preferred,
615                                  (int) (req.preferred
616                                         / columnWidths[c].getValue()));
617                         relTotal.maximum =
618                           Math.max(relTotal.maximum,
619                                  (int) (req.maximum
620                                         / columnWidths[c].getValue()));
621                         totalPercent += columnWidths[c].getValue();
622                       }
623                     realCol += 1;
624                   }
625               }
626           }
627 
628         // Update the total requirements as follows:
629         // 1. Multiply the absolute requirements with 1 - totalPercent. This
630         //    gives the total requirements based on the wishes of the absolute
631         //    cells.
632         // 2. Take the maximum of this value and the total relative
633         //    requirements. Now we should have enough space for whatever cell
634         //    in this column.
635         // 3. Take the maximum of this value and the previous maximum value.
636         total.minimum *= 1.F / (1.F - totalPercent);
637         total.preferred *= 1.F / (1.F - totalPercent);
638         total.maximum *= 1.F / (1.F - totalPercent);
639 
640         int rowTotalMin = Math.max(total.minimum, relTotal.minimum);
641         int rowTotalPref = Math.max(total.preferred, relTotal.preferred);
642         int rowTotalMax = Math.max(total.maximum, relTotal.maximum);
643         totalColumnRequirements.minimum =
644           Math.max(totalColumnRequirements.minimum, rowTotalMin);
645         totalColumnRequirements.preferred =
646           Math.max(totalColumnRequirements.preferred, rowTotalPref);
647         totalColumnRequirements.maximum =
648           Math.max(totalColumnRequirements.maximum, rowTotalMax);
649       }
650 
651     // Now we know what we want and can fix up the actual relative
652     // column requirements.
653     int numCols = columnRequirements.length;
654     for (int i = 0; i < numCols; i++)
655       {
656         if (columnWidths[i] != null)
657           {
658             columnRequirements[i].minimum = (int)
659               columnWidths[i].getValue(totalColumnRequirements.minimum);
660             columnRequirements[i].preferred = (int)
661               columnWidths[i].getValue(totalColumnRequirements.preferred);
662             columnRequirements[i].maximum = (int)
663               columnWidths[i].getValue(totalColumnRequirements.maximum);
664           }
665       }
666   }
667 
668   /**
669    * Lays out the columns.
670    *
671    * @param targetSpan the target span into which the table is laid out
672    */
layoutColumns(int targetSpan)673   private void layoutColumns(int targetSpan)
674   {
675     // Set the spans to the preferred sizes. Determine the space
676     // that we have to adjust the sizes afterwards.
677     long sumPref = 0;
678     int n = columnRequirements.length;
679     for (int i = 0; i < n; i++)
680       {
681         SizeRequirements col = columnRequirements[i];
682         if (columnWidths[i] != null)
683           columnSpans[i] = (int) columnWidths[i].getValue(targetSpan);
684         else
685           columnSpans[i] = col.preferred;
686         sumPref += columnSpans[i];
687       }
688 
689     // Try to adjust the spans so that we fill the targetSpan.
690     // For adjustments we have to use the targetSpan minus the cumulated
691     // cell spacings.
692     long diff = targetSpan - (n + 1) * cellSpacing - sumPref;
693     float factor = 0.0F;
694     int[] diffs = null;
695     if (diff != 0)
696       {
697         long total = 0;
698         diffs = new int[n];
699         for (int i = 0; i < n; i++)
700           {
701             // Only adjust the width if we haven't set a column width here.
702             if (columnWidths[i] == null)
703               {
704                 SizeRequirements col = columnRequirements[i];
705                 int span;
706                 if (diff < 0)
707                   {
708                     span = col.minimum;
709                     diffs[i] = columnSpans[i] - span;
710                   }
711                 else
712                   {
713                     span = col.maximum;
714                     diffs[i] = span - columnSpans[i];
715                   }
716                 total += span;
717               }
718             else
719               total += columnSpans[i];
720           }
721 
722         float maxAdjust = Math.abs(total - sumPref);
723         factor = diff / maxAdjust;
724         factor = Math.min(factor, 1.0F);
725         factor = Math.max(factor, -1.0F);
726       }
727 
728     // Actually perform adjustments.
729     int totalOffs = cellSpacing;
730     for (int i = 0; i < n; i++)
731       {
732         columnOffsets[i] = totalOffs;
733         if (diff != 0)
734           {
735             float adjust = factor * diffs[i];
736             columnSpans[i] += Math.round(adjust);
737           }
738         // Avoid overflow here.
739         totalOffs = (int) Math.min((long) totalOffs + (long) columnSpans[i]
740                                    + (long) cellSpacing, Integer.MAX_VALUE);
741       }
742   }
743 
744   /**
745    * Updates the arrays that contain the row and column data in response
746    * to a change to the table structure.
747    *
748    * Package private to avoid accessor methods.
749    */
updateGrid()750   void updateGrid()
751   {
752     if (! gridValid)
753       {
754         AttributeSet atts = getAttributes();
755         StyleSheet ss = getStyleSheet();
756         float emBase = ss.getEMBase(atts);
757         float exBase = ss.getEXBase(atts);
758         int maxColumns = 0;
759         int numRows = getViewCount();
760         for (int r = 0; r < numRows; r++)
761           {
762             View rowView = getView(r);
763             int numCols = 0;
764             if (rowView instanceof RowView)
765               {
766                 int numCells = ((RowView) rowView).getViewCount();
767                 for (int i = 0; i < numCells; i++)
768                   {
769                     View v = rowView.getView(i);
770                     if (v instanceof CellView)
771                       numCols += ((CellView) v).colSpan;
772                   }
773               }
774             maxColumns = Math.max(numCols, maxColumns);
775           }
776         numColumns = maxColumns;
777         columnWidths = new Length[maxColumns];
778         int[] rowSpans = new int[maxColumns];
779         for (int r = 0; r < numRows; r++)
780           {
781             View view = getView(r);
782             if (view instanceof RowView)
783               {
784                 RowView rowView = (RowView) view;
785                 rowView.rowIndex = r;
786                 rowView.overlap = new boolean[maxColumns];
787                 int colIndex = 0;
788                 int colCount = rowView.getViewCount();
789                 for (int c = 0; c < maxColumns;)
790                   {
791                     if (rowSpans[c] > 0)
792                       {
793                         rowSpans[c]--;
794                         rowView.overlap[c] = true;
795                         c++;
796                       }
797                     else if (colIndex < colCount)
798                       {
799                         View v = rowView.getView(colIndex);
800                         colIndex++;
801                         if (v instanceof CellView)
802                           {
803                             CellView cv = (CellView) v;
804                             Object o =
805                               cv.getAttributes().getAttribute(CSS.Attribute.WIDTH);
806                             if (o != null && columnWidths[c] == null
807                                 && o instanceof Length)
808                               {
809                                 columnWidths[c]= (Length) o;
810                                 columnWidths[c].setFontBases(emBase, exBase);
811                               }
812                             int rs = cv.rowSpan - 1;
813                             for (int col = cv.colSpan - 1; col >= 0; col--)
814                               {
815                                 rowSpans[c] = rs;
816                                 c++;
817                               }
818                           }
819                       }
820                     else
821                       {
822                         c++;
823                       }
824                   }
825               }
826           }
827         columnRequirements = new SizeRequirements[maxColumns];
828         for (int i = 0; i < maxColumns; i++)
829           columnRequirements[i] = new SizeRequirements();
830         columnOffsets = new int[maxColumns];
831         columnSpans = new int[maxColumns];
832 
833         gridValid = true;
834       }
835   }
836 
837   /**
838    * Overridden to restrict the table width to the preferred size.
839    */
getMaximumSpan(int axis)840   public float getMaximumSpan(int axis)
841   {
842     float span;
843     if (axis == X_AXIS)
844       span = super.getPreferredSpan(axis);
845     else
846       span = super.getMaximumSpan(axis);
847     return span;
848   }
849 
850   /**
851    * Overridden to fetch the CSS attributes when view gets connected.
852    */
setParent(View parent)853   public void setParent(View parent)
854   {
855     super.setParent(parent);
856     if (parent != null)
857       setPropertiesFromAttributes();
858   }
859 
860   /**
861    * Fetches CSS and HTML layout attributes.
862    */
setPropertiesFromAttributes()863   protected void setPropertiesFromAttributes()
864   {
865     super.setPropertiesFromAttributes();
866 
867     // Fetch and parse cell spacing.
868     AttributeSet atts = getAttributes();
869     StyleSheet ss = getStyleSheet();
870     float emBase = ss.getEMBase(atts);
871     float exBase = ss.getEXBase(atts);
872     Object o = atts.getAttribute(CSS.Attribute.BORDER_SPACING);
873     if (o != null && o instanceof Length)
874       {
875         Length l = (Length) o;
876         l.setFontBases(emBase, exBase);
877         cellSpacing = (int) l.getValue();
878       }
879     o = atts.getAttribute(CSS.Attribute.WIDTH);
880     if (o != null && o instanceof Length)
881       {
882         width = (Length) o;
883         width.setFontBases(emBase, exBase);
884       }
885   }
886 
887   /**
888    * Overridden to adjust for cellSpacing.
889    */
calculateMajorAxisRequirements(int axis, SizeRequirements r)890   protected SizeRequirements calculateMajorAxisRequirements(int axis,
891                                                             SizeRequirements r)
892   {
893     r = super.calculateMajorAxisRequirements(axis, r);
894     int adjust = (getViewCount() + 1) * cellSpacing;
895     r.minimum += adjust;
896     r.preferred += adjust;
897     r.maximum += adjust;
898     return r;
899   }
900 
901   /**
902    * Overridden to adjust for cellSpacing.
903    */
layoutMajorAxis(int targetSpan, int axis, int[] offsets, int spans[])904   protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
905                                  int spans[])
906   {
907     // Mark all rows as invalid along their minor axis to force correct
908     // layout of multi-row cells.
909     int n = getViewCount();
910     for (int i = 0; i < n; i++)
911       {
912         View row = getView(i);
913         if (row instanceof RowView)
914           ((RowView) row).layoutChanged(axis);
915       }
916 
917     int adjust = (getViewCount() + 1) * cellSpacing;
918     super.layoutMajorAxis(targetSpan - adjust, axis, offsets, spans);
919     for (int i = 0; i < offsets.length; i++)
920       {
921         offsets[i] += (i + 1) * cellSpacing;
922       }
923   }
924 
925   /**
926    * Overridden to replace view factory with this one.
927    */
insertUpdate(DocumentEvent e, Shape a, ViewFactory f)928   public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
929   {
930     super.insertUpdate(e, a, this);
931   }
932 
933   /**
934    * Overridden to replace view factory with this one.
935    */
removeUpdate(DocumentEvent e, Shape a, ViewFactory f)936   public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f)
937   {
938     super.removeUpdate(e, a, this);
939   }
940 
941   /**
942    * Overridden to replace view factory with this one.
943    */
changedUpdate(DocumentEvent e, Shape a, ViewFactory f)944   public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
945   {
946     super.changedUpdate(e, a, this);
947   }
948 
replace(int offset, int len, View[] views)949   public void replace(int offset, int len, View[] views)
950   {
951     gridValid = false;
952     super.replace(offset, len, views);
953   }
954 
955   /**
956    * We can't use the super class's paint() method because it might cut
957    * off multi-row children. Instead we trigger painting for all rows
958    * and let the rows sort out what to paint and what not.
959    */
paint(Graphics g, Shape a)960   public void paint(Graphics g, Shape a)
961   {
962     Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
963     painter.paint(g, rect.x, rect.y, rect.width, rect.height, this);
964     int nRows = getViewCount();
965     Rectangle inside = getInsideAllocation(a);
966     for (int r = 0; r < nRows; r++)
967       {
968         tmpRect.setBounds(inside);
969         childAllocation(r, tmpRect);
970         paintChild(g, tmpRect, r);
971       }
972   }
973 
974 }
975