1 /*******************************************************************************
\r
2 * Copyright (c) 2006 Sybase, Inc. and others.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * Sybase, Inc. - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.eclipse.jst.pagedesigner.css2.layout;
\r
14 import java.util.List;
\r
16 import org.eclipse.draw2d.Figure;
\r
17 import org.eclipse.draw2d.IFigure;
\r
18 import org.eclipse.draw2d.geometry.Point;
\r
19 import org.eclipse.draw2d.geometry.Rectangle;
\r
20 import org.eclipse.jst.pagedesigner.css2.ICSSStyle;
\r
21 import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID;
\r
22 import org.eclipse.jst.pagedesigner.css2.property.PositionMeta;
\r
23 import org.eclipse.jst.pagedesigner.css2.property.VerticalAlignMeta;
\r
24 import org.eclipse.jst.pagedesigner.css2.value.Length;
\r
25 import org.eclipse.jst.pagedesigner.ui.preferences.PDPreferences;
\r
28 * CSSLayout is the base layout manager for different CSS layouts, such as block
\r
29 * layout, inline layout (possible in the future table layout, etc)
\r
33 public abstract class CSSLayout extends FlowFigureLayout implements FlowContext {
\r
34 private BlockFlowContext _absoluteContext;
\r
36 // when doing absolute layout, and if top/left are both "auto", it will be
\r
37 // relating to the normaly flow position. The following two fields try to
\r
38 // catch normal flow layout position.
\r
39 // int _xForAbsolute;
\r
40 // int _yForAbsolute;
\r
41 private FlowBox _boxForAbsolute;
\r
46 protected LineBox _currentLine;
\r
48 private boolean _calculatingMaxWidth = false;
\r
51 * @param flowFigure
\r
52 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#FlowFigureLayout(FlowFigure)
\r
54 protected CSSLayout(CSSFigure flowFigure) {
\r
59 * a shortcut method to get the style associated with the figure.
\r
61 * @return the css style
\r
63 public ICSSStyle getCSSStyle() {
\r
64 return getCSSFigure().getCSSStyle();
\r
68 * @return the absolute context
\r
70 protected final BlockFlowContext getAbsoluteContext() {
\r
71 return _absoluteContext;
\r
75 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#addToCurrentLine(FlowBox)
\r
77 public void addToCurrentLine(FlowBox block) {
\r
78 getCurrentLine().add(block);
\r
82 * Used by getCurrentLine().
\r
84 protected abstract void createNewLine();
\r
87 * Used by getCurrentLine(int topmargin)
\r
91 protected void createNewLine(int topMargin) {
\r
96 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentLine()
\r
98 public LineBox getCurrentLine() {
\r
99 if (_currentLine == null) {
\r
102 return _currentLine;
\r
108 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentLine(int)
\r
110 public LineBox getCurrentLine(int topMargin) {
\r
111 if (_currentLine == null) {
\r
112 createNewLine(topMargin);
\r
114 // if the current line only contains an empty string, reset the current
\r
115 // line using the given margin.
\r
116 else if (_currentLine.isEmptyStringLine()) {
\r
117 List list = _currentLine.getFragments();
\r
118 createNewLine(topMargin);
\r
119 _currentLine._fragments.addAll(list);
\r
121 return _currentLine;
\r
125 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#isCurrentLineOccupied
\r
127 public boolean isCurrentLineOccupied() {
\r
128 return _currentLine != null && _currentLine.isOccupied();
\r
132 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#layout()
\r
134 protected void layout() {
\r
142 * @return true if is absolute position
\r
144 protected final boolean isAbsolutePosition() {
\r
145 ICSSStyle style = getCSSStyle();
\r
147 // FIXME: Some layout don't support absolute, need check here
\r
148 if (style != null) {
\r
149 Object obj = style.getStyleProperty(ICSSPropertyID.ATTR_POSITION);
\r
150 if (PositionMeta.ABSOLUTE.equals(obj)
\r
151 || PositionMeta.FIXED.equals(obj))
\r
153 PDPreferences prefs = new PDPreferences();
\r
154 return prefs.isCssAbsolutePositioningEnabled();
\r
161 * Child class could override this method.
\r
163 * @return true if supports absolute position
\r
165 protected boolean supportAbsolutePosition() {
\r
166 if (findContainingPositionedFigure() == null) {
\r
173 * Perform a prelayout
\r
175 protected void preLayout() {
\r
176 ICSSStyle style = this.getCSSStyle();
\r
177 if (style != null) {
\r
178 style.processCounters();
\r
181 if (isAbsolutePosition()) {
\r
182 FlowContext parentFigureContext = getParentFigureContext();
\r
183 _absoluteContext = new BlockFlowContext(parentFigureContext, style);
\r
184 _boxForAbsolute = new FlowBox();// size is 0. Just as a flag, so
\r
186 // could figure out where will this figure be
\r
187 // be put in case of not absolute
\r
188 _boxForAbsolute.setVerticalAlignData(VerticalAlignMeta.TOP);
\r
189 parentFigureContext.addToCurrentLine(_boxForAbsolute);
\r
191 _absoluteContext = null;
\r
198 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#getFlowContext()
\r
200 public FlowContext getFlowContext() {
\r
201 if (_absoluteContext != null) {
\r
202 return _absoluteContext;
\r
204 return getOriginalFlowContext();
\r
208 * @return the flow context
\r
210 private FlowContext getParentFigureContext() {
\r
211 return super.getFlowContext();
\r
214 final void postValidateForAbsolute() {
\r
215 if (_absoluteContext != null) {
\r
216 ICSSStyle style = this.getCSSStyle();
\r
218 _absoluteContext.endBlock();
\r
223 ICSSFigure containingPositionedFigure = findContainingPositionedFigure();
\r
224 IFigure parentFigure = this.getCSSFigure().getParent();
\r
226 xOffset = calculatePositionRelativeToParent(style,
\r
227 containingPositionedFigure, parentFigure, true);
\r
228 yOffset = calculatePositionRelativeToParent(style,
\r
229 containingPositionedFigure, parentFigure, false);
\r
230 move(_absoluteContext._blockBox, xOffset, yOffset);
\r
236 * @param containingPositionedFigure
\r
237 * @param parentFigure
\r
240 private int calculatePositionRelativeToParent(ICSSStyle style,
\r
241 ICSSFigure containingPositionedFigure, IFigure parentFigure,
\r
242 boolean horizontal) {
\r
244 Object left = horizontal ? style
\r
245 .getStyleProperty(ICSSPropertyID.ATTR_LEFT) : style
\r
246 .getStyleProperty(ICSSPropertyID.ATTR_TOP);
\r
247 Object right = horizontal ? style
\r
248 .getStyleProperty(ICSSPropertyID.ATTR_RIGHT) : style
\r
249 .getStyleProperty(ICSSPropertyID.ATTR_BOTTOM);
\r
251 if (!(left instanceof Length) && !(right instanceof Length)) {
\r
252 // _boxForAbsolute partipated the layout of the parent figure, and
\r
253 // is already relative to parent.
\r
254 return horizontal ? _boxForAbsolute._x : _boxForAbsolute._y;
\r
257 // ok, user specified left or right. let's calculate the left
\r
259 if (left instanceof Length) {
\r
260 Length leftLength = (Length) left;
\r
261 leftValue = leftLength.getValue();
\r
262 if (leftLength.isPercentage()) {
\r
263 leftValue = (horizontal ? containingPositionedFigure
\r
264 .getBounds().width : containingPositionedFigure
\r
265 .getBounds().height)
\r
269 Length rightLength = (Length) right;
\r
270 int lengthValue = rightLength.getValue();
\r
271 if (rightLength.isPercentage()) {
\r
272 lengthValue = (horizontal ? containingPositionedFigure
\r
273 .getBounds().width : containingPositionedFigure
\r
274 .getBounds().height)
\r
275 * lengthValue / 100;
\r
279 leftValue = containingPositionedFigure.getBounds().width
\r
280 - _absoluteContext._blockBox.getWidth() - lengthValue;
\r
282 leftValue = containingPositionedFigure.getBounds().height
\r
283 - _absoluteContext._blockBox.getHeight() - lengthValue;
\r
288 // xOffset is relative to the first box of the containing figure
\r
289 List fragments = containingPositionedFigure
\r
290 .getFragmentsForRead();
\r
291 if (fragments.size() > 0) {
\r
292 FlowBox box = (FlowBox) fragments.get(0);
\r
293 // box._x is the x location relative to containingPositionedFigure's
\r
295 // so now xOffset is relative to containingPositionedFigure's
\r
297 xOffset = (horizontal ? box._x : box._y) + leftValue;
\r
299 xOffset = leftValue; // should not happen.
\r
303 p = new Point(xOffset, 0);
\r
305 p = new Point(0, xOffset);
\r
307 containingPositionedFigure.translateFromParent(p);
\r
308 containingPositionedFigure.translateToAbsolute(p);
\r
309 parentFigure.translateToRelative(p);
\r
310 return horizontal ? p.x : p.y;
\r
316 private ICSSFigure findContainingPositionedFigure() {
\r
317 IFigure figure = this.getCSSFigure().getParent();
\r
318 while (figure instanceof ICSSFigure) {
\r
319 return (ICSSFigure) figure;
\r
320 // ICSSStyle style = ((ICSSFigure) figure).getCSSStyle();
\r
321 // if (DisplayToLayout.isPositioned(style))
\r
323 // return (ICSSFigure) figure;
\r
325 // figure = figure.getParent();
\r
336 private void move(CompositeBox compBox, int x, int y) {
\r
339 List list = compBox.getFragments();
\r
340 for (int i = 0; i < list.size(); i++) {
\r
341 FlowBox box = (FlowBox) list.get(i);
\r
343 if (box instanceof CompositeBox && !(box instanceof BlockBox)) {
\r
344 move((CompositeBox) box, x, y);
\r
353 * Layout all children.
\r
355 protected void layoutChildren() {
\r
356 List children = getFlowFigure().getChildren();
\r
357 for (int i = 0; i < children.size(); i++) {
\r
358 Figure f = (Figure) children.get(i);
\r
367 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getLastMarginRight()
\r
369 public int getLastMarginRight() {
\r
370 if (_currentLine == null || !_currentLine.isOccupied()) {
\r
373 FlowBox box = (FlowBox) _currentLine.getFragments().get(
\r
374 _currentLine.getFragments().size() - 1);
\r
376 return box.getMarginInsets().right;
\r
384 public void setCalculatingMaxWidth(boolean c) {
\r
385 _calculatingMaxWidth = c;
\r
389 * @return the calculated maximum width
\r
391 public boolean getCalcuatingMaxWidth() {
\r
392 return _calculatingMaxWidth;
\r
398 * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#isCalculatingMaxWidth()
\r
400 public boolean isCalculatingMaxWidth() {
\r
401 if (_calculatingMaxWidth) {
\r
403 } else if (this.getFlowContext() == null) {
\r
406 return this.getFlowContext().isCalculatingMaxWidth();
\r
411 * Called after {@link #layoutChildren()}when all children have been laid
\r
412 * out. This method exists to flush the last line.
\r
414 protected abstract void flush();
\r
417 * Flush anything pending and free all temporary data used during layout.
\r
419 protected abstract void cleanup();
\r
421 // ------------------------------------------------------------------------------------
\r
424 * @return the css figure
\r
426 protected final CSSFigure getCSSFigure() {
\r
427 return (CSSFigure) getFlowFigure();
\r
432 * @return the fragments for read
\r
434 public abstract List getFragmentsForRead();
\r
437 * postValidate the child figures of this CSSFigure. Normally layout fall
\r
438 * into the first category need implement this method.
\r
440 public abstract void postValidate();
\r
443 * setBounds is called on the CSSFigure. Normally layout fall into the
\r
444 * second category need implement this method.
\r
447 * @param invalidate
\r
449 public void setBoundsCalled(Rectangle rect, boolean invalidate) {
\r
454 * Child class can override this. Normally block figure will return true.
\r
456 * @return true if should use local coordinates
\r
458 protected boolean useLocalCoordinates() {
\r
463 * If CSSLayout will call paint rountine to draw Border for its box, this
\r
464 * method will return true, else return false, for example,the input file
\r
465 * will return false.
\r
467 * @return true if handling border block
\r
469 protected boolean handlingBorderForBlock() {
\r
474 * This method is called when the corresponding figure is revalidated.
\r
477 protected void figureRevalidate() {
\r
478 // child class can override.
\r