This implements CSS absolute/relative positioning. This is used for
example on Planet Classpath to place the sidebox at its place. It now
renders correctly like this:

http://kennke.org/~roman/planet6.png

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

        * javax/swing/text/BoxView.java
        (paint): Replaced painting algorithm with more simple and more
        reliable painting of the box.
        * javax/swing/text/html/BlockView.java
        (PositionInfo): New inner class. Stores additional CSS
positioning
        information.
        (positionInfo): New field.
        (BlockView): Initialize positionInfo field.
        (fetchLayoutInfo): New helper method. Fetches additional
        CSS positioning information.
        (layoutMajorAxis): Perform additional CSS layout.
        (layoutMinorAxis): Perform additional CSS layout.
        (positionView): New helper method.
        (replace): Overridden to fetch additional layout information.
        * javax/swing/text/html/CSS.java
        (Attribute.POSITION): New field.
        (Attribute.LEFT): New field.
        (Attribute.RIGHT): New field.
        (Attribute.TOP): New field.
        (Attribute.BOTTOM): New field.
        (getValue): Create Length for left, right, top and bottom
        attributes.

/Roman
Index: javax/swing/text/BoxView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/BoxView.java,v
retrieving revision 1.27
diff -u -1 -5 -r1.27 BoxView.java
--- javax/swing/text/BoxView.java	20 Nov 2006 11:18:04 -0000	1.27
+++ javax/swing/text/BoxView.java	3 Dec 2006 23:41:43 -0000
@@ -273,113 +273,38 @@
   /**
    * Renders the <code>Element</code> that is associated with this
    * <code>View</code>.
    *
    * @param g the <code>Graphics</code> context to render to
    * @param a the allocated region for the <code>Element</code>
    */
   public void paint(Graphics g, Shape a)
   {
     // Try to avoid allocation if possible (almost all cases).
     Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
 
     // This returns a cached instance.
     alloc = getInsideAllocation(alloc);
 
-    // The following algorithm optimizes painting of a BoxView by taking
-    // advantage of the layout order of the box children.
-    //
-    // 1. It first searches a child that which's allocation is inside the clip.
-    //    This is accomplished by an efficient binary search. This assumes
-    //    that the children of the BoxView are laid out in the same order
-    //    as their index within the view. This is true for the BoxView, but
-    //    might not be the case for all subclasses.
-    // 2. Starting from the found view, it paints the children in both
-    //    directions until the first view is hit that is outside the clip.
-
-    // First we search a child view that is inside the clip.
-
-    // Fetch the clip rect and calculate the center point of it.
-    clipRect = g.getClipBounds(clipRect);
-    int cX = clipRect.x + clipRect.width / 2;
-    int cY = clipRect.y + clipRect.height / 2;
-
-    int viewCount = getViewCount();
-    int up = viewCount;
-    int low = 0;
-    int mid = (up - low) / 2;
-    View start = getView(mid);
-
-    int newMid;
-    // Use another cached instance here to avoid allocations during
-    // painting.
-    tmpRect.setBounds(alloc);
-    // This modifies tmpRect.
-    childAllocation(mid, tmpRect);
-    while (! clipRect.intersects(tmpRect))
-      {
-        if (isBefore(cX, cY, tmpRect))
-          {
-            up = mid;
-            newMid = (up - low) / 2 + low;
-            mid = (newMid == mid) ? newMid - 1 : newMid;
-          }
-        else if (isAfter(cX, cY, tmpRect))
-          {
-            low = mid;
-            newMid = (up - low) / 2 + low;
-            mid = (newMid == mid) ? newMid + 1 : newMid;
-          }
-        else
-          break;
-        if (mid >= 0 && mid < viewCount)
-          {
-            start = getView(mid);
-            tmpRect.setBounds(alloc);
-            childAllocation(mid, tmpRect);
-          }
-        else
-          break;
-      }
-
-    if (mid >= 0 && mid < viewCount)
+    int count = getViewCount();
+    for (int i = 0; i < count; i++)
       {
-        // Ok, we found one view that is inside the clip rect. Now paint the
-        // children before it that are inside the clip.
-        boolean inClip = true;
-        for (int i = mid - 1; i >= 0 && inClip; i--)
-          {
-            start = getView(i);
-            tmpRect.setBounds(alloc);
-            childAllocation(i, tmpRect);
-            inClip = clipRect.intersects(tmpRect);
-            if (inClip)
-              paintChild(g, tmpRect, i);
-          }
-
-        // Now paint the found view and all views after it that lie inside the
-        // clip.
-        inClip = true;
-        for (int i = mid; i < viewCount && inClip; i++)
-          {
-            start = getView(i);
-            tmpRect.setBounds(alloc);
-            childAllocation(i, tmpRect);
-            inClip = clipRect.intersects(tmpRect);
-            if (inClip)
-              paintChild(g, tmpRect, i);
-          }
+        View child = getView(i);
+        tmpRect.setBounds(alloc);
+        childAllocation(i, tmpRect);
+        if (g.hitClip(tmpRect.x, tmpRect.y, tmpRect.width, tmpRect.height))
+          paintChild(g, tmpRect, i);
       }
   }
 
   /**
    * Returns the preferred span of the content managed by this
    * <code>View</code> along the specified <code>axis</code>.
    *
    * @param axis the axis
    *
    * @return the preferred span of this <code>View</code>.
    */
   public float getPreferredSpan(int axis)
   {
     updateRequirements(axis);
     // Add margin.
Index: javax/swing/text/html/BlockView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/BlockView.java,v
retrieving revision 1.7
diff -u -1 -5 -r1.7 BlockView.java
--- javax/swing/text/html/BlockView.java	1 Dec 2006 14:43:43 -0000	1.7
+++ javax/swing/text/html/BlockView.java	3 Dec 2006 23:41:43 -0000
@@ -32,73 +32,155 @@
 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]>
  */
 public class BlockView extends BoxView
 {
 
   /**
+   * Stores information about child positioning according to the
+   * CSS attributes position, left, right, top and bottom.
+   */
+  private static class PositionInfo
+  {
+    // TODO: Use enums when available.
+
+    /**
+     * Static positioning. This is the default and is thus rarely really
+     * used.
+     */
+    static final int STATIC = 0;
+
+    /**
+     * Relative positioning. The box is teaked relative to its static
+     * computed bounds.
+     */
+    static final int RELATIVE = 1;
+
+    /**
+     * Absolute positioning. The box is moved relative to the parent's box.
+     */
+    static final int ABSOLUTE = 2;
+
+    /**
+     * Like ABSOLUTE, with some fixation against the viewport (not yet
+     * implemented).
+     */
+    static final int FIXED = 3;
+
+    /**
+     * The type according to the constants of this class.
+     */
+    int type;
+
+    /**
+     * The left constraint, null if not set.
+     */
+    Length left;
+
+    /**
+     * The right constraint, null if not set.
+     */
+    Length right;
+
+    /**
+     * The top constraint, null if not set.
+     */
+    Length top;
+
+    /**
+     * The bottom constraint, null if not set.
+     */
+    Length bottom;
+
+    /**
+     * Creates a new PositionInfo object.
+     *
+     * @param typ the type to set
+     * @param l the left constraint
+     * @param r the right constraint
+     * @param t the top constraint
+     * @param b the bottom constraint
+     */
+    PositionInfo(int typ, Length l, Length r, Length t, Length b)
+    {
+      type = typ;
+      left = l;
+      right = r;
+      top = t;
+      bottom = b;
+    }
+  }
+
+  /**
    * The attributes for this view.
    */
   private AttributeSet attributes;
 
   /**
    * The box painter for this view.
    */
   private 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. 
    * This can be used for a number of elements.
    * 
    * @param elem - the element to create a view for
    * @param axis - either View.X_AXIS or View.Y_AXIS
    */
   public BlockView(Element elem, int axis)
   {
     super(elem, axis);
     cssSpans = new Length[2];
+    positionInfo = new HashMap();
   }
 
   /**
    * Creates the parent view for this. It is called before
    * any other methods, if the parent view is working properly.
    * Implemented to forward to the superclass and call
    * setPropertiesFromAttributes to set the paragraph 
    * properties.
    * 
    * @param parent - the new parent, or null if the view
    * is being removed from a parent it was added to. 
    */
   public void setParent(View parent)
   {
     super.setParent(parent);
@@ -271,53 +353,149 @@
         else
           max = (int) view.getMaximumSpan(axis);
 
         if (max < targetSpan)
           {
             // Align child.
             float align = view.getAlignment(axis);
             offsets[i] = (int) ((targetSpan - max) * align);
             spans[i] = max;
           }
         else
           {
             offsets[i] = 0;
             spans[i] = Math.max(min, targetSpan);
           }
+
+        // Adjust according to CSS position info.
+        positionView(targetSpan, axis, i, offsets, spans);
+      }
+  }
+
+  /**
+   * Overridden to perform additional CSS layout (absolute/relative
+   * positioning).
+   */
+  protected void layoutMajorAxis(int targetSpan, int axis,
+                                 int[] offsets, int[] spans)
+  {
+    super.layoutMajorAxis(targetSpan, axis, offsets, spans);
+
+    // Adjust according to CSS position info.
+    int viewCount = getViewCount();
+    for (int i = 0; i < viewCount; i++)
+      {
+        positionView(targetSpan, axis, i, offsets, spans);
+      }
+  }
+
+  /**
+   * Positions a view according to any additional CSS constraints.
+   *
+   * @param targetSpan the target span
+   * @param axis the axis
+   * @param i the index of the view
+   * @param offsets the offsets get placed here
+   * @param spans the spans get placed here
+   */
+  private void positionView(int targetSpan, int axis, int i, int[] offsets,
+                            int[] spans)
+  {
+    View view = getView(i);
+    PositionInfo pos = (PositionInfo) positionInfo.get(view);
+    if (pos != null)
+      {
+        int p0 = -1;
+        int p1 = -1;
+        if (axis == X_AXIS)
+          {
+            Length l = pos.left;
+            if (l != null)
+              p0 = (int) l.getValue(targetSpan);
+            l = pos.right;
+            if (l != null)
+              p1 = (int) l.getValue(targetSpan);
+          }
+        else
+          {
+            Length l = pos.top;
+            if (l != null)
+              p0 = (int) l.getValue(targetSpan);
+            l = pos.bottom;
+            if (l != null)
+              p1 = (int) l.getValue(targetSpan);
+          }
+        if (pos.type == PositionInfo.ABSOLUTE
+            || pos.type == PositionInfo.FIXED)
+          {
+            if (p0 != -1)
+              {
+                offsets[i] = p0;
+                if (p1 != -1)
+                  {
+                    // Overrides computed width. (Possibly overconstrained
+                    // when the width attribute was set too.)
+                    spans[i] = targetSpan - p1 - offsets[i];
+                  }
+              }
+            else if (p1 != -1)
+              {
+                // Preserve any computed width.
+                offsets[i] = targetSpan - p1 - spans[i];
+              }
+          }
+        else if (pos.type == PositionInfo.RELATIVE)
+          {
+            if (p0 != -1)
+              {
+                offsets[i] += p0;
+                if (p1 != -1)
+                  {
+                    // Overrides computed width. (Possibly overconstrained
+                    // when the width attribute was set too.)
+                    spans[i] = spans[i] - p0 - p1 - offsets[i];
+                  }
+              }
+            else if (p1 != -1)
+              {
+                // Preserve any computed width.
+                offsets[i] -= p1;
+              }
+          }
       }
   }
 
   /**
    * Paints using the given graphics configuration and shape.
    * This delegates to the css box painter to paint the
    * border and background prior to the interior.
    * 
    * @param g - Graphics configuration
    * @param a - the Shape to render into.
    */
   public void paint(Graphics g, Shape a)
   {
     Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
 
     // Debug output. Shows blocks in green rectangles.
     // g.setColor(Color.GREEN);
     // g.drawRect(rect.x, rect.y, rect.width, rect.height);
 
     painter.paint(g, rect.x, rect.y, rect.width, rect.height, this);
     super.paint(g, a);
   }
-  
+
   /**
    * Fetches the attributes to use when painting.
    * 
    * @return the attributes of this model.
    */
   public AttributeSet getAttributes()
   {
     if (attributes == null)
       attributes = getStyleSheet().getViewAttributes(this);
     return attributes;
   }
   
   /**
    * Gets the resize weight.
    * 
@@ -457,16 +635,86 @@
     cssSpans[Y_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.HEIGHT);
     if (cssSpans[Y_AXIS] != null)
       cssSpans[Y_AXIS].setFontBases(emBase, exBase);
   }
 
   /**
    * Gets the default style sheet.
    * 
    * @return the style sheet
    */
   protected StyleSheet getStyleSheet()
   {
     HTMLDocument doc = (HTMLDocument) getDocument();
     return doc.getStyleSheet();
   }
+
+  /**
+   * Overridden to fetch additional CSS layout information.
+   */
+  public void replace(int offset, int length, View[] views)
+  {
+    // First remove unneeded stuff.
+    for (int i = 0; i < length; i++)
+      {
+        View child = getView(i + offset);
+        positionInfo.remove(child);
+      }
+
+    // Call super to actually replace the views.
+    super.replace(offset, length, views);
+
+    // Now fetch the position infos for the new views.
+    for (int i = 0; i < views.length; i++)
+      {
+        fetchLayoutInfo(views[i]);
+      }
+  }
+
+  /**
+   * Fetches and stores the layout info for the specified view.
+   *
+   * @param view the view for which the layout info is stored
+   */
+  private void fetchLayoutInfo(View view)
+  {
+    AttributeSet atts = view.getAttributes();
+    Object o = atts.getAttribute(CSS.Attribute.POSITION);
+    if (o != null && o instanceof String && ! o.equals("static"))
+      {
+        int type;
+        if (o.equals("relative"))
+          type = PositionInfo.RELATIVE;
+        else if (o.equals("absolute"))
+          type = PositionInfo.ABSOLUTE;
+        else if (o.equals("fixed"))
+          type = PositionInfo.FIXED;
+        else
+          type = PositionInfo.STATIC;
+
+        if (type != PositionInfo.STATIC)
+          {
+            StyleSheet ss = getStyleSheet();
+            float emBase = ss.getEMBase(atts);
+            float exBase = ss.getEXBase(atts);
+            Length left = (Length) atts.getAttribute(CSS.Attribute.LEFT);
+            if (left != null)
+              left.setFontBases(emBase, exBase);
+            Length right = (Length) atts.getAttribute(CSS.Attribute.RIGHT);
+            if (right != null)
+              right.setFontBases(emBase, exBase);
+            Length top = (Length) atts.getAttribute(CSS.Attribute.TOP);
+            if (top != null)
+              top.setFontBases(emBase, exBase);
+            Length bottom = (Length) atts.getAttribute(CSS.Attribute.BOTTOM);
+            if (bottom != null)
+              bottom.setFontBases(emBase, exBase);
+            if (left != null || right != null || top != null || bottom != null)
+              {
+                PositionInfo pos = new PositionInfo(type, left, right, top,
+                                                    bottom);
+                positionInfo.put(view, pos);
+              }
+          }
+      }
+  }
 }
Index: javax/swing/text/html/CSS.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/html/CSS.java,v
retrieving revision 1.12
diff -u -1 -5 -r1.12 CSS.java
--- javax/swing/text/html/CSS.java	1 Dec 2006 20:34:07 -0000	1.12
+++ javax/swing/text/html/CSS.java	3 Dec 2006 23:41:43 -0000
@@ -406,31 +406,41 @@
       new Attribute("border-bottom-style", false, null);
     static final Attribute BORDER_LEFT_STYLE =
       new Attribute("border-left-style", false, null);
     static final Attribute BORDER_RIGHT_STYLE =
       new Attribute("border-right-style", false, null);
     static final Attribute BORDER_TOP_COLOR =
       new Attribute("border-top-color", false, null);
     static final Attribute BORDER_BOTTOM_COLOR =
       new Attribute("border-bottom-color", false, null);
     static final Attribute BORDER_LEFT_COLOR =
       new Attribute("border-left-color", false, null);
     static final Attribute BORDER_RIGHT_COLOR =
       new Attribute("border-right-color", false, null);
     static final Attribute BORDER_SPACING =
       new Attribute("border-spacing", false, null);
-    
+    static final Attribute POSITION =
+      new Attribute("position", false, null);
+    static final Attribute LEFT =
+      new Attribute("left", false, null);
+    static final Attribute RIGHT =
+      new Attribute("right", false, null);
+    static final Attribute TOP =
+      new Attribute("top", false, null);
+    static final Attribute BOTTOM =
+      new Attribute("bottom", false, null);
+
     /**
      * The attribute string.
      */
     String attStr;
 
     /**
      * Indicates if this attribute should be inherited from it's parent or
      * not.
      */
     boolean isInherited;
 
     /**
      * A default value for this attribute if one exists, otherwise null.
      */
     String defaultValue;
@@ -510,31 +520,33 @@
     else if (att == Attribute.FONT_STYLE)
       o = new FontStyle(v);
     else if (att == Attribute.COLOR || att == Attribute.BACKGROUND_COLOR
              || att == Attribute.BORDER_COLOR
              || att == Attribute.BORDER_TOP_COLOR
              || att == Attribute.BORDER_BOTTOM_COLOR
              || att == Attribute.BORDER_LEFT_COLOR
              || att == Attribute.BORDER_RIGHT_COLOR)
       o = new CSSColor(v);
     else if (att == Attribute.MARGIN || att == Attribute.MARGIN_BOTTOM
              || att == Attribute.MARGIN_LEFT || att == Attribute.MARGIN_RIGHT
              || att == Attribute.MARGIN_TOP || att == Attribute.WIDTH
              || att == Attribute.HEIGHT
              || att == Attribute.PADDING || att == Attribute.PADDING_BOTTOM
              || att == Attribute.PADDING_LEFT || att == Attribute.PADDING_RIGHT
-             || att == Attribute.PADDING_TOP)
+             || att == Attribute.PADDING_TOP
+             || att == Attribute.LEFT || att == Attribute.RIGHT
+             || att == Attribute.TOP || att == Attribute.BOTTOM)
       o = new Length(v);
     else if (att == Attribute.BORDER_WIDTH || att == Attribute.BORDER_TOP_WIDTH
              || att == Attribute.BORDER_LEFT_WIDTH
              || att == Attribute.BORDER_RIGHT_WIDTH
              || att == Attribute.BORDER_BOTTOM_WIDTH)
       o = new BorderWidth(v);
     else
       o = v;
     return o;
   }
 
   static void addInternal(MutableAttributeSet atts, Attribute a, String v)
   {
     if (a == Attribute.BACKGROUND)
       parseBackgroundShorthand(atts, v);

Reply via email to