Package org.eclipse.jst.pagedesigner.css2.layout.table

Source Code of org.eclipse.jst.pagedesigner.css2.layout.table.CSSTableLayout2

/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.css2.layout.table;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jst.jsf.common.ui.internal.logging.Logger;
import org.eclipse.jst.pagedesigner.PDPlugin;
import org.eclipse.jst.pagedesigner.css2.ICSSStyle;
import org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout;
import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure;
import org.eclipse.jst.pagedesigner.css2.layout.ICSSPainter;
import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID;
import org.eclipse.jst.pagedesigner.css2.style.ITagEditInfo;
import org.eclipse.swt.SWT;

/**
* see also http://www.w3.org/TR/REC-CSS2/tables.html
*
* @author mengbo
* @version 1.5
*/
public class CSSTableLayout2 extends CSSBlockFlowLayout implements ICSSPainter {
  static Logger _log = PDPlugin.getLogger(CSSTableLayout2.class);

  int _hspacing;

  int _vspacing;

  int[] _columnWidths;

  int[] _rowHeights;

  Dimension _captionSize;

  // _tableInfo will be initialized in preLayout
  TableInfo _tableInfo;

  private int _internalTableWidth;

  private int _internalTableHeight;

  private int _rowx;

  private int _rowwidth;

  /**
   * @param flowfigure
   */
  public CSSTableLayout2(CSSFigure flowfigure) {
    super(flowfigure);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#preLayout()
   */
  protected void preLayout() {
    // super.preLayout will setup the block box.
    super.preLayout();

    ICSSStyle style = this.getCSSStyle();

    _hspacing = _vspacing = 3; // default value

    if (style != null) {
      Object borderspacing = style
          .getStyleProperty(ICSSPropertyID.ATTR_BORDER_SPACING);
      if (borderspacing instanceof int[]) {
        int[] intvalues = (int[]) borderspacing;
        _hspacing = intvalues[0];
        _vspacing = intvalues[1];
      } else {
        ITagEditInfo info = (ITagEditInfo) style
            .getAdapter(ITagEditInfo.class);
        if (info != null && info.needTableDecorator()) {
          // default decorating value. to make things look more
          // separated.
          if (_hspacing < 5) {
            _hspacing = 5;
          }
          if (_vspacing < 5) {
            _vspacing = 5;
          }
        }
      }
    }

    // TODO: support caption
    _tableInfo = new TableInfo(getCSSFigure());

    // construct the table structure.
    _tableInfo.constructTable();

    // calculate the user specified width/height for table and cells.
    // contentWidth is the user specified content width. If <= 0 means no
    // user
    // specification.
    int contentWidth = this._blockBox.getContentWidth();
    int availableWidth = this._blockBox.getRecommendedContentWidth();
    int contentHeight = this._blockBox.getContentHeight();

    _tableInfo.calculateWidth(contentWidth, availableWidth);
    _tableInfo.calculateHeight(contentHeight);

    int columnCount = _tableInfo.getColumnCount();

    int columnMinWidths[] = new int[columnCount];
    int columnMaxWidths[] = new int[columnCount];

    // For each column, determine a maximum and minimum column width from
    // the cells that span only that column. The minimum is that required by
    // the cell with the largest minimum cell width (or the column 'width',
    // whichever is larger). The maximum is that required by the cell with
    // the
    // largest maximum cell width (or the column 'width', whichever is
    // larger).
    List cells = _tableInfo.getCells();
    for (int i = 0, size = cells.size(); i < size; i++) {
      TableCellInfo cellinfo = (TableCellInfo) cells.get(i);
      if (cellinfo.getColSpan() == 1) {
        int column = cellinfo.getColumnIndex();
        Dimension mincw = cellinfo.getMinCWDimension();
        Dimension maxcw = cellinfo.getMaxCWDimension();
        if (maxcw.width < mincw.width) {
          maxcw.width = mincw.width;
        }
        if (mincw.width > columnMinWidths[column]) {
          columnMinWidths[column] = mincw.width;
        }
        if (maxcw.width > columnMaxWidths[column]) {
          columnMaxWidths[column] = maxcw.width;
        }
      }
    }
    // For caption, determine a maximum and minimum width from it.
    int captionWidth = 0;
    if (_tableInfo.getCaption() != null) {
      captionWidth = _tableInfo.getCaption().getDimension().width;
    }

    // For each cell that spans more than one column, increase the
    // minimum widths of the columns it spans so that together, they
    // are at least as wide as the cell. Do the same for the maximum
    // widths. If possible, widen all spanned columns by approximately
    // the same amount.
    for (int i = 0, size = cells.size(); i < size; i++) {
      TableCellInfo cellinfo = (TableCellInfo) cells.get(i);
      int colspan = cellinfo.getColSpan();
      if (colspan > 1) {
        int column = cellinfo.getColumnIndex();
        Dimension mincw = cellinfo.getMinCWDimension();
        Dimension maxcw = cellinfo.getMaxCWDimension();

        adjustWidth(column, colspan, mincw.width, columnMinWidths);
        adjustWidth(column, colspan, maxcw.width, columnMaxWidths);
      }
    }

    int sigmaMinWidth = 0;
    int sigmaMaxWidth = 0;
    for (int i = 0; i < columnMinWidths.length; i++) {
      sigmaMinWidth += columnMinWidths[i];
      if (columnMaxWidths[i] == Integer.MAX_VALUE) {
        sigmaMaxWidth = Integer.MAX_VALUE;
      } else if (sigmaMaxWidth != Integer.MAX_VALUE) {
        sigmaMaxWidth += columnMaxWidths[i];
        if (sigmaMaxWidth < 0) {
          sigmaMaxWidth = Integer.MAX_VALUE;
        }
      }
    }
    int spacingall = (columnMinWidths.length + 1) * _hspacing;
    sigmaMinWidth += spacingall;
    if (sigmaMaxWidth != Integer.MAX_VALUE) {
      sigmaMaxWidth += spacingall;
      if (sigmaMaxWidth < 0) {
        sigmaMaxWidth = Integer.MAX_VALUE;
      }
    }

    int tableWidth = _tableInfo.getTableWidth();
    if (tableWidth > 0) {
      // If the 'table' or 'inline-table' element's 'width' property has a
      // specified value (W) other than 'auto', the property's computed
      // value
      // is the greater of W and the minimum width required by all the
      // columns
      // plus cell spacing or borders (MIN). If W is greater than MIN, the
      // extra
      // width should be distributed over the columns.
      int maxMin = Math.max(captionWidth, sigmaMinWidth);
      if (maxMin >= tableWidth) {
        tableWidth = maxMin;
      }
      distribute(tableWidth - sigmaMinWidth, columnMinWidths,
          columnMaxWidths);
    } else {
      // If the 'table' or 'inline-table' element has 'width: auto', the
      // computed
      // table width is the greater of the table's containing block width
      // and MIN.
      // However, if the maximum width required by the columns plus cell
      // spacing or
      // borders (MAX) is less than that of the containing block, use MAX.
      // int availableWidth = this.getCurrentLine().getAvailableWidth();
      int maxMin = Math.max(captionWidth, sigmaMaxWidth);
      if (maxMin <= availableWidth) {
        // TODO: if _tableInfo.hasWidthPercentage, then we need take
        // that into consideration
        // to distribute the column width. Left to next version.
        tableWidth = maxMin;
        // columnMinWidths = columnMaxWidths;
      } else {
        tableWidth = availableWidth;
      }
      distribute(tableWidth - sigmaMinWidth, columnMinWidths,
          columnMaxWidths);
    }

    // now columnMinWidths contains width for each column
    _columnWidths = columnMinWidths;

    // ok, we have finished calculating column width.
    // next we need to find out row heights.
    _rowHeights = new int[_tableInfo.getRowCount()];

    // first find out those TR that has height settings and use them.
    List rows = _tableInfo.getRows();
    for (int i = 0, size = rows.size(); i < size && i < _rowHeights.length; i++) {
      TableRowInfo rowInfo = (TableRowInfo) rows.get(i);
      if (rowInfo.getSpecifiedRowHeight() > 0) {
        _rowHeights[i] = rowInfo.getSpecifiedRowHeight();
      }
    }

    // First the cells don't span multiple rows.
    cells = _tableInfo.getCells();
    for (int i = 0, size = cells.size(); i < size; i++) {
      TableCellInfo cellinfo = (TableCellInfo) cells.get(i);
      IFigure figure = cellinfo.getFigure();
      int rowspan = cellinfo.getRowSpan();
      if (rowspan == 1) {
        int cellWidth = getCellWidth(cellinfo, _columnWidths);
        Dimension d = figure.getPreferredSize(cellWidth, cellinfo
            .getHeight());
        if (d.height > _rowHeights[cellinfo.getRowIndex()]) {
          _rowHeights[cellinfo.getRowIndex()] = d.height;
        }
      }
    }

    // Next those cells span multiple rows.
    cells = _tableInfo.getCells();
    for (int i = 0, size = cells.size(); i < size; i++) {
      TableCellInfo cellinfo = (TableCellInfo) cells.get(i);
      IFigure figure = cellinfo.getFigure();
      int rowspan = cellinfo.getRowSpan();
      if (rowspan > 1) {
        int cellWidth = getCellWidth(cellinfo, _columnWidths);
        Dimension d = figure.getPreferredSize(cellWidth, cellinfo
            .getHeight());
        if (d.height > getCellHeight(cellinfo, _rowHeights)) {
          adjustHeight(cellinfo.getRowIndex(), rowspan, d.height,
              _rowHeights);
        }
      }
    }

    // Next we may need distribute height.
    int sigmaHeight = (_tableInfo.getRowCount() + 1) * _vspacing;
    for (int i = 0; i < _rowHeights.length; i++) {
      sigmaHeight += _rowHeights[i];
    }
    if (sigmaHeight < contentHeight) {
      distributeHeights(contentHeight - sigmaHeight, _rowHeights);
    }

    // now we have calculated the width and height of all cells.
    // FIXME: border?
    Insets insets = (style == null ? new Insets() : style.getBorderInsets()
        .getAdded(style.getPaddingInsets()));
    _internalTableWidth = (_tableInfo.getColumnCount() + 1) * _hspacing;
    for (int i = 0; i < _columnWidths.length; i++) {
      _internalTableWidth += _columnWidths[i];
    }
    int minWidth = getLengthValue(style, ICSSPropertyID.ATTR_MIN_WIDTH);
    _internalTableWidth = _internalTableWidth > minWidth ? _internalTableWidth
        : minWidth;

    _blockBox.setWidth(_internalTableWidth + insets.getWidth());
    _internalTableHeight = (_tableInfo.getRowCount() + 1) * _vspacing;
    for (int i = 0; i < _rowHeights.length; i++) {
      _internalTableHeight += _rowHeights[i];
    }
    int minHeight = getLengthValue(style, ICSSPropertyID.ATTR_MIN_HEIGHT);
    _internalTableHeight = _internalTableHeight > minHeight ? _internalTableHeight
        : minHeight;

    int captionHeight = 0;
    if (_tableInfo.getCaption() != null) {
      _captionSize = _tableInfo.getCaption().getFigure().getPreferredSize(
          _internalTableWidth, SWT.DEFAULT);
      captionHeight = _captionSize.height;
    } else {
      _captionSize = null;
    }
    _internalTableHeight += captionHeight;

    _blockBox.setHeight(_internalTableHeight + insets.getHeight());

    _rowwidth = _internalTableWidth - 2 * _hspacing;
    _rowx = _hspacing; // XXX: table border width left?
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#endBlock()
   */
  protected void endBlock() {
    _blockBox.setWidth(_internalTableWidth
        + _blockBox.getBorderPaddingWidth());
    _blockBox.setHeight(_internalTableHeight
        + _blockBox.getBorderPaddingHeight());
    super.endBlock();
  }

  //
  // /**
  // * when some of the column has percentage width, and sigmaMax smaller than
  // container,
  // * @param containerWidth
  // * @param columnMinWidths
  // * @param columnMaxWidths
  // * @return
  // */
  // private int distribute2(int containerWidth, int[] columnMinWidths, int[]
  // columnMaxWidths)
  // {
  //       
  // }
  //   
  /**
   * Distribute the additional width to columnMinWidths, using max width as a
   * possible reference on how to distribute.
   *
   * @param toDistribute
   * @param columnMinWidths
   * @param columnMaxWidths
   */
  private void distribute(int toDistribute, int[] columnMinWidths,
      int[] columnMaxWidths) {
    if (toDistribute <= 0)
      return;
    if (columnMinWidths.length == 0)
      return;

    int[] delta = new int[columnMinWidths.length];
    int sigmaDelta = 0;
    for (int i = 0; i < columnMinWidths.length; i++) {
      if (_tableInfo.getWidthSpecified()[i]) {
        delta[i] = 0;
      } else {
        delta[i] = columnMaxWidths[i] - columnMinWidths[i];
        if (delta[i] <= 0) {
          delta[i] = 0;
        }
        sigmaDelta += delta[i];
      }
    }

    // re-calculate the width of columns that use a percentage
    int[] widthPercentages = _tableInfo.getWidthPercentages();
    int[] calculatedWidths = new int[columnMaxWidths.length];
    int percentageWidthsTotal = 0;
    for (int i=0; i < widthPercentages.length; i++) {
      if (widthPercentages[i] > 0) {
        // add the widths of the percent width columns
        // back into the available pool
        toDistribute += columnMinWidths[i];
      }
    }

    for (int i=0; i < widthPercentages.length; i++) {
      if (widthPercentages[i] > 0) {
        double val = toDistribute * (widthPercentages[i] / 100.0);
        calculatedWidths[i] = (int) val;
        if (calculatedWidths[i] < columnMinWidths[i]) {
          // percent width is too small, so use
          // the columnMinWidth instead
          calculatedWidths[i] = columnMinWidths[i];
        }
        percentageWidthsTotal += calculatedWidths[i];
      } else {
        calculatedWidths[i] = 0;
      }
    }

    if (percentageWidthsTotal > toDistribute) {
      // calculated width is too large, so shrink the columns
      // to fit the available space
      int widthColumnCount = 0;
      for (int i=0; i < widthPercentages.length; i++) {
        if (widthPercentages[i] > 0) {
          widthColumnCount++;
        }
      }

      int extraSpace = percentageWidthsTotal - toDistribute;
      int shrinkBy = (int)
        Math.ceil((double) extraSpace / (double) widthColumnCount);

      for (int i=0; i < calculatedWidths.length; i++) {
        if (calculatedWidths[i] > 0) {
          calculatedWidths[i] -= shrinkBy;
        }
      }
    }

    // adjust the columnMinWidth values to compensate for the
    // calculated percentages
    for (int i=0; i < calculatedWidths.length; i++) {
      // if column size was calculated, then re-calculate the delta
      if (calculatedWidths[i] > 0) {
        // remove the previous calculation from the sigmaDelta
        int len = columnMaxWidths[i] - columnMinWidths[i];
        delta[i] = 0;
        if (len <= 0) {
          len = 0;
        }
        sigmaDelta -= len;

        // change the minSize to the calculated size
        columnMinWidths[i] = calculatedWidths[i];
        toDistribute -= columnMinWidths[i];
      }
    }

    if (sigmaDelta == 0) {
      // may happen with percent width column calculations.
      // find out how much space is left and distribute it
      // equally to all columns that are not fixed-width.
      int extraSpace = toDistribute;
      for (int i=0; i < columnMinWidths.length; i++) {
        extraSpace -= columnMinWidths[i];
      }

      averageDeltaToCell(columnMinWidths, extraSpace);
    } else {
      int left = toDistribute;
      for (int i = 0; i < columnMinWidths.length - 1; i++) {
        if (delta[i] > 0) {
          int add = delta[i] * toDistribute / sigmaDelta;
          left -= add;
          columnMinWidths[i] += add;
        }
      }
      columnMinWidths[columnMinWidths.length - 1] += left;
    }
  }

  private void averageDeltaToCell(int[] columnMinWidths, int toDistribute) {

    if (toDistribute <= 0) {
      return;
    }
    ArrayList list = new ArrayList();
    for (int i = 0; i < columnMinWidths.length; i++) {
      if (!_tableInfo.getWidthSpecified()[i]) {
        list.add(new Integer(i));
      }
    }
    if (list.size() == 0) {
      for (int i = 0; i < columnMinWidths.length; i++) {
        list.add(new Integer(i));
      }
    }
    int padding = toDistribute / list.size();
    int left = toDistribute % list.size();
    for (int i = 0, n = list.size(); i < n; i++) {
      columnMinWidths[((Integer) list.get(i)).intValue()] += padding;
    }
    if (left > 0) {
      for (int i = 0; i < left; i++) {
        columnMinWidths[((Integer) list.get(i)).intValue()] += 1;
      }
    }
  }

  /**
   * @param i
   * @param heights
   */
  private void distributeHeights(int toDistribute, int[] heights) {
    if (heights.length == 0)
      return;
    int eachDelta = toDistribute / heights.length;
    for (int i = 0; i < heights.length - 1; i++) {
      heights[i] += eachDelta;
    }
    heights[heights.length - 1] += toDistribute - (heights.length - 1)
        * eachDelta;
  }

  /**
   * @param cellinfo
   * @param heights
   * @return the cell height
   */
  public int getCellHeight(TableCellInfo cellinfo, int[] heights) {
    int rowIndex = cellinfo.getRowIndex();
    int rowspan = cellinfo.getRowSpan();
    int h = 0;
    for (int i = 0; i < rowspan; i++) {
      h += heights[rowIndex + i];
    }
    h += (rowspan - 1) * _vspacing;
    return h;
  }

  /**
   * @param cellinfo
   * @param widths
   * @return the cell width
   */
  public int getCellWidth(TableCellInfo cellinfo, int[] widths) {
    int columnIndex = cellinfo.getColumnIndex();
    int colspan = cellinfo.getColSpan();
    int w = 0;
    for (int i = 0; i < colspan; i++) {
      w += widths[columnIndex + i];
    }
    w += (colspan - 1) * _hspacing;
    return w;
  }

  /**
   * @param column
   *            the start column
   * @param colspan
   *            number of columns
   * @param width
   *            desired width
   * @param columnWidths
   *            current columns widths. After the adjust, need make sure the
   *            columnWidths to be bigger than desired width
   */
  private void adjustWidth(int column, int colspan, int width,
      int[] columnWidths) {
    adjustSpan(column, colspan, width, columnWidths, _hspacing);
  }

  /**
   * @see #adjustWidth(int, int, int, int[])
   */
  private void adjustHeight(int rowIndex, int rowspan, int height,
      int[] heights) {
    adjustSpan(rowIndex, rowspan, height, heights, _vspacing);
  }

  static private void adjustSpan(int column, int colspan, int width,
      int[] columnWidths, int spacing) {
    int spanwidth = 0;
    for (int i = 0; i < colspan; i++) {
      spanwidth += columnWidths[column + i];
    }
    // XXX: vspacing here?
    spanwidth += (colspan - 1) * spacing;

    if (spanwidth >= width) {
      return;
    }
        int delta = width - spanwidth;
        int deltaeach = delta / colspan;
        for (int i = 0; i < colspan - 1; i++) {
          columnWidths[column + i] += deltaeach;
        }
        columnWidths[column + colspan - 1] += (delta - (colspan - 1)
            * deltaeach);
  }

  /**
   * @return the row heights
   */
  public int[] getRowHeights() {
    return _rowHeights;
  }

  /**
   * @return the column widths
   */
  public int[] getColumnWidths() {
    return _columnWidths;
  }

  /**
   * @return the vertical spacing value
   */
  public int getVSpacing() {
    return _vspacing;
  }

  /**
   * @return the horizontal spacing value
   */
  public int getHSpacing() {
    return _hspacing;
  }

  /**
   * @param figure
   * @return the table row info for the figure
   */
  public TableRowInfo getRowInfo(CSSFigure figure) {
    return _tableInfo.findRowInfo(figure);
  }

  /**
   * @return the table caption info
   */
  public TableCaptionInfo getCaptionInfo() {
    return _tableInfo.getCaption();
  }

  /**
   * @param figure
   * @return the table row group info for the figure
   */
  public TableRowGroupInfo getGroupInfo(CSSFigure figure) {
    return _tableInfo.findGroupInfo(figure);
  }

  /**
   * @return the row's x
   */
  public int getRowX() {
    return _rowx;
  }

  /**
   * @return the row's width
   */
  public int getRowWidth() {
    return _rowwidth;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#shouldExpand()
   */
  public boolean shouldExpand() {
    return false;
  }

  /**
   * @return the rendered dimensions of the table caption
   */
  public Dimension getCaptionSize() {
    return _captionSize;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSPainter#paintFigure(org.eclipse.draw2d.Graphics)
   */
  public void paintFigure(Graphics g) {
    ICSSStyle style = this.getCSSStyle();
    if (style != null) {
      ITagEditInfo info = (ITagEditInfo) style
          .getAdapter(ITagEditInfo.class);
      if (info != null && info.needTableDecorator()) {
        List cells = _tableInfo.getCells();
        for (int i = 0, size = cells.size(); i < size; i++) {
          TableCellInfo cellInfo = (TableCellInfo) cells.get(i);
          IFigure cellfigure = cellInfo.getFigure();
          Rectangle rect = cellfigure.getBounds().getCopy();
          rect = rect.expand(1, 1);
          g.setLineStyle(Graphics.LINE_SOLID);
          g.setLineWidth(1);
          g.setForegroundColor(ColorConstants.lightGray);
          g.drawRectangle(rect);
        }
      }
    }
  }

}
TOP

Related Classes of org.eclipse.jst.pagedesigner.css2.layout.table.CSSTableLayout2

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.