| /******************************************************************************* |
| * 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.List; |
| |
| import org.eclipse.draw2d.Figure; |
| import org.eclipse.draw2d.IFigure; |
| import org.eclipse.draw2d.geometry.Point; |
| 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.PositionMeta; |
| import org.eclipse.jst.pagedesigner.css2.property.VerticalAlignMeta; |
| import org.eclipse.jst.pagedesigner.css2.value.Length; |
| |
| /** |
| * CSSLayout is the base layout manager for different CSS layouts, such as block |
| * layout, inline layout (possible in the future table layout, etc) |
| * |
| * @author mengbo |
| */ |
| public abstract class CSSLayout extends FlowFigureLayout implements FlowContext { |
| private BlockFlowContext _absoluteContext; |
| |
| // when doing absolute layout, and if top/left are both "auto", it will be |
| // relating to the normaly flow position. The following two fields try to |
| // catch normal flow layout position. |
| // int _xForAbsolute; |
| // int _yForAbsolute; |
| private FlowBox _boxForAbsolute; |
| |
| /** |
| * the current line |
| */ |
| protected LineBox _currentLine; |
| |
| private boolean _calculatingMaxWidth = false; |
| |
| /** |
| * @param flowFigure |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#FlowFigureLayout(FlowFigure) |
| */ |
| protected CSSLayout(CSSFigure flowFigure) { |
| super(flowFigure); |
| } |
| |
| /** |
| * a shortcut method to get the style associated with the figure. |
| * |
| * @return the css style |
| */ |
| public ICSSStyle getCSSStyle() { |
| return getCSSFigure().getCSSStyle(); |
| } |
| |
| /** |
| * @return the absolute context |
| */ |
| protected final BlockFlowContext getAbsoluteContext() { |
| return _absoluteContext; |
| } |
| |
| /** |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#addToCurrentLine(FlowBox) |
| */ |
| public void addToCurrentLine(FlowBox block) { |
| getCurrentLine().add(block); |
| } |
| |
| /** |
| * Used by getCurrentLine(). |
| */ |
| protected abstract void createNewLine(); |
| |
| /** |
| * Used by getCurrentLine(int topmargin) |
| * |
| * @param topMargin |
| */ |
| protected void createNewLine(int topMargin) { |
| createNewLine(); |
| } |
| |
| /** |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentLine() |
| */ |
| public LineBox getCurrentLine() { |
| if (_currentLine == null) { |
| createNewLine(); |
| } |
| return _currentLine; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentLine(int) |
| */ |
| public LineBox getCurrentLine(int topMargin) { |
| if (_currentLine == null) { |
| createNewLine(topMargin); |
| } |
| // if the current line only contains an empty string, reset the current |
| // line using the given margin. |
| else if (_currentLine.isEmptyStringLine()) { |
| List list = _currentLine.getFragments(); |
| createNewLine(topMargin); |
| _currentLine._fragments.addAll(list); |
| } |
| return _currentLine; |
| } |
| |
| /** |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#isCurrentLineOccupied |
| */ |
| public boolean isCurrentLineOccupied() { |
| return _currentLine != null && _currentLine.isOccupied(); |
| } |
| |
| /** |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#layout() |
| */ |
| protected void layout() { |
| preLayout(); |
| layoutChildren(); |
| flush(); |
| cleanup(); |
| } |
| |
| /** |
| * @return true if is absolute position |
| */ |
| protected final boolean isAbsolutePosition() { |
| ICSSStyle style = getCSSStyle(); |
| |
| // FIXME: Some layout don't support absolute, need check here |
| if (style != null) { |
| Object obj = style.getStyleProperty(ICSSPropertyID.ATTR_POSITION); |
| if (PositionMeta.ABSOLUTE.equals(obj) |
| || PositionMeta.FIXED.equals(obj)) { |
| return supportAbsolutePosition(); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Child class could override this method. |
| * |
| * @return true if supports absolute position |
| */ |
| protected boolean supportAbsolutePosition() { |
| if (findContainingPositionedFigure() == null) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Perform a prelayout |
| */ |
| protected void preLayout() { |
| ICSSStyle style = this.getCSSStyle(); |
| if (style != null) { |
| style.processCounters(); |
| } |
| |
| if (isAbsolutePosition()) { |
| FlowContext parentFigureContext = getParentFigureContext(); |
| _absoluteContext = new BlockFlowContext(parentFigureContext, style); |
| _boxForAbsolute = new FlowBox();// size is 0. Just as a flag, so |
| // later we |
| // could figure out where will this figure be |
| // be put in case of not absolute |
| _boxForAbsolute.setVerticalAlignData(VerticalAlignMeta.TOP); |
| parentFigureContext.addToCurrentLine(_boxForAbsolute); |
| } else { |
| _absoluteContext = null; |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#getFlowContext() |
| */ |
| public FlowContext getFlowContext() { |
| if (_absoluteContext != null) { |
| return _absoluteContext; |
| } |
| return getOriginalFlowContext(); |
| } |
| |
| /** |
| * @return the flow context |
| */ |
| private FlowContext getParentFigureContext() { |
| return super.getFlowContext(); |
| } |
| |
| final void postValidateForAbsolute() { |
| if (_absoluteContext != null) { |
| ICSSStyle style = this.getCSSStyle(); |
| |
| _absoluteContext.endBlock(); |
| |
| int xOffset; |
| int yOffset; |
| |
| ICSSFigure containingPositionedFigure = findContainingPositionedFigure(); |
| IFigure parentFigure = this.getCSSFigure().getParent(); |
| |
| xOffset = calculatePositionRelativeToParent(style, |
| containingPositionedFigure, parentFigure, true); |
| yOffset = calculatePositionRelativeToParent(style, |
| containingPositionedFigure, parentFigure, false); |
| move(_absoluteContext._blockBox, xOffset, yOffset); |
| } |
| } |
| |
| /** |
| * @param style |
| * @param containingPositionedFigure |
| * @param parentFigure |
| * @return |
| */ |
| private int calculatePositionRelativeToParent(ICSSStyle style, |
| ICSSFigure containingPositionedFigure, IFigure parentFigure, |
| boolean horizontal) { |
| int xOffset; |
| Object left = horizontal ? style |
| .getStyleProperty(ICSSPropertyID.ATTR_LEFT) : style |
| .getStyleProperty(ICSSPropertyID.ATTR_TOP); |
| Object right = horizontal ? style |
| .getStyleProperty(ICSSPropertyID.ATTR_RIGHT) : style |
| .getStyleProperty(ICSSPropertyID.ATTR_BOTTOM); |
| |
| if (!(left instanceof Length) && !(right instanceof Length)) { |
| // _boxForAbsolute partipated the layout of the parent figure, and |
| // is already relative to parent. |
| return horizontal ? _boxForAbsolute._x : _boxForAbsolute._y; |
| } |
| |
| // ok, user specified left or right. let's calculate the left |
| int leftValue; |
| if (left instanceof Length) { |
| Length leftLength = (Length) left; |
| leftValue = leftLength.getValue(); |
| if (leftLength.isPercentage()) { |
| leftValue = (horizontal ? containingPositionedFigure |
| .getBounds().width : containingPositionedFigure |
| .getBounds().height) |
| * leftValue / 100; |
| } |
| } else { |
| Length rightLength = (Length) right; |
| int lengthValue = rightLength.getValue(); |
| if (rightLength.isPercentage()) { |
| lengthValue = (horizontal ? containingPositionedFigure |
| .getBounds().width : containingPositionedFigure |
| .getBounds().height) |
| * lengthValue / 100; |
| } |
| |
| if (horizontal) { |
| leftValue = containingPositionedFigure.getBounds().width |
| - _absoluteContext._blockBox.getWidth() - lengthValue; |
| } else { |
| leftValue = containingPositionedFigure.getBounds().height |
| - _absoluteContext._blockBox.getHeight() - lengthValue; |
| } |
| |
| } |
| |
| // xOffset is relative to the first box of the containing figure |
| List fragments = containingPositionedFigure |
| .getFragmentsForRead(); |
| if (fragments.size() > 0) { |
| FlowBox box = (FlowBox) fragments.get(0); |
| // box._x is the x location relative to containingPositionedFigure's |
| // parent. |
| // so now xOffset is relative to containingPositionedFigure's |
| // parent. |
| xOffset = (horizontal ? box._x : box._y) + leftValue; |
| } else { |
| xOffset = leftValue; // should not happen. |
| } |
| Point p; |
| if (horizontal) { |
| p = new Point(xOffset, 0); |
| } else { |
| p = new Point(0, xOffset); |
| } |
| containingPositionedFigure.translateFromParent(p); |
| containingPositionedFigure.translateToAbsolute(p); |
| parentFigure.translateToRelative(p); |
| return horizontal ? p.x : p.y; |
| } |
| |
| /** |
| * @return |
| */ |
| private ICSSFigure findContainingPositionedFigure() { |
| IFigure figure = this.getCSSFigure().getParent(); |
| while (figure instanceof ICSSFigure) { |
| return (ICSSFigure) figure; |
| // ICSSStyle style = ((ICSSFigure) figure).getCSSStyle(); |
| // if (DisplayToLayout.isPositioned(style)) |
| // { |
| // return (ICSSFigure) figure; |
| // } |
| // figure = figure.getParent(); |
| } |
| return null; |
| |
| } |
| |
| /** |
| * @param resultBox |
| * @param x |
| * @param y |
| */ |
| private void move(CompositeBox compBox, int x, int y) { |
| compBox._x += x; |
| compBox._y += y; |
| List list = compBox.getFragments(); |
| for (int i = 0; i < list.size(); i++) { |
| FlowBox box = (FlowBox) list.get(i); |
| |
| if (box instanceof CompositeBox && !(box instanceof BlockBox)) { |
| move((CompositeBox) box, x, y); |
| } else { |
| box._x += x; |
| box._y += y; |
| } |
| } |
| } |
| |
| /** |
| * Layout all children. |
| */ |
| protected void layoutChildren() { |
| List children = getFlowFigure().getChildren(); |
| for (int i = 0; i < children.size(); i++) { |
| Figure f = (Figure) children.get(i); |
| f.invalidate(); |
| f.validate(); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getLastMarginRight() |
| */ |
| public int getLastMarginRight() { |
| if (_currentLine == null || !_currentLine.isOccupied()) { |
| return 0; |
| } |
| FlowBox box = (FlowBox) _currentLine.getFragments().get( |
| _currentLine.getFragments().size() - 1); |
| if (box != null) { |
| return box.getMarginInsets().right; |
| } |
| return 0; |
| } |
| |
| /** |
| * @param c |
| */ |
| public void setCalculatingMaxWidth(boolean c) { |
| _calculatingMaxWidth = c; |
| } |
| |
| /** |
| * @return the calculated maximum width |
| */ |
| public boolean getCalcuatingMaxWidth() { |
| return _calculatingMaxWidth; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#isCalculatingMaxWidth() |
| */ |
| public boolean isCalculatingMaxWidth() { |
| if (_calculatingMaxWidth) { |
| return true; |
| } else if (this.getFlowContext() == null) { |
| return false; |
| } else { |
| return this.getFlowContext().isCalculatingMaxWidth(); |
| } |
| } |
| |
| /** |
| * Called after {@link #layoutChildren()}when all children have been laid |
| * out. This method exists to flush the last line. |
| */ |
| protected abstract void flush(); |
| |
| /** |
| * Flush anything pending and free all temporary data used during layout. |
| */ |
| protected abstract void cleanup(); |
| |
| // ------------------------------------------------------------------------------------ |
| |
| /** |
| * @return the css figure |
| */ |
| protected final CSSFigure getCSSFigure() { |
| return (CSSFigure) getFlowFigure(); |
| } |
| |
| /** |
| * |
| * @return the fragments for read |
| */ |
| public abstract List getFragmentsForRead(); |
| |
| /** |
| * postValidate the child figures of this CSSFigure. Normally layout fall |
| * into the first category need implement this method. |
| */ |
| public abstract void postValidate(); |
| |
| /** |
| * setBounds is called on the CSSFigure. Normally layout fall into the |
| * second category need implement this method. |
| * |
| * @param rect |
| * @param invalidate |
| */ |
| public void setBoundsCalled(Rectangle rect, boolean invalidate) { |
| // TODO: dead? |
| } |
| |
| /** |
| * Child class can override this. Normally block figure will return true. |
| * |
| * @return true if should use local coordinates |
| */ |
| protected boolean useLocalCoordinates() { |
| return false; |
| } |
| |
| /** |
| * If CSSLayout will call paint rountine to draw Border for its box, this |
| * method will return true, else return false, for example,the input file |
| * will return false. |
| * |
| * @return true if handling border block |
| */ |
| protected boolean handlingBorderForBlock() { |
| return true; |
| } |
| |
| /** |
| * This method is called when the corresponding figure is revalidated. |
| * |
| */ |
| protected void figureRevalidate() { |
| // child class can override. |
| } |
| } |