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

Source Code of org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout

/*******************************************************************************
* 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;

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

import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jst.pagedesigner.css2.ICSSStyle;
import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID;
import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyMeta;
import org.eclipse.jst.pagedesigner.css2.style.ITagEditInfo;
import org.eclipse.jst.pagedesigner.css2.value.Length;
import org.eclipse.jst.pagedesigner.css2.widget.BorderUtil;
import org.eclipse.swt.graphics.FontMetrics;

/**
* The block layout for {@link CSSFigure}figures. Basic code structure is from
* BlockFlowLayout.
*
* @author mengbo
*/
public class CSSBlockFlowLayout extends CSSLayout implements ICSSPainter2 {
  private LineBox _previousLine = null;

  /**
   * the block box for the layout object
   */
  protected BlockBox _blockBox = null;

  /**
   * The font metrics for this layout object
   */
  protected FontMetrics _fontMetrices;

  int _userSpecifiedWidth;

  int _userSpecifiedHeight;

  /*
   * whether we need HScroll and VScroll when overflow is set to "scroll".
   * will be updated in "endBlock" and used in "paintFigurePostClientArea"
   */
  boolean _needHScroll = false;

  boolean _needVScroll = false;

  /**
     * Creates a new CSSBlockFlowLayout with the given BlockFlow.
   * @param cssfigure
   */
  public CSSBlockFlowLayout(CSSFigure cssfigure) {
    super(cssfigure);
  }

  /**
   * @return true if this layout box has more than one line
   */
  protected boolean hasMoreThanOneLine() {
    return _previousLine != null;
  }

  /**
   * @return true if this layout block is inline
   */
  public boolean isInlineBlock() {
    String obj = getCSSStyle().getDisplay();
    return ICSSPropertyID.VAL_INLINE_BLOCK.equals(obj)
        || ICSSPropertyID.VAL_INLINE_TABLE.equals(obj);
  }

  /**
   * @return true if should expand the width to all available width.
   */
  public boolean shouldExpand() {
    ICSSStyle style = getCSSStyle();
    if (style == null) {
      return false;
    }
        return "block".equalsIgnoreCase(style.getDisplay()) //$NON-NLS-1$
            || "list-item".equalsIgnoreCase(style.getDisplay()); //$NON-NLS-1$
  }

  // ---------------------------------------------------------------------------------------------------
  // preLayout stage. Major job is get the top-left corner information of the
  // new block.

  /**
   * sets up the single block that contains all of the lines.
   */
  protected void setupBlock() {
    // int recommended = line.getAvailableWidth();
    // if (recommended != previousRecommendedWidth)
    // Remove all current Fragments
    _blockBox.clear();
    // Ask for a new line, in case we are in the middle of a line

    if (!isInlineBlock()) {
      LineBox lineBox = getFlowContext().getCurrentLine();
      if (lineBox != null && !lineBox.isEmptyStringLine()) {
        getFlowContext().endLine();
      }
    }

    ICSSStyle style = getCSSStyle();

    // endLine will result in context create a new line, so we are in the
    // new line now.
    // passing in the top margin, and context will consider that when create
    // the new line.
    int marginTop = style.getMarginInsets().top;
    LineBox line = getFlowContext().getCurrentLine(marginTop);

    // Setup the one fragment for this Block with the correct X and
    // available width

    // FIXME: according to spec, when using percentage width/height, should
    // percentage to
    // the "containing block". But we don't have very good "containing
    // block" resolution
    // implementation yet.

    // calculate the min size
    // int minWidth = 0;
    // int minHeight = 0;
    // if (style != null)
    // {
    // // try to see whether there is any designer specified min size
    // ITagEditInfo info = (ITagEditInfo)
    // style.getAdapter(ITagEditInfo.class);
    // if (info != null)
    // {
    // minWidth = info.getMinWidth();
    // minHeight = info.getMinHeight();
    // }
    //
    // // CSS also has the min-width/min-height property. We should also get
    // that,
    // // and using the max of the "min-width" css property and the designer
    // specified min size.
    // int height = getLengthValue(style,ICSSPropertyID.ATTR_MIN_HEIGHT);
    // if(height > minHeight)
    // {
    // minHeight = height;
    // }
    // int width = getLengthValue(style,ICSSPropertyID.ATTR_MIN_WIDTH);
    // if(width > minWidth)
    // {
    // minWidth = width;
    // }
    // }

    // keep track of user specified size, this will be used when handling
    // the "overflow" CSS property.
    _userSpecifiedWidth = 0;
    _userSpecifiedHeight = 0;

    {
      int width = getLengthValue(style, ICSSPropertyID.ATTR_WIDTH);

      int availableWidth = line.getAvailableWidth()
          - style.getMarginInsets().getWidth();
      if (width <= 0) {
        // no width setting
        if (isCalculatingMaxWidth()) {
          _blockBox.setRecommendedWidth(Integer.MAX_VALUE);
          // _blockBox.setWidth( (minWidth>0?minWidth:0));
        } else {
          _blockBox.setRecommendedWidth(availableWidth);
          if (shouldExpand()) {
            _blockBox.setWidth(availableWidth);
          } else {
            // _blockBox.setWidth( (minWidth>0?minWidth:0));
          }
        }
      } else {
        int w = width;
        if (!style.isSizeIncludeBorderPadding()) {
          w += style.getBorderInsets().getWidth()
              + style.getPaddingInsets().getWidth();
        }
        // XXX: should we use minWidth or follow user's choice?
        // if (w < minWidth)
        // {
        // w = minWidth;
        // }
        _userSpecifiedWidth = w;
        _blockBox.setWidth(w);
        _blockBox.setRecommendedWidth(w);
      }
    }

    {
      int height = getLengthValue(style, ICSSPropertyID.ATTR_HEIGHT);
      // Object height =
      // style.getStyleProperty(ICSSPropertyID.ATTR_HEIGHT);
      // Length heightLength = (height instanceof Length) ? (Length)
      // height : null;

      if (height <= 0) {
        // if (minHeight > 0)
        // {
        // // _blockBox.setHeight(minHeight);
        // _blockBox.setRecommendedHeight(minHeight);
        // }
        // else
        {
          _blockBox.setHeight(0);
          _blockBox.setRecommendedHeight(0);
        }
      } else {
        int h = height;
        if (handlingBorderForBlock()
            && !style.isSizeIncludeBorderPadding()) {
          h += style.getBorderInsets().getHeight()
              + style.getPaddingInsets().getHeight();
        }
        // XXX: should we follow minHeight or user's choice?
        // if (minHeight > h)
        // {
        // h = minHeight;
        // }
        _userSpecifiedHeight = h;
        _blockBox.setHeight(h);
        _blockBox.setRecommendedHeight(h);
      }
    }
    _blockBox.setMarginInsets(new Insets(style.getMarginInsets()));
    if (handlingBorderForBlock()) {
      BoxUtil.setupBorderPaddingMargin(_blockBox, getCSSStyle());
    }

    // as in designer, we don't want to the element to have zero size, so
    // set a minimun size here.
    // _blockBox.setWidth(Math.max(20, _blockBox.getWidth()));
    // int minHeight = getCSSStyle().getCSSFont().getFontSize() +
    // _blockBox.getBorderPaddingHeight();
    // _blockBox.setHeight(Math.max(minHeight, _blockBox.getHeight()));

    _blockBox._y = line._y;
    _blockBox._x = line._x;

    setBlockVerticalAlign(_blockBox);
  }

  /**
   * @param style
   * @param property
   * @return the length value
   */
  protected int getLengthValue(ICSSStyle style, String property) {
    int lengthValue = 0;
    if (style != null) {
      Object object = style.getStyleProperty(property);
      Length lengthObj = (object instanceof Length) ? (Length) object
          : null;

      if (lengthObj != null) {
        lengthValue = lengthObj.getValue();
        if (lengthObj.isPercentage()) {
          if (ICSSPropertyID.ATTR_WIDTH.equalsIgnoreCase(property)
              || ICSSPropertyID.ATTR_MIN_WIDTH
                  .equalsIgnoreCase(property)) {
            lengthValue = this.getFlowContext().getCurrentLine().getRecommendedContentWidth()
                * lengthValue / 100;
          } else if (ICSSPropertyID.ATTR_HEIGHT
              .equalsIgnoreCase(property)
              || ICSSPropertyID.ATTR_MIN_HEIGHT
                  .equalsIgnoreCase(property)) {
            // XXX: we should omit it because we don't support
            // percentage height now.
            lengthValue = 0;
          }
        }
      }
    }
    return lengthValue;
  }

  private void setBlockVerticalAlign(BlockBox box) {
    ICSSStyle style = getCSSStyle();
    if (style != null) {
      box.setVerticalAlignData(style
          .getStyleProperty(ICSSPropertyID.ATTR_VERTICAL_ALIGN));
    }
  }

  /**
   * @see FlowContainerLayout#preLayout()
   */
  protected void preLayout() {
    super.preLayout();
    _blockBox = new BlockBox();
    setupBlock();
    // Probably could setup current and previous line here, or just previous
  }

  // -------------------------------------------------------------------------------------------------------
  /**
   * layout the lines in this layout
   */
  protected void layoutLines() {
    List lines = _blockBox.getFragments();
    if (lines != null) {
      for (int i = 0; i < lines.size(); i++) {
        if (lines.get(i) instanceof LineBox) {
          layoutLine((LineBox) lines.get(i));
        }
      }
    }
  }

  /**
   * Called by flush(), adds the BlockBox associated with this BlockFlowLayout
   * to the current line and then ends the line.
   */
  protected void endBlock() {
    layoutLines();
    ICSSStyle style = getCSSStyle();
    if (style != null) {
      int minWidth = 0;
      int minHeight = 0;
      // try to see whether there is any designer specified min size
      ITagEditInfo info = (ITagEditInfo) style
          .getAdapter(ITagEditInfo.class);
      if (info != null) {
        minWidth = info.getMinWidth();
        minHeight = info.getMinHeight();
      }

      // CSS also has the min-width/min-height property. We should also
      // get that,
      // and using the max of the "min-width" css property and the
      // designer specified min size.
      int height = getLengthValue(style, ICSSPropertyID.ATTR_MIN_HEIGHT);
      if (height > minHeight) {
        minHeight = height;
      }
      int width = getLengthValue(style, ICSSPropertyID.ATTR_MIN_WIDTH);
      if (width > minWidth) {
        minWidth = width;
      }
      if (minHeight > _blockBox.getHeight()) {
        _blockBox.setHeight(minHeight);
      }
      if (minWidth > _blockBox.getWidth()) {
        _blockBox.setWidth(minWidth);
      }
    }

    // reset scroll information.
    this._needHScroll = this._needVScroll = false;

    // ok, now we need to adjust the _blockBox's size according to the
    // "overflow" setting.
    // depends on different "overflow" style of this block, different sizing
    // policy may apply.
    // ICSSStyle style = this.getCSSStyle();
    if (style != null) {
      Object overflow = style
          .getStyleProperty(ICSSPropertyID.ATTR_OVERFLOW);
      if (ICSSPropertyID.VAL_HIDDEN.equals(overflow)) {
        if (_userSpecifiedWidth > 0) {
          _blockBox.setWidth(_userSpecifiedWidth);
        }
        if (_userSpecifiedHeight > 0) {
          _blockBox.setHeight(_userSpecifiedHeight);
        }
      } else if (ICSSPropertyID.VAL_SCROLL.equals(overflow)
          || ICSSPropertyID.VAL_AUTO.equals(overflow)) {
        // adjust _needHScroll and _needVScroll
        if (_userSpecifiedWidth > 0
            && _userSpecifiedWidth < _blockBox.getWidth()) {
          _needHScroll = true;
        }
        if (_userSpecifiedHeight > 0
            && _userSpecifiedHeight < _blockBox.getHeight()) {
          _needVScroll = true;
        }
        if (_needHScroll && !_needVScroll) {
          if (_userSpecifiedHeight > 0
              && _blockBox.getInternalContentHeight() >= 0
              && _userSpecifiedHeight < _blockBox
                  .getInternalContentHeight()
                  + _blockBox.getPaddingInsets().getHeight()
                  + BorderUtil.SCROLL_WIDTH) {
            _needVScroll = true;
          }
        }
        if (!_needHScroll && _needVScroll) {
          if (_userSpecifiedWidth > 0
              && _blockBox.getInternalContentWidth() >= 0
              && _userSpecifiedWidth < _blockBox
                  .getInternalContentWidth()
                  + _blockBox.getPaddingInsets().getWidth()
                  + BorderUtil.SCROLL_WIDTH) {
            _needHScroll = true;
          }
        }

        if (_userSpecifiedWidth > 0) {
          _blockBox.setWidth(_userSpecifiedWidth);
        }
        if (_userSpecifiedHeight > 0) {
          _blockBox.setHeight(_userSpecifiedHeight);
        }
      }
    }

    if (getFlowContext().isCurrentLineOccupied()
        && getFlowContext().getCurrentLine().getAvailableWidth() < _blockBox._width
            + _blockBox.getMarginInsets().getWidth()) {
      getFlowContext().endLine();
    }
    if (!isInlineBlock()) {
      LineBox line = getFlowContext().getCurrentLine();
      line.setHorizonalData(getCSSStyle().getStyleProperty(
          ICSSPropertyID.ATTR_HORIZONTAL_ALIGN));
      line.setHtmlInitData(getCSSStyle().getHTMLelementInitValue(
          ICSSPropertyID.ATTR_HORIZONTAL_ALIGN));
      line.add(_blockBox);
      // getFlowContext().addToCurrentLine(_blockBox);
    } else {
      getFlowContext().addToCurrentLine(_blockBox);
    }
    getFlowContext().getCurrentLine().getMarginInsets().bottom = getCSSStyle()
        .getMarginInsets().bottom;

    if (!isInlineBlock()) {
      getFlowContext().endLine();
    }
  }

  /**
   * @param line
   */
  protected void layoutLine(LineBox line) {
    // currentLine.x = 0; //XXX: comment out, don't understand why set to 0,
    // because it has already
    // been set when setupLine(). And if do need, should
    // set to getBorderPaddingInsets().left
    // if (!isInlineBlock() && shouldExpand())
    // {
    // FIXME: currently we are using getRecommendedContentWidth,
    // what happen if after adding the new line, the new width is bigger
    // than
    // recommendedContentWidth? should we use getWidth() instead of
    // recommendedcontentWidth?
    Object textalign = line.getHorizonalData();
    if (textalign == null
        || ICSSPropertyMeta.NOT_SPECIFIED.equals(textalign)) {
      textalign = (getCSSStyle()
          .getStyleProperty(ICSSPropertyID.ATTR_TEXTALIGN));
    }
    if (textalign == null
        || ICSSPropertyMeta.NOT_SPECIFIED.equals(textalign)) {
      textalign = line.getHtmlInitData();
    }
    if (ICSSPropertyID.VAL_RIGHT.equals(textalign)) {
      line._x = _blockBox.getContentWidth() - line.getWidth();
    } else if (ICSSPropertyID.VAL_CENTER.equals(textalign)) {
      line._x = (_blockBox.getContentWidth() - line.getWidth()) / 2;
    }

    if (line._x < 0) {
      line._x = 0;
    }
    line.commit();
  }

  /**
   * Adjust all fragments in the current line to have the same baseline. Do
   * any additional adjustments, such as horizontal alignment.
   */
  protected void addCurrentLine() {
    // The follow code is commented out, and moved into layoutLine(line)
    // called by endBlock().
    // since only when endBlock is called we really know how big is this
    // block box, and then can
    // do horizontal alignment.
    // // currentLine.x = 0; //XXX: comment out, don't understand why set to
    // 0, because it has already
    // // been set when setupLine(). And if do need, should
    // // set to getBorderPaddingInsets().left
    // if (!isInlineBlock() && shouldExpand())
    // {
    // // FIXME: currently we are using getRecommendedContentWidth,
    // // what happen if after adding the new line, the new width is bigger
    // than
    // // recommendedContentWidth? should we use getWidth() instead of
    // // recommendedcontentWidth?
    //
    // Object textalign =
    // (getCSSStyle().getStyleProperty(ICSSPropertyID.ATTR_TEXTALIGN));
    // if (textalign == ICSSPropertyID.VAL_RIGHT)
    // {
    // _currentLine._x = _blockBox.getContentWidth() +
    // _blockBox.getBorderPaddingInsets().left - _currentLine.getWidth();
    // }
    // else if (textalign == ICSSPropertyID.VAL_CENTER)
    // {
    //
    // _currentLine._x = _blockBox.getBorderPaddingInsets().left +
    // (_blockBox.getContentWidth() - _currentLine.getWidth()) / 2;
    // }
    // if (_currentLine._x < 0)
    // _currentLine._x = 0;
    // }
    //
    // // FIXME: should check vertical alignment here?
    // _currentLine.commit();

    // layoutLine(_currentLine);
    _blockBox.add(_currentLine);
  }

  /**
   * @see FlowContainerLayout#flush()
   */
  protected void flush() {
    if (_currentLine != null && _currentLine.isOccupied()) {
      addCurrentLine();
    }
    endBlock();
  }

  /**
   * @see FlowContainerLayout#cleanup()
   */
  protected void cleanup() {
    _currentLine = _previousLine = null;
    _fontMetrices = null;
  }

  // ----------------------------------------------------------------------------------

  /**
   * Override to setup the line's x, remaining, and available width.
   *
   * @param line
   *            the LineBox to set up
   * @param topMargin
   */
  protected void setupLine(LineBox line, int topMargin) {
    line.clear();

    // the caller of getCurrentLine() may add leftMargin and leftPadding and
    // leftBorder to line.x
    line._x = 0;

    // FIXME: here should check the floating boxes, and minus the width of
    // them from
    // current line.
    line.setRecommendedWidth(_blockBox.getRecommendedContentWidth());
    if (_previousLine == null) {
      line._y = 0;
      if (topMargin != Integer.MIN_VALUE) {
        line._y += topMargin;
      }
    } else {
      if (topMargin == Integer.MIN_VALUE) {
        line._y = _previousLine._y + _previousLine.getHeight()
            + getLinePadding() + _previousLine.getMarginInsets().bottom; // XXX:
        // should
        // add
        // previous
        // margin
        // bottom?
      } else {
        line._y = _previousLine._y
            + _previousLine.getHeight()
            + Math.max(topMargin,
                _previousLine.getMarginInsets().bottom);
      }
    }
    setFontinfoForLine(line);
    // line.validate();
  }

  private void setFontinfoForLine(LineBox line) {

    ICSSStyle style = getCSSStyle();
    if (style != null) {
      if (_fontMetrices == null) {
        // as getSwtFont is resource consuming, so we cache the
        // _fontMetrics.
        _fontMetrices = FigureUtilities.getFontMetrics(style
            .getCSSFont().getSwtFont());
      }
      line.setFontMetrics(_fontMetrices);
    }
  }

  /**
   * @see FlowContainerLayout#createNewLine()
   */
  protected void createNewLine() {
    _currentLine = new LineBox();
    setupLine(_currentLine, Integer.MIN_VALUE);
  }

  protected void createNewLine(int topmargin) {
    _currentLine = new LineBox();
    setupLine(_currentLine, topmargin);
  }

  /**
   * @see FlowContext#endLine()
   */
  public void endLine() {
    // this is called from child layouts.
    // If there is no current line, state is equivalent to new line
    if (_currentLine == null) {
      return;
    }
    if (_currentLine.isOccupied()) {
      addCurrentLine(); // finalize the current line layout
    } else {
      _currentLine = null;
      return;
    }

    LineBox box = _currentLine;
    // _currentLine = _previousLine; //XXX: ???? why (yang)
    _previousLine = box;

    _currentLine = null;
    // setupLine(getCurrentLine());
  }

  /**
   * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentY()
   */
  public int getCurrentY() {
    return getCurrentLine()._y; // FIXME: margin of previous block?
  }

  int getLinePadding() {
    return 0;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.CSSLayout#useLocalCoordinates()
   */
  public boolean useLocalCoordinates() {
    return true;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#dispose()
   */
  public void dispose() {
    //
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSLayout#getFragmentsForRead()
   */
  public List getFragmentsForRead() {
    List r = new ArrayList(1);
    r.add(_blockBox);
    return r;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSLayout#postValidate()
   */
  public void postValidate() {

    Rectangle r = new Rectangle(_blockBox._x, _blockBox._y, _blockBox
        .getWidth(), _blockBox.getHeight());
    getCSSFigure().setBounds(r);
    List list = getCSSFigure().getChildren();
    for (int i = 0; i < list.size(); i++) {
      ((FlowFigure) list.get(i)).postValidate();
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getContainerWidth()
   */
  public int getContainerWidth() {
    int width = Math.max(0, Math.max(_blockBox.getWidth(), _blockBox
        .getRecommendedWidth()));
    return width;
  }

  /**
   * when the "overflow" is "scroll", we need to paint the scrollbar
   */
  public void paintFigurePostClientArea(Graphics g) {
    ICSSStyle style = this.getCSSStyle();
    if (style != null) {
      Object overflow = style
          .getStyleProperty(ICSSPropertyID.ATTR_OVERFLOW);
      if (ICSSPropertyID.VAL_SCROLL.equals(overflow)
          || ICSSPropertyID.VAL_AUTO.equals(overflow)) {
        if (this._needHScroll || this._needVScroll) {
          // as this is using localCoordinate, so translate to
          // relative to left/up corder of whole
          // blockbox.
          g.translate(-_blockBox.getBorderPaddingInsets().left,
              -_blockBox.getBorderPaddingInsets().top);

          Rectangle rect = new Rectangle(0, 0, _blockBox.getWidth(),
              _blockBox.getHeight());
          rect.crop(_blockBox.getBorderInsets());

          if (this._needHScroll && this._needVScroll) {
            BorderUtil.drawScrollBar(g, BorderUtil.SCROLL_WIDTH,
                rect, BorderUtil.BOTH);
          } else if (this._needHScroll) {
            BorderUtil.drawScrollBar(g, BorderUtil.SCROLL_WIDTH,
                rect, BorderUtil.HORIZONTAL_BAR);
          } else if (this._needVScroll) {
            BorderUtil.drawScrollBar(g, BorderUtil.SCROLL_WIDTH,
                rect, BorderUtil.VERTICAL_BAR);
          }
        }
      }
    }
  }
}
TOP

Related Classes of org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout

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.