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.myfaces.tobago.component.Attributes;
23 import org.apache.myfaces.tobago.internal.layout.Grid;
24 import org.apache.myfaces.tobago.internal.layout.IntervalList;
25 import org.apache.myfaces.tobago.internal.layout.LayoutUtils;
26 import org.apache.myfaces.tobago.internal.layout.OriginCell;
27 import org.apache.myfaces.tobago.internal.util.StringUtils;
28 import org.apache.myfaces.tobago.layout.AutoLayoutToken;
29 import org.apache.myfaces.tobago.layout.Display;
30 import org.apache.myfaces.tobago.layout.LayoutBox;
31 import org.apache.myfaces.tobago.layout.LayoutComponent;
32 import org.apache.myfaces.tobago.layout.LayoutContainer;
33 import org.apache.myfaces.tobago.layout.LayoutManager;
34 import org.apache.myfaces.tobago.layout.LayoutToken;
35 import org.apache.myfaces.tobago.layout.LayoutTokens;
36 import org.apache.myfaces.tobago.layout.Measure;
37 import org.apache.myfaces.tobago.layout.Orientation;
38 import org.apache.myfaces.tobago.layout.RelativeLayoutToken;
39 import org.apache.myfaces.tobago.model.SheetState;
40 import org.apache.myfaces.tobago.renderkit.LayoutComponentRenderer;
41 import org.apache.myfaces.tobago.util.LayoutInfo;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import javax.faces.component.UIColumn;
46 import javax.faces.component.UIComponent;
47 import javax.faces.context.FacesContext;
48 import java.util.List;
49 import java.util.Map;
50
51
52
53
54 public abstract class AbstractUISheetLayout extends AbstractUILayoutBase implements LayoutManager {
55
56 private static final Logger LOG = LoggerFactory.getLogger(AbstractUISheetLayout.class);
57
58 private boolean horizontalAuto;
59 private boolean verticalAuto;
60
61 public void init() {
62
63 layoutHeader();
64
65 for (LayoutComponent component : getLayoutContainer().getComponents()) {
66 if (component instanceof LayoutContainer) {
67 ((LayoutContainer) component).getLayoutManager().init();
68 }
69 }
70 }
71
72 public void fixRelativeInsideAuto(Orientation orientation, boolean auto) {
73
74 if (orientation == Orientation.HORIZONTAL) {
75 horizontalAuto = auto;
76 } else {
77 verticalAuto = auto;
78 }
79
80 for (LayoutComponent component : getLayoutContainer().getComponents()) {
81 if (component instanceof LayoutContainer) {
82 ((LayoutContainer) component).getLayoutManager().fixRelativeInsideAuto(orientation, auto);
83 }
84 }
85 }
86
87 public void preProcessing(Orientation orientation) {
88
89
90 IntervalList intervals = new IntervalList();
91 for (LayoutComponent component : getLayoutContainer().getComponents()) {
92
93 if (component != null) {
94 if (component instanceof LayoutContainer) {
95 ((LayoutContainer) component).getLayoutManager().preProcessing(orientation);
96 }
97
98
99
100
101
102
103
104 }
105 }
106
107
108
109
110
111
112
113
114
115
116 }
117
118 public void mainProcessing(Orientation orientation) {
119
120
121 if (orientation == Orientation.HORIZONTAL && !horizontalAuto
122 || orientation == Orientation.VERTICAL && !verticalAuto) {
123
124 final FacesContext facesContext = FacesContext.getCurrentInstance();
125 final LayoutContainer container = getLayoutContainer();
126 final AbstractUISheet sheet = (AbstractUISheet) container;
127
128 if (orientation == Orientation.HORIZONTAL) {
129
130 ensureColumnWidthList(facesContext, sheet);
131
132 final List<Integer> widthList = sheet.getWidthList();
133
134 int index = 0;
135 for (LayoutComponent component : sheet.getComponents()) {
136 if (component == null) {
137 LOG.error("fixme: UIColumnSelector must be a LayoutComponent!");
138 index++;
139 continue;
140 }
141 final UIColumn column = component instanceof AbstractUIColumnNode
142 ? (UIColumn) component
143 : (UIColumn) ((UIComponent) component).getParent();
144 if (!column.isRendered()) {
145
146 continue;
147 }
148 if (column instanceof LayoutBox) {
149 LayoutBox box = (LayoutBox) column;
150 Measure width = Measure.valueOf(widthList.get(index));
151 width = width.subtractNotNegative(LayoutUtils.getBorderBegin(orientation, box));
152 width = width.subtractNotNegative(LayoutUtils.getPaddingBegin(orientation, box));
153 width = width.subtractNotNegative(LayoutUtils.getPaddingEnd(orientation, box));
154 width = width.subtractNotNegative(LayoutUtils.getBorderEnd(orientation, box));
155 final LayoutComponentRenderer renderer = sheet.getLayoutComponentRenderer(facesContext);
156 width = width.subtractNotNegative(renderer.getCustomMeasure(facesContext, sheet, "columnSeparator"));
157 LayoutUtils.setCurrentSize(orientation, component, width);
158 component.setDisplay(Display.BLOCK);
159
160 if (component instanceof LayoutContainer) {
161 ((LayoutContainer) component).getLayoutManager().mainProcessing(orientation);
162 }
163 }
164 index++;
165 }
166 }
167 }
168 }
169
170 public void postProcessing(Orientation orientation) {
171
172 final AbstractUISheet sheet = (AbstractUISheet) getLayoutContainer();
173
174
175
176 for (LayoutComponent component : sheet.getComponents()) {
177
178 if (component != null) {
179
180 Measure position = LayoutUtils.getBorderBegin(orientation, sheet);
181 if (orientation == Orientation.HORIZONTAL) {
182 component.setLeft(position);
183 } else {
184 component.setTop(position);
185 }
186
187
188 if (component instanceof LayoutContainer) {
189 ((LayoutContainer) component).getLayoutManager().postProcessing(orientation);
190 }
191
192
193 }
194 }
195 }
196
197 private LayoutContainer getLayoutContainer() {
198
199 return ((LayoutContainer) getParent());
200 }
201
202 @Override
203 public boolean getRendersChildren() {
204 return false;
205 }
206
207 private void ensureColumnWidthList(FacesContext facesContext, AbstractUISheet data) {
208 List<Integer> currentWidthList = null;
209
210 List<AbstractUIColumn> renderedColumns = data.getRenderedColumns();
211
212 final Map attributes = data.getAttributes();
213 String widthListString = null;
214 SheetState state = data.getSheetState(facesContext);
215 if (state != null) {
216 widthListString = state.getColumnWidths();
217 }
218 if (widthListString == null) {
219 widthListString = (String) attributes.get(Attributes.WIDTH_LIST_STRING);
220 }
221
222 if (widthListString != null) {
223 try {
224 currentWidthList = StringUtils.parseIntegerList(widthListString);
225 } catch (NumberFormatException e) {
226 LOG.warn("Unexpected value for column width list: '" + widthListString + "'");
227 }
228 }
229 if (currentWidthList != null && currentWidthList.size() != renderedColumns.size() + 1) {
230 currentWidthList = null;
231 }
232
233 if (currentWidthList == null) {
234 LayoutTokens tokens = data.getColumnLayout();
235 List<AbstractUIColumn> allColumns = data.getAllColumns();
236 LayoutTokens newTokens = new LayoutTokens();
237 for (int i = 0; i < allColumns.size(); i++) {
238 AbstractUIColumn column = allColumns.get(i);
239 if (column.isRendered()) {
240 if (tokens == null) {
241 if (column instanceof AbstractUIColumn && column.getWidth() != null) {
242 newTokens.addToken(LayoutTokens.parseToken(column.getWidth().serialize()));
243 } else {
244 newTokens.addToken(RelativeLayoutToken.DEFAULT_INSTANCE);
245 }
246 } else {
247 if (i < tokens.getSize()) {
248 newTokens.addToken(tokens.get(i));
249 } else {
250 newTokens.addToken(RelativeLayoutToken.DEFAULT_INSTANCE);
251 }
252 }
253 }
254 }
255
256 Measure space = data.getCurrentWidth();
257 final LayoutComponentRenderer renderer = data.getLayoutComponentRenderer(facesContext);
258 space = space.subtractNotNegative(renderer.getBorderLeft(facesContext, data));
259 space = space.subtractNotNegative(renderer.getBorderRight(facesContext, data));
260 if (needVerticalScrollbar(facesContext, data)) {
261 space = space.subtractNotNegative(renderer.getVerticalScrollbarWeight(facesContext, data));
262 }
263
264
265
266
267 LayoutInfo layoutInfo =
268 new LayoutInfo(newTokens.getSize(), space.getPixel(), newTokens, data.getClientId(facesContext), false);
269 final Measure columnSelectorWidth
270 = data.getLayoutComponentRenderer(facesContext).getCustomMeasure(facesContext, data, "columnSelectorWidth");
271 parseFixedWidth(layoutInfo, renderedColumns, columnSelectorWidth);
272 layoutInfo.parseColumnLayout(space.getPixel());
273 currentWidthList = layoutInfo.getSpaceList();
274 currentWidthList.add(0);
275 }
276
277 if (renderedColumns.size() + 1 != currentWidthList.size()) {
278 LOG.warn("widthList.size() = " + currentWidthList.size()
279 + " != columns.size() = " + renderedColumns.size() + " + 1. The widthList: "
280 + LayoutInfo.listToTokenString(currentWidthList));
281 } else {
282 data.setWidthList(currentWidthList);
283 }
284 }
285
286 private boolean needVerticalScrollbar(FacesContext facesContext, AbstractUISheet sheet) {
287
288
289
290 if (sheet.getNeedVerticalScrollbar() != null) {
291 return sheet.getNeedVerticalScrollbar();
292 }
293
294 Boolean result = null;
295
296 final Object forceScrollbar = sheet.getAttributes().get(Attributes.FORCE_VERTICAL_SCROLLBAR);
297 if (forceScrollbar != null) {
298 if ("true".equals(forceScrollbar)) {
299 result = true;
300 } else if ("false".equals(forceScrollbar)) {
301 result = false;
302 } else if (!"auto".equals(forceScrollbar)) {
303 LOG.warn("Illegal value for attribute 'forceVerticalScrollbar': '" + forceScrollbar + "'");
304 }
305 }
306
307 if (result == null && !sheet.hasRowCount()) {
308 result = true;
309 }
310
311 if (result == null) {
312 if (sheet.getCurrentHeight() != null) {
313 int first = sheet.getFirst();
314 int rows = sheet.isRowsUnlimited()
315 ? sheet.getRowCount()
316 : Math.min(sheet.getRowCount(), first + sheet.getRows()) - first;
317 Measure heightNeeded = getRowHeight(facesContext, sheet).multiply(rows);
318 if (sheet.isShowHeader()) {
319 heightNeeded = heightNeeded.add(getHeaderHeight(facesContext, sheet));
320 }
321 if (sheet.isPagingVisible()) {
322 heightNeeded = heightNeeded.add(getFooterHeight(facesContext, sheet));
323 }
324 result = heightNeeded.greaterThan(sheet.getCurrentHeight());
325 } else {
326 result = false;
327 }
328 }
329 sheet.setNeedVerticalScrollbar(result);
330 return result;
331 }
332
333 private void parseFixedWidth(
334 LayoutInfo layoutInfo, List<AbstractUIColumn> renderedColumns, Measure columnSelectorWidth) {
335 LayoutTokens tokens = layoutInfo.getLayoutTokens();
336 for (int i = 0; i < tokens.getSize(); i++) {
337 LayoutToken token = tokens.get(i);
338 if (token instanceof AutoLayoutToken) {
339 int width = 0;
340 if (!renderedColumns.isEmpty()) {
341 if (i < renderedColumns.size()) {
342 AbstractUIColumn column = renderedColumns.get(i);
343 if (column instanceof AbstractUIColumnSelector) {
344 width = columnSelectorWidth.getPixel();
345 } else {
346 for (UIComponent component : column.getChildren()) {
347 width += 100;
348 LOG.error("100; // FIXME: make dynamic (was removed by changing the layout");
349 }
350 }
351 layoutInfo.update(width, i);
352 } else {
353 layoutInfo.update(0, i);
354 if (LOG.isWarnEnabled()) {
355 LOG.warn("More LayoutTokens found than rows! skipping!");
356 }
357 }
358 }
359 if (LOG.isDebugEnabled()) {
360 LOG.debug("set column " + i + " from 'auto' to width " + width);
361 }
362 }
363 }
364 }
365
366 private Measure getHeaderHeight(FacesContext facesContext, AbstractUISheet sheet) {
367 return sheet.isShowHeader()
368 ? sheet.getLayoutComponentRenderer(facesContext).getCustomMeasure(facesContext, sheet, "headerHeight")
369 : Measure.ZERO;
370 }
371
372 private Measure getRowHeight(FacesContext facesContext, AbstractUISheet sheet) {
373 return sheet.getLayoutComponentRenderer(facesContext).getCustomMeasure(facesContext, sheet, "rowHeight");
374 }
375
376 private Measure getFooterHeight(FacesContext facesContext, AbstractUISheet sheet) {
377 return sheet.isPagingVisible()
378 ? sheet.getLayoutComponentRenderer(facesContext).getCustomMeasure(facesContext, sheet, "footerHeight")
379 : Measure.ZERO;
380 }
381
382 private void layoutHeader() {
383 final AbstractUISheet sheet = (AbstractUISheet) getLayoutContainer();
384 final UIComponent header = sheet.getHeader();
385 final LayoutTokens columns = new LayoutTokens();
386 final List<AbstractUIColumn> renderedColumns = sheet.getRenderedColumns();
387 for (AbstractUIColumn ignored : renderedColumns) {
388 columns.addToken(RelativeLayoutToken.DEFAULT_INSTANCE);
389 }
390 final LayoutTokens rows = new LayoutTokens();
391 rows.addToken(AutoLayoutToken.INSTANCE);
392 final Grid grid = new Grid(columns, rows);
393
394 for(UIComponent child : header.getChildren()) {
395 if (child instanceof LayoutComponent) {
396 final LayoutComponent c = (LayoutComponent) child;
397 grid.add(new OriginCell(c), c.getColumnSpan(), c.getRowSpan());
398 } else {
399 if (LOG.isDebugEnabled()) {
400 LOG.debug("Found unknown component in header.");
401 }
402 }
403 }
404 sheet.setHeaderGrid(grid);
405 }
406 }