This implements multi-row cells and borders as shown in this example
screenshot:

http://kennke.org/~roman/table-with-rowspan-and-border.png

2006-12-05  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/text/html/BlockView.java
        (painter): Made package visible.
        * javax/swing/text/html/StyleSheet.java
        (translateBorder): New helper method.
        (translateHTMLToCSS): Add mappings for border attributes.
        * javax/swing/text/html/TableView.java
        Made class subclass of BlockView to get CSS goodness.
        (CellView.rowSpan): New field.
        (CellView.setPropertiesFromAttributes): Fetch rowspan.
        (RowView.overlap): New field.
        (RowView.rowIndex): New field.
        (RowView.layoutMajorAxis): Skip overlapping cells.
        (RowView.layoutMinorAxis): Layout cells that span more than 1
row.
        (numColumns): New field.
        (tmpRect): New field.
        (TableView): Initialize tmpRect.
        (calculateColumnRequirements): Adjusted and fixed for multirows.
        (getAlignment): Overridden to center tables.
        (paint): Overridden to fix clipping.
        (getStyleSheet): Made protected.
        (layoutMajorAxis): Invalidate rows.
        (setPropertiesFromAttributes): Made protected and call super.
        (updateGrid): Update the overlapping information for multirows.

/Roman

Index: javax/swing/text/html/BlockView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/BlockView.java,v
retrieving revision 1.8
diff -u -1 -5 -r1.8 BlockView.java
--- javax/swing/text/html/BlockView.java	3 Dec 2006 23:42:33 -0000	1.8
+++ javax/swing/text/html/BlockView.java	5 Dec 2006 21:51:46 -0000
@@ -28,31 +28,30 @@
 executable, regardless of the license terms of these independent
 modules, and to copy and distribute the resulting executable under
 terms of your choice, provided that you also meet, for each linked
 independent module, the terms and conditions of the license of that
 module.  An independent module is a module which is not derived from
 or based on this library.  If you modify this library, you may extend
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package javax.swing.text.html;
 
 import gnu.javax.swing.text.html.css.Length;
 
-import java.awt.Color;
 import java.awt.Graphics;
 import java.awt.Rectangle;
 import java.awt.Shape;
 import java.util.HashMap;
 
 import javax.swing.SizeRequirements;
 import javax.swing.event.DocumentEvent;
 import javax.swing.text.AttributeSet;
 import javax.swing.text.BoxView;
 import javax.swing.text.Element;
 import javax.swing.text.View;
 import javax.swing.text.ViewFactory;
 
 /**
  * @author Lillian Angel <[EMAIL PROTECTED]>
@@ -130,32 +129,34 @@
       type = typ;
       left = l;
       right = r;
       top = t;
       bottom = b;
     }
   }
 
   /**
    * The attributes for this view.
    */
   private AttributeSet attributes;
 
   /**
    * The box painter for this view.
+   *
+   * This is package private because the TableView needs access to it.
    */
-  private StyleSheet.BoxPainter painter;
+  StyleSheet.BoxPainter painter;
 
   /**
    * The width and height as specified in the stylesheet, null if not
    * specified. The first value is the X_AXIS, the second the Y_AXIS. You
    * can index this directly by the X_AXIS and Y_AXIS constants.
    */
   private Length[] cssSpans;
 
   /**
    * Stores additional CSS layout information.
    */
   private HashMap positionInfo;
 
   /**
    * Creates a new view that represents an html box. 
Index: javax/swing/text/html/StyleSheet.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/StyleSheet.java,v
retrieving revision 1.23
diff -u -1 -5 -r1.23 StyleSheet.java
--- javax/swing/text/html/StyleSheet.java	1 Dec 2006 23:20:28 -0000	1.23
+++ javax/swing/text/html/StyleSheet.java	5 Dec 2006 21:51:46 -0000
@@ -26,30 +26,31 @@
 As a special exception, the copyright holders of this library give you
 permission to link this library with independent modules to produce an
 executable, regardless of the license terms of these independent
 modules, and to copy and distribute the resulting executable under
 terms of your choice, provided that you also meet, for each linked
 independent module, the terms and conditions of the license of that
 module.  An independent module is a module which is not derived from
 or based on this library.  If you modify this library, you may extend
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package javax.swing.text.html;
 
+import gnu.javax.swing.text.html.css.BorderWidth;
 import gnu.javax.swing.text.html.css.CSSColor;
 import gnu.javax.swing.text.html.css.CSSParser;
 import gnu.javax.swing.text.html.css.CSSParserCallback;
 import gnu.javax.swing.text.html.css.FontSize;
 import gnu.javax.swing.text.html.css.FontStyle;
 import gnu.javax.swing.text.html.css.FontWeight;
 import gnu.javax.swing.text.html.css.Length;
 import gnu.javax.swing.text.html.css.Selector;
 
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.Graphics;
 import java.awt.Rectangle;
 import java.awt.Shape;
 import java.awt.font.FontRenderContext;
@@ -748,36 +749,69 @@
     if ((tag == HTML.Tag.TD || tag == HTML.Tag.TH)
         && htmlAttrSet instanceof Element)
       {
         Element el = (Element) htmlAttrSet;
         AttributeSet tableAttrs = el.getParentElement().getParentElement()
                                   .getAttributes();
         o = tableAttrs.getAttribute(HTML.Attribute.CELLPADDING);
         if (o != null)
           {
             Length l = new Length(o.toString());
             cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_BOTTOM, l);
             cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_LEFT, l);
             cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_RIGHT, l);
             cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_TOP, l);
           }
+        o = tableAttrs.getAttribute(HTML.Attribute.BORDER);
+        cssAttr = translateBorder(cssAttr, o);
       }
+
+    // Translate border attribute.
+    o = cssAttr.getAttribute(HTML.Attribute.BORDER);
+    cssAttr = translateBorder(cssAttr, o);
+
     // TODO: Add more mappings.
     return cssAttr;
   }
 
   /**
+   * Translates a HTML border attribute to a corresponding set of CSS
+   * attributes.
+   *
+   * @param cssAttr the original set of CSS attributes to add to 
+   * @param o the value of the border attribute
+   *
+   * @return the new set of CSS attributes
+   */
+  private AttributeSet translateBorder(AttributeSet cssAttr, Object o)
+  {
+    if (o != null)
+      {
+        BorderWidth l = new BorderWidth(o.toString());
+        if (l.getValue() > 0)
+          {
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_WIDTH, l);
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_STYLE,
+                                   "solid");
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_COLOR,
+                                   new CSSColor("black"));
+          }
+      }
+    return cssAttr;
+  }
+
+  /**
    * Adds an attribute to the given set and returns a new set. This is implemented
    * to convert StyleConstants attributes to CSS before forwarding them to the superclass.
    * The StyleConstants attribute do not have corresponding CSS entry, the attribute
    * is stored (but will likely not be used).
    * 
    * @param old - the old set
    * @param key - the non-null attribute key
    * @param value - the attribute value
    * @return the updated set 
    */
   public AttributeSet addAttribute(AttributeSet old, Object key,
                                    Object value)
   {
     // FIXME: Not implemented.
     return super.addAttribute(old, key, value);       
Index: javax/swing/text/html/TableView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/TableView.java,v
retrieving revision 1.12
diff -u -1 -5 -r1.12 TableView.java
--- javax/swing/text/html/TableView.java	1 Dec 2006 23:20:28 -0000	1.12
+++ javax/swing/text/html/TableView.java	5 Dec 2006 21:51:47 -0000
@@ -26,61 +26,73 @@
 As a special exception, the copyright holders of this library give you
 permission to link this library with independent modules to produce an
 executable, regardless of the license terms of these independent
 modules, and to copy and distribute the resulting executable under
 terms of your choice, provided that you also meet, for each linked
 independent module, the terms and conditions of the license of that
 module.  An independent module is a module which is not derived from
 or based on this library.  If you modify this library, you may extend
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package javax.swing.text.html;
 
+import java.awt.Graphics;
+import java.awt.Rectangle;
 import java.awt.Shape;
 
 import gnu.javax.swing.text.html.css.Length;
 
 import javax.swing.SizeRequirements;
 import javax.swing.event.DocumentEvent;
 import javax.swing.text.AttributeSet;
-import javax.swing.text.BoxView;
 import javax.swing.text.Element;
 import javax.swing.text.StyleConstants;
 import javax.swing.text.View;
 import javax.swing.text.ViewFactory;
 
 /**
  * A view implementation that renders HTML tables.
  *
  * This is basically a vertical BoxView that contains the rows of the table
  * and the rows are horizontal BoxViews that contain the actual columns.
  */
 class TableView
-  extends BoxView
+  extends BlockView
   implements ViewFactory
 {
 
   /**
    * Represents a single table row.
    */
   class RowView
     extends BlockView
   {
     /**
+     * Has true at column positions where an above row's cell overlaps into
+     * this row.
+     */
+    boolean[] overlap;
+
+    /**
+     * Stores the row index of this row.
+     */
+    int rowIndex;
+
+    /**
      * Creates a new RowView.
      *
      * @param el the element for the row view
      */
     RowView(Element el)
     {
       super(el, X_AXIS);
     }
 
     public void replace(int offset, int len, View[] views)
     {
       super.replace(offset, len, views);
       gridValid = false;
     }
 
@@ -125,68 +137,109 @@
                                                             SizeRequirements r)
     {
       if (r == null)
         r = new SizeRequirements();
       int adjust = (columnRequirements.length + 1) * cellSpacing;
       r.minimum = totalColumnRequirements.minimum + adjust;
       r.preferred = totalColumnRequirements.preferred + adjust;
       r.maximum = totalColumnRequirements.maximum + adjust;
       r.alignment = 0.0F;
       return r;
     }
 
     /**
      * Lays out the columns in this row.
      */
+    protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+                                   int spans[])
+    {
+      super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+
+      // Adjust columns that have rowSpan > 1.
+      int numCols = getViewCount();
+      for (int i = 0; i < numCols; i++)
+        {
+          View v = getView(i);
+          if (v instanceof CellView)
+            {
+              CellView cell = (CellView) v;
+              if (cell.rowSpan > 1)
+                {
+                  for (int r = 1; r < cell.rowSpan; r++)
+                    {
+                      spans[i] += TableView.this.getSpan(axis, rowIndex + r);
+                      spans[i] += cellSpacing;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Lays out the columns in this row.
+     */
     protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
                                    int spans[])
     {
       updateGrid();
       int numCols = offsets.length;
       int realColumn = 0;
-      for (int i = 0; i < numCols; i++)
+      for (int i = 0; i < numColumns;)
         {
-          View v = getView(i);
-          if (v instanceof CellView)
+          if (! overlap[i])
             {
-              CellView cv = (CellView) v;
-              offsets[i] = columnOffsets[realColumn];
-              spans[i] = 0;
-              for (int j = 0; j < cv.colSpan; j++, realColumn++)
+              View v = getView(realColumn);
+              if (v instanceof CellView)
                 {
-                  spans[i] += columnSpans[realColumn];
-                  if (j < cv.colSpan - 1)
-                    spans[i] += cellSpacing;
+                  CellView cv = (CellView) v;
+                  offsets[realColumn] = columnOffsets[i];
+                  spans[realColumn] = 0;
+                  for (int j = 0; j < cv.colSpan; j++, i++)
+                    {
+                      spans[realColumn] += columnSpans[i];
+                      if (j < cv.colSpan - 1)
+                        spans[realColumn] += cellSpacing;
+                    }
                 }
+              realColumn++;
+            }
+          else
+            {
+              i++;
             }
         }
     }
   }
 
   /**
    * A view that renders HTML table cells (TD and TH tags).
    */
   class CellView
     extends BlockView
   {
 
     /**
      * The number of columns that this view spans.
      */
     int colSpan;
 
     /**
+     * The number of rows that this cell spans.
+     */
+    int rowSpan;
+
+    /**
      * Creates a new CellView for the specified element.
      *
      * @param el the element for which to create the colspan
      */
     CellView(Element el)
     {
       super(el, Y_AXIS);
     }
 
     protected SizeRequirements calculateMajorAxisRequirements(int axis,
                                                             SizeRequirements r)
     {
       r = super.calculateMajorAxisRequirements(axis, r);
       r.maximum = Integer.MAX_VALUE;
       return r;
@@ -201,30 +254,44 @@
       colSpan = 1;
       AttributeSet atts = getAttributes();
       Object o = atts.getAttribute(HTML.Attribute.COLSPAN);
       if (o != null)
         {
           try
             {
               colSpan = Integer.parseInt(o.toString());
             }
           catch (NumberFormatException ex)
             {
               // Couldn't parse the colspan, assume 1.
               colSpan = 1;
             }
         }
+      rowSpan = 1;
+      o = atts.getAttribute(HTML.Attribute.ROWSPAN);
+      if (o != null)
+        {
+          try
+            {
+              rowSpan = Integer.parseInt(o.toString());
+            }
+          catch (NumberFormatException ex)
+            {
+              // Couldn't parse the colspan, assume 1.
+              rowSpan = 1;
+            }
+        }
     }
   }
 
 
   /**
    * The attributes of this view.
    */
   private AttributeSet attributes;
 
   /**
    * The column requirements.
    *
    * Package private to avoid accessor methods.
    */
   SizeRequirements[] columnRequirements;
@@ -244,55 +311,66 @@
   int[] columnOffsets;
 
   /**
    * The column layout, spans.
    *
    * Package private to avoid accessor methods.
    */
   int[] columnSpans;
 
   /**
    * The widths of the columns that have been explicitly specified.
    */
   Length[] columnWidths;
 
   /**
+   * The total number of columns.
+   */
+  int numColumns;
+
+  /**
    * The table width.
    */
   private Length width;
 
   /**
    * Indicates if the grid setup is ok.
    */
   boolean gridValid;
 
   /**
    * Additional space that is added _between_ table cells.
    *
    * This is package private to avoid accessor methods.
    */
   int cellSpacing;
 
   /**
+   * A cached Rectangle object for reuse in paint().
+   */
+  private Rectangle tmpRect;
+
+  /**
    * Creates a new HTML table view for the specified element.
    *
    * @param el the element for the table view
    */
   public TableView(Element el)
   {
     super(el, Y_AXIS);
     totalColumnRequirements = new SizeRequirements();
+    tmpRect = new Rectangle();
   }
 
   /**
    * Implementation of the ViewFactory interface for creating the
    * child views correctly.
    */
   public View create(Element elem)
   {
     View view = null;
     AttributeSet atts = elem.getAttributes();
     Object name = atts.getAttribute(StyleConstants.NameAttribute);
     if (name instanceof HTML.Tag)
       {
         HTML.Tag tag = (HTML.Tag) name;
         if (tag == HTML.Tag.TR)
@@ -331,31 +409,31 @@
    * Returns the attributes of this view. This is overridden to provide
    * the attributes merged with the CSS stuff.
    */
   public AttributeSet getAttributes()
   {
     if (attributes == null)
       attributes = getStyleSheet().getViewAttributes(this);
     return attributes;
   }
 
   /**
    * Returns the stylesheet associated with this view.
    *
    * @return the stylesheet associated with this view
    */
-  private StyleSheet getStyleSheet()
+  protected StyleSheet getStyleSheet()
   {
     HTMLDocument doc = (HTMLDocument) getDocument();
     return doc.getStyleSheet();
   }
 
   /**
    * Overridden to calculate the size requirements according to the
    * columns distribution.
    */
   protected SizeRequirements calculateMinorAxisRequirements(int axis,
                                                             SizeRequirements r)
   {
     updateGrid();
     calculateColumnRequirements();
 
@@ -393,31 +471,32 @@
 
     // Make it not resize in the horizontal direction.
     r.maximum = r.preferred;
     return r;
   }
 
   /**
    * Overridden to perform the table layout before calling the super
    * implementation.
    */
   protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, 
                                  int[] spans)
   {
     updateGrid();
 
-    // Mark all rows as invalid.
+    // Mark all rows as invalid along their minor axis to force correct
+    // layout of multi-row cells.
     int n = getViewCount();
     for (int i = 0; i < n; i++)
       {
         View row = getView(i);
         if (row instanceof RowView)
           ((RowView) row).layoutChanged(axis);
       }
 
     layoutColumns(targetSpan);
     super.layoutMinorAxis(targetSpan, axis, offsets, spans);
   }
 
   /**
    * Calculates the size requirements for the columns.
    */
@@ -434,128 +513,129 @@
       {
         View rowView = getView(r);
         int numCols;
         if (rowView instanceof RowView)
           numCols = ((RowView) rowView).getViewCount();
         else
           numCols = 0;
 
         // We collect the normal (non-relative) column requirements in the
         // total variable and the relative requirements in the relTotal
         // variable. In the end we create the maximum of both to get the
         // real requirements.
         SizeRequirements total = new SizeRequirements();
         SizeRequirements relTotal = new SizeRequirements();
         float totalPercent = 0.F;
-        for (int c = 0; c < numCols; )
+        int realCol = 0;
+        for (int c = 0; c < numCols; c++)
           {
             View v = rowView.getView(c);
             if (v instanceof CellView)
               {
                 CellView cellView = (CellView) v;
                 int colSpan = cellView.colSpan;
                 if (colSpan > 1)
                   {
                     int cellMin = (int) cellView.getMinimumSpan(X_AXIS);
                     int cellPref = (int) cellView.getPreferredSpan(X_AXIS);
                     int cellMax = (int) cellView.getMaximumSpan(X_AXIS);
                     int currentMin = 0;
                     int currentPref = 0;
                     long currentMax = 0;
                     for (int i = 0; i < colSpan; i++)
                       {
-                        SizeRequirements req = columnRequirements[c + i];
+                        SizeRequirements req = columnRequirements[realCol];
                         currentMin += req.minimum;
                         currentPref += req.preferred;
                         currentMax += req.maximum;
                       }
                     int deltaMin = cellMin - currentMin;
                     int deltaPref = cellPref - currentPref;
                     int deltaMax = (int) (cellMax - currentMax);
                     // Distribute delta.
                     for (int i = 0; i < colSpan; i++)
                       {
-                        SizeRequirements req = columnRequirements[c + i];
+                        SizeRequirements req = columnRequirements[realCol];
                         if (deltaMin > 0)
                           req.minimum += deltaMin / colSpan;
                         if (deltaPref > 0)
                           req.preferred += deltaPref / colSpan;
                         if (deltaMax > 0)
                           req.maximum += deltaMax / colSpan;
-                        if (columnWidths[c + i] == null
-                            || ! columnWidths[c + i].isPercentage())
+                        if (columnWidths[realCol] == null
+                            || ! columnWidths[realCol].isPercentage())
                           {
                             total.minimum += req.minimum;
                             total.preferred += req.preferred;
                             total.maximum += req.maximum;
                           }
                         else
                           {
                             relTotal.minimum =
                               Math.max(relTotal.minimum,
                                      (int) (req.minimum
-                                            * columnWidths[c + i].getValue()));
+                                          * columnWidths[realCol].getValue()));
                             relTotal.preferred =
                               Math.max(relTotal.preferred,
                                      (int) (req.preferred
-                                            * columnWidths[c + i].getValue()));
+                                          * columnWidths[realCol].getValue()));
                             relTotal.maximum =
                               Math.max(relTotal.maximum,
                                      (int) (req.maximum
-                                            * columnWidths[c + i].getValue()));
-                            totalPercent += columnWidths[c + i].getValue();
+                                          * columnWidths[realCol].getValue()));
+                            totalPercent += columnWidths[realCol].getValue();
                           }
                       }
+                    realCol += colSpan;
                   }
                 else
                   {
                     // Shortcut for colSpan == 1.
-                    SizeRequirements req = columnRequirements[c];
+                    SizeRequirements req = columnRequirements[realCol];
                     req.minimum = Math.max(req.minimum,
-                                           (int) cellView.getMinimumSpan(X_AXIS));
+                                        (int) cellView.getMinimumSpan(X_AXIS));
                     req.preferred = Math.max(req.preferred,
-                                             (int) cellView.getPreferredSpan(X_AXIS));
+                                      (int) cellView.getPreferredSpan(X_AXIS));
                     req.maximum = Math.max(req.maximum,
-                                           (int) cellView.getMaximumSpan(X_AXIS));
-                    if (columnWidths[c] == null
-                        || ! columnWidths[c].isPercentage())
+                                        (int) cellView.getMaximumSpan(X_AXIS));
+                    if (columnWidths[realCol] == null
+                        || ! columnWidths[realCol].isPercentage())
                       {
-                        total.minimum += columnRequirements[c].minimum;
-                        total.preferred += columnRequirements[c].preferred;
-                        total.maximum += columnRequirements[c].maximum;
+                        total.minimum += columnRequirements[realCol].minimum;
+                        total.preferred +=
+                          columnRequirements[realCol].preferred;
+                        total.maximum += columnRequirements[realCol].maximum;
                       }
                     else
                       {
                         relTotal.minimum =
                           Math.max(relTotal.minimum,
                                  (int) (req.minimum
                                         / columnWidths[c].getValue()));
                         relTotal.preferred =
                           Math.max(relTotal.preferred,
                                  (int) (req.preferred
                                         / columnWidths[c].getValue()));
                         relTotal.maximum =
                           Math.max(relTotal.maximum,
                                  (int) (req.maximum
                                         / columnWidths[c].getValue()));
                         totalPercent += columnWidths[c].getValue();
                       }
+                    realCol += 1;
                   }
-                c += colSpan;
               }
-            else
-              c++;
           }
 
         // Update the total requirements as follows:
         // 1. Multiply the absolute requirements with 1 - totalPercent. This
         //    gives the total requirements based on the wishes of the absolute
         //    cells.
         // 2. Take the maximum of this value and the total relative
         //    requirements. Now we should have enough space for whatever cell
         //    in this column.
         // 3. Take the maximum of this value and the previous maximum value.
         total.minimum *= 1.F / (1.F - totalPercent);
         total.preferred *= 1.F / (1.F - totalPercent);
         total.maximum *= 1.F / (1.F - totalPercent);
 
         int rowTotalMin = Math.max(total.minimum, relTotal.minimum);
@@ -669,62 +749,86 @@
    * Package private to avoid accessor methods.
    */
   void updateGrid()
   {
     if (! gridValid)
       {
         AttributeSet atts = getAttributes();
         StyleSheet ss = getStyleSheet();
         float emBase = ss.getEMBase(atts);
         float exBase = ss.getEXBase(atts);
         int maxColumns = 0;
         int numRows = getViewCount();
         for (int r = 0; r < numRows; r++)
           {
             View rowView = getView(r);
-            int numCols;
+            int numCols = 0;
             if (rowView instanceof RowView)
-              numCols = ((RowView) rowView).getViewCount();
-            else
-              numCols = 0;
+              {
+                int numCells = ((RowView) rowView).getViewCount();
+                for (int i = 0; i < numCells; i++)
+                  {
+                    View v = rowView.getView(i);
+                    if (v instanceof CellView)
+                      numCols += ((CellView) v).colSpan;
+                  }
+              }
             maxColumns = Math.max(numCols, maxColumns);
           }
+        numColumns = maxColumns;
         columnWidths = new Length[maxColumns];
+        int[] rowSpans = new int[maxColumns];
         for (int r = 0; r < numRows; r++)
           {
-            View rowView = getView(r);
-            int numCols;
-            if (rowView instanceof RowView)
-              numCols = ((RowView) rowView).getViewCount();
-            else
-              numCols = 0;
-            int colIndex = 0;
-            for (int c = 0; c < numCols; c++)
+            View view = getView(r);
+            if (view instanceof RowView)
               {
-                View v = rowView.getView(c);
-                if (v instanceof CellView)
+                RowView rowView = (RowView) view;
+                rowView.rowIndex = r;
+                rowView.overlap = new boolean[maxColumns];
+                int colIndex = 0;
+                for (int c = 0; c < maxColumns;)
                   {
-                    CellView cv = (CellView) v;
-                    Object o =
-                      cv.getAttributes().getAttribute(CSS.Attribute.WIDTH);
-                    if (o != null && columnWidths[colIndex] == null
-                        && o instanceof Length)
+                    if (rowSpans[c] > 0)
+                      {
+                        rowSpans[c]--;
+                        rowView.overlap[c] = true;
+                        c++;
+                      }
+                    else
                       {
-                        columnWidths[colIndex]= (Length) o;
-                        columnWidths[colIndex].setFontBases(emBase, exBase);
+                        View v = rowView.getView(colIndex);
+                        colIndex++;
+                        if (v instanceof CellView)
+                          {
+                            CellView cv = (CellView) v;
+                            Object o =
+                              cv.getAttributes().getAttribute(CSS.Attribute.WIDTH);
+                            if (o != null && columnWidths[c] == null
+                                && o instanceof Length)
+                              {
+                                columnWidths[c]= (Length) o;
+                                columnWidths[c].setFontBases(emBase, exBase);
+                              }
+                            int rs = cv.rowSpan - 1;
+                            for (int col = cv.colSpan - 1; col >= 0; col--)
+                              {
+                                rowSpans[c] = rs;
+                                c++;
+                              }
+                          }
                       }
-                    colIndex += cv.colSpan;
                   }
               }
           }
         columnRequirements = new SizeRequirements[maxColumns];
         for (int i = 0; i < maxColumns; i++)
           columnRequirements[i] = new SizeRequirements();
         columnOffsets = new int[maxColumns];
         columnSpans = new int[maxColumns];
 
         gridValid = true;
       }
   }
 
   /**
    * Overridden to restrict the table width to the preferred size.
@@ -740,32 +844,34 @@
   }
 
   /**
    * Overridden to fetch the CSS attributes when view gets connected.
    */
   public void setParent(View parent)
   {
     super.setParent(parent);
     if (parent != null)
       setPropertiesFromAttributes();
   }
 
   /**
    * Fetches CSS and HTML layout attributes.
    */
-  private void setPropertiesFromAttributes()
+  protected void setPropertiesFromAttributes()
   {
+    super.setPropertiesFromAttributes();
+
     // Fetch and parse cell spacing.
     AttributeSet atts = getAttributes();
     StyleSheet ss = getStyleSheet();
     float emBase = ss.getEMBase(atts);
     float exBase = ss.getEXBase(atts);
     Object o = atts.getAttribute(CSS.Attribute.BORDER_SPACING);
     if (o != null && o instanceof Length)
       {
         Length l = (Length) o;
         l.setFontBases(emBase, exBase);
         cellSpacing = (int) l.getValue();
       }
     o = atts.getAttribute(CSS.Attribute.WIDTH);
     if (o != null && o instanceof Length)
       {
@@ -782,30 +888,40 @@
   {
     r = super.calculateMajorAxisRequirements(axis, r);
     int adjust = (getViewCount() + 1) * cellSpacing;
     r.minimum += adjust;
     r.preferred += adjust;
     r.maximum += adjust;
     return r;
   }
 
   /**
    * Overridden to adjust for cellSpacing.
    */
   protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
                                  int spans[])
   {
+    // Mark all rows as invalid along their minor axis to force correct
+    // layout of multi-row cells.
+    int n = getViewCount();
+    for (int i = 0; i < n; i++)
+      {
+        View row = getView(i);
+        if (row instanceof RowView)
+          ((RowView) row).layoutChanged(axis);
+      }
+
     int adjust = (getViewCount() + 1) * cellSpacing;
     super.layoutMajorAxis(targetSpan - adjust, axis, offsets, spans);
     for (int i = 0; i < offsets.length; i++)
       {
         offsets[i] += (i + 1) * cellSpacing;
       }
   }
 
   /**
    * Overridden to replace view factory with this one.
    */
   public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
   {
     super.insertUpdate(e, a, this);
   }
@@ -819,16 +935,46 @@
   }
 
   /**
    * Overridden to replace view factory with this one.
    */
   public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
   {
     super.changedUpdate(e, a, this);
   }
 
   public void replace(int offset, int len, View[] views)
   {
     super.replace(offset, len, views);
     gridValid = false;
   }
+
+  /**
+   * We can't use the super class's paint() method because it might cut
+   * off multi-row children. Instead we trigger painting for all rows
+   * and let the rows sort out what to paint and what not.
+   */
+  public void paint(Graphics g, Shape a)
+  {
+    Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+    painter.paint(g, rect.x, rect.y, rect.width, rect.height, this);
+    int nRows = getViewCount();
+    Rectangle inside = getInsideAllocation(a);
+    for (int r = 0; r < nRows; r++)
+      {
+        tmpRect.setBounds(inside);
+        childAllocation(r, tmpRect);
+        paintChild(g, tmpRect, r);
+      }
+  }
+
+  /**
+   * Overridden to place a table centered in the horizontal direction.
+   */
+  public float getAlignment(int axis)
+  {
+    if (axis == X_AXIS)
+      return 0.5F;
+    else
+      return super.getAlignment(axis);
+  }
 }

Reply via email to