blob: 99081a7983f3352265335898c9d9717f3c47d1fd [file] [log] [blame]
/*******************************************************************************
* 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 {
protected 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;
/**
* @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
*/
public ICSSStyle getCSSStyle() {
return getCSSFigure().getCSSStyle();
}
/**
* @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();
}
protected 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
*/
protected boolean supportAbsolutePosition() {
if (findContainingPositionedFigure() == null) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.pagedesigner.css2.layout.FlowContainerLayout#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();
}
public FlowContext getParentFigureContext() {
return super.getFlowContext();
}
public 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._marginInsets.right;
}
return 0;
}
public void setCalculatingMaxWidth(boolean c) {
_calculatingMaxWidth = c;
}
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();
// ------------------------------------------------------------------------------------
public CSSFigure getCSSFigure() {
return (CSSFigure) getFlowFigure();
}
/**
*
* @return
*/
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
*/
public 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
*/
public boolean handlingBorderForBlock() {
return true;
}
/**
* This method is called when the corresponding figure is revalidated.
*
*/
public void figureRevalidate() {
// child class can override.
}
}