diff options
Diffstat (limited to 'jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout')
49 files changed, 9313 insertions, 0 deletions
diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockBox.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockBox.java new file mode 100644 index 000000000..7c635a59c --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockBox.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.geometry.Rectangle; + +/** + * A CompositeBox suitable for containing multiple LineBox fragments. Based on + * BlockBox of draw2d. + * + * @author mengbo + */ +public class BlockBox extends CompositeBox { + // internalContent dimension is for the closure of the FlowBox(es) added + // into the BlockBox. + private int _internalContentWidth = -1; + + private int _internalContentHeight = -1; + + Rectangle toRectangle() { + return new Rectangle(_x, _y, Math.max(_width, _recommendedWidth), + _height); + } + + /** + * Sets the height. + * + * @param h + * The height + */ + public void setHeight(int h) { + _height = h; + } + + /** + * Unions the dimensions of this with the dimensions of the passed FlowBox. + * For BlockBox, each time unionInfo is called, the passed in object + * represents a line. + * + * @param box + * The FlowBox to union this with + */ + protected void unionInfo(FlowBox box) { + _width = Math.max(_width, box._width + this.getBorderPaddingWidth()); + _height = Math.max(_height, box._y + box._height + + this.getBorderPaddingHeight()); + + _internalContentWidth = Math.max(_internalContentWidth, box._width); + _internalContentHeight = Math.max(_internalContentHeight, box._y + + box._height); + } + + public int getInternalContentWidth() { + return _internalContentWidth; + } + + public int getInternalContentHeight() { + return _internalContentHeight; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowBox#getAscent() + */ + public int getAscent() { + // XXX: some hard coded things here. If the blockbox is only for a + // single widget, and if that widget support ascent, then we'll + // delegate to that widget for ascent support. + // if (_fragments.size()==1) + // { + // FlowBox box = (FlowBox) _fragments.get(0); + // if (box instanceof LineBox) + // { + // List linecomponents = ((LineBox) box).getFragments(); + // if (linecomponents != null && linecomponents.size() == 1) + // { + // FlowBox box2 = (FlowBox) linecomponents.get(0); + // if (box2 instanceof WidgetBox) + // { + // WidgetBox widgetBox = (WidgetBox) box2; + // if (widgetBox.supportAscent()) + // { + // return widgetBox.getAscent() + this.getBorderPaddingInsets().top; + // } + // } + // } + // } + // } + return super.getAscent(); + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlow.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlow.java new file mode 100644 index 000000000..35eb4c7c2 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlow.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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.PositionConstants; + +/** + * A <code>FlowFigure</code> represented by a single {@link BlockBox}fragment + * containing one or more lines. A BlockFlow is a creator of LineBoxes, which + * its children require during layout. A BlockFlow can be thought of as a + * paragraph. + * <P> + * BlockFlows should be nested inside other BlockFlows, but it is also valid to + * place them in InlineFlows. {@link FlowPage}can be used as a "root" block and + * can be added to normal draw2d Figures. + * <P> + * Only {@link FlowFigure}s can be added to a BlockFlow. + */ +public class BlockFlow extends FlowFigure { + + final BlockBox _blockBox; + + private int _aligment; + + /** + * Constructs a new BlockFlow. + */ + public BlockFlow() { + setLayoutManager(createDefaultFlowLayout()); + _blockBox = createBlockBox(); + } + + BlockBox createBlockBox() { + return new BlockBox(); + } + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigure#createDefaultFlowLayout() + */ + protected FlowFigureLayout createDefaultFlowLayout() { + return new BlockFlowLayout(this); + } + + /** + * Returns the BlockBox associated with this. + * + * @return This BlockFlow's BlockBox + */ + protected BlockBox getBlockBox() { + return _blockBox; + } + + /** + * Returns the horizontal aligment. + * + * @return the hotizontal aligment + */ + public int getHorizontalAligment() { + return _aligment & PositionConstants.LEFT_CENTER_RIGHT; + } + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigure#postValidate() + */ + public void postValidate() { + setBounds(getBlockBox().toRectangle().expand(getInsets())); + List v = getChildren(); + for (int i = 0, n = v.size(); i < n; i++) { + ((FlowFigure) v.get(i)).postValidate(); + } + } + + /** + * Sets the horitontal aligment of the block. Valid values are: + * <UL> + * <LI>{@link org.eclipse.draw2d.PositionConstants#LEFT} + * <LI>{@link org.eclipse.draw2d.PositionConstants#RIGHT} + * <LI>{@link org.eclipse.draw2d.PositionConstants#CENTER} + * + * @param value + * the aligment + */ + public void setHorizontalAligment(int value) { + if (!(value == PositionConstants.LEFT + || value == PositionConstants.RIGHT || value == PositionConstants.CENTER)) { + throw new IllegalArgumentException( + "Horizontal Aligment must be one of: LEFT, CENTER, RIGHT"); + } + this._aligment &= ~PositionConstants.LEFT_CENTER_RIGHT; + this._aligment |= value; + revalidate(); + } + + /** + * @see org.eclipse.draw2d.Figure#useLocalCoordinates() + */ + protected boolean useLocalCoordinates() { + return true; + } + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlowContext.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlowContext.java new file mode 100644 index 000000000..55faec578 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlowContext.java @@ -0,0 +1,284 @@ +/******************************************************************************* + * 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 org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.jst.pagedesigner.css2.value.Length; + +/** + * When doing absolute positioning, we need to create a block. But that block + * don't have a corresponding figure. So we need a block without corresponding + * figure. + * + * @author mengbo + * @version 1.5 + */ +public class BlockFlowContext implements FlowContext { + protected LineBox _currentLine; + + private LineBox _previousLine = null; + + protected BlockBox _blockBox; + + protected FlowContext _originalContext; + + protected ICSSStyle _style; + + /** + * + */ + public BlockFlowContext(FlowContext originalContext, ICSSStyle style) { + this._originalContext = originalContext; + this._style = style; + setup(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getContainerWidth() + */ + public int getContainerWidth() { + + return _originalContext.getContainerWidth(); + } + + public void setup() { + _blockBox = new BlockBox(); + _blockBox.setRecommendedWidth(getRecommendedWidth()); + _currentLine = this.getCurrentLine(); + _previousLine = null; + } + + private int getRecommendedWidth() { + int containerWidth = getContainerWidth(); + Object leftObj = _style.getStyleProperty(ICSSPropertyID.ATTR_LEFT); + if (leftObj != null && leftObj instanceof Length) { + Length left = (Length) leftObj; + int intLeft = left.getValue(); + if (left.isPercentage()) { + intLeft = containerWidth * intLeft / 100; + } + if (intLeft < containerWidth) { + return containerWidth - intLeft; + } + } + return containerWidth; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#addToCurrentLine(org.eclipse.jst.pagedesigner.css2.layout.FlowBox) + */ + public void addToCurrentLine(FlowBox block) { + getCurrentLine().add(block); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.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()) + layoutLine(); // finalize the current line layout + else + return; + + LineBox box = _currentLine; + // _currentLine = _previousLine; //XXX: ???? why (yang) + _previousLine = box; + + _currentLine = null; + // setupLine(getCurrentLine()); + + } + + /** + * @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); + return _currentLine; + } + + /** + * @param topMargin + */ + private void createNewLine(int topMargin) { + createNewLine(); + } + + protected void createNewLine() { + _currentLine = new LineBox(); + setupLine(_currentLine, Integer.MIN_VALUE); + } + + /** + * Override to setup the line's x, remaining, and available width. + * + * @param line + * the LineBox to set up + */ + 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 = _blockBox._borderInsets.left + _blockBox._paddingInsets.left; + + // FIXME: here should check the floating boxes, and minus the width of + // them from + // current line. + // XXX: the RecommendedContentWidth is related with the RecommendedWidth + // of container that + // usually larger than it needed.here we do not set the RecommendedWidth + // for the sake of + // layouting right absolute position. + // /shortcoming:the box will break into multi-line after every white + // space. + // line.setRecommendedWidth(_blockBox.getRecommendedContentWidth()); + if (_previousLine == null) { + line._y = _blockBox._borderInsets.top + + _blockBox._paddingInsets.top; + if (topMargin != Integer.MIN_VALUE) + line._y += topMargin; + } else { + if (topMargin == Integer.MIN_VALUE) + line._y = _previousLine._y + _previousLine.getHeight() + + getLinePadding() + _previousLine._marginInsets.bottom; // XXX: + // should + // add + // previous + // margin + // bottom? + else + line._y = _previousLine._y + + _previousLine.getHeight() + + Math.max(topMargin, + _previousLine._marginInsets.bottom); + } + // line.validate(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentY() + */ + public int getCurrentY() { + return getCurrentLine()._y; // FIXME: margin of previous block? + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#isCurrentLineOccupied() + */ + public boolean isCurrentLineOccupied() { + return _currentLine != null && _currentLine.isOccupied(); + } + + /* + * (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; + } else { + return 0; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#isCalculatingMaxWidth() + */ + public boolean isCalculatingMaxWidth() { + return false; + } + + /** + * Adjust all fragments in the current line to have the same baseline. Do + * any additional adjustments, such as horizontal alignment. + */ + protected void layoutLine() { + // 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.getRecommendedContentWidth() + + // _blockBox.getBorderPaddingInsets().left - _currentLine.getWidth(); + // } + // else if (textalign == ICSSPropertyID.VAL_CENTER) + // { + // + // _currentLine._x = _blockBox.getBorderPaddingInsets().left + + // (_blockBox.getRecommendedContentWidth() - _currentLine.getWidth()) / + // 2; + // } + // if (_currentLine._x < 0) + // _currentLine._x = 0; + // } + + // FIXME: should check vertical alignment here? + _currentLine.commit(); + _blockBox.add(_currentLine); + } + + public void endBlock() { + endLine(); + } + + int getLinePadding() { + return 0; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlowLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlowLayout.java new file mode 100644 index 000000000..8647f293c --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BlockFlowLayout.java @@ -0,0 +1,251 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Insets; + +/** + * The layout for {@link BlockFlow}figures. + * <P> + * WARNING: This class is not intended to be subclassed by clients. + * + * @author mengbo + * @since 2.1 + */ +public class BlockFlowLayout extends FlowContainerLayout { + private LineBox _previousLine = null; + + BlockBox _blockBox; + + /** + * Creates a new BlockFlowLayout with the given BlockFlow. + * + * @param blockFlow + * the BlockFlow + */ + public BlockFlowLayout(BlockFlow blockFlow) { + super(blockFlow); + } + + /** + * @see FlowContainerLayout#cleanup() + */ + protected void cleanup() { + _currentLine = _previousLine = null; + } + + /** + * @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); + } + + /** + * Override to setup the line's x, remaining, and available width. + * + * @param line + * the LineBox to set up + */ + 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._marginInsets.bottom; // XXX: + // should + // add + // previous + // margin + // bottom? + } else { + line._y = _previousLine._y + + _previousLine.getHeight() + + Math.max(topMargin, + _previousLine._marginInsets.bottom); + } + } + // line.validate(); + } + + /** + * Called by flush(), adds the BlockBox associated with this BlockFlowLayout + * to the current line and then ends the line. + */ + protected void endBlock() { + getFlowContext().addToCurrentLine(_blockBox); + + // FIXME: here should tell the context the bottom margin. + getFlowContext().endLine(); + } + + /** + * @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()) { + layoutLine(); // finalize the current line layout + } else { + _currentLine = null; + return; + } + LineBox box = _currentLine; + _previousLine = box; + _currentLine = null;// _previousLine; //XXX: ???? why (yang) + + // setupLine(getCurrentLine()); + } + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentY() + */ + public int getCurrentY() { + return getCurrentLine()._y; // FIXME: margin of previous block? + } + + /** + * Returns the BlockFlow associated with this BlockFlowLayout + * + * @return the BlockFlow + */ + protected final BlockFlow getBlockFlow() { + return (BlockFlow) getFlowFigure(); + } + + /** + * Adjust all fragments in the current line to have the same baseline. Do + * any additional adjustments, such as horizontal alignment. + */ + protected void layoutLine() { + // 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 + switch (getBlockFlow().getHorizontalAligment()) { + case PositionConstants.RIGHT: + _currentLine._x = _blockBox.getContentWidth() + - getBorderPaddingInsets().right - _currentLine.getWidth(); + break; + case PositionConstants.CENTER: + _currentLine._x = (_blockBox.getContentWidth() + + getBorderPaddingInsets().left + - getBorderPaddingInsets().right - _currentLine.getWidth()) / 2; + break; + } + // FIXME: should check vertical alignment here? + _currentLine.commit(); + _blockBox.add(_currentLine); + } + + /** + * @see FlowContainerLayout#flush() + */ + protected void flush() { + if (_currentLine != null) + layoutLine(); + endBlock(); + } + + /** + * @see FlowContainerLayout#preLayout() + */ + protected void preLayout() { + _blockBox = getBlockFlow().getBlockBox(); + setupBlock(); + // Probably could setup current and previous line here, or just previous + } + + /** + * sets up the single block that contains all of the lines. + */ + protected void setupBlock() { + // Ask for a new line, in case we are in the middle of a line + + // FIXME: the endLine() should tell context the top margin of this + // block. + getFlowContext().endLine(); + + LineBox line = getFlowContext().getCurrentLine(); + // int recommended = line.getAvailableWidth(); + // if (recommended != previousRecommendedWidth) + // Remove all current Fragments + _blockBox.clear(); + + // Setup the one fragment for this Block with the correct X and + // available width + + // FIXME: here should check whether the CSS already set recommended + // width for this + // block. + _blockBox.setRecommendedWidth(line.getAvailableWidth()); + + _blockBox._y = getFlowContext().getCurrentY(); + + // FIXME: blockBox.x should be context.getBorderPaddingInsets().left + // or just line.x ? + _blockBox._x = 0; + } + + Insets getBorderPaddingInsets() { + // FIXME: + return new Insets(); + } + + int getLinePadding() { + return 0; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#dispose() + */ + public void dispose() { + } + + /* + * (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; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BoxUtil.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BoxUtil.java new file mode 100644 index 000000000..54978d145 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/BoxUtil.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.swt.graphics.Color; + +/** + * @author mengbo + */ +public class BoxUtil { + /** + * @param box + * @param style + */ + public static void setupBorderPaddingMargin(FlowBox box, ICSSStyle style) { + box._marginInsets = new Insets(style.getMarginInsets()); + box._borderInsets = new Insets(style.getBorderInsets()); + box._paddingInsets = new Insets(style.getPaddingInsets()); + + if (box.getBorderPaddingHeight() > box.getHeight()) { + box.setHeight(box.getBorderPaddingHeight()); + } + if (box.getBorderPaddingWidth() > box.getWidth()) { + box.setWidth(box.getBorderPaddingWidth()); + } + } + + /** + * Debug code. + * + * @param g + * @param box + */ + public static void drawBox(Graphics g, FlowBox box) { + Color color = null; + if (box instanceof BlockBox) { + // color = ColorConstants.red; + } else if (box instanceof LineBox) { + color = ColorConstants.blue; + } else if (box instanceof TextFragmentBox) { + color = ColorConstants.green; + } else { + color = ColorConstants.darkGreen; + } + if (color != null) { + g.setForegroundColor(color); + g.setLineStyle(Graphics.LINE_DASH); + g.setLineWidth(1); + g.drawRectangle(box._x, box._y, box.getWidth(), box.getHeight()); + } + } + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSBlockFlowLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSBlockFlowLayout.java new file mode 100644 index 000000000..6460f5ffd --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSBlockFlowLayout.java @@ -0,0 +1,723 @@ +/******************************************************************************* + * 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; + + protected BlockBox _blockBox = null; + + 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. + */ + public CSSBlockFlowLayout(CSSFigure cssfigure) { + super(cssfigure); + } + + protected boolean hasMoreThanOneLine() { + return _previousLine != null; + } + + public boolean isInlineBlock() { + String obj = getCSSStyle().getDisplay(); + return ICSSPropertyID.VAL_INLINE_BLOCK.equals(obj) + || ICSSPropertyID.VAL_INLINE_TABLE.equals(obj); + } + + /** + * whether should expand the width to all available width. + * + * @return + */ + public boolean shouldExpand() { + ICSSStyle style = getCSSStyle(); + if (style == null) { + return false; + } else { + return "block".equalsIgnoreCase(style.getDisplay()) + || "list-item".equalsIgnoreCase(style.getDisplay()); + } + } + + // --------------------------------------------------------------------------------------------------- + // 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._marginInsets = 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); + } + + 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().getContainerWidth() + * 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 + } + + // ------------------------------------------------------------------------------------------------------- + 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._paddingInsets.getHeight() + + BorderUtil.SCROLL_WIDTH) { + _needVScroll = true; + } + } + if (!_needHScroll && _needVScroll) { + if (_userSpecifiedWidth > 0 + && _blockBox.getInternalContentWidth() >= 0 + && _userSpecifiedWidth < _blockBox + .getInternalContentWidth() + + _blockBox._paddingInsets.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._marginInsets.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()._marginInsets.bottom = getCSSStyle() + .getMarginInsets().bottom; + + if (!isInlineBlock()) { + getFlowContext().endLine(); + } + } + + 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 + */ + 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._marginInsets.bottom; // XXX: + // should + // add + // previous + // margin + // bottom? + } else { + line._y = _previousLine._y + + _previousLine.getHeight() + + Math.max(topMargin, + _previousLine._marginInsets.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._borderInsets); + + 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); + } + } + } + } + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSBrFlowLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSBrFlowLayout.java new file mode 100644 index 000000000..b343aaea9 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSBrFlowLayout.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * 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.Graphics; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.jst.pagedesigner.PDPlugin; +import org.eclipse.swt.graphics.Image; + +/** + * @author mengbo + */ +public class CSSBrFlowLayout extends CSSInlineFlowLayout implements ICSSPainter { + /** + * @param flow + */ + public CSSBrFlowLayout(CSSFigure flow) { + super(flow); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSInlineFlowLayout#flush() + */ + protected void flush() { + FlowBox forcedBox = new FlowBox(); + forcedBox.setWidth(16); + forcedBox.setHeight(getCSSStyle().getCSSFont().getXHeight()); + addToCurrentLine(forcedBox); + endLine(); + + FlowBox flowbox = new FlowBox(); + flowbox.setHeight(getCSSStyle().getCSSFont().getFontSize()); + getCurrentLine().add(flowbox); + + super.flush(); + } + + public void paintFigure(Graphics g) { + List fragments = getFragmentsForRead(); + if (!fragments.isEmpty()) { + FlowBox box = (FlowBox) fragments.get(0); + g.drawImage(getSharedHTMLImage(), new Point(box._x, box._y)); + } + } + + private static Image getSharedHTMLImage() { + return PDPlugin.getDefault().getImage("palette/HTML/small/HTML_BR.gif"); + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSFigure.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSFigure.java new file mode 100644 index 000000000..60580d2bb --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSFigure.java @@ -0,0 +1,518 @@ +/******************************************************************************* + * 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.Border; +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.LayoutManager; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.PDPlugin; +import org.eclipse.jst.pagedesigner.common.logging.Logger; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.border.CSSBorder; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.jst.pagedesigner.css2.property.VisibilityMeta; +import org.eclipse.jst.pagedesigner.css2.style.DefaultStyle; +import org.eclipse.jst.pagedesigner.css2.style.ITagEditInfo; +import org.eclipse.jst.pagedesigner.css2.widget.BorderUtil; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; + +/** + * Normally a CSSFigure is a container. It's layout will be driven by different + * display type information from the style. + * + * Each CSSFigure will be driven by ICSSStyle, the display type of the ICSSStyle + * will decide the layout to be used for the figure. + * + * @author mengbo + */ +public class CSSFigure extends FlowFigure implements ICSSFigure { + private static Logger _log = PDPlugin.getLogger(CSSFigure.class); + + private static final Rectangle PRIVATE_RECT = new Rectangle(); + + ICSSStyle _style; + + // NOTE: here keep the element is only for debug use. CSSFigure shouldn't + // require an element. + // Element _element; + + // if this field is set, then regetLayout() will still return this layout, + // without going through the CSS resolution + CSSLayout _fixedLayout; + + public CSSFigure() { + _style = DefaultStyle.getInstance(); + invalidateCSS(); + } + + public CSSFigure(ICSSStyle style) { + _style = style; + // _element = element; + invalidateCSS(); + } + + public ICSSStyle getCSSStyle() { + return _style; + } + + public void setCSSStyle(ICSSStyle style) { + _style = style; + invalidateCSS(); + } + + public void revalidate() { + CSSLayout layout = (CSSLayout) getLayoutManager(); + layout.figureRevalidate(); + super.revalidate(); + } + + /** + * this method is called when the css source noticed style change. So tell + * the figure should invalidate its cached data. + */ + public void invalidateCSS() { + // maybe we changed from inline to block or block to inline + // XXX: or even to table? + CSSLayout layout = regetLayout(getLayoutManager()); + this.setLayoutManager(layout); + } + + public void setFixedLayoutManager(CSSLayout layout) { + this._fixedLayout = layout; + this.setLayoutManager(regetLayout(getLayoutManager())); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.Figure#setLayoutManager(org.eclipse.draw2d.LayoutManager) + */ + public void setLayoutManager(LayoutManager manager) { + LayoutManager old = getLayoutManager(); + if (old != manager) { + FlowContext context = null; + if (old instanceof FlowFigureLayout) { + context = ((FlowFigureLayout) old).getOriginalFlowContext(); + } + if (manager instanceof FlowFigureLayout) { + ((FlowFigureLayout) manager).setOriginalFlowContext(context); + } + + if (manager instanceof FlowContext) { + List list = getChildren(); + for (int i = 0, size = list.size(); i < size; i++) { + try { + ((FlowFigure) list.get(i)) + .setOriginalFlowContext((FlowContext) manager); + } catch (ClassCastException classcastexception) { + // Error in flowContext setting. + _log.error("Error.CSSFigure.0", classcastexception); //$NON-NLS-1$ + } + } + } + } + super.setLayoutManager(manager); + } + + protected CSSLayout regetLayout(LayoutManager old) { + if (_fixedLayout != null) { + return _fixedLayout; + } + CSSLayout layout = DisplayToLayout.displayToLayout(this, getCSSStyle() + .getDisplay(), old); + if (layout != null) { + return layout; + } else { + return new CSSInlineFlowLayout(this); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.Figure#containsPoint(int, int) + */ + public boolean containsPoint(int x, int y) { + // check whether any float figure contains it. + // FIXME: need check floating figure here!!! + if (!super.containsPoint(x, y)) { + return false; + } + List frags = getFragmentsForRead(); + // Here we should not get void pointer. + if (frags != null) { + for (int i = 0; i < frags.size(); i++) { + FlowBox box = (FlowBox) frags.get(i); + if (box != null && box.containsPoint(x, y)) { + return true; + } + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure#getFragmentsForRead() + */ + public List getFragmentsForRead() { + CSSLayout layout = (CSSLayout) getLayoutManager(); + return layout.getFragmentsForRead(); + } + + /** + * this method is a shortcut to getFragmentsForRead + * + * @return + */ + public Rectangle[] getFragmentsBounds() { + List list = getFragmentsForRead(); + if (list == null || list.size() == 0) { + // should not happen. but still handle it. + return new Rectangle[] { getBounds() }; + } else { + Rectangle[] ret = new Rectangle[list.size()]; + for (int i = 0, size = list.size(); i < size; i++) { + FlowBox box = (FlowBox) list.get(i); + ret[i] = new Rectangle(box._x, box._y, box.getWidth(), box + .getHeight()); + } + return ret; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.IFigure#setBounds(org.eclipse.draw2d.geometry.Rectangle) + */ + public void setBounds(Rectangle r) { + if (getBounds().equals(r)) { + return; + } + boolean invalidate = getBounds().width != r.width + || getBounds().height != r.height; + super.setBounds(r); + + CSSLayout layout = (CSSLayout) this.getLayoutManager(); + layout.setBoundsCalled(r, invalidate); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigure#postValidate() + */ + public void postValidate() { + CSSLayout layout = (CSSLayout) getLayoutManager(); + layout.postValidateForAbsolute(); + layout.postValidate(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.IFigure#validate() + */ + public void validate() { + super.validate(); + // should not call this.postValidate() here. PostValidate() should + // only be started from the FlowPage. Otherwise it will be called + // multiple times on a figure. + // this.postValidate(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.Figure#useLocalCoordinates() + */ + protected boolean useLocalCoordinates() { + CSSLayout layout = (CSSLayout) getLayoutManager(); + if (layout == null) { + return false; + } else { + return layout.useLocalCoordinates(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.IFigure#paint(org.eclipse.draw2d.Graphics) + */ + public void paint(Graphics graphics) { + ICSSStyle style = getCSSStyle(); + if (style != null) { + Object visibility = style + .getStyleProperty(ICSSPropertyID.ATTR_VISIBILITY); + // handle visibility: hidden here. + // TODO: "collapse" is not supported yet! + if (VisibilityMeta.HIDDEN.equals(visibility)) { + return; + } + } + + CSSLayout layout = (CSSLayout) this.getLayoutManager(); + graphics.pushState(); + try { + paintFigure(graphics); + graphics.restoreState(); + paintClientArea(graphics); + if (layout instanceof ICSSPainter2) { + if (useLocalCoordinates()) { + graphics.translate(getBounds().x + getInsets().left, + getBounds().y + getInsets().top); + ((ICSSPainter2) layout).paintFigurePostClientArea(graphics); + graphics.restoreState(); + } else { + ((ICSSPainter2) layout).paintFigurePostClientArea(graphics); + } + } + paintBorder(graphics); + } finally { + graphics.popState(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigure#paintFigure(org.eclipse.draw2d.Graphics) + */ + protected void paintFigure(Graphics g) { + Color rgbColor = null; + boolean fillArea = false; + Object bg = getCSSStyle().getBackgroundColor(); + if (bg instanceof RGB) { + rgbColor = new Color(null, (RGB) bg); + g.setBackgroundColor(rgbColor); + fillArea = true; + } else if (bg instanceof Color) { + g.setBackgroundColor((Color) bg); + fillArea = true; + } + if (fillArea) { + List fragments = getFragmentsForRead(); + + for (int i = 0, n = fragments.size(); i < n; i++) { + Object obj = fragments.get(i); + if (obj instanceof FlowBox) { + FlowBox box = (FlowBox) obj; + g.fillRectangle(box._x, box._y, box.getWidth(), box + .getHeight()); + } + } + } + if (rgbColor != null) { + rgbColor.dispose(); + } + g.restoreState(); + + LayoutManager layout = getLayoutManager(); + if (layout instanceof ICSSPainter) { + if (useLocalCoordinates()) { + g.translate(getBounds().x + getInsets().left, getBounds().y + + getInsets().top); + ((ICSSPainter) layout).paintFigure(g); + g.restoreState(); + } else { + ((ICSSPainter) layout).paintFigure(g); + } + } + + // paint selected mode here. + paintSelection(g); + + if (Debug.DEBUG_BOX) { + // draw two levels of boxes. Since normally each figure will only + // have two levels of boxes. + List fragments = this.getFragmentsForRead(); + for (int i = 0, size = fragments.size(); i < size; i++) { + FlowBox box = (FlowBox) fragments.get(i); + BoxUtil.drawBox(g, box); + if (box instanceof BlockBox) { + BlockBox compositeBox = (BlockBox) box; + List list = compositeBox.getFragments(); + for (int j = 0; j < list.size(); j++) { + g.translate(this.getInsets().left, + this.getInsets().right); + BoxUtil.drawBox(g, (FlowBox) list.get(j)); + g.restoreState(); + } + } + } + } + if (Debug.DEBUG_BASELINE) { + List fragments = this.getFragmentsForRead(); + for (int i = 0, size = fragments.size(); i < size; i++) { + Object obj = fragments.get(i); + if (obj instanceof LineBox) { + LineBox linebox = (LineBox) obj; + g.setForegroundColor(ColorConstants.red); + g.drawLine(linebox._x, linebox._y + linebox.getAscent(), + linebox._x + linebox.getWidth(), linebox._y + + linebox.getAscent()); + } + } + } + + if (Debug.DEBUG_BORDERPADDING) { + if (this.getLayoutManager() instanceof CSSBlockFlowLayout) { + g.setLineWidth(1); + Rectangle rect = getBounds().getCopy().crop(getInsets()); + g.setForegroundColor(ColorConstants.green); + g.drawRectangle(rect); + g.setForegroundColor(ColorConstants.red); + g.drawRectangle(getBounds()); + } + } + + if (Debug.DEBUG_BOX) { + CSSLayout csslayout = (CSSLayout) this.getLayoutManager(); + if (csslayout._absoluteContext != null) { + BlockBox blockbox = csslayout._absoluteContext._blockBox; + g.setLineWidth(1); + g.setForegroundColor(ColorConstants.green); + g.drawRectangle(blockbox._x, blockbox._y, blockbox.getWidth(), + blockbox.getHeight()); + } + } + } + + /** + * Paints this Figure's client area. The client area is typically defined as + * the anything inside the Figure's {@link Border} or {@link Insets}, and + * by default includes the children of this Figure. On return, this method + * must leave the given Graphics in its initial state. + * + * @param graphics + * The Graphics used to paint + * @since 2.0 + */ + protected void paintClientArea(Graphics graphics) { + if (this.getChildren().isEmpty()) { + return; + } + + Object overflow = ICSSPropertyID.VAL_VISIBLE; + ICSSStyle style = this.getCSSStyle(); + if (style != null) { + overflow = style.getStyleProperty(ICSSPropertyID.ATTR_OVERFLOW); + } + + boolean optimizeClip = ICSSPropertyID.VAL_VISIBLE.equals(overflow); + + if (useLocalCoordinates()) { + graphics.translate(getBounds().x + getInsets().left, getBounds().y + + getInsets().top); + if (!optimizeClip) { + graphics.clipRect(getClientArea(PRIVATE_RECT)); + } + graphics.pushState(); + paintChildren(graphics); + graphics.popState(); + graphics.restoreState(); + } else { + if (optimizeClip) { + paintChildren(graphics); + } else { + graphics.clipRect(getClientArea(PRIVATE_RECT)); + graphics.pushState(); + paintChildren(graphics); + graphics.popState(); + graphics.restoreState(); + } + } + } + + /** + * @param g + */ + protected void paintSelection(Graphics g) { + ICSSStyle style = this.getCSSStyle(); + if (style != null) { + if (style.isInSelection()) { + ITagEditInfo editInfo = (ITagEditInfo) style + .getAdapter(ITagEditInfo.class); + if (editInfo != null && editInfo.isWidget()) { + BorderUtil.maskFigure(this, g); + } + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.Figure#paintBorder(org.eclipse.draw2d.Graphics) + */ + protected void paintBorder(Graphics graphics) { + CSSLayout layout = (CSSLayout) getLayoutManager(); + if (layout != null && !layout.handlingBorderForBlock()) { + return; + } + + ICSSStyle style = this.getCSSStyle(); + if (style != null) { + CSSBorder border = new CSSBorder(this.getCSSStyle()); + border.paint(this, graphics, NO_INSETS); + + // draw a border for those special elements like <h:form>, etc. + ITagEditInfo editInfo = (ITagEditInfo) style + .getAdapter(ITagEditInfo.class); + if (editInfo != null && editInfo.needBorderDecorator()) { + BorderUtil.drawBorderDecorator(this, graphics); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.IFigure#getInsets() + */ + public Insets getInsets() { + CSSLayout layout = (CSSLayout) getLayoutManager(); + if (layout != null && !layout.handlingBorderForBlock()) { + return new Insets(); + } + ICSSStyle style = this.getCSSStyle(); + if (style != null) { + return style.getBorderInsets().getAdded(style.getPaddingInsets()); + } + return new Insets(); + } + + /** + * FIXME: need trace the implementation of Figure.invalidate() We want to + * just mark this figure as invalid, but don't want to the layout get + * invalidated. + * + */ + public void invalidate2() { + if (!isValid()) + return; + // if (getLayoutManager() != null) + // getLayoutManager().invalidate(); + setValid(false); + + } + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSInlineFlowLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSInlineFlowLayout.java new file mode 100644 index 000000000..97f4571c0 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSInlineFlowLayout.java @@ -0,0 +1,319 @@ +/******************************************************************************* + * 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.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.jst.pagedesigner.css2.style.ITagEditInfo; + +/** + * The layout manager for {@link CSSFigure}figures. This class is based on + * InlineFlowLayout of draw2d. + * + * @author mengbo + */ +public class CSSInlineFlowLayout extends CSSLayout { + List _fragments = new ArrayList(); + + /** + * Creates a new InlineFlowLayout with the given FlowFigure. + * + * @param flow + * The FlowFigure + */ + public CSSInlineFlowLayout(CSSFigure flow) { + super(flow); + } + + /** + * Clears out all fragments prior to the call to layoutChildren(). + */ + public void preLayout() { + super.preLayout(); + _fragments.clear(); + // force creating of the first line. avoid empty element don't have + // fragments. + // createFirstLine(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContainerLayout#layoutChildren() + */ + protected void layoutChildren() { + // For designer, to make it to have some size. otherwise can't + // be found on screen. + // List children = getCSSFigure().getChildren(); + // if (children.size() == 0) + // { + // FlowBox box = new FlowBox(); + // box._height = getCSSStyle().getCSSFont().getFontSize(); + // box._width = 2; + // addToCurrentLine(box); + // + // } + super.layoutChildren(); + } + + /** + * Adds the given FlowBox to the current line of this InlineFlowLayout. + * + * @param block + * the FlowBox to add to the current line + */ + public void addToCurrentLine(FlowBox block) { + getCurrentLine().add(block); + // XXX: ???: will currentLine be added multiple times to fragments? + // (yang) + // _fragments.add(_currentLine); + } + + private void createFirstLine() { + _currentLine = new LineBox(); + setupLine(_currentLine, true); + _fragments.add(_currentLine); + } + + /** + * @see FlowContainerLayout#createNewLine() + */ + protected void createNewLine() { + _currentLine = new LineBox(); + setupLine(_currentLine, false); + _fragments.add(_currentLine); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContainerLayout#createNewLine(int) + */ + protected void createNewLine(int topMargin) { + // inline flow don't support vertical margin. + createNewLine(); + } + + /** + * @see FlowContainerLayout#cleanup() + */ + protected void cleanup() { + _currentLine = null; + } + + /** + * @see FlowContainerLayout#flush() + */ + protected void flush() { + if (_fragments.isEmpty()) { + createFirstLine(); + } else if (_fragments.size() == 1) { + + ICSSStyle style = getCSSStyle(); + int minWidth = 0, 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(); + } + FlowBox box = (FlowBox) _fragments.get(0); + if (minWidth > box._width) { + box._width = minWidth; + } + if (minHeight > box._height) { + box._height = minHeight; + } + } + + if (_currentLine != null /* && _currentLine.isOccupied() */) { + _currentLine._marginInsets.right = getCSSStyle().getMarginInsets().right; + getFlowContext().addToCurrentLine(_currentLine); + } + + } + + /** + * @see FlowContext#endLine() + */ + public void endLine() { + if (_currentLine == null) { + getFlowContext().endLine(); + return; + } + // If nothing was ever placed in the line, ignore it. and if the line is + // the first line, just remove it. + if (_currentLine.isOccupied()) { + getFlowContext().addToCurrentLine(_currentLine); + } else if (_fragments.size() == 1) { + _fragments.remove(0); + } + getFlowContext().endLine(); + _currentLine = null; + } + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentY() + */ + public int getCurrentY() { + return getCurrentLine()._y; + } + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContainerLayout#isCurrentLineOccupied() + */ + public boolean isCurrentLineOccupied() { + if (_currentLine == null) { + return getFlowContext().isCurrentLineOccupied(); + } else if (_currentLine.getFragments().isEmpty()) { + return getFlowContext().isCurrentLineOccupied(); + } else { + return true; + } + } + + /** + * Initializes the given LineBox. Called by createNewLine(). + * + * @param line + * The LineBox to initialize. + */ + protected void setupLine(LineBox line, boolean firstline) { + LineBox parent = getFlowContext().getCurrentLine(); + line._x = 0; + line._y = getFlowContext().getCurrentY(); + + line.setRecommendedWidth(parent.getAvailableWidth()); + + setLineVerticalAlign(line); + setFontinfoForLine(line); + + if (firstline && getCSSStyle() != null) { + ICSSStyle style = getCSSStyle(); + int minWidth = 0, 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 (minWidth > 0) { + line.setWidth(minWidth); + } + int fontHeight = this.getCSSStyle().getCSSFont().getXHeight(); + if (minHeight > 0 && minHeight > fontHeight) { + line.setHeight(minHeight); + } else { + line.setHeight(fontHeight); + } + } + } + + private void setLineVerticalAlign(LineBox box) { + ICSSStyle style = getCSSStyle(); + if (style != null) { + box.setVerticalAlignData(style + .getStyleProperty(ICSSPropertyID.ATTR_VERTICAL_ALIGN)); + } + } + + private void setFontinfoForLine(LineBox line) { + + ICSSStyle style = getCSSStyle(); + if (style != null) { + line.setFontMetrics(FigureUtilities.getFontMetrics(style + .getCSSFont().getSwtFont())); + } + } + + /* + * (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() { + return _fragments; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSLayout#postValidate() + */ + public void postValidate() { + List list = _fragments; + + FlowBox box; + int left = Integer.MAX_VALUE, top = left; + int right = Integer.MIN_VALUE, bottom = right; + for (int i = 0; i < list.size(); i++) { + box = (FlowBox) list.get(i); + // if (box instanceof LineBox && !((LineBox) box).isOccupied()) + // { + // continue; // skip unoccupied line + // } + left = Math.min(left, box._x); + right = Math.max(right, box._x + box._width); + top = Math.min(top, box._y); + bottom = Math.max(bottom, box._y + box._height); + } + getCSSFigure().setBounds( + new Rectangle(left, top, right - left, bottom - top)); + 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() { + // FIXME: don't really understand what means for inline + return this.getFlowContext().getContainerWidth(); + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSLayout.java new file mode 100644 index 000000000..c38052946 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSLayout.java @@ -0,0 +1,457 @@ +/******************************************************************************* + * 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; + } else { + 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 = ((ICSSFigure) 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; + } else { + 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) { + } + + /** + * 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. + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSListItemLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSListItemLayout.java new file mode 100644 index 000000000..a3608ddad --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSListItemLayout.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * 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.FigureUtilities; +import org.eclipse.draw2d.Graphics; +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.list.CounterHelper; +import org.eclipse.jst.pagedesigner.css2.list.ICounterValueGenerator; +import org.eclipse.jst.pagedesigner.css2.marker.CounterUtil; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.jst.pagedesigner.css2.style.DefaultStyle; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; + +/** + * @author mengbo + */ +public class CSSListItemLayout extends CSSBlockFlowLayout implements + ICSSPainter { + private static final String DEFAULT_LIST_COUNTER = "_anonymous"; + + private static final int CIRCLE_DIAMETER = 6; + + private static final int DISC_DIAMETER = 5; + + private static final int ROUNDRECT_ARC = 2; + + private static final int TEXT_PADDING = 16; + + private int _count; + + /** + * @param cssfigure + */ + public CSSListItemLayout(CSSFigure cssfigure) { + super(cssfigure); + } + + /* + * (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) { + style = DefaultStyle.getInstance(); + } + + Rectangle drawArea = null; + Font font = getCSSStyle().getCSSFont().getSwtFont(); + + // draw the marker box + Object styleType = style + .getStyleProperty(ICSSPropertyID.ATTR_LIST_STYLE_TYPE); + + g.pushState(); + + Color newColor = null; + Object color = style.getColor(); + if (color instanceof Color) { + g.setForegroundColor((Color) color); + g.setBackgroundColor((Color) color); + } else if (color instanceof RGB) { + newColor = new Color(Display.getCurrent(), (RGB) color); + g.setForegroundColor(newColor); + g.setBackgroundColor(newColor); + } + + if (styleType instanceof String) { + int type = CounterHelper.toTypeInt((String) styleType); + switch (type) { + case CounterHelper.LIST_T_UPPER_ALPHA: + case CounterHelper.LIST_T_LOWER_ALPHA: + case CounterHelper.LIST_T_LOWER_ROMAN: + case CounterHelper.LIST_T_UPPER_ROMAN: + case CounterHelper.LIST_T_DECIMAL: + g.setFont(font); + String displayString = CounterUtil.convertCount(_count, type); + Point point = getDrawPointForText(displayString); + g.drawString(displayString, point); + break; + case CounterHelper.LIST_T_CIRCLE: + drawArea = getDrawAreaForGraph(CIRCLE_DIAMETER, CIRCLE_DIAMETER); + g.drawArc(drawArea, 0, 360); + break; + case CounterHelper.LIST_T_SQUARE: + drawArea = getDrawAreaForGraph(DISC_DIAMETER, DISC_DIAMETER); + g.fillRectangle(drawArea); + case CounterHelper.LIST_T_DECIMAL_LEADING_ZERO: + case CounterHelper.LIST_T_LOWER_GREEK: + case CounterHelper.LIST_T_ARMENIAN: + case CounterHelper.LIST_T_GEORGIAN: + case CounterHelper.LIST_T_IMAGE: + case CounterHelper.LIST_T_NONE: + default: + drawArea = getDrawAreaForGraph(DISC_DIAMETER, DISC_DIAMETER); + g.fillRoundRectangle(drawArea, ROUNDRECT_ARC, ROUNDRECT_ARC); + break; + } + } + g.popState(); + + if (newColor != null) { + newColor.dispose(); + } + } + + /** + * @param g + * @return + */ + private Rectangle getDrawAreaForGraph(int width, int height) { + Rectangle drawArea; + + int x = 0; + int y = 0; + + List list = _blockBox.getFragments(); + Rectangle box = _blockBox.toRectangle().getCopy().expand( + _blockBox.getBorderPaddingInsets().getAdded( + _blockBox._marginInsets)); + if (list != null && !list.isEmpty()) { + LineBox line = (LineBox) list.get(0); + y = line.getBaseline() - CIRCLE_DIAMETER; + x = box.x; + } else { + x = box.x; + y = box.height / 2 - CIRCLE_DIAMETER; + } + drawArea = new Rectangle(x - CIRCLE_DIAMETER * 5 / 2, y, width, height); + return drawArea; + } + + private Point getDrawPointForText(String displayString) { + Font font = getCSSStyle().getCSSFont().getSwtFont(); + + int x = 0; + int y = 0; + + Rectangle box = _blockBox.toRectangle().getCopy().expand( + _blockBox.getBorderPaddingInsets().getAdded( + _blockBox._marginInsets)); + + x = box.x - FigureUtilities.getTextWidth(displayString, font); + x = x + - (TEXT_PADDING - FigureUtilities.getFontMetrics(font) + .getDescent()); + + return new Point(x, y); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContainerLayout#layoutChildren() + */ + protected void layoutChildren() { + ICounterValueGenerator counter = this.getCSSStyle().findCounter( + DEFAULT_LIST_COUNTER, true); + if (counter != null) { + _count = counter.getCurrentCount(); + } else { + // should not happen. + _count = 1; // use 1 as the default value + } + super.layoutChildren(); + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSPageFlowLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSPageFlowLayout.java new file mode 100644 index 000000000..c74963a58 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSPageFlowLayout.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * 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.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; + +/** + * This layout if for those thigns that it's parent will decide its size. Such + * as table cell. + * + * @author mengbo + * @version 1.5 + */ +public class CSSPageFlowLayout extends CSSBlockFlowLayout { + private Dimension _pageSize = new Dimension(); + + private int _recommendedWidth; + + private int _pageSizeCacheKeys[] = new int[4]; + + private Dimension _pageSizeCacheValues[] = new Dimension[4]; + + private Dimension _cacheMaxWidthSize = null; + + /** + * @param cssfigure + */ + public CSSPageFlowLayout(CSSFigure cssfigure) { + super(cssfigure); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#invalidate() + */ + public void invalidate() { + super.invalidate(); + _pageSizeCacheKeys = new int[4]; + _pageSizeCacheValues = new Dimension[4]; + _pageSize = new Dimension(); + _recommendedWidth = 0; + _cacheMaxWidthSize = null; + } + + protected void endBlock() { + layoutLines(); + } + + /** + * TODO: This method is not being called. + */ + public void postValidate() { + Rectangle r = new Rectangle(_blockBox._x, _blockBox._y, _blockBox + .getWidth(), _blockBox.getHeight()); + r = r.expand(getCSSFigure().getInsets()); + _pageSize.width = r.width; + _pageSize.height = r.height; + + List list = getCSSFigure().getChildren(); + for (int i = 0; i < list.size(); i++) { + ((FlowFigure) list.get(i)).postValidate(); + } + + } + + /** + * Setup blockBox to the initial bounds of the Page + */ + protected void setupBlock() { + // Remove all current Fragments + _blockBox.clear(); + + // Setup the one fragment for this Block with the correct X and + // available width + int recommendedWidth = getRecommendedWidth(); + _blockBox.setRecommendedWidth(recommendedWidth); + + if (recommendedWidth > 0 && recommendedWidth != Integer.MAX_VALUE) { + _blockBox.setWidth(recommendedWidth); + } + + _blockBox._x = 0; + } + + public int getRecommendedWidth() { + return _recommendedWidth; + } + + private void setRecommendedWidth(int width) { + if (_recommendedWidth == width) { + return; + } + _recommendedWidth = width; + getCSSFigure().invalidate2(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSLayout#setBoundsCalled(org.eclipse.jst.pagedesigner.css2.layout.CSSFigure, + * org.eclipse.draw2d.geometry.Rectangle, boolean) + */ + public void setBoundsCalled(Rectangle r, boolean invalidate) { + super.setBoundsCalled(r, invalidate); + CSSFigure figure = getCSSFigure(); + int newWidth = r.width - figure.getInsets().getWidth(); + if (invalidate || getRecommendedWidth() != newWidth) { + setRecommendedWidth(newWidth); + figure.getUpdateManager().addInvalidFigure(figure); + } + } + + /** + * @see org.eclipse.draw2d.Figure#getPreferredSize(int, int) + */ + public Dimension getPreferredSize(IFigure container, int width, int h) { + if (width >= 0) { + width = Math.max(0, width - container.getInsets().getWidth()); + } + + for (int i = 0; i < 4; i++) { + if (_pageSizeCacheKeys[i] == width + && _pageSizeCacheValues[i] != null) { + return _pageSizeCacheValues[i]; + } + } + + _pageSizeCacheKeys[3] = _pageSizeCacheKeys[2]; + _pageSizeCacheKeys[2] = _pageSizeCacheKeys[1]; + _pageSizeCacheKeys[1] = _pageSizeCacheKeys[0]; + _pageSizeCacheKeys[0] = width; + + _pageSizeCacheValues[3] = _pageSizeCacheValues[2]; + _pageSizeCacheValues[2] = _pageSizeCacheValues[1]; + _pageSizeCacheValues[1] = _pageSizeCacheValues[0]; + + // Flowpage must temporarily layout to determine its preferred size + int oldWidth = getRecommendedWidth(); + setRecommendedWidth(width); + container.validate(); + _pageSizeCacheValues[0] = _pageSize.getExpanded(container.getInsets() + .getWidth(), container.getInsets().getHeight()); + + if (width != oldWidth) { + setRecommendedWidth(oldWidth); + container.getUpdateManager().addInvalidFigure(container); + } + return _pageSizeCacheValues[0]; + } + + public Dimension getMaxContentWidthSize(IFigure container) { + if (this._cacheMaxWidthSize == null) { + boolean b = getCalcuatingMaxWidth(); + setCalculatingMaxWidth(true); + + // Flowpage must temporarily layout to determine its preferred size + int oldWidth = getRecommendedWidth(); + setRecommendedWidth(Integer.MAX_VALUE); + container.validate(); + _cacheMaxWidthSize = _pageSize.getExpanded(container.getInsets() + .getWidth(), container.getInsets().getHeight()); + + if (0 != oldWidth) { + setRecommendedWidth(oldWidth); + container.getUpdateManager().addInvalidFigure(container); + } + + setCalculatingMaxWidth(b); + } + return _cacheMaxWidthSize; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSTextFigure.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSTextFigure.java new file mode 100644 index 000000000..a5a2871d1 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSTextFigure.java @@ -0,0 +1,302 @@ +/******************************************************************************* + * 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.ColorConstants; +import org.eclipse.draw2d.Graphics; +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.provider.ICSSTextProvider; +import org.eclipse.jst.pagedesigner.css2.style.DefaultStyle; +import org.eclipse.jst.pagedesigner.css2.style.StyleUtil; +import org.eclipse.jst.pagedesigner.viewer.CaretPositionResolver; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; + +/** + * @author mengbo + */ +public class CSSTextFigure extends FlowFigure implements ICSSFigure { + private ICSSTextProvider _provider; + + private List _fragments = new ArrayList(1); + + public CSSTextFigure(ICSSTextProvider provider) { + _provider = provider; + this.setLayoutManager(createDefaultFlowLayout()); + } + + public ICSSStyle getCSSStyle() { + IFigure parentFigure = this.getParent(); + if (parentFigure instanceof ICSSFigure) { + ICSSStyle style = ((ICSSFigure) parentFigure).getCSSStyle(); + if (style != null) { + return style; + } + } + return DefaultStyle.getInstance(); + } + + /** + * @see org.eclipse.draw2d.IFigure#containsPoint(int, int) + */ + public boolean containsPoint(int x, int y) { + if (!super.containsPoint(x, y)) { + return false; + } + List frags = getFragments(); + for (int i = 0, n = frags.size(); i < n; i++) { + if (((FlowBox) frags.get(i)).containsPoint(x, y)) { + return true; + } + } + return false; + } + + /** + * @see FlowFigure#createDefaultFlowLayout() + */ + protected FlowFigureLayout createDefaultFlowLayout() { + return new CSSTextLayout(this); + } + + /** + * Returns the <code>LineBox</code> fragments contained in this InlineFlow + * + * @return The fragments + */ + public List getFragments() { + return _fragments; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure#getFragmentsForRead() + */ + public List getFragmentsForRead() { + return getFragments(); + } + + public String getText() { + return _provider.getTextData(); + } + + /** + * @see FlowFigure#postValidate() + */ + public void postValidate() { + List list = getFragments(); + FlowBox box; + int left = Integer.MAX_VALUE, top = left; + int right = Integer.MIN_VALUE, bottom = right; + for (int i = 0, n = list.size(); i < n; i++) { + box = (FlowBox) list.get(i); + left = Math.min(left, box._x); + right = Math.max(right, box._x + box._width); + top = Math.min(top, box._y); + bottom = Math.max(bottom, box._y + box._height); + } + setBounds(new Rectangle(left, top, right - left, bottom - top)); + list = getChildren(); + for (int i = 0, n = list.size(); i < n; i++) { + ((FlowFigure) list.get(i)).postValidate(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.Figure#paintBorder(org.eclipse.draw2d.Graphics) + */ + protected void paintBorder(Graphics graphics) { + if (Debug.DEBUG_TEXTBORDER) { + if (_fragments != null) { + graphics.setForegroundColor(ColorConstants.darkBlue); + for (int i = 0, size = _fragments.size(); i < size; i++) { + FlowBox box = (FlowBox) _fragments.get(i); + BoxUtil.drawBox(graphics, box); + } + graphics.restoreState(); + } + } + } + + /** + * @see org.eclipse.draw2d.Figure#paintFigure(Graphics) + */ + protected void paintFigure(Graphics g) { + Object result = this.getCSSStyle().getColor(); + Color color; + if (result instanceof Color) { + color = (Color) result; + } else if (result instanceof RGB) { + color = new Color(null, (RGB) result); + } else { + color = null; + } + int[] range = null; + if (!StyleUtil.isInWidget(this.getCSSStyle())) { + range = _provider.getSelectedRange(); + } + if (range == null || range[0] == range[1]) { + // we are not in selection + TextLayoutSupport.paintTextFigure(g, _fragments, getCSSStyle() + .getCSSFont().getSwtFont(), color, ((Integer) getCSSStyle() + .getStyleProperty(ICSSPropertyID.ATTR_TEXTDECORATION)) + .intValue()); + } else { + TextLayoutSupport.paintTextFigureWithSelection(g, _fragments, + _provider.getTextData(), getCSSStyle().getCSSFont() + .getSwtFont(), color, ((Integer) getCSSStyle() + .getStyleProperty( + ICSSPropertyID.ATTR_TEXTDECORATION)) + .intValue(), range[0], range[1], + ColorConstants.white, ColorConstants.blue); + } + if (color != result && color != null) { + color.dispose(); + } + } + + /** + * Find out lines which has closer y coordinate to point, and then line + * which has closer x coordinate. + * + * @param relative + * @return + */ + public int getNewInsertionOffset(Point relative) { + TextFragmentBox closestBox = null; + // if there is one which are at the same line with relative, calculate + // that line first; + for (int i = 0, n = _fragments.size(); i < n; i++) { + TextFragmentBox box = (TextFragmentBox) _fragments.get(i); + if (box.containsPoint(relative.x, relative.y)) { + int index = FlowUtilities.getTextInWidth(box.getTextData(), + getCSSStyle().getCSSFont().getSwtFont(), relative.x + - box._x, TextLayoutSupport + .getAverageCharWidth(box)); + return box._offset + index; + } else { + if (closestBox == null) { + closestBox = box; + } else { + // box is above point + TextFragmentBox tempBox = box; + int offset1 = Math + .abs(CaretPositionResolver.getYDistance( + new Rectangle(tempBox._x, tempBox._y, + tempBox._width, tempBox._height), + relative)); + tempBox = closestBox; + int offset2 = Math + .abs(CaretPositionResolver.getYDistance( + new Rectangle(tempBox._x, tempBox._y, + tempBox._width, tempBox._height), + relative)); + if (offset1 < offset2) { + closestBox = box; + } + } + // at the same line + if (box.containsPoint(box._x, relative.y)) { + TextFragmentBox tempBox = box; + int offset1 = Math + .abs(CaretPositionResolver.getXDistance( + new Rectangle(tempBox._x, tempBox._y, + tempBox._width, tempBox._height), + relative)); + tempBox = closestBox; + int offset2 = Math + .abs(CaretPositionResolver.getXDistance( + new Rectangle(tempBox._x, tempBox._y, + tempBox._width, tempBox._height), + relative)); + if (offset1 < offset2) { + closestBox = box; + } + } + } + } + + if (closestBox.containsPoint(closestBox._x, relative.y) + || closestBox.containsPoint(relative.x, closestBox._y)) { + int offset = relative.x - closestBox._x; + int index = FlowUtilities.getTextInWidth(closestBox.getTextData(), + getCSSStyle().getCSSFont().getSwtFont(), offset, + TextLayoutSupport.getAverageCharWidth(closestBox)); + return closestBox._offset + index; + } else { + return -1; + } + } + + public int getInsertionOffset(Point relative) { + for (int i = 0, n = _fragments.size(); i < n; i++) { + TextFragmentBox box = (TextFragmentBox) _fragments.get(i); + if (box.containsPoint(relative.x, relative.y)) { + int index = FlowUtilities.getTextInWidth(box.getTextData(), + getCSSStyle().getCSSFont().getSwtFont(), relative.x + - box._x, TextLayoutSupport + .getAverageCharWidth(box)); + return box._offset + index; + } + } + return -1; + } + + /** + * the returned rectangle will be relative to this text figure. + * + * @param offset + * @return + */ + public Rectangle calculateCaretPosition(int offset) { + // search reverse order, find the latest box that has _offset small than + // the specified one + if (offset > 0) { + for (int i = _fragments.size() - 1; i >= 0; i--) { + TextFragmentBox box = (TextFragmentBox) _fragments.get(i); + if (box._offset <= offset) { + // ok, we find the box. + if (box._offset + box._length < offset) { + return new Rectangle(box._x + box._width, box._y, 1, + box._height); + } else { + String s = box.getTextData().substring(0, + offset - box._offset); + int width = FlowUtilities.getTextExtents(s, + getCSSStyle().getCSSFont().getSwtFont()).width; + return new Rectangle(box._x + width, box._y, 1, + box._height); + } + } + } + } else { + if (_fragments.size() > 0) { + TextFragmentBox box = (TextFragmentBox) _fragments.get(0); + return new Rectangle(box._x, box._y, 1, box._height); + } + } + // should only reach here when there is no fragments. + Rectangle bounds = this.getBounds(); + return new Rectangle(bounds.x, bounds.y, 1, bounds.height); + } + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSTextLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSTextLayout.java new file mode 100644 index 000000000..b61a37a94 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSTextLayout.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * 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.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.swt.graphics.Font; + +/** + * @author mengbo + */ +// NOTE: CSSTextLayout does not extends CSSFlowLayout. Since text is a little +// special, +// we don't want to do things like "preLayout()" as in CSSFlowLayout. +public class CSSTextLayout extends FlowFigureLayout { + /** + * Wrapping will ONLY occur at valid line breaks + */ + public static final int WORD_WRAP_HARD = 0; + + /** + * Wrapping will always occur at the end of the available space, breaking in + * the middle of a word. + */ + public static final int WORD_WRAP_SOFT = 1; + + /** + * Wrapping will always occur at the end of available space, truncating a + * word if it doesn't fit. + */ + // don't support this flag + // public static final int WORD_WRAP_TRUNCATE = 2; + private int _wrappingStyle = WORD_WRAP_HARD; + + public CSSTextLayout(CSSTextFigure textfigure) { + super(textfigure); + } + + // -------------------------------------------------------------------------------------------------- + FlowBox findLastNonLineBox(LineBox box) { + List fragments = box.getFragments(); + for (int i = fragments.size() - 1; i >= 0; i--) { + FlowBox item = (FlowBox) fragments.get(i); + if (item instanceof LineBox) { + FlowBox found = findLastNonLineBox((LineBox) item); + if (found != null) { + return found; + } + } else { + return item; + } + } + return null; + } + + // boolean isElementContentWhitespaceEnding() + // { + // if (!this._context.isCurrentLineOccupied()) + // return true; + // LineBox line = this._context.getCurrentLine(); + // FlowBox lastNoneLinebox = findLastNonLineBox(line); + // if (lastNoneLinebox instanceof TextFragmentBox) + // return ((TextFragmentBox) lastNoneLinebox)._isLastCharWhitespace; + // else + // return true; + // } + // + // String normalize(String text) + // { + // text = EntityMap.translateAndCompact(text); + // if (text.length() > 0 && + // Character.isElementContentWhitespace(text.charAt(0)) && + // isElementContentWhitespaceEnding()) + // return text.substring(1); + // else + // return text; + // } + + private void layoutEmptyString(List fragments, Font font) { + // empty node! we want to create a fake fragment, so things can be + // consistent + // that all the CSSTextFigure will have something inside, also in this + // way, even + // empty text node will have a position, thus we can support showing + // caret associated + // with this text figure. + fragments.clear(); + TextFragmentBox box = TextLayoutSupport.getFragment(0, fragments); + box._length = 0; + box._offset = 0; + box._height = 0; + box._width = 0; + box.setTextData(""); + + // {following comments deprecated XXX: If is empty string, we only want + // to this figure to have a size, but don't + // want to it to be added into current line. Otherwise, a line with only + // a empty string + // will also take a line's space.} + + // please reference LineBox.isOccupied() + // now we treat a line with only an empty text as not occupied. + getFlowContext().getCurrentLine().add(box); + } + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#layout() + */ + protected void layout() { + CSSTextFigure flowFigure = (CSSTextFigure) getFlowFigure(); + + List fragments = flowFigure.getFragments();// Reuse the previous List + // of fragments + String text = flowFigure.getText(); + Font font = flowFigure.getCSSStyle().getCSSFont().getSwtFont(); + Object whitespace = flowFigure.getCSSStyle().getStyleProperty( + ICSSPropertyID.ATTR_WHITESPACE); + + if (whitespace == ICSSPropertyID.VAL_PRE) { + if (text == null || text.length() == 0) + layoutEmptyString(fragments, font); + else + TextLayoutSupport.layoutNoWrap(getFlowContext(), text, + fragments, font); + } else if (whitespace == ICSSPropertyID.VAL_NOWRAP) { + if (text == null || text.length() == 0) + layoutEmptyString(fragments, font); + else + TextLayoutSupport.layoutNoWrap(getFlowContext(), text, + fragments, font); + } else { + if (text == null || text.length() == 0) + layoutEmptyString(fragments, font); + else { + boolean trimLeadingChar = (text.charAt(0) == ' ' && shouldTrimLeadingWhitespace(getFlowContext())); + TextLayoutSupport.layoutNormal(getFlowContext(), text, + fragments, font, _wrappingStyle, trimLeadingChar); + } + } + } + + // XXX: maybe should move to TextSupport later. + public boolean shouldTrimLeadingWhitespace(FlowContext context) { + if (!context.isCurrentLineOccupied()) { + return true; + } + while (context instanceof CSSInlineFlowLayout) { + context = ((CSSInlineFlowLayout) context).getFlowContext(); + } + LineBox line = context.getCurrentLine(); + if (line == null || !line.isOccupied()) { + return true; + } + FlowBox lastNoneLinebox = findLastNonLineBox(line); + if (lastNoneLinebox == null || lastNoneLinebox.getWidth() == 0) { + return true; + } else if (lastNoneLinebox instanceof TextFragmentBox) { + return ((TextFragmentBox) lastNoneLinebox)._isLastCharWhitespace; + } else { + return false; + } + } + + public void dispose() { + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSWidgetLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSWidgetLayout.java new file mode 100644 index 000000000..1c83653d2 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CSSWidgetLayout.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.css2.provider.DimensionInfo; +import org.eclipse.jst.pagedesigner.css2.provider.ICSSWidgetProvider; + +/** + * @author mengbo + */ +public class CSSWidgetLayout extends CSSBlockFlowLayout implements ICSSPainter { + private WidgetBox _widgetBox; + + private ICSSWidgetProvider _provider; + + /** + * @param flowfigure + */ + public CSSWidgetLayout(CSSFigure flowfigure, ICSSWidgetProvider provider) { + super(flowfigure); + _provider = provider; + } + + /** + * normally this method is called directly after constructor + * + * @param provider + */ + public void setProvider(ICSSWidgetProvider provider) { + _provider = provider; + } + + public ICSSWidgetProvider getProvider() { + // return ((CSSWidgetFigure)this.getFlowFigure()).getProvider(); + return _provider; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#isInlineBlock() + */ + public boolean isInlineBlock() { + ICSSWidgetProvider provider = getProvider(); + return provider.isInline(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#layout() + */ + protected void layoutChildren() { + ICSSWidgetProvider provider = getProvider(); + + // if we did endLine, then will result in context create a new line, so + // we may in the new line now. + // passing in the top margin, and context will consider that when + // creating the new line. + + int suggestedWith = _blockBox.getContentWidth(); + int suggestedHeight = _blockBox.getContentHeight(); + // int suggestedWith = getSuggestedWidth(line, style, provider); + // int suggestedHeight = getSuggestedHeight(line, style, provider); + + DimensionInfo resultInfo = provider.getPreferredDimension( + suggestedWith, suggestedHeight); + Dimension resultSize = resultInfo.getDimension(); + + _widgetBox = new WidgetBox(); // ((CSSWidgetFigure)getFlowFigure()).getWidgetBox(); + // if (provider.isHandlingBorder() || style == null) + // { + _widgetBox.setWidth(resultSize.width); + _widgetBox.setHeight(resultSize.height); + _widgetBox.setAscent(resultInfo.getAscent()); + // } + // else + // { + // widgetBox.setWidth(resultSize.width + + // style.getBorderInsets().getWidth()); + // widgetBox.setHeight(resultSize.height + + // style.getBorderInsets().getHeight()); + // widgetBox.setAscent(resultInfo.getAscent()+style.getBorderInsets().top); + // } + this.addToCurrentLine(_widgetBox); + // if (!provider.isInline()) + // { + // context.endLine(); + // } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#dispose() + */ + public void dispose() { + } + + // public int getSuggestedWidth(LineBox line, ICSSStyle style, + // ICSSWidgetProvider provider) + // { + // if (style == null) return -1; + // + // Object width = style.getStyleProperty(ICSSPropertyID.ATTR_WIDTH); + // Length recommendedWidth = (width instanceof Length) ? (Length) width : + // null; + // + // int rw = 0; + // if (recommendedWidth == null || recommendedWidth.getValue() <= 0) + // { + // return -1; + // } + // else + // { + // if (recommendedWidth.isPercentage()) + // { + // rw = line.getAvailableWidth() * recommendedWidth.getValue() / 100; + // } + // else + // { + // rw = recommendedWidth.getValue(); + // } + // + // if (!style.isSizeIncludeBorderPadding() && provider.isHandlingBorder()) + // { + // rw += style.getBorderInsets().getWidth() + + // style.getPaddingInsets().getWidth(); + // } + // else if (style.isSizeIncludeBorderPadding() && + // !provider.isHandlingBorder()) + // { + // rw -= style.getBorderInsets().getWidth() + + // style.getPaddingInsets().getWidth(); + // } + // } + // + // return rw; + // } + // + // public int getSuggestedHeight(LineBox line, ICSSStyle style, + // ICSSWidgetProvider provider) + // { + // if (style == null) return -1; + // + // Object height = style.getStyleProperty(ICSSPropertyID.ATTR_HEIGHT); + // Length recommendedHeight = (height instanceof Length) ? (Length) height : + // null; + // + // int rh = 0; + // if (recommendedHeight == null || recommendedHeight.getValue() <= 0) + // { + // return -1; + // } + // else + // { + // if (recommendedHeight.isPercentage()) + // { + // // we don't support percentage height for this version, ignore + // return -1; + // } + // else + // { + // rh = recommendedHeight.getValue(); + // } + // + // if (!style.isSizeIncludeBorderPadding() && provider.isHandlingBorder()) + // { + // rh += style.getBorderInsets().getHeight() + + // style.getPaddingInsets().getHeight(); + // } + // else if (style.isSizeIncludeBorderPadding() && + // !provider.isHandlingBorder()) + // { + // rh -= style.getBorderInsets().getHeight() + + // style.getPaddingInsets().getHeight(); + // } + // } + // + // return rh; + // } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSPainter#paintFigure(org.eclipse.draw2d.Graphics) + */ + public void paintFigure(Graphics g) { + ICSSWidgetProvider provider = this.getProvider(); + if (provider != null && _widgetBox != null) { + provider.paintFigure(g, new Rectangle(_widgetBox._x, _widgetBox._y, + _widgetBox.getWidth(), _widgetBox.getHeight())); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSLayout#handlingBorderForBlock() + */ + public boolean handlingBorderForBlock() { + ICSSWidgetProvider provider = this.getProvider(); + if (provider != null) { + return provider.isHandlingBorder(); + } + return super.handlingBorderForBlock(); + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CompositeBox.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CompositeBox.java new file mode 100644 index 000000000..6cce9545a --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/CompositeBox.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * 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; + +/** + * A FlowBox that can contain other BlockInfos. The contained BlockInfos are + * called <i>fragments </i>. + * + * @author mengbo + * @since 2.1 + */ +public abstract class CompositeBox extends FlowBox { + + /** + * The contained fragments. + */ + protected List _fragments = new ArrayList(); + + int _recommendedWidth; + + int _recommendedHeight; + + /** + * Adds the specified FlowBox. Updates the width, height, and ascent + * properties. + * + * @param block + * the FlowBox being added + */ + public void add(FlowBox block) { + // The order is critical.see the first "if" block in the unionInfo() + // method. + unionInfo(block); + _fragments.add(block); + } + + /** + * Removes all owned fragments and invalidates this CompositeBox. + */ + public void clear() { + _fragments.clear(); + resetInfo(); + } + + /** + * Overridden to ensure that the CompositeBox is valid. + * + * @see FlowBox#getBounds() + */ + // public Rectangle getBounds() { + // validate(); + // return this; + // } + /** + * @return the List of fragments + */ + public List getFragments() { + return _fragments; + } + + /** + * Returns the recommended width for this CompositeBox. + * + * @return the recommended width + */ + public int getRecommendedWidth() { + return _recommendedWidth; + } + + /** + * Returns the recommended height for this compositebox. + * + * @return + */ + public int getRecommendedHeight() { + return _recommendedHeight; + } + + // public int getInnerTop() { + // validate(); + // return y; + // } + + /** + * resets fields before unioning the data from the fragments. + */ + protected void resetInfo() { + _width = _height = 0; + } + + /** + * Sets the recommended width for this CompositeBox. + * + * @param w + * the width + */ + public void setRecommendedWidth(int w) { + _recommendedWidth = w; + } + + public void setRecommendedHeight(int h) { + _recommendedHeight = h; + } + + /** + * unions the fragment's width, height, and ascent into this composite. + * + * @param box + * the fragment + */ + protected void unionInfo(FlowBox box) { + int right = Math.max(_x + _width, box._x + box._width); + int bottom = Math.max(_y + _height, box._y + box._height); + _x = Math.min(_x, box._x); + _y = Math.min(_y, box._y); + _width = right - _x; + _height = bottom - _y; + } + + public int getContentWidth() { + return getWidth() - getBorderPaddingWidth(); + } + + public int getContentHeight() { + return getHeight() - getBorderPaddingHeight(); + } + + public int getRecommendedContentWidth() { + return Math.max(0, getRecommendedWidth() - getBorderPaddingWidth()); + } + // + // public int getRecommendedContentHeight() + // { + // return Math.max(0, getRecommendedHeight() - getBorderPaddingHeight()); + // } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/Debug.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/Debug.java new file mode 100644 index 000000000..c15f21da0 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/Debug.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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; + +/** + * Debug constants. + * + * @author mengbo + * @version 1.5 + */ +public class Debug { + public static final boolean DEBUG_BASELINE = false; + + public static final boolean DEBUG_BOX = false; + + public static final boolean DEBUG_BORDERPADDING = false; + + public static final boolean DEBUG_TEXTBORDER = false; +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/DisplayToLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/DisplayToLayout.java new file mode 100644 index 000000000..de2eaa8d9 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/DisplayToLayout.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.LayoutManager; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.layout.table.CSSTRGroupLayout; +import org.eclipse.jst.pagedesigner.css2.layout.table.CSSTRLayout; +import org.eclipse.jst.pagedesigner.css2.layout.table.CSSTableCaptionLayout; +import org.eclipse.jst.pagedesigner.css2.layout.table.CSSTableCellLayout; +import org.eclipse.jst.pagedesigner.css2.layout.table.CSSTableLayout2; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.jst.pagedesigner.css2.property.PositionMeta; + +/** + * @author mengbo + */ +public class DisplayToLayout { + /** + * @param figure + * @param display + * @param old + * @return + */ + public static CSSLayout displayToLayout(CSSFigure figure, String display, + LayoutManager old) { + if ("block".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + return new CSSBlockFlowLayout(figure); + } else if ("inline".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + return new CSSInlineFlowLayout(figure); + } else if ("table".equalsIgnoreCase(display) || "inline-table".equalsIgnoreCase(display)) //$NON-NLS-1$ $NON-NLS-2$ + { + return new CSSTableLayout2(figure); + } else if ("table-row".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + return new CSSTRLayout(figure); + } else if ("table-row-group".equalsIgnoreCase(display) //$NON-NLS-1$ + || "table-header-group".equalsIgnoreCase(display) //$NON-NLS-1$ + || "table-footer-group".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + return new CSSTRGroupLayout(figure); + } else if ("table-cell".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + return new CSSTableCellLayout(figure); + } else if (display.equalsIgnoreCase("table-caption")) //$NON-NLS-1$ + { + return new CSSTableCaptionLayout(figure); + } else if ("inline-block".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + return new CSSBlockFlowLayout(figure) { + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#isInlineBlock() + */ + public boolean isInlineBlock() { + return true; + } + }; + } else if (ICSSPropertyID.VAL_LIST_ITEM.equalsIgnoreCase(display)) { + return new CSSListItemLayout(figure); + } + return null; + } + + /** + * @param figure + * @param display + * @param old + * @return + */ + public static boolean isInline(String display) { + return "inline".equalsIgnoreCase(display) //$NON-NLS-1$ + || "inline-block".equalsIgnoreCase(display); //$NON-NLS-1$ + } + + /** + * @param style + * @return + */ + public static boolean isPositioned(ICSSStyle style) { + Object position = style.getStyleProperty(ICSSPropertyID.ATTR_POSITION); + if (PositionMeta.STATIC.equalsIgnoreCase((String) position)) { + return false; + } else { + return true; + } + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FigureUtil.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FigureUtil.java new file mode 100644 index 000000000..60e3f6fcb --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FigureUtil.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Translatable; + +/** + * @author mengbo + * @version 1.5 + */ +public class FigureUtil { + // XXX: + // seemed Figure.translateToRelative is bug? + public static final void translateToRelative(IFigure figure, Translatable t) { + if (figure.getParent() != null) { + translateToRelative(figure.getParent(), t); + // figure.getParent().translateToRelative(t); + figure.translateFromParent(t); + } + } + + // XXX: + // seemed Figure.translateToAbsolute is bug? + public static final void translateToAbsolute(IFigure figure, Translatable t) { + if (figure.getParent() != null) { + figure.translateToParent(t); + translateToAbsolute(figure.getParent(), t); + // figure.getParent().translateToAbsolute(t); + } + + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowBox.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowBox.java new file mode 100644 index 000000000..149fbd9a4 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowBox.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Rectangle; + +/** + * This class represents the CSS box model. See chapter 8 of CSS2 spec. + * + * @see http://www.w3.org/TR/REC-CSS2/box.html + */ +public class FlowBox { + private Object _verticalAlignData = null; + + /** + * The x location + */ + public int _x; + + /** + * The y location + */ + public int _y; + + int _width; + + int _height; + + public Insets _marginInsets = new Insets(); + + public Insets _borderInsets = new Insets(); + + public Insets _paddingInsets = new Insets(); + + /** + * This method must be called on a block that is completely positioned and + * committed. + * + * @param x + * X + * @param y + * Y + * @return <code>true</code> if the FlowBox contains the point + */ + public boolean containsPoint(int x, int y) { + return x >= this._x && y >= this._y && x < this._x + this._width + && y < this._y + this._height; + } + + /** + * By default, a FlowBox is all ascent, and no descent, so the height is + * returned. + * + * @return the <i>ascent </i> in pixels above the baseline + */ + public int getAscent() { + return getHeight(); + } + + /** + * By default, a simple FlowBox is all ascent, and no descent. Zero is + * returned. + * + * @return the <i>descent </i> in pixels below the baseline + */ + public final int getDescent() { + return getHeight() - getAscent(); + } + + /** + * Returns the height + * + * @return height + */ + public int getHeight() { + return _height; + } + + /** + * Returns the width + * + * @return width + */ + public int getWidth() { + return _width; + } + + public void setWidth(int w) { + _width = w; + } + + public void setHeight(int h) { + _height = h; + } + + /** + * Used to set the baseline of this FlowBox to the specified value. + * + * @param value + * the new baseline + */ + public void makeBaseline(int value) { + _y = (value - getAscent()); + } + + public int getBorderPaddingWidth() { + return _borderInsets.getWidth() + _paddingInsets.getWidth(); + } + + /** + * @return + */ + public int getBorderPaddingHeight() { + return _borderInsets.getHeight() + _paddingInsets.getHeight(); + } + + /** + * @return + */ + public Insets getBorderPaddingInsets() { + Insets temp = new Insets(_borderInsets); + return temp.add(_paddingInsets); + } + + public void setXYWidthHeight(Rectangle rect) { + this._x = rect.x; + this._y = rect.y; + this.setWidth(rect.width); + this.setHeight(rect.height); + } + + /** + * @return Returns the _verticalAlignData. + */ + public Object getVerticalAlignData() { + return _verticalAlignData; + } + + /** + * @param alignData + * The _verticalAlignData to set. + */ + public void setVerticalAlignData(Object alignData) { + _verticalAlignData = alignData; + } + + public Rectangle getRectangle() { + return new Rectangle(this._x, this._y, this.getWidth(), this + .getHeight()); + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowContainerLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowContainerLayout.java new file mode 100644 index 000000000..b53cf7f2c --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowContainerLayout.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * 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.jst.pagedesigner.PDPlugin; +import org.eclipse.jst.pagedesigner.common.logging.Logger; + +/** + * A layout for FlowFigures with children. + * <P> + * WARNING: This class is not intended to be subclassed by clients. + * + * @author mengbo + * @since 2.1 + */ +public abstract class FlowContainerLayout extends FlowFigureLayout implements + FlowContext { + private static Logger _log = PDPlugin.getLogger(FlowContainerLayout.class); + + /** + * the current line + */ + protected LineBox _currentLine; + + private boolean _calculatingMaxWidth = false; + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#FlowFigureLayout(FlowFigure) + */ + protected FlowContainerLayout(FlowFigure flowFigure) { + super(flowFigure); + } + + /** + * @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(); + } + + /** + * 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; + } else { + 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 before layoutChildren() to setup any necessary state. + */ + protected abstract void preLayout(); + + /** + * 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(); +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowContext.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowContext.java new file mode 100644 index 000000000..dbca0dd53 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowContext.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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; + +/** + * Copied from draw2d. Enhanced to meet page designer's needs. The context that + * a {@link FlowFigureLayout}uses to perform its layout. + * <P> + * WARNING: This interface is not intended to be implemented by clients. It + * exists to define the API between the layout and its context. + */ +public interface FlowContext { + + /** + * Adds the given box into the current line. + * + * @param box + * the FlowBox to add + */ + void addToCurrentLine(FlowBox box); + + /** + * The current line should be committed if it is occupied, and then set to + * <code>null</code>. Otherwise, do nothing. + */ + void endLine(); + + /** + * Obtains the current line, creating a new line if there is no current + * line. if create a new line, the new line's x will be set correctly + * without considering the new element's left margin. Also, if create new + * line, it will treat as the new line's top margin is 0. + * + * @return the current line + */ + LineBox getCurrentLine(); + + /** + * if create a new line, the new line's x will be set correctly without + * considering the new element's left margin. + * + * @param topMargin + * @return + */ + LineBox getCurrentLine(int topMargin); + + /** + * Returns the current Y value. + * + * @return the current Y value + */ + int getCurrentY(); + + /** + * @return <code>true</code> if the current line contains any fragments + */ + boolean isCurrentLineOccupied(); + + /** + * @return + */ + int getLastMarginRight(); + + /** + * when layout table, we need to calculate max width of a cell. This is done + * by don't break line (other than explicit required). Currently, the + * solution is to make the recommended width to be very big, and when create + * block element we don't set the block element's size to be recommended + * width. Please see CSSBlockFlowLayout + * + * @return + */ + boolean isCalculatingMaxWidth(); + + /** + * when calculating percentage width, we need the container width + * + * @return + */ + int getContainerWidth(); +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowFigure.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowFigure.java new file mode 100644 index 000000000..7c15e1278 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowFigure.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * 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.Graphics; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Rectangle; + +/** + * The base implementation for text flow figures. A flow figure is used to + * render a document in which elements are laid out horizontally within a "line" + * until that line is filled. Layout continues on the next line. + * <p> + * WARNING: This class is not intended to be subclassed by clients. Future + * versions may contain additional abstract methods. + * + * @author mengbo + * @since 2.1 + */ +public abstract class FlowFigure extends Figure { + + // static final boolean SHOW_BASELINE = true; + + /** + * Constructs a new FlowFigure. + */ + public FlowFigure() { + // setLayoutManager(createDefaultFlowLayout()); + } + + /** + * If the child is a <code>FlowFigure</code>, its FlowContext is passed + * to it. + * + * @see org.eclipse.draw2d.IFigure#add(IFigure, Object, int) + */ + public void add(IFigure child, Object constraint, int index) { + super.add(child, constraint, index); + if (child instanceof FlowFigure) { + FlowFigure ff = (FlowFigure) child; + if (getLayoutManager() instanceof FlowContext) { + ff.setOriginalFlowContext((FlowContext) getLayoutManager()); + } else { + // should not happen + // FIXME: logging + System.err.println("layout is not FlowContext"); + } + } + } + + /** + * Creates the default layout manager + * + * @return The default layout + */ + // protected abstract FlowFigureLayout createDefaultFlowLayout(); + /** + * @see Figure#paintFigure(Graphics) + */ + protected void paintFigure(Graphics g) { + super.paintFigure(g); + // g.drawRectangle(getBounds().getResized(-1,-1)); + } + + /** + * Called after validate has occurred. This is used to update the bounds of + * the FlowFigure to encompass its new flow boxed created during validate. + */ + public abstract void postValidate(); + + /** + * FlowFigures override setBounds() to prevent translation of children. + * "bounds" is a derived property for FlowFigures, calculated from the + * fragments that make up the FlowFigure. + * + * @see Figure#setBounds(Rectangle) + */ + public void setBounds(Rectangle r) { + if (getBounds().equals(r)) + return; + erase(); + bounds.x = r.x; + bounds.y = r.y; + bounds.width = r.width; + bounds.height = r.height; + fireFigureMoved(); + repaint(); + } + + /** + * Sets the flow context. + * + * @param flowContext + * the flow context for this flow figure + */ + public void setOriginalFlowContext(FlowContext flowContext) { + ((FlowFigureLayout) getLayoutManager()) + .setOriginalFlowContext(flowContext); + } + + public void setDisplayString(String s) { + _displayString = s; + } + + public String toString() { + if (_displayString == null) + return super.toString(); + else + return _displayString + " " + getClass().getName(); + } + + String _displayString; // for debug + + /** + * @return + */ + public FlowContext getFlowContext() { + return ((FlowFigureLayout) getLayoutManager()).getFlowContext(); + } + + // ---------------------------------------------------------------------- + // as absolute positioning and relative positioning may have children + // out-side + // of parent bounds, so we want to disable clipping when drawing figures + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.Figure#paintChildren(org.eclipse.draw2d.Graphics) + */ + protected void paintChildren(Graphics graphics) { + IFigure child; + + Rectangle clip = Rectangle.SINGLETON; + List children = this.getChildren(); + for (int i = 0; i < children.size(); i++) { + child = (IFigure) children.get(i); + if (child.isVisible() && child.intersects(graphics.getClip(clip))) { + // graphics.clipRect(child.getBounds()); + child.paint(graphics); + graphics.restoreState(); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.Figure#paintClientArea(org.eclipse.draw2d.Graphics) + */ + protected void paintClientArea(Graphics graphics) { + if (this.getChildren().isEmpty()) + return; + + // boolean optimizeClip = getBorder() == null || getBorder().isOpaque(); + + if (useLocalCoordinates()) { + graphics.translate(getBounds().x + getInsets().left, getBounds().y + + getInsets().top); + // if (!optimizeClip) + // graphics.clipRect(getClientArea(PRIVATE_RECT)); + graphics.pushState(); + paintChildren(graphics); + graphics.popState(); + graphics.restoreState(); + } else { + // if (optimizeClip) + paintChildren(graphics); + // else { + // graphics.clipRect(getClientArea(PRIVATE_RECT)); + // graphics.pushState(); + // paintChildren(graphics); + // graphics.popState(); + // graphics.restoreState(); + // } + } + + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowFigureLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowFigureLayout.java new file mode 100644 index 000000000..3f80478cf --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowFigureLayout.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.AbstractLayout; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; + +public abstract class FlowFigureLayout extends AbstractLayout { + + /** + * <code>true</code> if the context has changed, and a layout is needed. + */ + protected boolean _invalid = true; + + /** + * The flow context in which this LayoutManager exists. + */ + private FlowContext _context; + + /** + * The figure passed by layout(Figure) is held for convenience. + */ + private final FlowFigure _flowFigure; + + /** + * Constructs a new FlowFigureLayout with the given FlowFigure. + * + * @param flowfigure + * the FlowFigure + */ + protected FlowFigureLayout(FlowFigure flowfigure) { + this._flowFigure = flowfigure; + } + + /** + * TextFlowLayouts do not calculate a preferred size because it is too + * expensive. {@link FlowPage}will actually layout itself in order to + * calculate preferredSize. + * + * @see AbstractLayout#calculatePreferredSize(IFigure) + */ + public Dimension calculatePreferredSize(IFigure f, int w, int h) { + return null; + } + + /** + * @return the FlowFigure + */ + protected FlowFigure getFlowFigure() { + return _flowFigure; + } + + /** + * Marks this layout as invalid. + * + * @see org.eclipse.draw2d.LayoutManager#invalidate() + */ + public void invalidate() { + _invalid = true; + super.invalidate(); + } + + /** + * @see org.eclipse.draw2d.LayoutManager#layout(IFigure) + */ + public final void layout(IFigure figure) { + layout(); + _invalid = false; + } + + /** + * Called during {@link #layout(IFigure)}. The {@link #_invalid}flag is + * reset after this method is called. + */ + protected abstract void layout(); + + /** + * Sets the context for this layout manager. + * + * @param flowContext + * the context of this layout + */ + public void setOriginalFlowContext(FlowContext flowContext) { + _context = flowContext; + } + + public FlowContext getOriginalFlowContext() { + return _context; + } + + /** + * get flow context. + * + * @return + */ + public FlowContext getFlowContext() { + return _context; + } + + public String toString() { + // for debug purpose. + return _flowFigure.toString(); + } + + abstract public void dispose(); +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowPage.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowPage.java new file mode 100644 index 000000000..31d44de44 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowPage.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * 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.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Rectangle; + +/** + * The root of a Flow hierarchy. A flow page can be treated as a normal figure, + * but contains FlowFigures. + * <P> + * A FlowPage will not have a defined width unless it is inside a figure whose + * layout provides width hints when calling + * {@link org.eclipse.draw2d.IFigure#getPreferredSize(int, int)}. + * <P> + * WARNING: This class is not intended to be subclassed by clients. + */ +public class FlowPage extends BlockFlow { + + private Dimension _pageSize = new Dimension(); + + private int _recommendedWidth; + + private int _pageSizeCacheKeys[] = new int[4]; + + private Dimension _pageSizeCacheValues[] = new Dimension[4]; + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.BlockFlow#createDefaultFlowLayout() + */ + protected FlowFigureLayout createDefaultFlowLayout() { + return new PageFlowLayout(this); + } + + /** + * @see org.eclipse.draw2d.Figure#getMinimumSize() + */ + public Dimension getMinimumSize(int w, int h) { + return getPreferredSize(w, h); + } + + /** + * @see org.eclipse.draw2d.Figure#invalidate() + */ + public void invalidate() { + _pageSizeCacheValues = new Dimension[4]; + super.invalidate(); + } + + /** + * @see org.eclipse.draw2d.Figure#getPreferredSize(int, int) + */ + public Dimension getPreferredSize(int width, int h) { + if (width >= 0) + width = Math.max(0, width - getInsets().getWidth()); + + for (int i = 0; i < 4; i++) { + if (_pageSizeCacheKeys[i] == width + && _pageSizeCacheValues[i] != null) + return _pageSizeCacheValues[i]; + } + + _pageSizeCacheKeys[3] = _pageSizeCacheKeys[2]; + _pageSizeCacheKeys[2] = _pageSizeCacheKeys[1]; + _pageSizeCacheKeys[1] = _pageSizeCacheKeys[0]; + _pageSizeCacheKeys[0] = width; + + _pageSizeCacheValues[3] = _pageSizeCacheValues[2]; + _pageSizeCacheValues[2] = _pageSizeCacheValues[1]; + _pageSizeCacheValues[1] = _pageSizeCacheValues[0]; + + // Flowpage must temporarily layout to determine its preferred size + int oldWidth = getRecommendedWidth(); + setRecommendedWidth(width); + validate(); + _pageSizeCacheValues[0] = _pageSize.getExpanded(getInsets().getWidth(), + getInsets().getHeight()); + + if (width != oldWidth) { + setRecommendedWidth(oldWidth); + getUpdateManager().addInvalidFigure(this); + } + return _pageSizeCacheValues[0]; + } + + int getRecommendedWidth() { + return _recommendedWidth; + } + + /** + * @see BlockFlow#postValidate() + */ + public void postValidate() { + Rectangle r = getBlockBox().toRectangle(); + _pageSize.width = r.width; + _pageSize.height = r.height; + List v = getChildren(); + for (int i = 0; i < v.size(); i++) + ((FlowFigure) v.get(i)).postValidate(); + } + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigure#setBounds(Rectangle) + */ + public void setBounds(Rectangle r) { + if (getBounds().equals(r)) + return; + boolean invalidate = getBounds().width != r.width + || getBounds().height != r.height; + super.setBounds(r); + int newWidth = r.width - getInsets().getWidth(); + if (invalidate || getRecommendedWidth() != newWidth) { + setRecommendedWidth(newWidth); + getUpdateManager().addInvalidFigure(this); + } + } + + private void setRecommendedWidth(int width) { + if (_recommendedWidth == width) + return; + _recommendedWidth = width; + super.invalidate(); + } + + /** + * @see org.eclipse.draw2d.Figure#validate() + */ + public void validate() { + if (isValid()) + return; + super.validate(); + postValidate(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.Figure#setValid(boolean) + */ + public void setValid(boolean value) { + super.setValid(value); + } + + public Insets getInsets() { + return new Insets(8); + } + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowUtilities.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowUtilities.java new file mode 100644 index 000000000..5ebde828b --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/FlowUtilities.java @@ -0,0 +1,260 @@ +/******************************************************************************* + * 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.text.BreakIterator; + +import org.eclipse.draw2d.FigureUtilities; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontMetrics; + +/** + * Utility class for FlowFigures. + * + */ +public class FlowUtilities extends FigureUtilities { + /** + * Returns the number of characters from the specified String that will fit + * in the available amount of space. An average character width can be + * provided as a hint for faster calculation. + * + * @param frag + * the TextFragmentBox + * @param string + * the String + * @param font + * the Font used for measuring + * @param availableWidth + * the available width in pixels + * @param avg + * 0.0, or an avg character width to use during calculation + * @param wrapping + * the word wrap style + * @return the number of characters that will fit in the space + */ + public static int setupFragmentBasedOnTextSpace(TextFragmentBox frag, + String string, Font font, int availableWidth, float avg, + int wrapping) { + int result = getTextForSpace(string, font, availableWidth, avg, + wrapping); + frag._length = result; + setupFragment(frag, font, string); + return result; + } + + /** + * given the text string, font and available width and wrapping mode. + * Calculate how much text can fit into. + * + * @param string + * @param font + * @param availableWidth + * @param avg + * @param wrapping + * @return + */ + public static int getTextForSpace(String string, Font font, + int availableWidth, float avg, int wrapping) { + if (string.length() == 0) { + return 0; + } + + FontMetrics metrics = getFontMetrics(font); + BreakIterator breakItr = BreakIterator.getLineInstance(); + breakItr.setText(string); + int MIN, min, max; + if (avg == 0.0) { + avg = metrics.getAverageCharWidth(); + } + + int firstBreak = breakItr.next(); + + int winNL = string.indexOf("\r\n"); //$NON-NLS-1$ + int macNL = string.indexOf('\r'); + int unixNL = string.indexOf('\n'); + + MIN = min = (wrapping == CSSTextLayout.WORD_WRAP_HARD) ? firstBreak : 1; + if (macNL == winNL) { + macNL = -1; // If the Mac newline is just the prefix to the win NL, + // ignore it + } + + max = string.length() + 1; + + if (winNL != -1) { + max = Math.min(max, winNL); + min = Math.min(min, winNL); + } + if (unixNL != -1) { + max = Math.min(max, unixNL); + min = Math.min(min, unixNL); + } + if (macNL != -1) { + max = Math.min(max, macNL); + min = Math.min(min, macNL); + } + + int origMax = max; + // The size of the current guess + int guess = 0, guessSize = 0; + + while ((max - min) > 1) { + // Pick a new guess size + // New guess is the last guess plus the missing width in pixels + // divided by the average character size in pixels + guess = guess + (int) ((availableWidth - guessSize) / avg); + + if (guess >= max) { + guess = max - 1; + } + if (guess <= min) { + guess = min + 1; + } + + // Measure the current guess + guessSize = getStringExtents2(string.substring(0, guess), font).width; + + if (guessSize <= availableWidth) { + // We did not use the available width + min = guess; + } else { + // We exceeded the available width + max = guess; + } + } + + int result = string.length(); + switch (wrapping) { + case CSSTextLayout.WORD_WRAP_HARD: + if (min == string.length() || min == winNL || min == unixNL + || min == macNL) { + result = min; + } else if (max == origMax + && getStringExtents2(string.substring(0, max), font).width <= availableWidth) { + result = max; + } else { + result = Math.max(MIN, breakItr.preceding(Math.min(max, string + .length() - 1))); + } + break; + + case CSSTextLayout.WORD_WRAP_SOFT: + if (min == string.length() || min == winNL || min == unixNL + || min == macNL) { + result = min; + } else if (max == origMax + && getStringExtents2(string.substring(0, max), font).width <= availableWidth) { + result = max; + } else if (breakItr.isBoundary(min)) { + result = min; + } else if (breakItr.isBoundary(Math.min(max, string.length() - 1))) { + result = max; + } else { + result = breakItr.preceding(Math.min(max, string.length() - 1)); + } + if (result <= 0) { + result = min; + } + break; + // case CSSTextLayout.WORD_WRAP_TRUNCATE: + // if (min == string.length() || min == winNL || min == unixNL || min == + // macNL) + // { + // result = frag._length = min; + // setupFragment(frag, font, string); + // if (frag.getWidth() <= availableWidth) + // return result; + // min -= 1; + // } + // else if (max == origMax && getStringExtents(string.substring(0, max), + // font).width <= availableWidth) + // { + // result = frag._length = max; + // setupFragment(frag, font, string); + // return result; + // } + // result = breakItr.preceding(Math.min(max + 1, string.length() - 1)); + // if (result <= 0) + // { + // ELLIPSIS_SIZE = + // FigureUtilities.getStringExtents(CSSTextFigure.ELLIPSIS, font); + // getTextForSpace(frag, string, font, availableWidth - + // ELLIPSIS_SIZE.width, avg, CSSTextLayout.WORD_WRAP_SOFT); + // //frag.length = min; + // frag._truncated = true; + // result = breakItr.following(min); + // if (result == BreakIterator.DONE) + // result = string.length(); + // } + // else + // { + // frag._length = result; + // } + } + + return result; + } + + public static int getTextInWidth(String string, Font font, + int availableWidth, float avg) { + if (string.length() == 0) { + return 0; + } + int guess = 0; + while (true) { + Dimension a = getTextExtents(string.substring(0, guess), font); + if (a.width >= availableWidth) { + return guess; + } + guess++; + if (guess == string.length()) { + return guess; + } + } + } + + /** + * change the parent implementation of getStringExtents(). Don't expend the + * 1 width. So empty string will not have any width. + * + * @param s + * @param f + * @return + */ + public static Dimension getStringExtents2(String s, Font f) { + return new Dimension(getStringDimension(s, f)); + } + + static void setupFragment(TextFragmentBox frag, Font f, String s) { + // if (frag.length != s.length()) + // we don't skip whitespace here. since already truncated in + // CSSTextLayout + + // while (frag.length > 0 && + // Character.isElementContentWhitespace(s.charAt(frag.length - 1))) + // frag.length--; + frag.setTextData(s.substring(0, frag._length)); + Dimension d = getStringExtents2(s.substring(0, frag._length), f); + FontMetrics fm = getFontMetrics(f); + frag.setHeight(fm.getHeight()); + frag.setAscent(fm.getAscent() + fm.getLeading()); + if (frag._length > 0 + && Character.isWhitespace(s.charAt(frag._length - 1))) { + frag._isLastCharWhitespace = true; + } else { + frag._isLastCharWhitespace = false; + } + frag.setWidth(d.width); + } + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSFigure.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSFigure.java new file mode 100644 index 000000000..b0a193562 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSFigure.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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.IFigure; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; + +/** + * @author mengbo + */ +public interface ICSSFigure extends IFigure { + /** + * get fragments of this figure. Each item of the list will be a FlowBox. + * Note, this method is for read only, caller should not change the returned + * list and items in the returned list. + * + * @return + */ + public List getFragmentsForRead(); + + /** + * get the CSSStyle of this CSS figure. + * + * @return + */ + public ICSSStyle getCSSStyle(); +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSLayout.java new file mode 100644 index 000000000..1320103f3 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSLayout.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.LayoutManager; + +/** + * There are several kinds of layout involved. 1. the layout need let the child + * figures do certain layouting of themselves first, then decide the final + * result based on child information. 2. the layout could decide the size + * information of this figure without child information. + * + * @author mengbo + * @version 1.5 + */ +public interface ICSSLayout extends LayoutManager { + /** + * Each ICSSLayout is dedicated to a single CSSFigure. + * + * @return + */ + public ICSSFigure getICSSFigure(); + + /** + * + * @return + */ + // public List getFragmentsForRead(); + /** + * postValidate the child figures of this CSSFigure. Normally layout fall + * into the first category need implement this method. + */ + // public 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); + /** + * @return + */ + // public boolean useLocalCoordinates(); +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSPainter.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSPainter.java new file mode 100644 index 000000000..5f83cb868 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSPainter.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.Graphics; + +/** + * @author mengbo + */ +public interface ICSSPainter { + /** + * this method is called in the figure's <code>paintFigure</code> method, + * before <code>paintClientArea</code>. So it is called before children. + * Thus, children may override its effects. + * + * @param g + */ + public void paintFigure(Graphics g); + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSPainter2.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSPainter2.java new file mode 100644 index 000000000..f93fb8447 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/ICSSPainter2.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.Graphics; + +/** + * If the layout implements this interface, then it will have chance to paint + * something to override children effect. + * + * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSPainter + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSFigure + * + * @author mengbo + * @version 1.5 + */ +public interface ICSSPainter2 { + /** + * this method is called after <code>paintClientArea</code>. So it is + * called after children. Thus, it could override some children effects. + * + * @param g + */ + public void paintFigurePostClientArea(Graphics g); +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/LineBox.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/LineBox.java new file mode 100644 index 000000000..5ab842f2f --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/LineBox.java @@ -0,0 +1,414 @@ +/******************************************************************************* + * 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 org.eclipse.jst.pagedesigner.css2.property.VerticalAlignMeta; +import org.eclipse.jst.pagedesigner.css2.value.Length; +import org.eclipse.swt.graphics.FontMetrics; + +/** + * A composite box representing a single line. LineBox calculates its ascent and + * descent from the child boxes it contains. Clients can call + * {@link #getAscent()}or {@link#getHeight()}at any time and expect valid + * values. The child boxes that are added to a line have unspecied locations + * until {@link #commit()}is called, at which time the child boxes are layed + * out in left-to-right order, and their baselines are all aligned vertically. + * + */ +public class LineBox extends CompositeBox { + private final static int BASELINE = 0; + + private final static int MIDDLE = 1; + + private final static int SUB = 2; + + private final static int SUPER = 3; + + private final static int TEXT_TOP = 4; + + private final static int TEXT_BOTTOM = 5; + + private final static int TOP = 6; + + private final static int BOTTOM = 7; + + private final static int LENGTH = 8; + + private int _ascent = 0; + + private int _descent = 0; + + private int _fontAscent = 0; + + private int _fontDescent = 0; + + private int _fontLeading = 0; + + private Object _horizonalData = null; + + private Object _htmlInitData = null; + + private int _accumlatedWidth = 0; + + /** + * Removes all owned fragments and invalidates this CompositeBox. + */ + public void clear() { + super.clear(); + _horizonalData = null; + _htmlInitData = null; + } + + /** + * Committing a LineBox will position its children correctly. All children + * boxes are made to have the same baseline, and are layed out from + * left-to-right. + */ + public void commit() { + int baseline = getBaseline(); + int xLocation = _x; + for (int i = 0; i < _fragments.size(); i++) { + FlowBox block = (FlowBox) _fragments.get(i); + block._x = xLocation + block._marginInsets.left; + xLocation = block._x + block._width + block._marginInsets.right; + + if (_fragments.size() > 1 && block instanceof TextFragmentBox) { + TextFragmentBox textBox = (TextFragmentBox) block; + if (textBox.getTextData().length() == 0) { + textBox._height = _fontAscent + _fontDescent + _fontLeading; + textBox.setAscent(_fontAscent + _fontLeading); + block._y = this._y; + continue; + } + } + + switch (getVerticalAlignType(block)) { + case TOP: + block._y = this._y; + break; + case BOTTOM: + block._y = this.getBaseline() - (block.getHeight() - _descent); + break; + case MIDDLE: + int halfXHeight = getHalfXHeight(); + block._y = this.getBaseline() - halfXHeight + - (block.getHeight() + 1) / 2; + break; + case TEXT_TOP: + block._y = this.getBaseline() - _fontAscent - _fontLeading; + break; + case TEXT_BOTTOM: + block._y = this.getBaseline() - (block._height - _fontDescent); + break; + case LENGTH: + block._y = this.getBaseline() + getIncrement(block); + break; + case SUPER: + block._y = this.getBaseline() - getHalfXHeight() * 2 + - block._height; + break; + case SUB: + block._y = this.getBaseline() - block._height * _fontLeading + / getFontHeight(); + break; + case BASELINE: + default: + block.makeBaseline(baseline); + break; + } + if (block instanceof LineBox) { + ((LineBox) block).commit(); + } + } + } + + protected int getVerticalAlignType(FlowBox box) { + Object data = box.getVerticalAlignData(); + + if (data != null) { + if (data instanceof Length) { + return LENGTH; + } else if (VerticalAlignMeta.BASELINE.equals(data)) { + return BASELINE; + } else if (VerticalAlignMeta.MIDDLE.equals(data)) { + return MIDDLE; + } else if (VerticalAlignMeta.SUB.equals(data)) { + return SUB; + } else if (VerticalAlignMeta.SUPER.equals(data)) { + return SUPER; + } else if (VerticalAlignMeta.TEXT_TOP.equals(data)) { + return TEXT_TOP; + } else if (VerticalAlignMeta.TEXT_BOTTOM.equals(data)) { + return TEXT_BOTTOM; + } else if (VerticalAlignMeta.TOP.equals(data)) { + return TOP; + } else if (VerticalAlignMeta.BOTTOM.equals(data)) { + return BOTTOM; + } + return BASELINE; + } + return BASELINE; + } + + /** + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowBox#getAscent() + */ + public int getAscent() { + // because at initial, ascent is 0. And the linebox + // could have some size setting without children. In + // that case, we need handle differently. + if (_ascent == 0 && _fragments.isEmpty()) { + return getHeight(); + } + return _ascent; + } + + /** + * Returns the width available to child fragments. + * + * @return the width in pixels + */ + public int getAvailableWidth() { + if (_recommendedWidth < 0) { + return Integer.MAX_VALUE; + } + int availableWidth = _recommendedWidth - _accumlatedWidth; + if (availableWidth < 0) { + availableWidth = 0; + } + return availableWidth; + } + + /** + * Returns the baseline of this LineBox, which is the y value plus the + * ascent. + * + * @return the baseline value. + */ + public int getBaseline() { + return _y + getAscent(); + } + + /** + * @see CompositeBox#resetInfo() + */ + protected void resetInfo() { + super.resetInfo(); + _accumlatedWidth = 0; + _ascent = 0; + } + + /** + * @see CompositeBox#unionInfo(FlowBox) + */ + protected void unionInfo(FlowBox blockInfo) { + if (blockInfo instanceof TextFragmentBox) { + if (((TextFragmentBox) blockInfo).getTextData().length() == 0) { + return; + } + } + + if (_fragments == null || _fragments.isEmpty()) { + this._ascent = 0; + this._descent = 0; + this._height = 0; + } + + int valign = getVerticalAlignType(blockInfo); + + if (valign == BASELINE) { + _ascent = Math.max(_ascent, blockInfo.getAscent()); + if (blockInfo instanceof WidgetBox) { + _descent = 0; + } else { + _descent = Math.max(_descent, blockInfo.getDescent()); + } + _height = Math.max(_height, _ascent + _descent); + } else if (valign == MIDDLE) { + int halfXHeight = getHalfXHeight(); + _ascent = Math.max(_ascent, (blockInfo.getHeight() + 1) / 2 + + halfXHeight); + _descent = Math.max(_descent, blockInfo.getHeight() / 2 + - halfXHeight); + _height = Math.max(_height, _ascent + _descent); + } else if (valign == TEXT_TOP) { + _ascent = Math.max(_ascent, _fontAscent + _fontLeading); + _descent = Math.max(_descent, blockInfo.getHeight() - _fontAscent + - _fontLeading); + _height = Math.max(_height, _ascent + _descent); + } else if (valign == TEXT_BOTTOM) { + _ascent = Math.max(_ascent, blockInfo.getHeight() - _fontDescent); + _descent = Math.max(_descent, _fontDescent); + _height = Math.max(_height, _ascent + _descent); + } else if (valign == SUB) { + int blockTop = blockInfo._height * _fontLeading / getFontHeight(); + _ascent = Math.max(_ascent, blockTop); + _descent = Math.max(_descent, blockInfo.getHeight() - blockTop); + _height = Math.max(_height, _ascent + _descent); + } else if (valign == SUPER) { + int blockTop = blockInfo._height; + _ascent = Math.max(_ascent, getHalfXHeight() * 2 + blockTop); + _height = Math.max(_height, _ascent + _descent); + } else if (valign == LENGTH) { + int increment = getIncrement(blockInfo); + _ascent = Math.max(_ascent, blockInfo.getAscent() + increment); + _descent = Math.max(_descent, blockInfo.getDescent() - increment); + _height = Math.max(_height, _ascent + _descent); + } else if (valign == TOP) { + _descent = Math.max(_descent, blockInfo.getHeight() - _ascent); + _height = Math.max(_height, _ascent + _descent); + } else if (valign == BOTTOM) { + // XXX:the render of IE is not consistent with spec, mozilla is. so + // we follow mozilla's implementation. + _ascent = Math.max(_ascent, blockInfo.getHeight() - _descent); + _height = Math.max(_height, _ascent + _descent); + } else { + _ascent = Math.max(_ascent, blockInfo.getAscent()); + _descent = Math.max(_descent, blockInfo.getDescent()); + _height = Math.max(_height, blockInfo.getHeight()); + } + + _accumlatedWidth += blockInfo._width + + blockInfo._marginInsets.getWidth(); + if (_accumlatedWidth > _width) { + _width = _accumlatedWidth; + } + } + + private int getIncrement(FlowBox blockInfo) { + int valign = getVerticalAlignType(blockInfo); + if (valign == LENGTH) { + int increment = 0; + Length length = (Length) blockInfo.getVerticalAlignData(); + if (length.isPercentage()) { + increment = length.getValue() * getFontHeight() / 100; + } else { + increment = length.getValue(); + } + return increment; + } + return 0; + } + + /** + * @see org.eclipse.draw2d.geometry.Rectangle#isEmpty() + */ + public boolean isOccupied() { + if (_width > 0) { + return true; + } + + if (_fragments.isEmpty()) { + return false; + } + // int size = _fragments.size(); + // if (size > 1) + // { + // return true; + // } + // ok, we have one segment + // FlowBox box = (FlowBox) _fragments.get(0); + // if (box instanceof TextFragmentBox) + // { + // if (((TextFragmentBox) box).getTextData().length() == 0) + // { + // // this is an empty string text box. + // return false; + // } + // } + return true; + } + + public boolean isEmptyStringLine() { + // if(this.getWidth() == 0) + // { + // return true; + // } + // else + // { + // return false; + // } + if (_fragments.size() == 1) { + FlowBox box = (FlowBox) _fragments.get(0); + if (box instanceof TextFragmentBox) { + if (box instanceof TextFragmentBox) { + if (((TextFragmentBox) box).getTextData().length() == 0) { + return true; + } + } + } + } + return false; + } + + /** + * @param fontMetrics + */ + public void setFontMetrics(FontMetrics fontMetrics) { + if (fontMetrics != null) { + _fontAscent = fontMetrics.getAscent(); + _fontDescent = fontMetrics.getDescent(); + _fontLeading = fontMetrics.getLeading(); + // if (_fragments == null || _fragments.isEmpty()) + // { + // this._ascent = _fontAscent + _fontLeading; + // this._descent = _fontDescent; + // if (this._height < this._ascent + this._descent) + // { + // this._height = this._ascent + this._descent; + // } + // } + } else { + _fontAscent = 0; + _fontDescent = 0; + _fontLeading = 0; + } + } + + private int getHalfXHeight() { + return (_fontAscent + _fontDescent + _fontLeading) / 5; + } + + private int getFontHeight() { + return _fontAscent + _fontDescent + _fontLeading; + } + + /** + * @return Returns the horizonalData. + */ + public Object getHorizonalData() { + return _horizonalData; + } + + /** + * @param horizonalData + * The horizonalData to set. + */ + public void setHorizonalData(Object horizonalData) { + this._horizonalData = horizonalData; + } + + /** + * @return Returns the htmlInitData. + */ + public Object getHtmlInitData() { + return _htmlInitData; + } + + /** + * @param htmlInitData + * The htmlInitData to set. + */ + public void setHtmlInitData(Object htmlInitData) { + this._htmlInitData = htmlInitData; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/MultiLineLabel.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/MultiLineLabel.java new file mode 100644 index 000000000..24ed25588 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/MultiLineLabel.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.FigureUtilities; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.Label; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontMetrics; + +public class MultiLineLabel extends Label { + private static String ELLIPSIS = "..."; //$NON-NLS-1$ + + protected void paintFigure(Graphics graphics) { + if (isOpaque()) { + graphics.fillRectangle(getBounds()); + } + Rectangle bounds = getBounds(); + graphics.translate(bounds.x, bounds.y); + drawText(graphics); + graphics.translate(-bounds.x, -bounds.y); + } + + private void drawText(Graphics graphics) { + String[] strings = splitString(getText()); + int y = 0; + int lineHeight = FigureUtilities.getFontMetrics(getFont()).getHeight(); + for (int i = 0; i < strings.length; i++) { + graphics.drawText(getSubStringText(strings[i]), 0, y); + y += lineHeight; + } + + } + + private String[] splitString(String text) { + String[] lines = new String[1]; + int start = 0, pos; + do { + pos = text.indexOf('\n', start); + if (pos == -1) { + lines[lines.length - 1] = text.substring(start); + } else { + boolean crlf = (pos > 0) && (text.charAt(pos - 1) == '\r'); + lines[lines.length - 1] = text.substring(start, pos + - (crlf ? 1 : 0)); + start = pos + 1; + String[] newLines = new String[lines.length + 1]; + System.arraycopy(lines, 0, newLines, 0, lines.length); + lines = newLines; + } + } while (pos != -1); + return lines; + } + + public String getSubStringText(String text) { + String subStringText = text; + + Font currentFont = getFont(); + int textWidth = FigureUtilities.getTextWidth(text, currentFont); + if (textWidth - getSize().width <= 0) { + return subStringText; + } + + Dimension effectiveSize = new Dimension(getSize().width, 0); + + int dotsWidth = FigureUtilities.getTextWidth(ELLIPSIS, currentFont); + + if (effectiveSize.width < dotsWidth) { + effectiveSize.width = dotsWidth; + } + + int subStringLength = getLargestSubstringConfinedTo(text, currentFont, + effectiveSize.width - dotsWidth); + subStringText = new String(text.substring(0, subStringLength) + + ELLIPSIS); + return subStringText; + } + + int getLargestSubstringConfinedTo(String s, Font f, int availableWidth) { + FontMetrics metrics = FigureUtilities.getFontMetrics(f); + int min, max; + float avg = metrics.getAverageCharWidth(); + min = 0; + max = s.length() + 1; + + // The size of the current guess + int guess = 0, guessSize = 0; + while ((max - min) > 1) { + // Pick a new guess size + // New guess is the last guess plus the missing width in pixels + // divided by the average character size in pixels + guess = guess + (int) ((availableWidth - guessSize) / avg); + + if (guess >= max) { + guess = max - 1; + } + if (guess <= min) { + guess = min + 1; + } + + // Measure the current guess + guessSize = FigureUtilities + .getTextExtents(s.substring(0, guess), f).width; + + if (guessSize < availableWidth) { + // We did not use the available width + min = guess; + } else { + // We exceeded the available width + max = guess; + } + } + return min; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/PageFlowLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/PageFlowLayout.java new file mode 100644 index 000000000..54dedbfaf --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/PageFlowLayout.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.FigureUtilities; +import org.eclipse.jst.pagedesigner.css2.font.CSSFont; +import org.eclipse.jst.pagedesigner.css2.font.CSSFontManager; +import org.eclipse.swt.graphics.Font; + +/** + * A block layout which requires no FlowContext to perform its layout. This + * class is used by {@link FlowPage}. + * <p> + * WARNING: This class is not intended to be subclassed by clients. + */ +public class PageFlowLayout extends BlockFlowLayout { + + /** + * Creates a new PageFlowLayout with the given FlowPage + * + * @param page + * the FlowPage + */ + public PageFlowLayout(FlowPage page) { + super(page); + } + + /** + * @see BlockFlowLayout#endBlock() + */ + protected void endBlock() { + } + + /** + * TODO: This method is not being called. + */ + public void postValidate() { + } + + protected void setupLine(LineBox line, int topMargin) { + super.setupLine(line, topMargin); + + CSSFontManager fontManager = CSSFontManager.getInstance(); + Font font = fontManager.getSwtFont((CSSFont) fontManager + .createDefaultFont()); + line.setFontMetrics(FigureUtilities.getFontMetrics(font)); + } + + /** + * Setup blockBox to the initial bounds of the Page + */ + protected void setupBlock() { + // Remove all current Fragments + _blockBox.clear(); + + // Setup the one fragment for this Block with the correct X and + // available width + _blockBox.setRecommendedWidth(((FlowPage) getFlowFigure()) + .getRecommendedWidth()); + _blockBox._x = 0; + } + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/TextFragmentBox.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/TextFragmentBox.java new file mode 100644 index 000000000..00b181da8 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/TextFragmentBox.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * 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; + +/** + * A Geometric object for representing a TextFragment region on a line of Text. + */ +public class TextFragmentBox extends FlowBox { + + /** The offset in pixels * */ + public int _offset; + + /** The length in pixels * */ + public int _length; + + private int _ascent; + + // boolean _truncated; + + public boolean _isLastCharWhitespace = false; + + private String _textData; + + /** + * Creates a new TextFragmentBox + */ + public TextFragmentBox() { + } + + /** + * Returns the ascent of this TextFragmentBox + * + * @return the ascent + */ + public int getAscent() { + return _ascent; + } + + /** + * Sets the ascent of this TextFragmentBox to the given value + * + * @param a + * the ascent + */ + public void setAscent(int a) { + _ascent = a; + } + + /** + * Sets the height of this TextFragmentBox to the given value + * + * @param h + * the height + */ + public void setHeight(int h) { + _height = h; + } + + /** + * Sets the width of this TextFragmentBox to the given value + * + * @param w + * the width + */ + public void setWidth(int w) { + _width = w; + } + + public String getTextData() { + return _textData; + } + + public void setTextData(String txt) { + _textData = txt; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/TextLayoutSupport.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/TextLayoutSupport.java new file mode 100644 index 000000000..008d7369b --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/TextLayoutSupport.java @@ -0,0 +1,386 @@ +/******************************************************************************* + * 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.Graphics; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.PDPlugin; +import org.eclipse.jst.pagedesigner.common.logging.Logger; +import org.eclipse.jst.pagedesigner.css2.property.TextDecorationMeta; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; + +/** + * @author mengbo + */ +public class TextLayoutSupport { + private static final Logger _log = PDPlugin + .getLogger(TextLayoutSupport.class); + + private static final String[] DELIMITERS = { "\r\n", //$NON-NLS-1$ + "\n", //$NON-NLS-1$ + "\r" //$NON-NLS-1$ + }; //$NON-NLS-1$ + + static private int delimeterLength; + + /** + * Reuses an existing <code>TextFragmentBox</code>, or creates a new one. + * + * @param i + * the index + * @param fragments + * the original list of fragments + * @return a TextFragmentBox + */ + // copied from TextLayout + protected static TextFragmentBox getFragment(int i, List fragments) { + if (fragments.size() > i) { + return (TextFragmentBox) fragments.get(i); + } + TextFragmentBox box = new TextFragmentBox(); + fragments.add(box); + return box; + } + + /** + * Returns the average character width of given TextFragmentbox + * + * @param fragment + * the TextFragmentBox + * @return the average character width + */ + public static float getAverageCharWidth(TextFragmentBox fragment) { + if (fragment._width != 0 && fragment._length != 0) { + return fragment._width / (float) fragment._length; + } + return 0.0f; + } + + // ---------------------------------------------------------------------------------------- + /** + * this method will create a set of TextFragment. Each fragment will offset + * to the original text (whole text for the text figure). + */ + public static void layoutNormal(FlowContext context, String text, + List fragments, Font font, int wrappingStyle, boolean trimLeading) { + int i = 0; // The index of the current fragment; + int offset = 0; + if (trimLeading) { + offset = 1; + text = text.substring(1); + } + + int length = 0; // The length of the current fragment + float prevAvgCharWidth; + LineBox currentLine; + TextFragmentBox fragment; + + while (text.length() > 0) { + fragment = null; + prevAvgCharWidth = 0f; + fragment = getFragment(i, fragments); + prevAvgCharWidth = getAverageCharWidth(fragment); + + // Check for newline, if it exists, call context.endLine and skip + // over the newline + // Exccept for first time through, don't do this. + if (i != 0) { + boolean changed = false; + if (text.charAt(0) == '\r') { + text = text.substring(1); + changed = true; + offset += 1; + } + if (text.length() != 0 && text.charAt(0) == '\n') { + text = text.substring(1); + changed = true; + offset += 1; + } + if (changed) { + context.endLine(); + } + } + + fragment._offset = offset; + + // This loop is done at most twice. + // The second time through, a context.endLine() + // was requested, and the loop will break. + while (true) { + currentLine = context.getCurrentLine(); + length = FlowUtilities.setupFragmentBasedOnTextSpace(fragment, + text, font, currentLine.getAvailableWidth(), + prevAvgCharWidth, wrappingStyle); + + if (fragment._width <= currentLine.getAvailableWidth() + || !context.isCurrentLineOccupied()) { + break; + } + context.endLine(); + } + // fragment.x = context.getCurrentX(); + context.addToCurrentLine(fragment); + text = text.substring(length); + offset += length; + if (text.length() > 0) { + context.endLine(); + } + i++; + } + + // Remove the remaining unused fragments. + while (i < fragments.size()) { + fragments.remove(fragments.size() - 1); + } + } + + public static void layoutNoWrap(FlowContext context, String text, + List fragments, Font font) { + TextFragmentBox fragment; + int i = 0; + int offset = 0; + + while (offset < text.length()) { + int result = nextLineBreak(text, offset); + fragment = getFragment(i++, fragments); + fragment._length = result - offset; + fragment._offset = offset; + FlowUtilities.setupFragment(fragment, font, text.substring(offset)); + context.getCurrentLine().add(fragment); + offset = result + delimeterLength; + if (delimeterLength != 0) { + // in nextLineBreak we fo + context.endLine(); + } + + } + // Remove the remaining unused fragments. + while (i < fragments.size()) { + fragments.remove(i++); + } + } + + private static int nextLineBreak(String text, int offset) { + int result = text.length(); + delimeterLength = 0; + int current; + for (int i = 0; i < DELIMITERS.length; i++) { + current = text.indexOf(DELIMITERS[i], offset); + if (current != -1 && current < result) { + result = current; + delimeterLength = DELIMITERS[i].length(); + } + } + return result; + } + + public static void paintTextFigure(Graphics g, List fragments, Font font, + int textDecoration) { + paintTextFigure(g, fragments, font, null, textDecoration); + } + + public static void paintTextDecoration(Graphics g, Rectangle rect, + int textDecoration) { + if ((textDecoration & TextDecorationMeta.UNDERLINE) != 0) { + g.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width + - 1, rect.y + rect.height - 1); + } + if ((textDecoration & TextDecorationMeta.OVERLINE) != 0) { + g.drawLine(rect.x, rect.y + 1, rect.x + rect.width - 1, rect.y + 1); + } + if ((textDecoration & TextDecorationMeta.LINETHROUGH) != 0) { + g.drawLine(rect.x, rect.y + rect.height / 2, rect.x + rect.width + - 1, rect.y + rect.height / 2); + } + } + + public static void paintTextFigure(Graphics g, List fragments, Font font, + Color color, int textDecoration) { + // FIXME: It happens there is problem in this method's parameters. what + // exception should be catched? + try { + TextFragmentBox frag; + // XXX: adjust font. Here is not using setFont(), because that will + // result in revalidate + g.setFont(font); + + for (int i = 0; i < fragments.size(); i++) { + frag = (TextFragmentBox) fragments.get(i); + // if (!g.getClip(Rectangle.SINGLETON).intersects(frag)) + // continue; + String draw; + draw = frag.getTextData(); + + if (color != null) { + g.setForegroundColor(color); + } + g.drawText(draw, frag._x, frag._y); + if ((textDecoration & TextDecorationMeta.UNDERLINE) != 0) { + g.drawLine(frag._x, frag._y + frag.getHeight() - 1, frag._x + + frag.getWidth(), frag._y + frag.getHeight() - 1); + } + if ((textDecoration & TextDecorationMeta.OVERLINE) != 0) { + g.drawLine(frag._x, frag._y, frag._x + frag.getWidth(), + frag._y); + } + if ((textDecoration & TextDecorationMeta.LINETHROUGH) != 0) { + g.drawLine(frag._x, frag._y + frag.getHeight() / 2, frag._x + + frag.getWidth(), frag._y + frag.getHeight() / 2); + } + + if (Debug.DEBUG_BASELINE) { + g.drawLine(frag._x, frag._y + frag.getAscent(), frag._x + + frag.getWidth(), frag._y + frag.getAscent()); + } + } + } catch (Exception e) { + // "Error in text painting:" + _log.info("TextLayoutSupport.Info.1", e); //$NON-NLS-1$ + } + } + + /** + * + * @param g + * @param fragments + * @param text + * all the text in the Text figure. + * @param font + * @param color + * @param textDecoration + * @param start + * @param end + * @param selectionForeColor + * @param selectionBackColor + */ + public static void paintTextFigureWithSelection(Graphics g, List fragments, + String text, Font font, Color color, int textDecoration, int start, + int end, Color selectionForeColor, Color selectionBackColor) { + // FIXME: It happens there is problem in this method's parameters. what + // exception should be catched? + try { + TextFragmentBox frag; + + Color originalForeground = g.getForegroundColor(); + Color originalBackgroud = g.getBackgroundColor(); + + // XXX: adjust font. Here is not using setFont(), because that will + // result in revalidate + g.setFont(font); + + for (int i = 0, n = fragments.size(); i < n; i++) { + frag = (TextFragmentBox) fragments.get(i); + + // to make things simpler, we always draw the line using default + // color + if (color != null) { + g.setForegroundColor(color); + } + + // if (!g.getClip(Rectangle.SINGLETON).intersects(frag)) + // continue; + String draw; + draw = frag.getTextData(); + if (frag._offset >= end || frag._offset + frag._length <= start) { + // we are not in selection. no need to change color + g.drawText(draw, frag._x, frag._y); + paintTextDecoration(g, frag.getRectangle(), textDecoration); + } else if (frag._offset >= start + && frag._offset + frag._length <= end) { + // we are fully in selection + g.setForegroundColor(selectionForeColor); + g.setBackgroundColor(selectionBackColor); + g + .fillRectangle(frag._x, frag._y, FlowUtilities + .getTextExtents(draw, font).width, frag + .getHeight()); + g.drawText(draw, frag._x, frag._y); + paintTextDecoration(g, frag.getRectangle(), textDecoration); + } else { + // partial of the fragment's text is in selection. + + // draw the original string first + g.drawText(draw, frag._x, frag._y); + // then override with the selected parts. + g.setForegroundColor(selectionForeColor); + g.setBackgroundColor(selectionBackColor); + int partialStart = frag._offset > start ? frag._offset + : start; + int partialEnd = (frag._offset + frag._length > end) ? end + : (frag._offset + frag._length); + int x = 0; + String skip = text.substring(frag._offset, partialStart); + x = FlowUtilities.getTextExtents(skip, font).width; + String todraw = text.substring(partialStart, partialEnd); + if (todraw.length() > 0) { + Dimension dimension = FlowUtilities.getTextExtents(skip + + todraw, font); + g.fillRectangle(frag._x + x, frag._y, dimension.width + - x, dimension.height); + g.drawText(skip + todraw, frag._x, frag._y); + if (color != null) { + g.setForegroundColor(color); + } else { + g.setForegroundColor(originalForeground); + } + g.drawText(skip, frag._x, frag._y); + paintTextDecoration(g, frag.getRectangle(), + textDecoration); + g.setForegroundColor(selectionForeColor); + paintTextDecoration(g, + new Rectangle(frag._x + x, frag._y, + dimension.width - x, dimension.height), + textDecoration); + } + } + + // we do this in each loop, to make sure we are using correct + // color + g.setForegroundColor(originalForeground); + g.setBackgroundColor(originalBackgroud); + + } + } catch (Exception e) { + // "Error in text painting:" + _log.info("TextLayoutSupport.Info.1", e); //$NON-NLS-1$ + } + } + + public static int getBeginX(Object textAlign, Rectangle rect, int textWidth) { + int x = rect.x; + if (textAlign != null) { + String align = textAlign.toString(); + if ("left".equalsIgnoreCase(align)) //$NON-NLS-1$ + { + x = rect.x + 1; + } else if ("right".equalsIgnoreCase(align)) //$NON-NLS-1$ + { + x = rect.x + rect.width - textWidth - 1; + if (x < 1) { + x = 1; + } + } else if ("center".equalsIgnoreCase(align)) //$NON-NLS-1$ + { + int offset = (rect.width - textWidth) / 2; + if (offset <= 0) { + offset = 0; + } + x = x + offset + 1; + } + } + return x; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/WidgetBox.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/WidgetBox.java new file mode 100644 index 000000000..7143e83d2 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/WidgetBox.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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; + +/** + * Simple box support ascent. + * + * @author mengbo + * @version 1.5 + */ +public class WidgetBox extends FlowBox { + private int _ascent = -1; + + public int getAscent() { + if (_ascent < 0) { + return super.getAscent(); + } else { + return _ascent; + } + } + + public void setAscent(int ascent) { + _ascent = ascent; + } + + /** + * @return + */ + public boolean supportAscent() { + return _ascent > 0; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTRGroupLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTRGroupLayout.java new file mode 100644 index 000000000..814c7026a --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTRGroupLayout.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * 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.List; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LayoutManager; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout; +import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; +import org.eclipse.jst.pagedesigner.css2.layout.FlowFigure; +import org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure; + +/** + * @author mengbo + * @version 1.5 + */ +public class CSSTRGroupLayout extends CSSBlockFlowLayout { + + /** + * @param cssfigure + */ + public CSSTRGroupLayout(CSSFigure cssfigure) { + super(cssfigure); + } + + /** + * the parent figure of TRGroup should be table figure. If so, return the + * corresponding table layout. + * + * @return + */ + public CSSTableLayout2 getTableLayoutContext() { + IFigure parent = getCSSFigure().getParent(); + if (parent != null) { + LayoutManager parentLayout = parent.getLayoutManager(); + if (parentLayout instanceof CSSTableLayout2) { + return (CSSTableLayout2) parentLayout; + } + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#postValidate() + */ + public void postValidate() { + CSSTableLayout2 tableLayout = getTableLayoutContext(); + if (tableLayout == null) { + super.postValidate(); + } else { + Rectangle r = getTRGroupRect(tableLayout); + if (r != null) { + _blockBox.setXYWidthHeight(r); + getCSSFigure().setBounds(r); + List list = getCSSFigure().getChildren(); + for (int i = 0; i < list.size(); i++) { + ((FlowFigure) list.get(i)).postValidate(); + } + } else { + super.postValidate(); + } + } + } + + /** + * @return + */ + private Rectangle getTRGroupRect(CSSTableLayout2 tableLayout) { + TableRowGroupInfo groupinfo = tableLayout.getGroupInfo(this + .getCSSFigure()); + int rowIndex = groupinfo.getRowIndex(); + int rowCount = groupinfo.getRowCount(); + int y = (rowIndex + 1) * tableLayout.getVSpacing(); + for (int k = 0; k < rowIndex; k++) { + y += tableLayout.getRowHeights()[k]; + } + if (tableLayout.getCaptionInfo() != null + && "top".equalsIgnoreCase(tableLayout.getCaptionInfo().getAlign())) //$NON-NLS-1$ + { + y += tableLayout.getCaptionSize().height; + } + + int height = (rowCount - 1) * tableLayout.getVSpacing(); + for (int k = 0; k < rowCount; k++) { + height += tableLayout.getRowHeights()[rowIndex + k]; + } + ICSSFigure figure = groupinfo.getFigure(); + return new Rectangle(tableLayout.getRowX(), y, tableLayout + .getRowWidth(), height); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#useLocalCoordinates() + */ + public boolean useLocalCoordinates() { + // if is in table, we don't use local coordinates. + return getTableLayoutContext() == null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#endBlock() + */ + protected void endBlock() { + if (getTableLayoutContext() == null) { + super.endBlock(); + } else { + layoutLines(); + } + } + + // /* (non-Javadoc) + // * @see + // org.eclipse.jst.pagedesigner.css2.layout.FlowContainerLayout#layout() + // */ + // protected void layout() + // { + // CSSTableLayout2 tableLayout = getTableLayoutContext(); + // if (tableLayout == null) + // { + // // we are not in table? treat as block. + // super.layout(); + // } + // else + // { + // // ok, we are in table. we need to layout our children. + // TableRowGroupInfo groupInfo = + // tableLayout.getGroupInfo(this.getCSSFigure()); + // int[] rowHeights = tableLayout.getRowHeights(); + // int vspacing = tableLayout.getVSpacing(); + // int rowwidth = getCSSFigure().getBounds().width;// XXX: get from table + // layout? + // int grouprowindex = groupInfo.getRowIndex(); + // List rows = groupInfo.getRowList(); + // for (int i=0, size=rows.size(); i<size; i++) + // { + // TableRowInfo rowinfo = (TableRowInfo) rows.get(i); + // ICSSFigure figure = rowinfo.getFigure(); + // + // int y = 0; + // int rowindex = rowinfo.getRowIndex(); + // for (int row=grouprowindex; row<rowindex; row++) + // { + // y += rowHeights[row]; + // y += vspacing; + // } + // int height = rowHeights[rowindex]; + // Rectangle rect = new Rectangle(0, y, rowwidth, height); + // figure.setBounds(rect); + // } + // } + // } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSLayout#handlingBorderForBlock() + */ + public boolean handlingBorderForBlock() { + return false; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTRLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTRLayout.java new file mode 100644 index 000000000..d29fdfcf4 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTRLayout.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * 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.List; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LayoutManager; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout; +import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; +import org.eclipse.jst.pagedesigner.css2.layout.FlowFigure; +import org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure; + +/** + * @author mengbo + * @version 1.5 + */ +public class CSSTRLayout extends CSSBlockFlowLayout { + /** + * @param cssfigure + */ + public CSSTRLayout(CSSFigure cssfigure) { + super(cssfigure); + } + + /** + * the parent figure of TRGroup should be table figure. If so, return the + * corresponding table layout. + * + * @return + */ + public CSSTableLayout2 getTableLayoutContext() { + IFigure parent = getCSSFigure().getParent(); + if (parent != null) { + LayoutManager parentLayout = parent.getLayoutManager(); + if (parentLayout instanceof CSSTableLayout2) { + return (CSSTableLayout2) parentLayout; + } else if (parentLayout instanceof CSSTRGroupLayout) { + return ((CSSTRGroupLayout) parentLayout) + .getTableLayoutContext(); + } + } + + return null; + } + + public CSSTRGroupLayout getTRGroupLayout() { + IFigure parent = getCSSFigure().getParent(); + if (parent != null) { + LayoutManager parentLayout = parent.getLayoutManager(); + if (parentLayout instanceof CSSTRGroupLayout) { + return ((CSSTRGroupLayout) parentLayout); + } + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#postValidate() + */ + public void postValidate() { + CSSTableLayout2 tableLayout = getTableLayoutContext(); + if (tableLayout == null) { + // we are not in table? treat as block. + super.postValidate(); + } else { + Rectangle r = getTRRect(tableLayout, getTRGroupLayout()); + if (r != null) { + _blockBox.setXYWidthHeight(r); + getCSSFigure().setBounds(r); + List list = getCSSFigure().getChildren(); + for (int i = 0; i < list.size(); i++) { + ((FlowFigure) list.get(i)).postValidate(); + } + } else { + super.postValidate(); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#endBlock() + */ + protected void endBlock() { + if (this.getTableLayoutContext() == null) { + super.endBlock(); + } else { + layoutLines(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#useLocalCoordinates() + */ + public boolean useLocalCoordinates() { + return this.getTableLayoutContext() == null; + } + + private Rectangle getTRRect(CSSTableLayout2 tableLayout, + CSSTRGroupLayout groupLayout) { + TableRowInfo rowinfo = tableLayout.getRowInfo(this.getCSSFigure()); + int rowIndex = rowinfo.getRowIndex(); + int y = (rowIndex + 1) * tableLayout.getVSpacing(); + for (int k = 0; k < rowIndex; k++) { + y += tableLayout.getRowHeights()[k]; + } + if (tableLayout.getCaptionInfo() != null + && "top".equalsIgnoreCase(tableLayout.getCaptionInfo().getAlign())) //$NON-NLS-1$ + { + y += tableLayout.getCaptionSize().height; + } + + int height = tableLayout.getRowHeights()[rowIndex]; + ICSSFigure figure = rowinfo.getFigure(); + return new Rectangle(tableLayout.getRowX(), y, tableLayout + .getRowWidth(), height); + } + + /** + * @param tableLayout + * @param groupLayout + * @return + */ + // private Rectangle getTRRect(CSSTableLayout2 tableLayout, CSSTRGroupLayout + // groupLayout) + // { + // TableRowGroupInfo groupInfo = null; + // if (groupLayout != null) + // { + // groupInfo = tableLayout.getGroupInfo(groupLayout.getCSSFigure()); + // } + // if (groupInfo != null) + // { + // // This TR is in tr group + // int[] rowHeights = tableLayout.getRowHeights(); + // int vspacing = tableLayout.getVSpacing(); + // int rowwidth = tableLayout.getRowWidth(); + // int grouprowindex = groupInfo.getRowIndex(); + // TableRowInfo rowinfo = tableLayout.getRowInfo(this.getCSSFigure()); + // ICSSFigure figure = rowinfo.getFigure(); + // + // int y = 0; + // int rowindex = rowinfo.getRowIndex(); + // for (int row = grouprowindex; row < rowindex; row++) + // { + // y += rowHeights[row]; + // y += vspacing; + // } + // int height = rowHeights[rowindex]; + // Rectangle rect = new Rectangle(0, y, rowwidth, height); + // return rect; + // } + // else + // { + // TableRowInfo rowinfo = tableLayout.getRowInfo(this.getCSSFigure()); + // int rowIndex = rowinfo.getRowIndex(); + // int y = (rowIndex+1) * tableLayout.getVSpacing(); + // int rowHeights[] = tableLayout.getRowHeights(); + // for (int k=0; k<rowIndex; k++) + // { + // y += rowHeights[k]; + // } + // int height = rowHeights[rowIndex]; + // return new Rectangle(tableLayout.getRowX(), y, tableLayout.getRowWidth(), + // height); + // } + // } + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSLayout#handlingBorderForBlock() + */ + public boolean handlingBorderForBlock() { + return false; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableCaptionLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableCaptionLayout.java new file mode 100644 index 000000000..27d15717c --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableCaptionLayout.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LayoutManager; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; + +/** + * @author mengbo + * @version 1.5 + */ +public class CSSTableCaptionLayout extends CachedTableCellLayout { + private CSSTableLayout2 _tableLayout; + + private TableCaptionInfo _caption; + + /** + * @param cssfigure + */ + public CSSTableCaptionLayout(CSSFigure cssfigure) { + super(cssfigure); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#invalidate() + */ + public void invalidate() { + super.invalidate(); + + _tableLayout = null; + _caption = null; + } + + public Rectangle getCellRect() { + int x = 0; + + int[] rowHeights = _tableLayout.getRowHeights(); + int vspacing = _tableLayout.getVSpacing(); + int y = vspacing; + if (_caption != null && "bottom".equalsIgnoreCase(_caption.getAlign())) //$NON-NLS-1$ + { + for (int row = 0; row < rowHeights.length; row++) { + y += rowHeights[row]; + y += vspacing; + } + } + + int height = 0; + height = _tableLayout.getCaptionSize().height; + int width = _tableLayout.getCaptionSize().width; + Rectangle rect = new Rectangle(x, y, width, height); + return rect; + } + + /** + * the parent figure of TRGroup should be table figure. If so, return the + * corresponding table layout. + * + * @return + */ + public CSSTableLayout2 getTableLayoutContext() { + IFigure parent = getCSSFigure().getParent(); + if (parent != null) { + LayoutManager parentLayout = parent.getLayoutManager(); + if (parentLayout instanceof CSSTableLayout2) { + return (CSSTableLayout2) parentLayout; + } + } + return null; + } + + /** + * @return + */ + public boolean initializeTableInfo() { + _caption = null; + _tableLayout = getTableLayoutContext(); + if (_tableLayout != null) { + _caption = _tableLayout.getCaptionInfo(); + return _caption != null; + } + return false; + } + + public CSSTableLayout2 getTableLayout() { + return _tableLayout; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSLayout#isCalculatingMaxWidth() + */ + public boolean isCalculatingMaxWidth() { + return false; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableCellLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableCellLayout.java new file mode 100644 index 000000000..f09529ef0 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableCellLayout.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * 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.List; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LayoutManager; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; +import org.eclipse.jst.pagedesigner.css2.layout.FlowBox; +import org.eclipse.jst.pagedesigner.css2.layout.LineBox; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.jst.pagedesigner.css2.property.VerticalAlignMeta; + +/** + * This layout is for those thigns that it's parent will decide its size. Such + * as table cell. + * + * @author mengbo + * @version 1.5 + */ +public class CSSTableCellLayout extends CachedTableCellLayout { + private CSSTableLayout2 _tableLayout; + + private TableRowInfo _rowinfo; + + private TableCellInfo _cellinfo; + + /** + * @param cssfigure + */ + public CSSTableCellLayout(CSSFigure cssfigure) { + super(cssfigure); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#invalidate() + */ + public void invalidate() { + super.invalidate(); + + _tableLayout = null; + _rowinfo = null; + _cellinfo = null; + } + + protected void endBlock() { + if (isTable()) { + verticalLayoutLines(); + layoutLines(); + } else { + super.endBlock(); + } + } + + protected void verticalLayoutLines() { + List lines = _blockBox.getFragments(); + + String verticalStyle = getVerticalAlign(); + int linesHeight = 0; + + if (lines != null && !lines.isEmpty()) { + FlowBox bottomBox = ((FlowBox) lines.get(lines.size() - 1)); + FlowBox topBox = ((FlowBox) lines.get(0)); + linesHeight = bottomBox._y + bottomBox.getHeight() - topBox._y; + } + int movement = 0; + if (VerticalAlignMeta.BOTTOM.equals(verticalStyle)) { + movement = _blockBox.getHeight() - linesHeight + - _blockBox.getBorderPaddingHeight() / 2; + } else if (VerticalAlignMeta.TOP.equals(verticalStyle)) { + movement = 0; + } + // else if (VerticalAlignMeta.BASELINE.equals(verticalStyle)) + // { + // movement = _blockBox.getHeight() - linesHeight; + // } + else // if (VerticalAlignMeta.MIDDLE.equals(verticalStyle)) + { + movement = (_blockBox.getHeight() - linesHeight - _blockBox + .getBorderPaddingHeight()) / 2; + } + // VerticalAlignMeta.TOP, ICSSPropertyID.VAL_AUTO and others + // else + // { + // movement = 0; + // } + if (lines != null) { + for (int i = 0, n = lines.size(); i < n; i++) { + if (lines.get(i) instanceof LineBox) { + LineBox lineBox = (LineBox) lines.get(i); + int LineMovement = Math.max(lineBox._marginInsets + .getHeight(), movement); + lineBox._y = lineBox._y + LineMovement + - lineBox._marginInsets.getHeight(); + } + } + } + } + + private String getVerticalAlign() { + ICSSStyle style = getCSSStyle(); + if (style != null) { + return style.getStyleProperty(ICSSPropertyID.ATTR_VERTICAL_ALIGN) + .toString(); + } + return VerticalAlignMeta.MIDDLE; + } + + public Rectangle getCellRect() { + int columnIndex = _cellinfo.getColumnIndex(); + int rowIndex = _cellinfo.getRowIndex(); + int[] columnWidths = _tableLayout.getColumnWidths(); + int hspacing = _tableLayout.getHSpacing(); + int x = hspacing; + for (int col = 0; col < columnIndex; col++) { + x += columnWidths[col]; + x += hspacing; + } + + int[] rowHeights = _tableLayout.getRowHeights(); + int vspacing = _tableLayout.getVSpacing(); + int y = vspacing; + for (int row = 0; row < rowIndex; row++) { + y += rowHeights[row]; + y += vspacing; + } + if (_tableLayout.getCaptionInfo() != null + && "top".equalsIgnoreCase(_tableLayout.getCaptionInfo().getAlign())) //$NON-NLS-1$ + { + y += _tableLayout.getCaptionSize().height; + } + + int width = _tableLayout.getCellWidth(_cellinfo, columnWidths); + int height = _tableLayout.getCellHeight(_cellinfo, rowHeights); + + Rectangle rect = new Rectangle(x, y, width, height); + return rect; + } + + /** + * the parent figure of TRGroup should be table figure. If so, return the + * corresponding table layout. + * + * @return + */ + public CSSTableLayout2 getTableLayoutContext() { + IFigure parent = getCSSFigure().getParent(); + if (parent != null) { + LayoutManager parentLayout = parent.getLayoutManager(); + if (parentLayout instanceof CSSTRLayout) { + return ((CSSTRLayout) parentLayout).getTableLayoutContext(); + } + } + + return null; + } + + /** + * @return + */ + public boolean initializeTableInfo() { + _rowinfo = null; + _cellinfo = null; + _tableLayout = getTableLayoutContext(); + if (_tableLayout != null) { + _rowinfo = _tableLayout.getRowInfo((CSSFigure) this.getCSSFigure() + .getParent()); + if (_rowinfo != null) { + _cellinfo = _rowinfo.getCellInfo(this.getCSSFigure()); + if (_cellinfo != null) { + return true; + } + } + } + return false; + } + + public CSSTableLayout2 getTableLayout() { + return _tableLayout; + } + + public TableCellInfo getTableCellInfo() { + return _cellinfo; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableLayout2.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableLayout2.java new file mode 100644 index 000000000..5acdfb1af --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CSSTableLayout2.java @@ -0,0 +1,628 @@ +/******************************************************************************* + * 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.pagedesigner.PDPlugin; +import org.eclipse.jst.pagedesigner.common.logging.Logger; +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 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._caption != null) { + captionWidth = _tableInfo._caption.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._caption != null) { + _captionSize = _tableInfo._caption.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 && toDistribute > 0; i++) { + if (_tableInfo._widthSpecified[i]) { + delta[i] = 0; + } else { + delta[i] = columnMaxWidths[i] - columnMinWidths[i]; + if (delta[i] <= 0) { + delta[i] = 0; + } + sigmaDelta += delta[i]; + } + } + if (sigmaDelta == 0) { + // should not happen, but anyway, distribute all to the last column + // columnMinWidths[columnMinWidths.length-1] += toDistribute; + averageDeltaToCell(columnMinWidths, toDistribute); + } 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._widthSpecified[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 + */ + 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 + */ + 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; + } else { + 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 + */ + public int[] getRowHeights() { + return _rowHeights; + } + + /** + * @return + */ + public int[] getColumnWidths() { + return _columnWidths; + } + + /** + * @return + */ + public int getVSpacing() { + return _vspacing; + } + + /** + * @return + */ + public int getHSpacing() { + return _hspacing; + } + + /** + * @param figure + * @return + */ + public TableRowInfo getRowInfo(CSSFigure figure) { + return _tableInfo.findRowInfo(figure); + } + + public TableCaptionInfo getCaptionInfo() { + return _tableInfo._caption; + } + + /** + * @param figure + * @return + */ + public TableRowGroupInfo getGroupInfo(CSSFigure figure) { + return _tableInfo.findGroupInfo(figure); + } + + /** + * @return + */ + public int getRowX() { + return _rowx; + } + + /** + * @return + */ + public int getRowWidth() { + return _rowwidth; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#shouldExpand() + */ + public boolean shouldExpand() { + return false; + } + + 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.lightBlue); + g.drawRectangle(rect); + } + } + } + } + +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CachedTableCellLayout.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CachedTableCellLayout.java new file mode 100644 index 000000000..40c734e00 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/CachedTableCellLayout.java @@ -0,0 +1,251 @@ +/******************************************************************************* + * 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.List; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.jst.pagedesigner.css2.layout.BoxUtil; +import org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout; +import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; +import org.eclipse.jst.pagedesigner.css2.layout.FlowFigure; + +/** + * @author mengbo + * @version 1.5 + */ +public abstract class CachedTableCellLayout extends CSSBlockFlowLayout { + protected Dimension _pageSize = new Dimension(); + + protected boolean _calculatingSize = false; + + private int _pageSizeCacheKeys[] = new int[4]; + + private Dimension _pageSizeCacheValues[] = new Dimension[4]; + + private int _recommendedWidth; + + private Dimension _cacheMaxWidthSize = null; + + private boolean _isTable; + + /** + * @param cssfigure + */ + public CachedTableCellLayout(CSSFigure cssfigure) { + super(cssfigure); + } + + /** + * when figure revalidated, means some child or itself get changed somehow, + * so clear the cache information here. + */ + public void figureRevalidate() { + super.figureRevalidate(); + _pageSizeCacheKeys = new int[4]; + _pageSizeCacheValues = new Dimension[4]; + _pageSize = new Dimension(); + _recommendedWidth = 0; + _cacheMaxWidthSize = null; + _isTable = false; + } + + /** + * TODO: This method is not being called. + */ + public void postValidate() { + if (_isTable) { + if (_calculatingSize) { + _pageSize.width = _blockBox.getWidth(); + _pageSize.height = _blockBox.getHeight(); + } else { + if (_isTable) { + Rectangle rect = getCellRect(); + _blockBox.setXYWidthHeight(rect); + this.getCSSFigure().setBounds(rect); + } + } + List list = getCSSFigure().getChildren(); + for (int i = 0, n = list.size(); i < n; i++) { + ((FlowFigure) list.get(i)).postValidate(); + } + } else { + super.postValidate(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jst.pagedesigner.css2.layout.CSSBlockFlowLayout#preLayout() + */ + protected void preLayout() { + _isTable = initializeTableInfo(); + if (_isTable) { + if (!_calculatingSize) { + // XXX: I don't know why need to call setValid(false) here, if I + // don't call + // it, the layout will be wrong. + getCSSFigure().setValid(false); + } + } + super.preLayout(); + } + + public abstract Rectangle getCellRect(); + + public abstract boolean initializeTableInfo(); + + protected void setupBlock() { + if (_isTable) { + // Remove all current Fragments + _blockBox.clear(); + + if (_calculatingSize) { + // we are not in the real layout + // Setup the one fragment for this Block with the correct X and + // available width + int recommendedWidth = getRecommendedWidth(); + _blockBox.setRecommendedWidth(recommendedWidth); + + if (recommendedWidth > 0 + && recommendedWidth != Integer.MAX_VALUE) { + _blockBox.setWidth(recommendedWidth); + } + } else { + Rectangle rect = getCellRect(); + _blockBox.setWidth(rect.width); + _blockBox.setRecommendedWidth(rect.width); + _blockBox.setHeight(rect.height); + _blockBox.setRecommendedHeight(rect.height); + } + + BoxUtil.setupBorderPaddingMargin(_blockBox, getCSSStyle()); + } else { + super.setupBlock(); + } + } + + /** + * @see org.eclipse.draw2d.Figure#getPreferredSize(int, int) + */ + public Dimension getPreferredSize(IFigure container, int width, int h) { + if (this.isCalculatingMaxWidth()) { + return getMaxContentWidthSize(container, width, h); + } + try { + _calculatingSize = true; + // if (width >= 0) + // { + // width = Math.max(0, width - container.getInsets().getWidth()); + // } + + for (int i = 0; i < 4; i++) { + if (_pageSizeCacheKeys[i] == width + && _pageSizeCacheValues[i] != null) { + if (h > _pageSizeCacheValues[i].height) { + return new Dimension(_pageSizeCacheValues[i].width, h); + } + return _pageSizeCacheValues[i]; + } + } + + _pageSizeCacheKeys[3] = _pageSizeCacheKeys[2]; + _pageSizeCacheKeys[2] = _pageSizeCacheKeys[1]; + _pageSizeCacheKeys[1] = _pageSizeCacheKeys[0]; + _pageSizeCacheKeys[0] = width; + + _pageSizeCacheValues[3] = _pageSizeCacheValues[2]; + _pageSizeCacheValues[2] = _pageSizeCacheValues[1]; + _pageSizeCacheValues[1] = _pageSizeCacheValues[0]; + + // Flowpage must temporarily layout to determine its preferred size + int oldWidth = getRecommendedWidth(); + setRecommendedWidth(width); + ((CSSFigure) container).setValid(false); + container.validate(); + ((CSSFigure) container).postValidate(); + _pageSizeCacheValues[0] = new Dimension(_pageSize); + + if (width != oldWidth) { + setRecommendedWidth(oldWidth); + // container.getUpdateManager().addInvalidFigure(container); + } + if (h > _pageSizeCacheValues[0].height) { + return new Dimension(_pageSizeCacheValues[0].width, h); + } else { + return _pageSizeCacheValues[0]; + } + } finally { + _calculatingSize = false; + } + } + + public int getRecommendedWidth() { + return _recommendedWidth; + } + + private void setRecommendedWidth(int width) { + if (_recommendedWidth == width) { + return; + } + _recommendedWidth = width; + } + + public Dimension getMaxContentWidthSize(IFigure container, int width, + int height) { + try { + _calculatingSize = true; + + if (this._cacheMaxWidthSize == null) { + boolean b = getCalcuatingMaxWidth(); + setCalculatingMaxWidth(true); + + // Flowpage must temporarily layout to determine its preferred + // size + int oldWidth = getRecommendedWidth(); + if (width <= 0) { + setRecommendedWidth(Integer.MAX_VALUE); + } else { + setRecommendedWidth(width); + } + ((CSSFigure) container).setValid(false); + container.validate(); + + ((CSSFigure) container).postValidate(); + _cacheMaxWidthSize = new Dimension(_pageSize); + if (height > _pageSize.height) { + _cacheMaxWidthSize.height = height; + } + + if (0 != oldWidth) { + setRecommendedWidth(oldWidth); + // container.getUpdateManager().addInvalidFigure(container); + } + + setCalculatingMaxWidth(b); + } + return _cacheMaxWidthSize; + } finally { + _calculatingSize = false; + } + } + + /** + * @return Returns the _isTable. + */ + protected boolean isTable() { + return _isTable; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableCaptionInfo.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableCaptionInfo.java new file mode 100644 index 000000000..8a8b5c18f --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableCaptionInfo.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.swt.SWT; + +/** + * @author mengbo + * @version 1.5 + */ +public class TableCaptionInfo extends TableItemInfo { + String _align; + + /** + * @param figure + */ + public TableCaptionInfo(ICSSFigure figure) { + super(figure); + ICSSStyle style = figure.getCSSStyle(); + if (style != null) { + _align = style.getStyleProperty( + ICSSPropertyID.ATTR_HORIZONTAL_ALIGN).toString(); + } + } + + /** + * @return + */ + public Dimension getDimension(int width, int height) { + return getFigure().getPreferredSize(width, height); + } + + public Dimension getDimension() { + return getDimension(SWT.DEFAULT, SWT.DEFAULT); + } + + /** + * @return Returns the align. + */ + public String getAlign() { + // TODO:We do not support left/right align of caption currently. so we + // treat them as top. + if ("bottom".equalsIgnoreCase(_align)) //$NON-NLS-1$ + { + return _align; + } else { + return "top"; //$NON-NLS-1$ + } + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableCellInfo.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableCellInfo.java new file mode 100644 index 000000000..cc41b1f92 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableCellInfo.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * 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 org.eclipse.draw2d.LayoutManager; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.jst.pagedesigner.css2.value.Length; +import org.eclipse.jst.pagedesigner.utils.IntFlexArray; + +/** + * @author mengbo + * @version 1.5 + */ +public class TableCellInfo extends TableItemInfo { + int _rowSpan = 1; + + int _colSpan = 1; + + int _colIndex; + + int _rowIndex; + + int _cellWidth = 0; + + int _cellHeight = 0; + + /** + * @param childfigure + */ + public TableCellInfo(ICSSFigure childfigure) { + super(childfigure); + } + + /** + * @return + */ + public int getColSpan() { + return _colSpan; + } + + /** + * @return + */ + public int getRowSpan() { + return _rowSpan; + } + + /** + * @return + */ + public int getRowIndex() { + return _rowIndex; + } + + /** + * @return + */ + public int getColumnIndex() { + return _colIndex; + } + + /** + * @return + */ + public Dimension getMinCWDimension() { + return getFigure().getPreferredSize(_cellWidth, _cellHeight); + } + + /** + * @return + */ + public Dimension getMaxCWDimension() { + ICSSFigure figure = getFigure(); + LayoutManager layout = figure.getLayoutManager(); + if (layout instanceof CSSTableCellLayout) { + Dimension d = ((CSSTableCellLayout) layout).getMaxContentWidthSize( + figure, _cellWidth, _cellHeight); + return d; + } else { + // should not happen + return getMinCWDimension(); + } + } + + /** + * @param context + */ + public void calculateCellInfo(TableInfoContext context) { + ICSSStyle style = this.getStyle(); + _rowSpan = style.getRowSpan(); + _colSpan = style.getColSpan(); + + // FIXME: we don't support rowspan and colspan to be 0. + // by spec, 0 means span from current col/row to end. + if (_rowSpan <= 0) { + _rowSpan = 1; + } + if (_colSpan <= 0) { + _colSpan = 1; + } + + _rowIndex = context.getCurrentRow(); + + IntFlexArray array = context.getIntFlexArray(); + int currentCol = context.getCurrentCol(); + + // find a cell that is not occupied by cells in previous rows. + while (array.getAt(currentCol) > 0) { + currentCol++; + } + + // ok, now array.getAt(currentCol) == 0 + _colIndex = currentCol; + + for (int i = 0; i < _colSpan; i++, currentCol++) { + array.setAt(currentCol, _rowSpan); + } + context.setCurrentCol(currentCol); + } + + /** + * @param tablewidth + * table width + */ + public void calculateWidth(TableInfo tableInfo, int tablewidth) { + ICSSStyle style = this.getFigure().getCSSStyle(); + if (style == null) { + _cellWidth = -1; + } else { + Object width = style.getStyleProperty(ICSSPropertyID.ATTR_WIDTH); + Length recommendedWidth = (width instanceof Length) ? (Length) width + : null; + + int rw = 0; + if (recommendedWidth == null || recommendedWidth.getValue() <= 0) { + rw = 0; + } else { + if (recommendedWidth.isPercentage()) { + // percentage width is used for remaining width + // distribution, so not used here. + int colspan = this.getColSpan(); + for (int i = 0; i < colspan; i++) { + tableInfo.setWidthPercentage(this.getColumnIndex() + i, + recommendedWidth.getValue() / colspan); + } + } else { + rw = recommendedWidth.getValue(); + if (!style.isSizeIncludeBorderPadding()) { + rw += style.getBorderInsets().getWidth() + + style.getPaddingInsets().getWidth(); + } + if (this.getColSpan() == 1) { + tableInfo._widthSpecified[this.getColumnIndex()] = true; + } + } + + } + _cellWidth = rw; + } + + } + + /** + * @param height + */ + public void calculateHeight(TableInfo tableInfo, int tableheight) { + ICSSStyle style = this.getFigure().getCSSStyle(); + if (style == null) { + _cellHeight = -1; + } else { + Object height = style.getStyleProperty(ICSSPropertyID.ATTR_HEIGHT); + Length recommendedHeight = (height instanceof Length) ? (Length) height + : null; + + int rh = 0; + if (recommendedHeight == null || recommendedHeight.getValue() <= 0) { + rh = 0; + } else { + if (recommendedHeight.isPercentage()) { + int rowspan = this.getRowSpan(); + for (int i = 0; i < rowspan; i++) { + tableInfo.setHeightPercentage(this.getRowIndex() + i, + recommendedHeight.getValue() / rowspan); + } + } else { + rh = recommendedHeight.getValue(); + } + if (!style.isSizeIncludeBorderPadding()) { + rh += style.getBorderInsets().getHeight() + + style.getPaddingInsets().getHeight(); + } + } + _cellHeight = rh; + } + + } + + /** + * @return + */ + public int getHeight() { + return _cellHeight; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableInfo.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableInfo.java new file mode 100644 index 000000000..a458c0e62 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableInfo.java @@ -0,0 +1,368 @@ +/******************************************************************************* + * 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.IFigure; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; +import org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure; + +/** + * @author mengbo + * @version 1.5 + */ +public class TableInfo extends TableItemInfo { + List _tableHeaderGroups = new ArrayList(); + + List _tableTRandTRGs = new ArrayList(); + + List _tableFooterGroups = new ArrayList(); + + TableCaptionInfo _caption; + + int _columnCount; + + int _rowCount; + + List _cells = null; + + private List _rows = null; + + int _tableWidth; // calculated table width, valid after calling to + + // calculateTableWidth + + int _availableWidth; + + int _tableHeight; + + private int[] _widthPercentage; + + private int[] _heightPercentage; + + boolean[] _widthSpecified; + + /** + * @param figure + */ + public TableInfo(ICSSFigure figure) { + super(figure); + } + + public List getTableHeaderGroups() { + return _tableHeaderGroups; + } + + public List getTRandTRGs() { + return _tableTRandTRGs; + } + + public List getTableFooterGroups() { + return _tableFooterGroups; + } + + public int getColumnCount() { + return _columnCount; + } + + public int getRowCount() { + return _rowCount; + } + + protected void constructTable() { + List child = getFigure().getChildren(); + for (int i = 0, size = child.size(); i < size; i++) { + IFigure childfigure = (IFigure) child.get(i); + if (childfigure instanceof ICSSFigure) { + ICSSStyle style = ((ICSSFigure) childfigure).getCSSStyle(); + if (style != null) { + String display = style.getDisplay(); + if ("table-caption".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + _caption = new TableCaptionInfo( + (ICSSFigure) childfigure); + } else if ("table-row".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + TableRowInfo rowInfo = new TableRowInfo( + (ICSSFigure) childfigure); + _tableTRandTRGs.add(rowInfo); + } else if ("table-row-group".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + TableRowGroupInfo groupInfo = new TableRowGroupInfo( + (ICSSFigure) childfigure); + _tableTRandTRGs.add(groupInfo); + } else if ("table-header-group".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + TableRowGroupInfo groupInfo = new TableRowGroupInfo( + (ICSSFigure) childfigure); + _tableHeaderGroups.add(groupInfo); + } else if ("table-footer-group".equalsIgnoreCase(display)) //$NON-NLS-1$ + { + TableRowGroupInfo groupInfo = new TableRowGroupInfo( + (ICSSFigure) childfigure); + _tableFooterGroups.add(groupInfo); + } else { + // something unexpected inside table + } + } else { + // something unexpected inside table + } + } else { + // something unexpected inside table + } + } + + TableInfoContext context = new TableInfoContext(); + // now we have the rows ordered, need to calculate row details now. + for (int i = 0, size = _tableHeaderGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableHeaderGroups + .get(i); + groupInfo.calculateRowGroup(context); + } + for (int i = 0, size = _tableTRandTRGs.size(); i < size; i++) { + Object obj = _tableTRandTRGs.get(i); + if (obj instanceof TableRowGroupInfo) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) obj; + groupInfo.calculateRowGroup(context); + } else { + TableRowInfo rowInfo = (TableRowInfo) obj; + rowInfo.calculateRow(context); + } + } + for (int i = 0, size = _tableFooterGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableFooterGroups + .get(i); + groupInfo.calculateRowGroup(context); + } + context.finishTable(); + + _columnCount = context.getColumnCount(); + _rowCount = context.getRowCount(); + + this._widthPercentage = new int[_columnCount]; + this._heightPercentage = new int[_rowCount]; + + this._widthSpecified = new boolean[_columnCount]; + for (int i = 0; i < _columnCount; i++) { + this._widthSpecified[i] = false; + } + } + + public void setWidthPercentage(int columnIndex, int percentageValue) { + if (percentageValue > this._widthPercentage[columnIndex]) { + this._widthPercentage[columnIndex] = percentageValue; + } + } + + public void setHeightPercentage(int rowIndex, int percentageValue) { + if (percentageValue > this._heightPercentage[rowIndex]) { + this._heightPercentage[rowIndex] = percentageValue; + } + } + + /** + * width percentage will be used to calculate remaining width distribution. + * + * @return + */ + public int[] getWidthPercentages() { + return this._widthPercentage; + } + + public int[] getHeightPercentages() { + return this._heightPercentage; + } + + public List getRows() { + if (_rows == null) { + this._rows = new ArrayList(); + + for (int i = 0, size = _tableHeaderGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableHeaderGroups + .get(i); + _rows.addAll(groupInfo.getRowList()); + } + for (int i = 0, size = _tableTRandTRGs.size(); i < size; i++) { + Object obj = _tableTRandTRGs.get(i); + if (obj instanceof TableRowGroupInfo) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) obj; + _rows.addAll(groupInfo.getRowList()); + } else { + TableRowInfo rowInfo = (TableRowInfo) obj; + _rows.add(rowInfo); + } + } + for (int i = 0, size = _tableFooterGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableFooterGroups + .get(i); + _rows.addAll(groupInfo.getRowList()); + } + } + return _rows; + } + + public List getCells() { + if (_cells == null) { + _cells = new ArrayList(); + + for (int i = 0, size = _tableHeaderGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableHeaderGroups + .get(i); + groupInfo.getCells(_cells); + } + for (int i = 0, size = _tableTRandTRGs.size(); i < size; i++) { + Object obj = _tableTRandTRGs.get(i); + if (obj instanceof TableRowGroupInfo) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) obj; + groupInfo.getCells(_cells); + } else { + TableRowInfo rowInfo = (TableRowInfo) obj; + rowInfo.getCells(_cells); + } + } + for (int i = 0, size = _tableFooterGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableFooterGroups + .get(i); + groupInfo.getCells(_cells); + } + } + return _cells; + } + + /** + * @param containerWidth + * if the width specification is percentage, then will use + * container width. + */ + public void calculateWidth(int contentWidth, int availableWidth) { + _tableWidth = contentWidth; + _availableWidth = availableWidth; + + // next calculate cell width + List cells = getCells(); + for (int i = 0, size = cells.size(); i < size; i++) { + TableCellInfo cellinfo = (TableCellInfo) cells.get(i); + cellinfo.calculateWidth(this, _tableWidth); + } + } + + public void calculateHeight(int contentHeight) { + _tableHeight = contentHeight; + + List rows = getRows(); + for (int i = 0, size = rows.size(); i < size; i++) { + TableRowInfo rowinfo = (TableRowInfo) rows.get(i); + rowinfo.calculateHeight(this, _tableHeight); + } + + // next calculate cell width + List cells = getCells(); + for (int i = 0, size = cells.size(); i < size; i++) { + TableCellInfo cellinfo = (TableCellInfo) cells.get(i); + cellinfo.calculateHeight(this, _tableHeight); + } + } + + /** + * @return + */ + public int getTableWidth() { + return _tableWidth; + } + + /** + * @param figure + * @return + */ + public TableRowGroupInfo findGroupInfo(CSSFigure figure) { + for (int i = 0, size = _tableHeaderGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableHeaderGroups + .get(i); + if (figure == groupInfo.getFigure()) { + return groupInfo; + } + } + for (int i = 0, size = _tableTRandTRGs.size(); i < size; i++) { + Object obj = _tableTRandTRGs.get(i); + if (obj instanceof TableRowGroupInfo) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) obj; + if (figure == groupInfo.getFigure()) { + return groupInfo; + } + + } + } + for (int i = 0, size = _tableFooterGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableFooterGroups + .get(i); + if (figure == groupInfo.getFigure()) { + return groupInfo; + } + } + return null; // should not happen. + } + + /** + * @param figure + * @return + */ + public TableRowInfo findRowInfo(CSSFigure figure) { + for (int i = 0, size = _tableHeaderGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableHeaderGroups + .get(i); + TableRowInfo rowinfo = groupInfo.findRowInfo(figure); + if (rowinfo != null) { + return rowinfo; + } + } + for (int i = 0, size = _tableTRandTRGs.size(); i < size; i++) { + Object obj = _tableTRandTRGs.get(i); + if (obj instanceof TableRowGroupInfo) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) obj; + TableRowInfo rowinfo = groupInfo.findRowInfo(figure); + if (rowinfo != null) { + return rowinfo; + } + } else if (obj instanceof TableRowInfo) { + TableRowInfo info = (TableRowInfo) obj; + if (figure == info.getFigure()) { + return info; + } + } + } + for (int i = 0, size = _tableFooterGroups.size(); i < size; i++) { + TableRowGroupInfo groupInfo = (TableRowGroupInfo) _tableFooterGroups + .get(i); + TableRowInfo rowinfo = groupInfo.findRowInfo(figure); + if (rowinfo != null) { + return rowinfo; + } + } + return null; // should not happen. + } + + /** + * + * @return + */ + public boolean hasWidthPercentage() { + for (int i = 0; i < this._widthPercentage.length; i++) { + if (this._widthPercentage[i] > 0) { + return true; + } + } + return false; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableInfoContext.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableInfoContext.java new file mode 100644 index 000000000..4cd3fbdd2 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableInfoContext.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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 org.eclipse.jst.pagedesigner.PDPlugin; +import org.eclipse.jst.pagedesigner.common.logging.Logger; +import org.eclipse.jst.pagedesigner.utils.IntFlexArray; + +/** + * @author mengbo + * @version 1.5 + */ +public class TableInfoContext { + static Logger _log = PDPlugin.getLogger(TableInfoContext.class); + + int _currentCol = 0; + + int _currentRow = 0; + + IntFlexArray _array = new IntFlexArray(); + + int _colCount = 0; + + int _rowCount = 0; + + /** + * + */ + public TableInfoContext() { + } + + /** + * @return + */ + public IntFlexArray getIntFlexArray() { + return _array; + } + + /** + * @return + */ + public int getCurrentCol() { + return _currentCol; + } + + public void setCurrentCol(int currentcol) { + _currentCol = currentcol; + } + + public int getCurrentRow() { + return _currentRow; + } + + /** + * @return + */ + public int getColumnCount() { + return _colCount; + } + + /** + * + */ + public void finishRow() { + if (_currentCol > _colCount) { + _colCount = _currentCol; + } + _currentCol = 0; + _currentRow++; + for (int i = 0; i < _colCount; i++) { + if (_array.getAt(i) > 0) { + _array.setAt(i, _array.getAt(i) - 1); + } + } + } + + /** + * + */ + public void finishTable() { + // do some checking here. + int additionalRow = 0; + for (int i = 0; i < _colCount; i++) { + if (_array.getAt(i) > additionalRow) { + additionalRow = _array.getAt(i); + } + } + _rowCount = _currentRow + additionalRow; + } + + /** + * + */ + public void finishRowGroup() { + } + + /** + * @return + */ + public int getRowCount() { + return _rowCount; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableItemInfo.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableItemInfo.java new file mode 100644 index 000000000..33c6e12e3 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableItemInfo.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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 org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure; + +/** + * @author mengbo + * @version 1.5 + */ +public class TableItemInfo { + public ICSSFigure _figure; + + /** + * + */ + public TableItemInfo(ICSSFigure figure) { + _figure = figure; + } + + public ICSSFigure getFigure() { + return _figure; + } + + public ICSSStyle getStyle() { + return _figure.getCSSStyle(); + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableRowGroupInfo.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableRowGroupInfo.java new file mode 100644 index 000000000..d9ae2feb3 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableRowGroupInfo.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * 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.IFigure; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; +import org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure; + +/** + * @author mengbo + * @version 1.5 + */ +public class TableRowGroupInfo extends TableItemInfo { + List _rowList = new ArrayList(); + + private int _rowIndex; + + private int _rowCount; + + /** + * @param figure + */ + public TableRowGroupInfo(ICSSFigure figure) { + super(figure); + } + + public List getRowList() { + return _rowList; + } + + public int getRowIndex() { + return _rowIndex; + } + + public int getRowCount() { + return this._rowCount; + } + + /** + * @param context + */ + public void calculateRowGroup(TableInfoContext context) { + this._rowIndex = context.getCurrentRow(); + List children = getFigure().getChildren(); + for (int i = 0, size = children.size(); i < size; i++) { + IFigure childfigure = (IFigure) children.get(i); + if (childfigure instanceof ICSSFigure) { + ICSSStyle childstyle = ((ICSSFigure) childfigure).getCSSStyle(); + if (childstyle != null + && "table-row" + .equalsIgnoreCase(childstyle.getDisplay())) { + TableRowInfo rowInfo = new TableRowInfo( + (ICSSFigure) childfigure); + _rowList.add(rowInfo); + rowInfo.calculateRow(context); + } else { + // skip + } + } else { + // skip + } + } + context.finishRowGroup(); + this._rowCount = context.getCurrentRow() - this._rowIndex; + } + + /** + * @param _cells + */ + public void getCells(List _cells) { + for (int i = 0, size = _rowList.size(); i < size; i++) { + TableRowInfo rowInfo = (TableRowInfo) _rowList.get(i); + rowInfo.getCells(_cells); + } + } + + /** + * @param figure + * @return + */ + public TableRowInfo findRowInfo(CSSFigure figure) { + for (int i = 0, size = _rowList.size(); i < size; i++) { + TableRowInfo rowInfo = (TableRowInfo) _rowList.get(i); + if (figure == rowInfo.getFigure()) { + return rowInfo; + } + } + return null; + } +} diff --git a/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableRowInfo.java b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableRowInfo.java new file mode 100644 index 000000000..f305d0dfd --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.pagedesigner/src/org/eclipse/jst/pagedesigner/css2/layout/table/TableRowInfo.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * 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.IFigure; +import org.eclipse.jst.pagedesigner.css2.ICSSStyle; +import org.eclipse.jst.pagedesigner.css2.layout.CSSFigure; +import org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure; +import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; +import org.eclipse.jst.pagedesigner.css2.value.Length; + +/** + * @author mengbo + * @version 1.5 + */ +public class TableRowInfo extends TableItemInfo { + List _cells = new ArrayList(); + + int _rowIndex; + + private int _rowHeight; + + /** + * @param figure + */ + public TableRowInfo(ICSSFigure figure) { + super(figure); + } + + public List getCells() { + return _cells; + } + + public int getRowIndex() { + return _rowIndex; + } + + public int getSpecifiedRowHeight() { + return _rowHeight; + } + + /** + * @param context + */ + public void calculateRow(TableInfoContext context) { + this._rowIndex = context.getCurrentRow(); + + List children = getFigure().getChildren(); + for (int i = 0, size = children.size(); i < size; i++) { + IFigure childfigure = (IFigure) children.get(i); + if (childfigure instanceof ICSSFigure) { + ICSSStyle childstyle = ((ICSSFigure) childfigure).getCSSStyle(); + if (childstyle != null) { + String display = childstyle.getDisplay(); + if ("table-cell".equalsIgnoreCase(display)) { + TableCellInfo cellInfo = new TableCellInfo( + (ICSSFigure) childfigure); + cellInfo.calculateCellInfo(context); + _cells.add(cellInfo); + } else { + // skip + } + } + } else { + // skip + } + } + // ok, we have finished a row + context.finishRow(); + } + + /** + * @param _cells2 + */ + public void getCells(List cells) { + cells.addAll(this._cells); + } + + /** + * @param figure + * @return + */ + public TableCellInfo getCellInfo(CSSFigure figure) { + for (int i = 0, size = _cells.size(); i < size; i++) { + TableCellInfo cellinfo = (TableCellInfo) _cells.get(i); + if (cellinfo.getFigure() == figure) { + return cellinfo; + } + } + return null; + } + + /** + * @param info + * @param height + */ + public void calculateHeight(TableInfo info, int tableHeight) { + ICSSStyle style = this.getFigure().getCSSStyle(); + if (style == null) { + this._rowHeight = -1; + } else { + Object height = style.getStyleProperty(ICSSPropertyID.ATTR_HEIGHT); + Length recommendedHeight = (height instanceof Length) ? (Length) height + : null; + + int rh = 0; + if (recommendedHeight == null || recommendedHeight.getValue() <= 0) { + rh = 0; + } else { + if (recommendedHeight.isPercentage()) { + // not supported. + } else { + rh = recommendedHeight.getValue(); + } + if (rh > 0 && !style.isSizeIncludeBorderPadding()) { + rh += style.getBorderInsets().getHeight() + + style.getPaddingInsets().getHeight(); + } + } + this._rowHeight = rh; + } + + } +} |