HTMLTableView.java :  » Scripting » seco » seco » notebook » html » view » Java Open Source

Java Open Source » Scripting » seco 
seco » seco » notebook » html » view » HTMLTableView.java
/*
 * This file is part of the Scriba source distribution. This is free, open-source 
 * software. For full licensing information, please see the LicensingInformation file
 * at the root level of the distribution.
 *
 * Copyright (c) 2006-2007 Kobrix Software, Inc.
 */
package seco.notebook.html.view;

/*
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the proprietary information of Sun Microsystems, Inc.
* Use is subject to license terms.
*
 */

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.text.html.*;

import seco.notebook.html.Util;

import java.util.*;

/**
 * HTML table view copied from javax.swing.text.html.TableView.java.
 *
 * The original intention was to implement a box painter other
 * than StyleSheet.BoxPainter which obviously is not complete.
 *
 * This could have been a simple task just overriding TableView.create
 * with our own ViewFactory implementing an alternate BoxPainter.
 *
 * Unfortunately while create is public, TableView itself is
 * not (why???). We have to copy the whole thing and just
 * adapt one line to make inner class CellView a subclass of
 * our own HTMLBlockView instead of javax.swing.text.html.BlockView.
 *
 * To make things worse, TableView uses some methods from class CSS which
 * are not public as well (again not clear why) so these had to be
 * copied into this class too.
 *
 * All in all a totally ugly solution which hopefully can be corrected
 * once Sun decided to tidy up the CSS part of Swing.
 *
 * @version stage 11, April 27, 2003
 */

public class HTMLTableView extends BoxView implements ViewFactory {

  /**
   * Constructs a TableView for the given element.
   *
   * @param elem the element that this view is responsible for
   */
  public HTMLTableView(Element elem) {
    super(elem, View.Y_AXIS);
    rows = new Vector();
    gridValid = false;
    captionIndex = -1;
    totalColumnRequirements = new SizeRequirements();
  }

  /**
   * Creates a new table row.
   *
   * @param elem an element
   * @return the row
   */
  protected RowView createTableRow(Element elem) {
    // PENDING(prinz) need to add support for some of the other
    // elements, but for now just ignore anything that is not
    // a TR.
    Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
    if (o == HTML.Tag.TR) {
      return new RowView(elem);
    }
    return null;
  }

  /**
   * The number of columns in the table.
   */
  public int getColumnCount() {
    return columnSpans.length;
  }

  /**
   * Fetches the span (width) of the given column.
   * This is used by the nested cells to query the
   * sizes of grid locations outside of themselves.
   */
  public int getColumnSpan(int col) {
    if (col < columnSpans.length) {
      return columnSpans[col];
    }
    return 0;
  }

  /**
   * The number of rows in the table.
   */
  public int getRowCount() {
    return rows.size();
  }

  /**
   * Fetch the span of multiple rows.  This includes
   * the border area.
   */
  public int getMultiRowSpan(int row0, int row1) {
    RowView rv0 = getRow(row0);
    RowView rv1 = getRow(row1);
    if ((rv0 != null) && (rv1 != null)) {
      int index0 = rv0.viewIndex;
      int index1 = rv1.viewIndex;
      int span = getOffset(Y_AXIS, index1) - getOffset(Y_AXIS, index0) +
                 getSpan(Y_AXIS, index1);
      return span;
    }
    return 0;
  }

  /**
   * Fetches the span (height) of the given row.
   */
  public int getRowSpan(int row) {
    RowView rv = getRow(row);
    if (rv != null) {
      return getSpan(Y_AXIS, rv.viewIndex);
    }
    return 0;
  }

  RowView getRow(int row) {
    if (row < rows.size()) {
      return (RowView) rows.elementAt(row);
    }
    return null;
  }

  /**
   * Determines the number of columns occupied by
   * the table cell represented by given element.
   */
  protected int getColumnsOccupied(View v) {
    AttributeSet a = v.getElement().getAttributes();

    if (a.isDefined(HTML.Attribute.COLSPAN)) {
      String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
      if (s != null) {
        try {
          return Integer.parseInt(s);
        } catch (NumberFormatException nfe) {
          // fall through to one column
        }
      }
    }

    return 1;
  }

  /**
   * Determines the number of rows occupied by
   * the table cell represented by given element.
   */
  protected int getRowsOccupied(View v) {
    AttributeSet a = v.getElement().getAttributes();

    if (a.isDefined(HTML.Attribute.ROWSPAN)) {
      String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
      if (s != null) {
        try {
          return Integer.parseInt(s);
        } catch (NumberFormatException nfe) {
          // fall through to one row
        }
      }
    }

    return 1;
  }

  protected void invalidateGrid() {
    gridValid = false;
  }

  protected StyleSheet getStyleSheet() {
    HTMLDocument doc = (HTMLDocument) getDocument();
    return doc.getStyleSheet();
  }

  /**
   * Update the insets, which contain the caption if there
   * is a caption.
   */
  void updateInsets() {
    short top = (short) painter.getInset(TOP, this);
    short bottom = (short) painter.getInset(BOTTOM, this);
    if (captionIndex != -1) {
      View caption = getView(captionIndex);
      short h = (short) caption.getPreferredSpan(Y_AXIS);
     // AttributeSet a = caption.getAttributes();
            /* change in retrieval of CSS.Attribute.CAPTION_SIDE
               as this Attribute is not public */
      //de.calcom.cclib.html.HTMLDiag hd = new de.calcom.cclib.html.HTMLDiag();
      //hd.listAttributes(a, 2);
     Object align = null;
     //Object align = a.getAttribute(CSS.Attribute.CAPTION_SIDE);
            /* change end */
      if ((align != null) && (align.equals("bottom"))) {
        bottom += h;
      } else {
        top += h;
      }
    }
    setInsets(top, (short) painter.getInset(LEFT, this),
              bottom, (short) painter.getInset(RIGHT, this));
  }

  /**
   * Update any cached values that come from attributes.
   */
  protected void setPropertiesFromAttributes() {
    StyleSheet sheet = getStyleSheet();
    attr = sheet.getViewAttributes(this);
    painter = sheet.getBoxPainter(attr);
    if (attr != null) {
      setInsets((short) painter.getInset(TOP, this),
                (short) painter.getInset(LEFT, this),
                (short) painter.getInset(BOTTOM, this),
                (short) painter.getInset(RIGHT, this));
/*
          CSS.LengthValue lv = (CSS.LengthValue)
              attr.getAttribute(CSS.Attribute.BORDER_SPACING);
          if (lv != null) {
              cellSpacing = (int) lv.getValue();
          } else {
              cellSpacing = 0;
          }
          */
    }
  }

  /**
   * Fill in the grid locations that are placeholders
   * for multi-column, multi-row, and missing grid
   * locations.
   */
  void updateGrid() {
    if (! gridValid) {
      relativeCells = false;
      multiRowCells = false;

      // determine which views are table rows and clear out
      // grid points marked filled.
      captionIndex = -1;
      rows.removeAllElements();
      int n = getViewCount();
      for (int i = 0; i < n; i++) {
        View v = getView(i);
        if (v instanceof RowView) {
          rows.addElement(v);
          RowView rv = (RowView) v;
          rv.clearFilledColumns();
          rv.rowIndex = rows.size() - 1;
          rv.viewIndex = i;
        } else {
          Object o = v.getElement().getAttributes().getAttribute(StyleConstants.NameAttribute);
          if (o instanceof HTML.Tag) {
            HTML.Tag kind = (HTML.Tag) o;
            if (kind == HTML.Tag.CAPTION) {
              captionIndex = i;
            }
          }
        }
      }

      int maxColumns = 0;
      int nrows = rows.size();
      for (int row = 0; row < nrows; row++) {
        RowView rv = getRow(row);
        int col = 0;
        for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
          View cv = rv.getView(cell);
          if (! relativeCells) {
            AttributeSet a = cv.getAttributes();
                      /*LengthVal lv = (LengthVal)
                          a.getAttribute(CSS.Attribute.WIDTH);*/
            Object lv = a.getAttribute(CSS.Attribute.WIDTH);
            if ((lv != null) /*&& (lv.isPercentage())*/) {
              if(lv.toString().trim().endsWith("%")) {
                relativeCells = true;
              }
            }
          }
          // advance to a free column
          for (; rv.isFilled(col); col++);
          int rowSpan = getRowsOccupied(cv);
          if (rowSpan > 1) {
            multiRowCells = true;
          }
          int colSpan = getColumnsOccupied(cv);
          if ((colSpan > 1) || (rowSpan > 1)) {
            // fill in the overflow entries for this cell
            int rowLimit = row + rowSpan;
            int colLimit = col + colSpan;
            for (int i = row; i < rowLimit; i++) {
              for (int j = col; j < colLimit; j++) {
                if (i != row || j != col) {
                  addFill(i, j);
                }
              }
            }
            if (colSpan > 1) {
              col += colSpan - 1;
            }
          }
        }
        maxColumns = Math.max(maxColumns, col);
      }

      // setup the column layout/requirements
      columnSpans = new int[maxColumns];
      columnOffsets = new int[maxColumns];
      columnRequirements = new SizeRequirements[maxColumns];
      for (int i = 0; i < maxColumns; i++) {
        columnRequirements[i] = new SizeRequirements();
        columnRequirements[i].maximum = Integer.MAX_VALUE;
      }
      gridValid = true;
    }
  }

  /**
   * Mark a grid location as filled in for a cells overflow.
   */
  void addFill(int row, int col) {
    RowView rv = getRow(row);
    if (rv != null) {
      rv.fillColumn(col);
    }
  }

  /**
   * Layout the columns to fit within the given target span.
   *
   * @param targetSpan the given span for total of all the table
   *  columns
   * @param reqs the requirements desired for each column.  This
   *  is the column maximum of the cells minimum, preferred, and
   *  maximum requested span
   * @param spans the return value of how much to allocated to
   *  each column
   * @param offsets the return value of the offset from the
   *  origin for each column
   * @return the offset from the origin and the span for each column
   *  in the offsets and spans parameters
   */
  protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,
                               SizeRequirements[] reqs) {
    //System.out.println("layoutColumns offsets=");
    colIterator.setLayoutArrays(offsets, spans, targetSpan);
    //System.out.println("call to calculateTiledLayout from SHTMLTableView.layoutColumns");
    calculateTiledLayout(colIterator, targetSpan);
  }

  /**
   * Calculate the requirements for each column.  The calculation
   * is done as two passes over the table.  The table cells that
   * occupy a single column are scanned first to determine the
   * maximum of minimum, preferred, and maximum spans along the
   * give axis.  Table cells that span multiple columns are excluded
   * from the first pass.  A second pass is made to determine if
   * the cells that span multiple columns are satisfied.  If the
   * column requirements are not satisified, the needs of the
   * multi-column cell is mixed into the existing column requirements.
   * The calculation of the multi-column distribution is based upon
   * the proportions of the existing column requirements and taking
   * into consideration any constraining maximums.
   */
  void calculateColumnRequirements(int axis) {
    // pass 1 - single column cells
    boolean hasMultiColumn = false;
    int nrows = getRowCount();
    for (int i = 0; i < nrows; i++) {
      RowView row = getRow(i);
      int col = 0;
      int ncells = row.getViewCount();
      for (int cell = 0; cell < ncells; cell++, col++) {
        View cv = row.getView(cell);
        for (; row.isFilled(col); col++); // advance to a free column
        //int rowSpan = getRowsOccupied(cv);
        int colSpan = getColumnsOccupied(cv);
        if (colSpan == 1) {
          checkSingleColumnCell(axis, col, cv);
        } else {
          hasMultiColumn = true;
          col += colSpan - 1;
        }
      }
    }

    // pass 2 - multi-column cells
    if (hasMultiColumn) {
      for (int i = 0; i < nrows; i++) {
        RowView row = getRow(i);
        int col = 0;
        int ncells = row.getViewCount();
        for (int cell = 0; cell < ncells; cell++, col++) {
          View cv = row.getView(cell);
          for (; row.isFilled(col); col++); // advance to a free column
          int colSpan = getColumnsOccupied(cv);
          if (colSpan > 1) {
            checkMultiColumnCell(axis, col, colSpan, cv);
            col += colSpan - 1;
          }
        }
      }
    }
  }

  /**
   * check the requirements of a table cell that spans a single column.
   */
  void checkSingleColumnCell(int axis, int col, View v) {
    SizeRequirements req = columnRequirements[col];
    req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
    req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
  }

  /**
   * check the requirements of a table cell that spans multiple
   * columns.
   */
  void checkMultiColumnCell(int axis, int col, int ncols, View v) {
    // calculate the totals
    long min = 0;
    long pref = 0;
    long max = 0;
    for (int i = 0; i < ncols; i++) {
      SizeRequirements req = columnRequirements[col + i];
      min += req.minimum;
      pref += req.preferred;
      max += req.maximum;
    }

    // check if the minimum size needs adjustment.
    int cmin = (int) v.getMinimumSpan(axis);
    if (cmin > min) {
          /*
      * the columns that this cell spans need adjustment to fit
      * this table cell.... calculate the adjustments.
           */
      SizeRequirements[] reqs = new SizeRequirements[ncols];
      for (int i = 0; i < ncols; i++) {
        reqs[i] = columnRequirements[col + i];
      }
      int[] spans = new int[ncols];
      int[] offsets = new int[ncols];
      SizeRequirements.calculateTiledPositions(cmin, null, reqs,
          offsets, spans);
      // apply the adjustments
      for (int i = 0; i < ncols; i++) {
        SizeRequirements req = reqs[i];
        req.minimum = Math.max(spans[i], req.minimum);
        req.preferred = Math.max(req.minimum, req.preferred);
        req.maximum = Math.max(req.preferred, req.maximum);
      }
    }

    // check if the preferred size needs adjustment.
    int cpref = (int) v.getPreferredSpan(axis);
    if (cpref > pref) {
          /*
      * the columns that this cell spans need adjustment to fit
      * this table cell.... calculate the adjustments.
           */
      SizeRequirements[] reqs = new SizeRequirements[ncols];
      for (int i = 0; i < ncols; i++) {
        reqs[i] = columnRequirements[col + i];
      }
      int[] spans = new int[ncols];
      int[] offsets = new int[ncols];
      SizeRequirements.calculateTiledPositions(cpref, null, reqs,
          offsets, spans);
      // apply the adjustments
      for (int i = 0; i < ncols; i++) {
        SizeRequirements req = reqs[i];
        req.preferred = Math.max(spans[i], req.preferred);
        req.maximum = Math.max(req.preferred, req.maximum);
      }
    }

  }

  // --- BoxView methods -----------------------------------------

  /**
   * Calculate the requirements for the minor axis.  This is called by
   * the superclass whenever the requirements need to be updated (i.e.
   * a preferenceChanged was messaged through this view).
   * <p>
   * This is implemented to calculate the requirements as the sum of the
   * requirements of the columns and then adjust it if the
   * CSS width or height attribute is specified and applicable to
   * the axis.
   */
  protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
    updateGrid();

    // calculate column requirements for each column
    calculateColumnRequirements(axis);


    // the requirements are the sum of the columns.
    if (r == null) {
      r = new SizeRequirements();
    }
    long min = 0;
    long pref = 0;
    int n = columnRequirements.length;
    for (int i = 0; i < n; i++) {
      SizeRequirements req = columnRequirements[i];
      min += req.minimum;
      pref += req.preferred;
    }
    int adjust = (n - 1) * cellSpacing;
    min += adjust;
    pref += adjust;
    r.minimum = (int) min;
    r.preferred = (int) pref;
    r.maximum = (int) pref;


    AttributeSet attr = getAttributes();
      /*LengthVal cssWidth = (LengthVal)attr.getAttribute(
                                                  CSS.Attribute.WIDTH);*/
    Object cssWidth = attr.getAttribute(CSS.Attribute.WIDTH);

    if (HTMLBlockView.spanSetFromAttributes(axis, r, cssWidth, null)) {
      if (r.minimum < (int)min) {
        // The user has requested a smaller size than is needed to
        // show the table, override it.
        r.maximum = r.minimum = r.preferred = (int) min;
      }
    }
    totalColumnRequirements.minimum = r.minimum;
    totalColumnRequirements.preferred = r.preferred;
    totalColumnRequirements.maximum = r.maximum;

    // set the alignment
    Object o = attr.getAttribute(CSS.Attribute.TEXT_ALIGN);
    if (o != null) {
      // set horizontal alignment
      String ta = o.toString();
      if (ta.equals("left")) {
        r.alignment = 0;
      } else if (ta.equals("center")) {
        r.alignment = 0.5f;
      } else if (ta.equals("right")) {
        r.alignment = 1;
      } else {
        r.alignment = 0;
      }
    } else {
      r.alignment = 0;
    }

    return r;
  }

  /**
   * Calculate the requirements for the major axis.  This is called by
   * the superclass whenever the requirements need to be updated (i.e.
   * a preferenceChanged was messaged through this view).
   * <p>
   * This is implemented to provide the superclass behavior adjusted for
   * multi-row table cells.
   */
  protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
    updateInsets();
    rowIterator.updateAdjustments();
    //System.out.println("call to calculateTiledRequirements from SHTMLTableView.calculateMajorAxisRequirements");
    r = calculateTiledRequirements(rowIterator, r);
    r.maximum = r.preferred;
    return r;
  }

  /**
   * Perform layout for the minor axis of the box (i.e. the
   * axis orthoginal to the axis that it represents).  The results
   * of the layout should be placed in the given arrays which represent
   * the allocations to the children along the minor axis.  This
   * is called by the superclass whenever the layout needs to be
   * updated along the minor axis.
   * <p>
   * This is implemented to call the
   * <a href="#layoutColumns">layoutColumns</a> method, and then
   * forward to the superclass to actually carry out the layout
   * of the tables rows.
   *
   * @param targetSpan the total span given to the view, which
   *  whould be used to layout the children
   * @param axis the axis being layed out
   * @param offsets the offsets from the origin of the view for
   *  each of the child views.  This is a return value and is
   *  filled in by the implementation of this method
   * @param spans the span of each child view;  this is a return
   *  value and is filled in by the implementation of this method
   * @return the offset and span for each child view in the
   *  offsets and spans parameters
   */
  protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
    // make grid is properly represented
    updateGrid();

    // all of the row layouts are invalid, so mark them that way
    int n = getRowCount();
    for (int i = 0; i < n; i++) {
      RowView row = getRow(i);
      row.layoutChanged(axis);
    }

    // calculate column spans
    layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);

    // continue normal layout
    super.layoutMinorAxis(targetSpan, axis, offsets, spans);
  }


  /**
   * Perform layout for the major axis of the box (i.e. the
   * axis that it represents).  The results
   * of the layout should be placed in the given arrays which represent
   * the allocations to the children along the minor axis.  This
   * is called by the superclass whenever the layout needs to be
   * updated along the minor axis.
   * <p>
   * This method is where the layout of the table rows within the
   * table takes place.  This method is implemented to call the use
   * the RowIterator and the CSS collapsing tile to layout
   * with border spacing and border collapsing capabilities.
   *
   * @param targetSpan the total span given to the view, which
   *  whould be used to layout the children
   * @param axis the axis being layed out
   * @param offsets the offsets from the origin of the view for
   *  each of the child views; this is a return value and is
   *  filled in by the implementation of this method
   * @param spans the span of each child view; this is a return
   *  value and is filled in by the implementation of this method
   * @return the offset and span for each child view in the
   *  offsets and spans parameters
   */
  protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
    rowIterator.setLayoutArrays(offsets, spans);
    //System.out.println("call to calculateTiledLayout from SHTMLTableView.layoutMajorAxis");
    calculateTiledLayout(rowIterator, targetSpan);

    if (captionIndex != -1) {
      // place the caption
      View caption = getView(captionIndex);
      int h = (int) caption.getPreferredSpan(Y_AXIS);
      spans[captionIndex] = h;
      short boxBottom = (short) painter.getInset(BOTTOM, this);
      if (boxBottom != getBottomInset()) {
        offsets[captionIndex] = targetSpan + boxBottom;
      } else {
        offsets[captionIndex] = - getTopInset();
      }
    }
  }

  /**
   * Fetches the child view that represents the given position in
   * the model.  This is implemented to walk through the children
   * looking for a range that contains the given position.  In this
   * view the children do not necessarily have a one to one mapping
   * with the child elements.
   *
   * @param pos  the search position >= 0
   * @param a  the allocation to the table on entry, and the
   *   allocation of the view containing the position on exit
   * @return  the view representing the given position, or
   *   null if there isn't one
   */
  protected View getViewAtPosition(int pos, Rectangle a) {
    int n = getViewCount();
    for (int i = 0; i < n; i++) {
      View v = getView(i);
      int p0 = v.getStartOffset();
      int p1 = v.getEndOffset();
      if ((pos >= p0) && (pos < p1)) {
        // it's in this view.
        if (a != null) {
          childAllocation(i, a);
        }
        return v;
      }
    }
    if (pos == getEndOffset()) {
      View v = getView(n - 1);
      if (a != null) {
        this.childAllocation(n - 1, a);
      }
      return v;
    }
    return null;
  }

  // --- View methods ---------------------------------------------

  /**
   * Fetches the attributes to use when rendering.  This is
   * implemented to multiplex the attributes specified in the
   * model with a StyleSheet.
   */
  public AttributeSet getAttributes() {
    if (attr == null) {
      StyleSheet sheet = getStyleSheet();
      attr = sheet.getViewAttributes(this);
    }
    return attr;
  }

  /**
   * Renders using the given rendering surface and area on that
   * surface.  This is implemented to delegate to the css box
   * painter to paint the border and background prior to the
   * interior.  The superclass culls rendering the children
   * that don't directly intersect the clip and the row may
   * have cells hanging from a row above in it.  The table
   * does not use the superclass rendering behavior and instead
   * paints all of the rows and lets the rows cull those
   * cells not intersecting the clip region.
   *
   * @param g the rendering surface to use
   * @param allocation the allocated region to render into
   * @see View#paint
   */
  public void paint(Graphics g, Shape allocation) {
    // paint the border
    Rectangle a = allocation.getBounds();
    setSize(a.width, a.height);
    if (captionIndex != -1) {
      // adjust the border for the caption
      short top = (short) painter.getInset(TOP, this);
      short bottom = (short) painter.getInset(BOTTOM, this);
      if (top != getTopInset()) {
        int h = getTopInset() - top;
        a.y += h;
        a.height -= h;
      } else {
        a.height -= getBottomInset() - bottom;
      }
    }
    //System.out.println("SHTMLTableView paint a.x=" + a.x + " a.y=" + a.y + "a.width=" + a.width + " a.height=" + a.height);
    painter.paint(g, a.x, a.y, a.width, a.height, this);

    // paint interior
    int n = getViewCount();
    for (int i = 0; i < n; i++) {
      View v = getView(i);
      v.paint(g, getChildAllocation(i, allocation));
    }
    //super.paint(g, a);
  }

  /**
   * Establishes the parent view for this view.  This is
   * guaranteed to be called before any other methods if the
   * parent view is functioning properly.
   * <p>
   * This is implemented
   * to forward to the superclass as well as call the
   * <a href="#setPropertiesFromAttributes">setPropertiesFromAttributes</a>
   * method to set the paragraph properties from the css
   * attributes.  The call is made at this time to ensure
   * the ability to resolve upward through the parents
   * view attributes.
   *
   * @param parent the new parent, or null if the view is
   *  being removed from a parent it was previously added
   *  to
   */
  public void setParent(View parent) {
    super.setParent(parent);
    if (parent != null) {
      setPropertiesFromAttributes();
    }
  }

  /**
   * Fetches the ViewFactory implementation that is feeding
   * the view hierarchy.
   * This replaces the ViewFactory with an implementation that
   * calls through to the createTableRow and createTableCell
   * methods.   If the element given to the factory isn't a
   * table row or cell, the request is delegated to the factory
   * produced by the superclass behavior.
   *
   * @return the factory, null if none
   */
  public ViewFactory getViewFactory() {
    return this;
  }

  /**
   * Gives notification that something was inserted into
   * the document in a location that this view is responsible for.
   * This replaces the ViewFactory with an implementation that
   * calls through to the createTableRow and createTableCell
   * methods.   If the element given to the factory isn't a
   * table row or cell, the request is delegated to the factory
   * passed as an argument.
   *
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#insertUpdate
   */
  public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
    super.insertUpdate(e, a, this);
  }

  /**
   * Gives notification that something was removed from the document
   * in a location that this view is responsible for.
   * This replaces the ViewFactory with an implementation that
   * calls through to the createTableRow and createTableCell
   * methods.   If the element given to the factory isn't a
   * table row or cell, the request is delegated to the factory
   * passed as an argument.
   *
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#removeUpdate
   */
  public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
    super.removeUpdate(e, a, this);
  }

  /**
   * Gives notification from the document that attributes were changed
   * in a location that this view is responsible for.
   * This replaces the ViewFactory with an implementation that
   * calls through to the createTableRow and createTableCell
   * methods.   If the element given to the factory isn't a
   * table row or cell, the request is delegated to the factory
   * passed as an argument.
   *
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see View#changedUpdate
   */
  public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
   attr = getStyleSheet().getViewAttributes(this);
   //System.out.println("HTMLTableView changedUpdate" + 
   //       attr.getAttribute(CSS.Attribute.WIDTH));
     super.changedUpdate(e, a, this);
  }

  protected void forwardUpdate(DocumentEvent.ElementChange ec,
                               DocumentEvent e, Shape a, ViewFactory f) {
    super.forwardUpdate(ec, e, a, f);
    // A change in any of the table cells usually effects the whole table,
    // so redraw it all!
    if (a != null) {
      Component c = getContainer();
      if (c != null) {
        Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
                          a.getBounds();
        c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
      }
    }
  }

  /**
   * Change the child views.  This is implemented to
   * provide the superclass behavior and invalidate the
   * grid so that rows and columns will be recalculated.
   */
  public void replace(int offset, int length, View[] views) {
    super.replace(offset, length, views);
    invalidateGrid();
  }

  // --- ViewFactory methods ------------------------------------------

  /**
   * The table itself acts as a factory for the various
   * views that actually represent pieces of the table.
   * All other factory activity is delegated to the factory
   * returned by the parent of the table.
   */
  public View create(Element elem) {
    Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
    if (o instanceof HTML.Tag) {
      HTML.Tag kind = (HTML.Tag) o;
      if (kind == HTML.Tag.TR) {
        return createTableRow(elem);
      } else if ((kind == HTML.Tag.TD) || (kind == HTML.Tag.TH)) {
        return new CellView(elem);
      } else if (kind == HTML.Tag.CAPTION) {
        return new javax.swing.text.html.ParagraphView(elem);
      }
    }
    // default is to delegate to the normal factory
    View p = getParent();
    if (p != null) {
      ViewFactory f = p.getViewFactory();
      if (f != null) {
        return f.create(elem);
      }
    }
    return null;
  }

  // ---- variables ----------------------------------------------------

  private AttributeSet attr;
  private StyleSheet.BoxPainter painter;

  private int cellSpacing;

  /**
   * The index of the caption view if there is a caption.
   * This has a value of -1 if there is no caption.  The
   * caption lives in the inset area of the table, and is
   * updated with each time the grid is recalculated.
   */
  private int captionIndex;

  /**
   * Do any of the table cells contain a relative size
   * specification?  This is updated with each call to
   * updateGrid().  If this is true, the ColumnIterator
   * will do extra work to calculate relative cell
   * specifications.
   */
  private boolean relativeCells;

  /**
   * Do any of the table cells span multiple rows?  If
   * true, the RowRequirementIterator will do additional
   * work to adjust the requirements of rows spanned by
   * a single table cell.  This is updated with each call to
   * updateGrid().
   */
  private boolean multiRowCells;

  int[] columnSpans;
  int[] columnOffsets;
  /**
   * SizeRequirements for all the columns.
   */
  SizeRequirements totalColumnRequirements;
  SizeRequirements[] columnRequirements;

  RowIterator rowIterator = new RowIterator();
  ColumnIterator colIterator = new ColumnIterator();

  Vector rows;

  boolean gridValid;
  static final private BitSet EMPTY = new BitSet();

  class ColumnIterator implements LayoutIterator {

    /**
     * Disable percentage adjustments which should only apply
     * when calculating layout, not requirements.
     */
    void disablePercentages() {
      percentages = null;
    }

     /**
     * Set the layout arrays to use for holding layout results
     */
    public void setLayoutArrays(int offsets[], int spans[], int targetSpan) {
      this.offsets = offsets;
      this.spans = spans;
      updatePercentagesAndAdjustmentWeights(targetSpan);
      //updatePercentages(targetSpan);
    }
    
    private void updatePercentagesAndAdjustmentWeights(int span) {
      adjustmentWeights = new int[columnRequirements.length];
      for (int i = 0; i < columnRequirements.length; i++) {
    adjustmentWeights[i] = 0;
      }
      if (relativeCells) {
    percentages = new int[columnRequirements.length];
      } else {
    percentages = null;
      }
      int nrows = getRowCount();
      for (int rowIndex = 0; rowIndex < nrows; rowIndex++) {
    RowView row = getRow(rowIndex);
    int col = 0;
    int ncells = row.getViewCount();
    for (int cell = 0; cell < ncells; cell++, col++) {
        View cv = row.getView(cell);
        for (; row.isFilled(col); col++); // advance to a free column
        //int rowSpan = getRowsOccupied(cv);
        int colSpan = getColumnsOccupied(cv);
        AttributeSet a = cv.getAttributes();
        Object lv = a.getAttribute(CSS.Attribute.WIDTH);
        if ( lv != null ) {
        int len = (int) ((Util.getPtValue(lv.toString()) * 
            new Integer(span).floatValue()) 
            / new Integer(colSpan).floatValue() + 0.5f);
        if(lv.toString().trim().endsWith("%")) {   
      for (int i = 0; i < colSpan; i++) {
        
        // add a percentage requirement
        percentages[col+i] = Math.max(percentages[col+i], len);
        adjustmentWeights[col+i] = WorstAdjustmentWeight;
          } //else {
        //adjustmentWeights[col+i] = WorstAdjustmentWeight - 1;
         // }
      }
        }
        col += colSpan - 1;
    }
      }
  } 

    // --- RequirementIterator methods -------------------

    public int getCount() {
      return columnRequirements.length;
    }

    public void setIndex(int i) {
      col = i;
    }

    public void setOffset(int offs) {
      offsets[col] = offs;
    }

    public int getOffset() {
      return offsets[col];
    }

    public void setSpan(int span) {
      spans[col] = span;
    }

    public int getSpan() {
      return spans[col];
    }

    public float getMinimumSpan(float parentSpan) {
      if ((percentages != null) && (percentages[col] != 0)) {
        return Math.max(percentages[col], columnRequirements[col].minimum);
      }
      return columnRequirements[col].minimum;
    }

    public float getPreferredSpan(float parentSpan) {
      if ((percentages != null) && (percentages[col] != 0)) {
        return Math.max(percentages[col], columnRequirements[col].preferred);
      }
      return columnRequirements[col].preferred;
    }

    public float getMaximumSpan(float parentSpan) {
      if ((percentages != null) && (percentages[col] != 0)) {
        return Math.max(percentages[col], columnRequirements[col].preferred);
      }
      return columnRequirements[col].maximum;
    }

    public float getLeadingCollapseSpan() {
      return cellSpacing;
    }

    public float getTrailingCollapseSpan() {
      return cellSpacing;
    }
    
    public int getAdjustmentWeight() {
      return adjustmentWeights[col];
  }
    
    private int borderWidth = 0;
    public float getBorderWidth() {
      return borderWidth;        
  }    
    
    private int[] adjustmentWeights;

    /**
     * Current column index
     */
    private int col;

    /**
     * percentage values (may be null since there
     * might not be any).
     */
    private int[] percentages;

    private int[] offsets;
    private int[] spans;
  }

  class RowIterator implements LayoutIterator {

    RowIterator() {
    }

    void updateAdjustments() {
      int axis = Y_AXIS;
      if (multiRowCells) {
        // adjust requirements of multi-row cells
        int n = getRowCount();
        adjustments = new int[n];
        for (int i = 0; i < n; i++) {
          RowView rv = getRow(i);
          if (rv.multiRowCells == true) {
            int ncells = rv.getViewCount();
            for (int j = 0; j < ncells; j++) {
              View v = rv.getView(j);
              int nrows = getRowsOccupied(v);
              if (nrows > 1) {
                int spanNeeded = (int) v.getPreferredSpan(axis);
                adjustMultiRowSpan(spanNeeded, nrows, i);
              }
            }
          }
        }
      } else {
        adjustments = null;
      }
    }
    
    private int borderWidth = 0;
    public float getBorderWidth() {
      return borderWidth;        
  }  
    
    public int getAdjustmentWeight() {
      return 0;
  }

    /**
     * Fixup preferences to accomodate a multi-row table cell
     * if not already covered by existing preferences.  This is
     * a no-op if not all of the rows needed (to do this check/fixup)
     * have arrived yet.
     */
    void adjustMultiRowSpan(int spanNeeded, int nrows, int rowIndex) {
      if ((rowIndex + nrows) > getCount()) {
        // rows are missing (could be a bad rowspan specification)
        // or not all the rows have arrived.  Do the best we can with
        // the current set of rows.
        nrows = getCount() - rowIndex;
        if (nrows < 1) {
          return;
        }
      }
      int span = 0;
      for (int i = 0; i < nrows; i++) {
        RowView rv = getRow(rowIndex + i);
        span += rv.getPreferredSpan(Y_AXIS);
      }
      if (spanNeeded > span) {
        int adjust = (spanNeeded - span);
        int rowAdjust = adjust / nrows;
        int firstAdjust = rowAdjust + (adjust - (rowAdjust * nrows));
        //RowView rv = getRow(rowIndex);
        adjustments[rowIndex] = Math.max(adjustments[rowIndex],
            firstAdjust);
        for (int i = 1; i < nrows; i++) {
          adjustments[rowIndex + i] = Math.max(
          adjustments[rowIndex + i], rowAdjust);
        }
      }
    }

    void setLayoutArrays(int[] offsets, int[] spans) {
      this.offsets = offsets;
      this.spans = spans;
    }

    // --- RequirementIterator methods -------------------

    public void setOffset(int offs) {
      RowView rv = getRow(row);
      if (rv != null) {
        offsets[rv.viewIndex] = offs;
      }
    }

    public int getOffset() {
      RowView rv = getRow(row);
      if (rv != null) {
        return offsets[rv.viewIndex];
      }
      return 0;
    }

    public void setSpan(int span) {
      RowView rv = getRow(row);
      if (rv != null) {
        spans[rv.viewIndex] = span;
      }
    }

    public int getSpan() {
      RowView rv = getRow(row);
      if (rv != null) {
        return spans[rv.viewIndex];
      }
      return 0;
    }

    public int getCount() {
      return rows.size();
    }

    public void setIndex(int i) {
      row = i;
    }

    public float getMinimumSpan(float parentSpan) {
      return getPreferredSpan(parentSpan);
    }

    public float getPreferredSpan(float parentSpan) {
      RowView rv = getRow(row);
      if (rv != null) {
        int adjust = (adjustments != null) ? adjustments[row] : 0;
        adjust += 2 * cellSpacing;
        return rv.getPreferredSpan(HTMLTableView.this.getAxis()) + adjust;
      }
      return 0;
    }

    public float getMaximumSpan(float parentSpan) {
      return getPreferredSpan(parentSpan);
    }

    public float getLeadingCollapseSpan() {
      return cellSpacing;
    }

    public float getTrailingCollapseSpan() {
      return cellSpacing;
    }

    /**
     * Current row index
     */
    private int row;

    /**
     * Adjustments to the row requirements to handle multi-row
     * table cells.
     */
    private int[] adjustments;

    private int[] offsets;
    private int[] spans;
  }

  /**
   * View of a row in a row-centric table.
   */
  public class RowView extends BoxView {

    /**
     * Constructs a TableView for the given element.
     *
     * @param elem the element that this view is responsible for
     */
    public RowView(Element elem) {
      super(elem, View.X_AXIS);
      fillColumns = new BitSet();
      RowView.this.setPropertiesFromAttributes();
    }

    void clearFilledColumns() {
      fillColumns.and(EMPTY);
    }

    void fillColumn(int col) {
      fillColumns.set(col);
    }

    boolean isFilled(int col) {
      return fillColumns.get(col);
    }

    /**
     * The number of columns present in this row.
     */
    int getColumnCount() {
      int nfill = 0;
      int n = fillColumns.size();
      for (int i = 0; i < n; i++) {
        if (fillColumns.get(i)) {
          nfill ++;
        }
      }
      return getViewCount() + nfill;
    }

    /**
     * Fetches the attributes to use when rendering.  This is
     * implemented to multiplex the attributes specified in the
     * model with a StyleSheet.
     */
    public AttributeSet getAttributes() {
      return attr;
    }

    protected StyleSheet getStyleSheet() {
      HTMLDocument doc = (HTMLDocument) getDocument();
      return doc.getStyleSheet();
    }

    /**
     * This is called by a child to indicate its
     * preferred span has changed.  This is implemented to
     * execute the superclass behavior and well as try to
     * determine if a row with a multi-row cell hangs across
     * this row.  If a multi-row cell covers this row it also
     * needs to propagate a preferenceChanged so that it will
     * recalculate the multi-row cell.
     *
     * @param child the child view
     * @param width true if the width preference should change
     * @param height true if the height preference should change
     */
    public void preferenceChanged(View child, boolean width, boolean height) {
      super.preferenceChanged(child, width, height);
      if (HTMLTableView.this.multiRowCells && height) {
        for (int i = rowIndex  - 1; i >= 0; i--) {
          RowView rv = HTMLTableView.this.getRow(i);
          if (rv.multiRowCells) {
            rv.preferenceChanged(null, false, true);
            break;
          }
        }
      }
    }

    // The major axis requirements for a row are dictated by the column
    // requirements. These methods use the value calculated by
    // TableView.
    protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
      SizeRequirements req = new SizeRequirements();
      req.minimum = totalColumnRequirements.minimum;
      req.maximum = totalColumnRequirements.maximum;
      req.preferred = totalColumnRequirements.preferred;
      req.alignment = 0f;
      return req;
    }

    public float getMinimumSpan(int axis) {
      float value;

      if (axis == View.X_AXIS) {
        value = totalColumnRequirements.minimum + getLeftInset() +
                getRightInset();
      }
      else {
        value = super.getMinimumSpan(axis);
      }
      return value;
    }

    public float getMaximumSpan(int axis) {
      float value;

      if (axis == View.X_AXIS) {
        // We're flexible.
        value = (float)Integer.MAX_VALUE;
      }
      else {
        value = super.getMaximumSpan(axis);
      }
      return value;
    }

    public float getPreferredSpan(int axis) {
      float value;

      if (axis == View.X_AXIS) {
        value = totalColumnRequirements.preferred + getLeftInset() +
                getRightInset();
      }
      else {
        value = super.getPreferredSpan(axis);
      }
      return value;
    }

    public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
      super.changedUpdate(e, a, f);
      int pos = e.getOffset();
      if (pos <= getStartOffset() && (pos + e.getLength()) >=
          getEndOffset()) {
        RowView.this.setPropertiesFromAttributes();
      }
    }

    /**
     * Renders using the given rendering surface and area on that
     * surface.  This is implemented to delegate to the css box
     * painter to paint the border and background prior to the
     * interior.
     *
     * @param g the rendering surface to use
     * @param allocation the allocated region to render into
     * @see View#paint
     */
    public void paint(Graphics g, Shape allocation) {
      Rectangle a = (Rectangle) allocation;
      painter.paint(g, a.x, a.y, a.width, a.height, this);
      super.paint(g, a);
    }

    /**
     * Change the child views.  This is implemented to
     * provide the superclass behavior and invalidate the
     * grid so that rows and columns will be recalculated.
     */
    public void replace(int offset, int length, View[] views) {
      super.replace(offset, length, views);
      invalidateGrid();
    }

    /**
     * Calculate the height requirements of the table row.  The
     * requirements of multi-row cells are not considered for this
     * calculation.  The table itself will check and adjust the row
     * requirements for all the rows that have multi-row cells spanning
     * them.  This method updates the multi-row flag that indicates that
     * this row and rows below need additional consideration.
     */
    protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
//      return super.calculateMinorAxisRequirements(axis, r);
      long min = 0;
      long pref = 0;
      long max = 0;
      multiRowCells = false;
      int n = getViewCount();
      for (int i = 0; i < n; i++) {
        View v = getView(i);
        if (getRowsOccupied(v) > 1) {
          multiRowCells = true;
          max = Math.max((int) v.getMaximumSpan(axis), max);
        } else {
          min = Math.max((int) v.getMinimumSpan(axis), min);
          pref = Math.max((int) v.getPreferredSpan(axis), pref);
          max = Math.max((int) v.getMaximumSpan(axis), max);
        }
      }

      if (r == null) {
        r = new SizeRequirements();
        r.alignment = 0.5f;
      }
      r.preferred = (int) pref;
      r.minimum = (int) min;
      r.maximum = (int) max;
      return r;
    }

    /**
     * Perform layout for the major axis of the box (i.e. the
     * axis that it represents).  The results of the layout should
     * be placed in the given arrays which represent the allocations
     * to the children along the major axis.
     * <p>
     * This is re-implemented to give each child the span of the column
     * width for the table, and to give cells that span multiple columns
     * the multi-column span.
     *
     * @param targetSpan the total span given to the view, which
     *  whould be used to layout the children
     * @param axis the axis being layed out
     * @param offsets the offsets from the origin of the view for
     *  each of the child views; this is a return value and is
     *  filled in by the implementation of this method
     * @param spans the span of each child view; this is a return
     *  value and is filled in by the implementation of this method
     * @return the offset and span for each child view in the
     *  offsets and spans parameters
     */
    protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
      int col = 0;
      int ncells = getViewCount();
      for (int cell = 0; cell < ncells; cell++, col++) {
        View cv = getView(cell);
        for (; isFilled(col); col++); // advance to a free column
        int colSpan = getColumnsOccupied(cv);
        spans[cell] = columnSpans[col];
        offsets[cell] = columnOffsets[col];
        if (colSpan > 1) {
          int n = columnSpans.length;
          for (int j = 1; j < colSpan; j++) {
            // Because the table may be only partially formed, some
            // of the columns may not yet exist.  Therefore we check
            // the bounds.
            if ((col+j) < n) {
              spans[cell] += columnSpans[col+j];
            }
          }
          col += colSpan - 1;
        }
      }
    }

    /**
     * Perform layout for the minor axis of the box (i.e. the
     * axis orthoginal to the axis that it represents).  The results
     * of the layout should be placed in the given arrays which represent
     * the allocations to the children along the minor axis.  This
     * is called by the superclass whenever the layout needs to be
     * updated along the minor axis.
     * <p>
     * This is implemented to delegate to the superclass, then adjust
     * the span for any cell that spans multiple rows.
     *
     * @param targetSpan the total span given to the view, which
     *  whould be used to layout the children
     * @param axis the axis being layed out
     * @param offsets the offsets from the origin of the view for
     *  each of the child views; this is a return value and is
     *  filled in by the implementation of this method
     * @param spans the span of each child view; this is a return
     *  value and is filled in by the implementation of this method
     * @return the offset and span for each child view in the
     *  offsets and spans parameters
     */
    protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
      super.layoutMinorAxis(targetSpan, axis, offsets, spans);
      int col = 0;
      int ncells = getViewCount();
      for (int cell = 0; cell < ncells; cell++, col++) {
        View cv = getView(cell);
        for (; isFilled(col); col++); // advance to a free column
        int colSpan = getColumnsOccupied(cv);
        int rowSpan = getRowsOccupied(cv);
        if (rowSpan > 1) {

          int row0 = rowIndex;
          int row1 = Math.min(rowIndex + rowSpan - 1, getRowCount()-1);
          spans[cell] = getMultiRowSpan(row0, row1);
        }
        if (colSpan > 1) {
          col += colSpan - 1;
        }
      }
    }

    /**
     * Determines the resizability of the view along the
     * given axis.  A value of 0 or less is not resizable.
     *
     * @param axis may be either View.X_AXIS or View.Y_AXIS
     * @return the resize weight
     * @exception IllegalArgumentException for an invalid axis
     */
    public int getResizeWeight(int axis) {
      return 1;
    }

    /**
     * Fetches the child view that represents the given position in
     * the model.  This is implemented to walk through the children
     * looking for a range that contains the given position.  In this
     * view the children do not necessarily have a one to one mapping
     * with the child elements.
     *
     * @param pos  the search position >= 0
     * @param a  the allocation to the table on entry, and the
     *   allocation of the view containing the position on exit
     * @return  the view representing the given position, or
     *   null if there isn't one
     */
    protected View getViewAtPosition(int pos, Rectangle a) {
      int n = getViewCount();
      for (int i = 0; i < n; i++) {
        View v = getView(i);
        int p0 = v.getStartOffset();
        int p1 = v.getEndOffset();
        if ((pos >= p0) && (pos < p1)) {
          // it's in this view.
          if (a != null) {
            childAllocation(i, a);
          }
          return v;
        }
      }
      if (pos == getEndOffset()) {
        View v = getView(n - 1);
        if (a != null) {
          this.childAllocation(n - 1, a);
        }
        return v;
      }
      return null;
    }

    /**
     * Update any cached values that come from attributes.
     */
    void setPropertiesFromAttributes() {
      StyleSheet sheet = getStyleSheet();
      attr = sheet.getViewAttributes(this);
      painter = sheet.getBoxPainter(attr);
    }

    private StyleSheet.BoxPainter painter;
    private AttributeSet attr;

    /** columns filled by multi-column or multi-row cells */
    BitSet fillColumns;

    /**
     * The row index within the overall grid
     */
    int rowIndex;

    /**
     * The view index (for row index to view index conversion).
     * This is set by the updateGrid method.
     */
    int viewIndex;

    /**
     * Does this table row have cells that span multiple rows?
     */
    boolean multiRowCells;

  }

  /**
   * Default view of an html table cell.  This needs to be moved
   * somewhere else.
   */
  class CellView extends HTMLBlockView {

    /**
     * Constructs a TableCell for the given element.
     *
     * @param elem the element that this view is responsible for
     */
    public CellView(Element elem) {
      super(elem, Y_AXIS);
    }

      /*
      public void paint(Graphics g, Shape allocation) {
        Rectangle a = allocation.getBounds();
    //System.out.println("SHTMLTableView.CellView paint a.x=" + a.x + " a.y=" + a.y + "a.width=" + a.width + " a.height=" + a.height);
        super.paint(g, allocation);
      }
      */

      /**
       * Perform layout for the major axis of the box (i.e. the
       * axis that it represents).  The results of the layout should
       * be placed in the given arrays which represent the allocations
       * to the children along the major axis.  This is called by the
       * superclass to recalculate the positions of the child views
       * when the layout might have changed.
       * <p>
       * This is implemented to delegate to the superclass to
       * tile the children.  If the target span is greater than
       * was needed, the offsets are adjusted to align the children
       * (i.e. position according to the html valign attribute).
       *
       * @param targetSpan the total span given to the view, which
       *  whould be used to layout the children
       * @param axis the axis being layed out
       * @param offsets the offsets from the origin of the view for
       *  each of the child views; this is a return value and is
       *  filled in by the implementation of this method
       * @param spans the span of each child view; this is a return
       *  value and is filled in by the implementation of this method
       * @return the offset and span for each child view in the
       *  offsets and spans parameters
       */
    protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
      super.layoutMajorAxis(targetSpan, axis, offsets, spans);

      // calculate usage
      int used = 0;
      int n = spans.length;
      for (int i = 0; i < n; i++) {
        used += spans[i];
      }

      // calculate adjustments
      int adjust = 0;
      if (used < targetSpan) {
        // PENDING(prinz) change to use the css alignment.
        String valign = (String) getElement().getAttributes().getAttribute(
            HTML.Attribute.VALIGN);
        if (valign == null) {
          AttributeSet rowAttr = getElement().getParentElement().getAttributes();
          valign = (String) rowAttr.getAttribute(HTML.Attribute.VALIGN);
        }
        if ((valign == null) || valign.equals("middle")) {
          adjust = (targetSpan - used) / 2;
        } else if (valign.equals("bottom")) {
          adjust = targetSpan - used;
        }
      }

      // make adjustments.
      if (adjust != 0) {
        for (int i = 0; i < n; i++) {
          offsets[i] += adjust;
        }
      }
    }

    /**
     * Calculate the requirements needed along the major axis.
     * This is called by the superclass whenever the requirements
     * need to be updated (i.e. a preferenceChanged was messaged
     * through this view).
     * <p>
     * This is implemented to delegate to the superclass, but
     * indicate the maximum size is very large (i.e. the cell
     * is willing to expend to occupy the full height of the row).
     *
     * @param axis the axis being layed out.
     * @param r the requirements to fill in.  If null, a new one
     *  should be allocated.
     */
    protected SizeRequirements calculateMajorAxisRequirements(int axis,
        SizeRequirements r) {
      SizeRequirements req = super.calculateMajorAxisRequirements(axis, r);
      req.maximum = Integer.MAX_VALUE;
      return req;
    }
  }

    /* ------------------------------------------------------
       start additional items from javax.swing.text.html.CSS
       ------------------------------------------------------ */

       /**
        * An iterator to express the requirements to use when computing
        * layout.
        *
        * (copied from javax.swing.text.html.CSS)
        */
  interface LayoutIterator {
    public static final int WorstAdjustmentWeight = 2;
    void setOffset(int offs);
    int getOffset();
    void setSpan(int span);
    int getSpan();
    int getCount();
    void setIndex(int i);
    float getMinimumSpan(float parentSpan);
    float getPreferredSpan(float parentSpan);
    float getMaximumSpan(float parentSpan);
    //float getAlignment();
    float getLeadingCollapseSpan();
    float getTrailingCollapseSpan();
    int getAdjustmentWeight();
    float getBorderWidth();
  }

  /**
   * Calculate a tiled layout for the given iterator.
   * This should be done collapsing the neighboring
   * margins to be a total of the maximum of the two
   * neighboring margin areas as described in the CSS spec.
   *
   * (copied from javax.swing.text.html.CSS)
   */
  static void calculateTiledLayout0(LayoutIterator iter, int targetSpan) {
    /*
    * first pass, calculate the preferred sizes, adjustments needed because
    * of margin collapsing, and the flexibility to adjust the sizes.
     */
    long minimum = 0;
    long maximum = 0;
    long preferred = 0;
    int lastMargin = 0;
    int collapsed = 0;
    int n = iter.getCount();
    for (int i = 0; i < n; i++) {
      iter.setIndex(i);
      int margin0 = lastMargin;
      //int margin1 = (int) iter.getLeadingCollapseSpan();
      //iter.setOffset(Math.max(margin0, margin1));
      iter.setOffset((int) preferred); // don't know, why, but it works
      if (iter.getOffset() != 0) {
        // There is a collapse area
        iter.setOffset(iter.getOffset() - margin0);
        collapsed += iter.getOffset();
      }
      iter.setSpan( (int) iter.getPreferredSpan(targetSpan));
      preferred += iter.getSpan();
      minimum += iter.getMinimumSpan(targetSpan);
      maximum += iter.getMaximumSpan(targetSpan);

      lastMargin = (int) iter.getTrailingCollapseSpan();
    }
  }
  
  static void calculateTiledLayout(LayoutIterator iter, int targetSpan) {
    
    /*
     * first pass, calculate the preferred sizes, adjustments needed because
     * of margin collapsing, and the flexibility to adjust the sizes.
     */
    long preferred = 0;
    long currentPreferred = 0;
    int lastMargin = 0;
    int totalSpacing = 0;
    int n = iter.getCount();
    int adjustmentWeightsCount = LayoutIterator.WorstAdjustmentWeight + 1;
      
    //max gain we can get adjusting elements with adjustmentWeight <= i
    long gain[] = new long[adjustmentWeightsCount]; 
    //max loss we can get adjusting elements with adjustmentWeight <= i
    long loss[] = new long[adjustmentWeightsCount]; 

    for (int i = 0; i < adjustmentWeightsCount; i++) {
        gain[i] = loss[i] = 0;
    }
    for (int i = 0; i < n; i++) {
        iter.setIndex(i);
        int margin0 = lastMargin;
        int margin1 = (int) iter.getLeadingCollapseSpan();

        iter.setOffset(Math.max(margin0, margin1));
        totalSpacing += iter.getOffset();

        currentPreferred = (long)iter.getPreferredSpan(targetSpan);
        iter.setSpan((int) currentPreferred);
        preferred += currentPreferred;
        gain[iter.getAdjustmentWeight()] += 
      (long)iter.getMaximumSpan(targetSpan) - currentPreferred;
        loss[iter.getAdjustmentWeight()] += 
      currentPreferred - (long)iter.getMinimumSpan(targetSpan);
        lastMargin = (int) iter.getTrailingCollapseSpan();
    }
    totalSpacing += lastMargin;               
    totalSpacing += 2 * iter.getBorderWidth();  
    
    for (int i = 1; i < adjustmentWeightsCount; i++) {
        gain[i] += gain[i - 1];
        loss[i] += loss[i - 1];
    }

    /*
     * Second pass, expand or contract by as much as possible to reach
     * the target span.  This takes the margin collapsing into account
     * prior to adjusting the span.
     */

    // determine the adjustment to be made
    int allocated = targetSpan - totalSpacing;
    long desiredAdjustment = allocated - preferred;
    long adjustmentsArray[] = (desiredAdjustment > 0) ? gain : loss;
    desiredAdjustment = Math.abs(desiredAdjustment);
    int adjustmentLevel = 0;
    for (;adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight; 
         adjustmentLevel++) { 
        // adjustmentsArray[] is sorted. I do not bother about
        // binary search though
        if (adjustmentsArray[adjustmentLevel] >= desiredAdjustment) {
      break;
        }
    }
    float adjustmentFactor = 0.0f;
    if (adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight) {
        desiredAdjustment -= (adjustmentLevel > 0) ? 
      adjustmentsArray[adjustmentLevel - 1] : 0;
        if (desiredAdjustment != 0) {
      float maximumAdjustment = 
          adjustmentsArray[adjustmentLevel] - 
          ((adjustmentLevel > 0) ? 
           adjustmentsArray[adjustmentLevel - 1] : 0
           );
      adjustmentFactor = desiredAdjustment / maximumAdjustment;
        }
    }
    // make the adjustments
    int totalOffset = (int)iter.getBorderWidth();;
    for (int i = 0; i < n; i++) {
        iter.setIndex(i);
        iter.setOffset( iter.getOffset() + totalOffset);
        if (iter.getAdjustmentWeight() < adjustmentLevel) {
      iter.setSpan((int)
             ((allocated > preferred) ? 
              Math.floor(iter.getMaximumSpan(targetSpan)) : 
              Math.ceil(iter.getMinimumSpan(targetSpan))
              )
             );
        } else if (iter.getAdjustmentWeight() == adjustmentLevel) {
      int availableSpan = (allocated > preferred) ? 
          (int) iter.getMaximumSpan(targetSpan) - iter.getSpan() : 
          iter.getSpan() - (int) iter.getMinimumSpan(targetSpan);
      int adj = (int)Math.floor(adjustmentFactor * availableSpan);
      iter.setSpan(iter.getSpan() + 
             ((allocated > preferred) ? adj : -adj));
        }
        totalOffset = (int) Math.min((long) iter.getOffset() + 
             (long) iter.getSpan(), 
             Integer.MAX_VALUE);
    }

    // while rounding we could lose several pixels.
          int roundError = targetSpan - totalOffset - 
        (int)iter.getTrailingCollapseSpan() - 
        (int)iter.getBorderWidth();
    int adj = (roundError > 0) ? 1 : -1;
    roundError *= adj;

          boolean canAdjust = true;
          while (roundError > 0 && canAdjust) {
        // check for infinite loop
        canAdjust = false;
        int offsetAdjust = 0;
        // try to distribute roundError. one pixel per cell
        for (int i = 0; i < n; i++) {
      iter.setIndex(i);
      iter.setOffset(iter.getOffset() + offsetAdjust);
                  int curSpan = iter.getSpan();
                  if (roundError > 0) {
          int boundGap = (adj > 0) ? 
        (int)Math.floor(iter.getMaximumSpan(targetSpan)) - curSpan : 
        curSpan - (int)Math.ceil(iter.getMinimumSpan(targetSpan));
          if (boundGap >= 1) {
        canAdjust = true;
        iter.setSpan(curSpan + adj);
        offsetAdjust += adj;
        roundError--;
          }
                  }
        }
    }
      }

  /**
   * Calculate the requirements needed to tile the requirements
   * given by the iterator that would be tiled.  The calculation
   * takes into consideration margin collapsing.
   *
   * (copied from javax.swing.text.html.CSS)
   */

  static SizeRequirements calculateTiledRequirements(LayoutIterator iter, SizeRequirements r) {
    //System.out.println("claculateTiledRequirements");
    long minimum = 0;
    long maximum = 0;
    long preferred = 0;
    int lastMargin = 0;
    int collapsed = 0;
    int n = iter.getCount();
    for (int i = 0; i < n; i++) {
      iter.setIndex(i);
      int margin0 = lastMargin;
      int margin1 = (int) iter.getLeadingCollapseSpan();
      int offset = Math.max(margin0, margin1);
      if (offset != 0) {
        // There is a collapse area
        collapsed += offset - margin0;
      }
      preferred += (int) iter.getPreferredSpan(0);
      minimum += iter.getMinimumSpan(0);
      maximum += iter.getMaximumSpan(0);

      lastMargin = (int) iter.getTrailingCollapseSpan();
    }
    // adjust for the collapsed area
    minimum -= collapsed;
    preferred -= collapsed;
    maximum -= collapsed;

    // set return value
    if (r == null) {
      r = new SizeRequirements();
    }
    r.minimum = (minimum > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)minimum;
    r.preferred = (preferred > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) preferred;
    r.maximum = (maximum > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) maximum;
    return r;
  }
    /* ------------------------------------------------------
       end additional items from javax.swing.text.html.CSS
       ------------------------------------------------------ */
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.