1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.component;
21
22 import org.apache.commons.lang.StringUtils;
23 import org.apache.myfaces.tobago.component.SupportsMarkup;
24 import org.apache.myfaces.tobago.context.ClientProperties;
25 import org.apache.myfaces.tobago.internal.layout.BankHead;
26 import org.apache.myfaces.tobago.internal.layout.Cell;
27 import org.apache.myfaces.tobago.internal.layout.FactorList;
28 import org.apache.myfaces.tobago.internal.layout.Grid;
29 import org.apache.myfaces.tobago.internal.layout.Interval;
30 import org.apache.myfaces.tobago.internal.layout.IntervalList;
31 import org.apache.myfaces.tobago.internal.layout.LayoutUtils;
32 import org.apache.myfaces.tobago.internal.layout.OriginCell;
33 import org.apache.myfaces.tobago.layout.AutoLayoutToken;
34 import org.apache.myfaces.tobago.layout.Display;
35 import org.apache.myfaces.tobago.layout.LayoutComponent;
36 import org.apache.myfaces.tobago.layout.LayoutContainer;
37 import org.apache.myfaces.tobago.layout.LayoutManager;
38 import org.apache.myfaces.tobago.layout.LayoutToken;
39 import org.apache.myfaces.tobago.layout.LayoutTokens;
40 import org.apache.myfaces.tobago.layout.Measure;
41 import org.apache.myfaces.tobago.layout.Orientation;
42 import org.apache.myfaces.tobago.layout.PixelLayoutToken;
43 import org.apache.myfaces.tobago.layout.RelativeLayoutToken;
44 import org.apache.myfaces.tobago.util.VariableResolverUtils;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import javax.faces.context.FacesContext;
49 import java.util.List;
50
51 public abstract class AbstractUIGridLayout extends AbstractUILayoutBase implements LayoutManager, SupportsMarkup {
52
53 private static final Logger LOG = LoggerFactory.getLogger(AbstractUIGridLayout.class);
54
55 public static final String COMPONENT_FAMILY = "org.apache.myfaces.tobago.GridLayout";
56
57 private Grid grid;
58
59
60
61
62 public void init() {
63
64 if (!getLayoutContainer().isLayoutChildren()) {
65 return;
66 }
67
68 grid = new Grid(LayoutTokens.parse(getColumns()), LayoutTokens.parse(getRows()));
69
70 List<LayoutComponent> components = getLayoutContainer().getComponents();
71 for (LayoutComponent component : components) {
72 component.setCurrentHeight(null);
73 component.setCurrentWidth(null);
74 grid.add(new OriginCell(component), component.getColumnSpan(), component.getRowSpan());
75 if (LOG.isDebugEnabled()) {
76 LOG.debug("\n" + grid);
77 }
78 if (component instanceof LayoutContainer) {
79 ((LayoutContainer) component).getLayoutManager().init();
80 }
81 }
82
83 grid.setColumnOverflow(isColumnOverflow());
84 grid.setRowOverflow(isRowOverflow());
85 }
86
87 public void fixRelativeInsideAuto(Orientation orientation, boolean auto) {
88
89 if (!getLayoutContainer().isLayoutChildren()) {
90 return;
91 }
92
93 BankHead[] heads = grid.getBankHeads(orientation);
94 BankHead[] heads2 = grid.getBankHeads(orientation.other());
95
96 if (auto) {
97 for (int i = 0; i < heads.length; i++) {
98 if (heads[i].getToken() instanceof RelativeLayoutToken) {
99 if (LOG.isDebugEnabled()) {
100 LOG.debug("Fixing layout token from * to auto, because a * in not allowed inside of a auto. "
101 + "Token * at index=" + i + ", orientation=" + orientation + ", grid=\n" + grid);
102 }
103 heads[i].setToken(AutoLayoutToken.INSTANCE);
104 }
105 }
106 }
107
108 for (int i = 0; i < heads.length; i++) {
109 boolean neitherRendered = true;
110 for (int j = 0; j < heads2.length; j++) {
111 Cell cell = grid.getCell(i, j, orientation);
112
113 if (cell != null && cell.getComponent().isRendered()) {
114 neitherRendered = false;
115 }
116
117 if (cell instanceof OriginCell) {
118 OriginCell origin = (OriginCell) cell;
119 LayoutComponent component = cell.getComponent();
120 if (component instanceof LayoutContainer) {
121 LayoutManager layoutManager = ((LayoutContainer) component).getLayoutManager();
122
123 boolean childAuto = origin.getSpan(orientation) == 1 && heads[i].getToken() instanceof AutoLayoutToken;
124 layoutManager.fixRelativeInsideAuto(orientation, childAuto);
125 }
126 }
127 }
128 if (neitherRendered) {
129 heads[i].setRendered(false);
130 }
131 }
132 }
133
134 public void preProcessing(Orientation orientation) {
135
136 if (!getLayoutContainer().isLayoutChildren()) {
137 return;
138 }
139
140 final BankHead[] heads = grid.getBankHeads(orientation);
141 final BankHead[] heads2 = grid.getBankHeads(orientation.other());
142
143
144 int i = 0;
145
146 for (BankHead head : heads) {
147 LayoutToken token = head.getToken();
148
149 if (token instanceof PixelLayoutToken && head.isRendered()) {
150 int pixel = ((PixelLayoutToken) token).getPixel();
151 heads[i].setCurrent(Measure.valueOf(pixel));
152 }
153
154 IntervalList intervalList = new IntervalList();
155 for (int j = 0; j < heads2.length; j++) {
156 Cell cell = grid.getCell(i, j, orientation);
157 if (cell instanceof OriginCell) {
158 OriginCell origin = (OriginCell) cell;
159 LayoutComponent component = cell.getComponent();
160
161 if (component instanceof LayoutContainer) {
162 ((LayoutContainer) component).getLayoutManager().preProcessing(orientation);
163 }
164
165 if (token instanceof AutoLayoutToken || token instanceof RelativeLayoutToken) {
166 if (origin.getSpan(orientation) == 1 && component.isRendered()) {
167 intervalList.add(new Interval(component, orientation));
168 } else {
169 if (LOG.isDebugEnabled()) {
170 LOG.debug("Components with span > 1 will be ignored in 'auto' layout rows/columns.");
171
172 }
173 }
174 }
175 }
176 }
177
178 intervalList.evaluate();
179 if (token instanceof AutoLayoutToken || token instanceof RelativeLayoutToken) {
180 heads[i].setIntervalList(intervalList);
181 }
182 if (token instanceof AutoLayoutToken) {
183 heads[i].setCurrent(intervalList.getCurrent());
184 }
185 i++;
186 }
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 Measure sum = Measure.ZERO;
204 for (BankHead head : heads) {
205 Measure size = null;
206 final LayoutToken token = head.getToken();
207 if (token instanceof RelativeLayoutToken) {
208
209
210 } else {
211 size = head.getCurrent();
212 }
213 if (size == null) {
214 sum = null;
215 break;
216
217 }
218 sum = sum.add(size);
219 }
220 if (sum != null) {
221
222 sum = sum.add(LayoutUtils.getBorderBegin(orientation, getLayoutContainer()));
223 sum = sum.add(LayoutUtils.getPaddingBegin(orientation, getLayoutContainer()));
224 sum = sum.add(getMarginBegin(orientation));
225 sum = sum.add(computeSpacing(orientation, 0, heads.length));
226 sum = sum.add(getMarginEnd(orientation));
227 sum = sum.add(LayoutUtils.getPaddingEnd(orientation, getLayoutContainer()));
228 sum = sum.add(LayoutUtils.getBorderEnd(orientation, getLayoutContainer()));
229 LayoutUtils.setCurrentSize(orientation, getLayoutContainer(), sum);
230 }
231 }
232
233 public void mainProcessing(Orientation orientation) {
234
235 if (!getLayoutContainer().isLayoutChildren()) {
236 return;
237 }
238
239 final BankHead[] heads = grid.getBankHeads(orientation);
240 final BankHead[] heads2 = grid.getBankHeads(orientation.other());
241
242
243 FactorList factorList = new FactorList();
244 for (BankHead head : heads) {
245 if (head.getToken() instanceof RelativeLayoutToken && head.isRendered()) {
246 factorList.add(((RelativeLayoutToken) head.getToken()).getFactor());
247 }
248 }
249 if (!factorList.isEmpty()) {
250
251 LayoutContainer container = getLayoutContainer();
252 Measure available = LayoutUtils.getCurrentSize(orientation, container);
253 if (available != null) {
254 for (BankHead head : heads) {
255 available = available.subtractNotNegative(head.getCurrent());
256 }
257 available = available.subtractNotNegative(LayoutUtils.getBorderBegin(orientation, container));
258 available = available.subtractNotNegative(LayoutUtils.getPaddingBegin(orientation, container));
259 available = available.subtractNotNegative(getMarginBegin(orientation));
260 available = available.subtractNotNegative(computeSpacing(orientation, 0, heads.length));
261 available = available.subtractNotNegative(getMarginEnd(orientation));
262 available = available.subtractNotNegative(LayoutUtils.getPaddingEnd(orientation, container));
263 available = available.subtractNotNegative(LayoutUtils.getBorderEnd(orientation, container));
264
265 if (grid.isOverflow(orientation.other())) {
266 ClientProperties client = VariableResolverUtils.resolveClientProperties(FacesContext.getCurrentInstance());
267 final Measure scrollbar = orientation
268 == Orientation.HORIZONTAL ? client.getVerticalScrollbarWeight() : client.getHorizontalScrollbarWeight();
269 available = available.subtractNotNegative(scrollbar);
270 }
271
272 List<Measure> partition = factorList.partition(available);
273
274
275 int i = 0;
276 int j = 0;
277 for (BankHead head : heads) {
278 if (head.getToken() instanceof RelativeLayoutToken && head.isRendered()) {
279
280 heads[i].setCurrent(Measure.max(partition.get(j), heads[i].getIntervalList().getMinimum()));
281 j++;
282 }
283 i++;
284 }
285 } else {
286 LOG.warn("No width/height set but needed for *!");
287 }
288 }
289
290
291 for (int i = 0; i < heads.length; i++) {
292 for (int j = 0; j < heads2.length; j++) {
293 Cell cell = grid.getCell(i, j, orientation);
294 if (cell instanceof OriginCell) {
295 LayoutComponent component = cell.getComponent();
296
297 component.setDisplay(Display.BLOCK);
298
299 Integer span = ((OriginCell) cell).getSpan(orientation);
300
301
302 Measure size = Measure.ZERO;
303 for (int k = 0; k < span; k++) {
304 size = size.add(heads[i + k].getCurrent());
305 }
306 size = size.add(computeSpacing(orientation, i, span));
307 Measure current = LayoutUtils.getCurrentSize(orientation, component);
308 if (current == null) {
309 LayoutUtils.setCurrentSize(orientation, component, size);
310 }
311
312
313 if (component instanceof LayoutContainer) {
314 ((LayoutContainer) component).getLayoutManager().mainProcessing(orientation);
315 }
316 }
317 }
318 }
319
320 Measure size = Measure.ZERO;
321 size = size.add(LayoutUtils.getPaddingBegin(orientation, getLayoutContainer()));
322 size = size.add(getMarginBegin(orientation));
323 size = size.add(computeSpacing(orientation, 0, heads.length));
324 for (BankHead head : heads) {
325 size = size.add(head.getCurrent());
326 }
327 size = size.add(getMarginEnd(orientation));
328 size = size.add(LayoutUtils.getPaddingEnd(orientation, getLayoutContainer()));
329 if (size.greaterThan(LayoutUtils.getCurrentSize(orientation, getLayoutContainer()))) {
330 grid.setOverflow(true, orientation);
331 }
332 }
333
334 public void postProcessing(Orientation orientation) {
335
336 if (!getLayoutContainer().isLayoutChildren()) {
337 return;
338 }
339
340 final BankHead[] heads = grid.getBankHeads(orientation);
341 final BankHead[] heads2 = grid.getBankHeads(orientation.other());
342
343
344 for (int i = 0; i < heads.length; i++) {
345 for (int j = 0; j < heads2.length; j++) {
346 Cell cell = grid.getCell(i, j, orientation);
347 if (cell instanceof OriginCell) {
348 LayoutComponent component = cell.getComponent();
349
350 component.setDisplay(Display.BLOCK);
351
352
353 Measure position = Measure.ZERO;
354 position = position.add(LayoutUtils.getPaddingBegin(orientation, getLayoutContainer()));
355 position = position.add(getMarginBegin(orientation));
356 for (int k = 0; k < i; k++) {
357 if (heads[k] != null
358 && heads[k].getCurrent() != null
359 && heads[k].isRendered()
360 && heads[k].getCurrent().greaterThan(Measure.ZERO)) {
361 position = position.add(heads[k].getCurrent());
362 position = position.add(getSpacing(orientation));
363 }
364 }
365 if (orientation == Orientation.HORIZONTAL) {
366 component.setLeft(position);
367 } else {
368 component.setTop(position);
369 }
370
371
372 if (component instanceof LayoutContainer) {
373 ((LayoutContainer) component).getLayoutManager().postProcessing(orientation);
374 }
375
376
377 final boolean scroll = grid.isOverflow(orientation);
378 if (orientation == Orientation.HORIZONTAL) {
379 getLayoutContainer().setOverflowX(scroll);
380 } else {
381 getLayoutContainer().setOverflowY(scroll);
382 }
383
384
385 }
386 }
387 }
388 }
389
390 private LayoutContainer getLayoutContainer() {
391
392 return ((LayoutContainer) getParent());
393 }
394
395 public Measure getSpacing(Orientation orientation) {
396 return orientation == Orientation.HORIZONTAL ? getColumnSpacing() : getRowSpacing();
397 }
398
399 public Measure getMarginBegin(Orientation orientation) {
400 return orientation == Orientation.HORIZONTAL ? getMarginLeft() : getMarginTop();
401 }
402
403 public Measure getMarginEnd(Orientation orientation) {
404 return orientation == Orientation.HORIZONTAL ? getMarginRight() : getMarginBottom();
405 }
406
407
408
409
410
411 private Measure computeSpacing(Orientation orientation, int startIndex, int length) {
412
413 final BankHead[] heads = grid.getBankHeads(orientation);
414
415 int count = 0;
416 for (int i = startIndex; i < startIndex + length; i++) {
417 if ((heads[i].isRendered())
418 && (heads[i].getCurrent() == null || heads[i].getCurrent().greaterThan(Measure.ZERO))) {
419 count++;
420 }
421 }
422 if (count > 0) {
423 return getSpacing(orientation).multiply(count - 1);
424 } else {
425 return Measure.ZERO;
426 }
427 }
428
429 public abstract String getRows();
430
431 public abstract void setRows(String rows);
432
433 public abstract String getColumns();
434
435 public abstract void setColumns(String columns);
436
437 @Deprecated
438 public abstract Measure getCellspacing();
439
440 public abstract Measure getRowSpacing();
441
442 public abstract Measure getColumnSpacing();
443
444 public abstract Measure getMarginLeft();
445
446 public abstract Measure getMarginTop();
447
448 public abstract Measure getMarginRight();
449
450 public abstract Measure getMarginBottom();
451
452 public abstract boolean isColumnOverflow();
453
454 public abstract boolean isRowOverflow();
455
456 @Override
457 public boolean getRendersChildren() {
458 return false;
459 }
460
461 public String toString(int depth) {
462 StringBuilder builder = new StringBuilder();
463 builder.append(getClass().getSimpleName()).append("#");
464 builder.append(getClientId(FacesContext.getCurrentInstance()));
465 builder.append("\n");
466 if (grid != null) {
467 builder.append(StringUtils.repeat(" ", depth + 4));
468 builder.append("horiz.: ");
469 BankHead[] heads = grid.getBankHeads(Orientation.HORIZONTAL);
470 for (int i = 0; i < heads.length; i++) {
471 if (i != 0) {
472 builder.append(StringUtils.repeat(" ", depth + 4 + 4));
473 }
474 builder.append(heads[i]);
475 builder.append("\n");
476 }
477 builder.append(StringUtils.repeat(" ", depth + 4));
478 builder.append("verti.: ");
479 heads = grid.getBankHeads(Orientation.VERTICAL);
480 for (int i = 0; i < heads.length; i++) {
481 if (i != 0) {
482 builder.append(StringUtils.repeat(" ", depth + 4 + 4));
483 }
484 builder.append(heads[i]);
485 builder.append("\n");
486 }
487 }
488 builder.setLength(builder.length() - 1);
489 return builder.toString();
490 }
491
492 @Override
493 public String toString() {
494 return toString(0);
495 }
496 }