Initialize
[sdk/ide/product.git] / org.eclipse.jst.pagedesigner / src / org / eclipse / jst / pagedesigner / css2 / layout / CSSLayout.java
1 /*******************************************************************************\r
2  * Copyright (c) 2006 Sybase, Inc. and others.\r
3  *\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
8  *\r
9  * Contributors:\r
10  *     Sybase, Inc. - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.eclipse.jst.pagedesigner.css2.layout;\r
13 \r
14 import java.util.List;\r
15 \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
26 \r
27 /**\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
30  * \r
31  * @author mengbo\r
32  */\r
33 public abstract class CSSLayout extends FlowFigureLayout implements FlowContext {\r
34         private BlockFlowContext _absoluteContext;\r
35 \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
42 \r
43         /**\r
44          * the current line\r
45          */\r
46         protected LineBox _currentLine;\r
47 \r
48         private boolean _calculatingMaxWidth = false;\r
49 \r
50         /**\r
51          * @param flowFigure \r
52          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#FlowFigureLayout(FlowFigure)\r
53          */\r
54         protected CSSLayout(CSSFigure flowFigure) {\r
55                 super(flowFigure);\r
56         }\r
57 \r
58         /**\r
59          * a shortcut method to get the style associated with the figure.\r
60          * \r
61          * @return the css style\r
62          */\r
63         public ICSSStyle getCSSStyle() {\r
64                 return getCSSFigure().getCSSStyle();\r
65         }\r
66 \r
67         /**\r
68          * @return the absolute context\r
69          */\r
70         protected final BlockFlowContext getAbsoluteContext() {\r
71         return _absoluteContext;\r
72     }\r
73 \r
74     /**\r
75          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#addToCurrentLine(FlowBox)\r
76          */\r
77         public void addToCurrentLine(FlowBox block) {\r
78                 getCurrentLine().add(block);\r
79         }\r
80 \r
81         /**\r
82          * Used by getCurrentLine().\r
83          */\r
84         protected abstract void createNewLine();\r
85 \r
86         /**\r
87          * Used by getCurrentLine(int topmargin)\r
88          * \r
89          * @param topMargin\r
90          */\r
91         protected void createNewLine(int topMargin) {\r
92                 createNewLine();\r
93         }\r
94 \r
95         /**\r
96          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentLine()\r
97          */\r
98         public LineBox getCurrentLine() {\r
99                 if (_currentLine == null) {\r
100                         createNewLine();\r
101                 }\r
102                 return _currentLine;\r
103         }\r
104 \r
105         /*\r
106          * (non-Javadoc)\r
107          * \r
108          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getCurrentLine(int)\r
109          */\r
110         public LineBox getCurrentLine(int topMargin) {\r
111                 if (_currentLine == null) {\r
112                         createNewLine(topMargin);\r
113                 }\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
120                 }\r
121                 return _currentLine;\r
122         }\r
123 \r
124         /**\r
125          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#isCurrentLineOccupied\r
126          */\r
127         public boolean isCurrentLineOccupied() {\r
128                 return _currentLine != null && _currentLine.isOccupied();\r
129         }\r
130 \r
131         /**\r
132          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#layout()\r
133          */\r
134         protected void layout() {\r
135                 preLayout();\r
136                 layoutChildren();\r
137                 flush();\r
138                 cleanup();\r
139         }\r
140 \r
141         /**\r
142          * @return true if is absolute position\r
143          */\r
144         protected final boolean isAbsolutePosition() {\r
145                 ICSSStyle style = getCSSStyle();\r
146 \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
152                         {\r
153                             PDPreferences prefs = new PDPreferences();\r
154                             return prefs.isCssAbsolutePositioningEnabled();\r
155                         }\r
156                 }\r
157                 return false;\r
158         }\r
159 \r
160         /**\r
161          * Child class could override this method.\r
162          * \r
163          * @return true if supports absolute position  \r
164          */\r
165         protected boolean supportAbsolutePosition() {\r
166                 if (findContainingPositionedFigure() == null) {\r
167                         return false;\r
168                 }\r
169                 return true;\r
170         }\r
171 \r
172         /**\r
173          * Perform a prelayout\r
174          */\r
175         protected void preLayout() {\r
176                 ICSSStyle style = this.getCSSStyle();\r
177                 if (style != null) {\r
178                         style.processCounters();\r
179                 }\r
180 \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
185                         // later we\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
190                 } else {\r
191                         _absoluteContext = null;\r
192                 }\r
193         }\r
194 \r
195         /*\r
196          * (non-Javadoc)\r
197          * \r
198          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#getFlowContext()\r
199          */\r
200         public FlowContext getFlowContext() {\r
201                 if (_absoluteContext != null) {\r
202                         return _absoluteContext;\r
203                 }\r
204         return getOriginalFlowContext();\r
205         }\r
206 \r
207         /**\r
208          * @return the flow context\r
209          */\r
210         private FlowContext getParentFigureContext() {\r
211                 return super.getFlowContext();\r
212         }\r
213 \r
214         final void postValidateForAbsolute() {\r
215                 if (_absoluteContext != null) {\r
216                         ICSSStyle style = this.getCSSStyle();\r
217 \r
218                         _absoluteContext.endBlock();\r
219 \r
220                         int xOffset;\r
221                         int yOffset;\r
222 \r
223                         ICSSFigure containingPositionedFigure = findContainingPositionedFigure();\r
224                         IFigure parentFigure = this.getCSSFigure().getParent();\r
225 \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
231                 }\r
232         }\r
233 \r
234         /**\r
235          * @param style\r
236          * @param containingPositionedFigure\r
237          * @param parentFigure\r
238          * @return\r
239          */\r
240         private int calculatePositionRelativeToParent(ICSSStyle style,\r
241                         ICSSFigure containingPositionedFigure, IFigure parentFigure,\r
242                         boolean horizontal) {\r
243                 int xOffset;\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
250 \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
255                 }\r
256 \r
257                 // ok, user specified left or right. let's calculate the left\r
258                 int leftValue;\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
266                                                 * leftValue / 100;\r
267                         }\r
268                 } else {\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
276                         }\r
277 \r
278                         if (horizontal) {\r
279                                 leftValue = containingPositionedFigure.getBounds().width\r
280                                                 - _absoluteContext._blockBox.getWidth() - lengthValue;\r
281                         } else {\r
282                                 leftValue = containingPositionedFigure.getBounds().height\r
283                                                 - _absoluteContext._blockBox.getHeight() - lengthValue;\r
284                         }\r
285 \r
286                 }\r
287 \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
294                         // parent.\r
295                         // so now xOffset is relative to containingPositionedFigure's\r
296                         // parent.\r
297                         xOffset = (horizontal ? box._x : box._y) + leftValue;\r
298                 } else {\r
299                         xOffset = leftValue; // should not happen.\r
300                 }\r
301                 Point p;\r
302                 if (horizontal) {\r
303                         p = new Point(xOffset, 0);\r
304                 } else {\r
305                         p = new Point(0, xOffset);\r
306                 }\r
307                 containingPositionedFigure.translateFromParent(p);\r
308                 containingPositionedFigure.translateToAbsolute(p);\r
309                 parentFigure.translateToRelative(p);\r
310                 return horizontal ? p.x : p.y;\r
311         }\r
312 \r
313         /**\r
314          * @return\r
315          */\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
322                         // {\r
323                         // return (ICSSFigure) figure;\r
324                         // }\r
325                         // figure = figure.getParent();\r
326                 }\r
327                 return null;\r
328 \r
329         }\r
330 \r
331         /**\r
332          * @param resultBox\r
333          * @param x\r
334          * @param y\r
335          */\r
336         private void move(CompositeBox compBox, int x, int y) {\r
337                 compBox._x += x;\r
338                 compBox._y += 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
342 \r
343                         if (box instanceof CompositeBox && !(box instanceof BlockBox)) {\r
344                                 move((CompositeBox) box, x, y);\r
345                         } else {\r
346                                 box._x += x;\r
347                                 box._y += y;\r
348                         }\r
349                 }\r
350         }\r
351 \r
352         /**\r
353          * Layout all children.\r
354          */\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
359                         f.invalidate();\r
360                         f.validate();\r
361                 }\r
362         }\r
363 \r
364         /*\r
365          * (non-Javadoc)\r
366          * \r
367          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#getLastMarginRight()\r
368          */\r
369         public int getLastMarginRight() {\r
370                 if (_currentLine == null || !_currentLine.isOccupied()) {\r
371                         return 0;\r
372                 }\r
373                 FlowBox box = (FlowBox) _currentLine.getFragments().get(\r
374                                 _currentLine.getFragments().size() - 1);\r
375                 if (box != null) {\r
376                         return box.getMarginInsets().right;\r
377                 }\r
378         return 0;\r
379         }\r
380 \r
381         /**\r
382          * @param c\r
383          */\r
384         public void setCalculatingMaxWidth(boolean c) {\r
385                 _calculatingMaxWidth = c;\r
386         }\r
387 \r
388         /**\r
389          * @return the calculated maximum width\r
390          */\r
391         public boolean getCalcuatingMaxWidth() {\r
392                 return _calculatingMaxWidth;\r
393         }\r
394 \r
395         /*\r
396          * (non-Javadoc)\r
397          * \r
398          * @see org.eclipse.jst.pagedesigner.css2.layout.FlowContext#isCalculatingMaxWidth()\r
399          */\r
400         public boolean isCalculatingMaxWidth() {\r
401                 if (_calculatingMaxWidth) {\r
402                         return true;\r
403                 } else if (this.getFlowContext() == null) {\r
404                         return false;\r
405                 } else {\r
406                         return this.getFlowContext().isCalculatingMaxWidth();\r
407                 }\r
408         }\r
409 \r
410         /**\r
411          * Called after {@link #layoutChildren()}when all children have been laid\r
412          * out. This method exists to flush the last line.\r
413          */\r
414         protected abstract void flush();\r
415 \r
416         /**\r
417          * Flush anything pending and free all temporary data used during layout.\r
418          */\r
419         protected abstract void cleanup();\r
420 \r
421         // ------------------------------------------------------------------------------------\r
422 \r
423         /**\r
424          * @return the css figure\r
425          */\r
426         protected final CSSFigure getCSSFigure() {\r
427                 return (CSSFigure) getFlowFigure();\r
428         }\r
429 \r
430         /**\r
431          * \r
432          * @return the fragments for read\r
433          */\r
434         public abstract List getFragmentsForRead();\r
435 \r
436         /**\r
437          * postValidate the child figures of this CSSFigure. Normally layout fall\r
438          * into the first category need implement this method.\r
439          */\r
440         public abstract void postValidate();\r
441 \r
442         /**\r
443          * setBounds is called on the CSSFigure. Normally layout fall into the\r
444          * second category need implement this method.\r
445          * \r
446          * @param rect\r
447          * @param invalidate\r
448          */\r
449         public void setBoundsCalled(Rectangle rect, boolean invalidate) {\r
450         // TODO: dead?\r
451         }\r
452 \r
453         /**\r
454          * Child class can override this. Normally block figure will return true.\r
455          * \r
456          * @return true if should use local coordinates\r
457          */\r
458         protected boolean useLocalCoordinates() {\r
459                 return false;\r
460         }\r
461 \r
462         /**\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
466          * \r
467          * @return true if handling border block\r
468          */\r
469         protected boolean handlingBorderForBlock() {\r
470                 return true;\r
471         }\r
472 \r
473         /**\r
474          * This method is called when the corresponding figure is revalidated.\r
475          * \r
476          */\r
477         protected void figureRevalidate() {\r
478                 // child class can override.\r
479         }\r
480 }\r