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.renderkit.html.scarborough.standard.tag;
21
22 import org.apache.myfaces.tobago.component.Attributes;
23 import org.apache.myfaces.tobago.component.ComponentTypes;
24 import org.apache.myfaces.tobago.component.Facets;
25 import org.apache.myfaces.tobago.component.RendererTypes;
26 import org.apache.myfaces.tobago.component.SupportsMarkup;
27 import org.apache.myfaces.tobago.component.UIColumnSelector;
28 import org.apache.myfaces.tobago.component.UICommand;
29 import org.apache.myfaces.tobago.component.UILink;
30 import org.apache.myfaces.tobago.component.UIMenu;
31 import org.apache.myfaces.tobago.component.UIMenuCommand;
32 import org.apache.myfaces.tobago.component.UIOut;
33 import org.apache.myfaces.tobago.component.UIReload;
34 import org.apache.myfaces.tobago.component.UISheet;
35 import org.apache.myfaces.tobago.component.UIToolBar;
36 import org.apache.myfaces.tobago.config.Configurable;
37 import org.apache.myfaces.tobago.context.ClientProperties;
38 import org.apache.myfaces.tobago.context.Markup;
39 import org.apache.myfaces.tobago.context.ResourceManager;
40 import org.apache.myfaces.tobago.context.ResourceManagerUtils;
41 import org.apache.myfaces.tobago.event.PageAction;
42 import org.apache.myfaces.tobago.internal.component.AbstractUIColumn;
43 import org.apache.myfaces.tobago.internal.component.AbstractUIColumnNode;
44 import org.apache.myfaces.tobago.internal.component.AbstractUIData;
45 import org.apache.myfaces.tobago.internal.component.AbstractUIOut;
46 import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
47 import org.apache.myfaces.tobago.internal.context.ResourceManagerFactory;
48 import org.apache.myfaces.tobago.internal.layout.Cell;
49 import org.apache.myfaces.tobago.internal.layout.Grid;
50 import org.apache.myfaces.tobago.internal.layout.OriginCell;
51 import org.apache.myfaces.tobago.internal.util.StringUtils;
52 import org.apache.myfaces.tobago.layout.Display;
53 import org.apache.myfaces.tobago.layout.LayoutBase;
54 import org.apache.myfaces.tobago.layout.Measure;
55 import org.apache.myfaces.tobago.layout.TextAlign;
56 import org.apache.myfaces.tobago.model.ExpandedState;
57 import org.apache.myfaces.tobago.model.SheetState;
58 import org.apache.myfaces.tobago.model.TreePath;
59 import org.apache.myfaces.tobago.renderkit.LayoutComponentRendererBase;
60 import org.apache.myfaces.tobago.renderkit.css.Classes;
61 import org.apache.myfaces.tobago.renderkit.css.Style;
62 import org.apache.myfaces.tobago.renderkit.html.Command;
63 import org.apache.myfaces.tobago.renderkit.html.CommandMap;
64 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
65 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
66 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
67 import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
68 import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
69 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
70 import org.apache.myfaces.tobago.renderkit.util.RenderUtils;
71 import org.apache.myfaces.tobago.util.ComponentUtils;
72 import org.apache.myfaces.tobago.util.CreateComponentUtils;
73 import org.apache.myfaces.tobago.util.FacetUtils;
74 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 import javax.faces.application.Application;
79 import javax.faces.component.UIColumn;
80 import javax.faces.component.UIComponent;
81 import javax.faces.component.UISelectOne;
82 import javax.faces.context.FacesContext;
83 import java.io.IOException;
84 import java.text.MessageFormat;
85 import java.util.ArrayList;
86 import java.util.Collections;
87 import java.util.List;
88 import java.util.Locale;
89 import java.util.Map;
90
91 public class SheetRenderer extends LayoutComponentRendererBase {
92
93 private static final Logger LOG = LoggerFactory.getLogger(SheetRenderer.class);
94
95 public static final String WIDTHS_POSTFIX = ComponentUtils.SUB_SEPARATOR + "widths";
96 public static final String SCROLL_POSTFIX = ComponentUtils.SUB_SEPARATOR + "scrollPosition";
97 public static final String SELECTED_POSTFIX = ComponentUtils.SUB_SEPARATOR + "selected";
98
99 private static final Integer HEIGHT_0 = 0;
100
101 @Override
102 public void prepareRender(FacesContext facesContext, UIComponent component) throws IOException {
103 super.prepareRender(facesContext, component);
104 ensureHeader(facesContext, (UISheet) component);
105 }
106
107 private void ensureHeader(FacesContext facesContext, UISheet sheet) {
108 UIComponent header = sheet.getHeader();
109 if (header == null) {
110 header = CreateComponentUtils.createComponent(facesContext, ComponentTypes.PANEL, null, "_header");
111
112
113 final List<AbstractUIColumn> columns = sheet.getRenderedColumns();
114 int i = 0;
115 for (AbstractUIColumn column : columns) {
116 final AbstractUIOut out = (AbstractUIOut) CreateComponentUtils.createComponent(
117 facesContext, ComponentTypes.OUT, RendererTypes.OUT, "_col" + i);
118
119 out.setValue(column.getAttributes().get(Attributes.LABEL));
120 header.getChildren().add(out);
121 i++;
122 }
123 sheet.setHeader(header);
124 }
125 }
126
127 @Override
128 public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException {
129
130 UISheet sheet = (UISheet) uiComponent;
131
132 Style style = new Style(facesContext, sheet);
133
134 final String sheetId = sheet.getClientId(facesContext);
135
136 TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
137
138
139 writer.startElement(HtmlElements.DIV, sheet);
140 writer.writeIdAttribute(sheetId);
141 HtmlRendererUtils.writeDataAttributes(facesContext, writer, sheet);
142 writer.writeClassAttribute(Classes.create(sheet));
143 writer.writeStyleAttribute(style);
144 UIComponent facetReload = sheet.getFacet(Facets.RELOAD);
145 if (facetReload != null && facetReload instanceof UIReload && facetReload.isRendered()) {
146 UIReload update = (UIReload) facetReload;
147 writer.writeAttribute(DataAttributes.RELOAD, update.getFrequency());
148 }
149
150 writer.writeAttribute(DataAttributes.PARTIALLY,
151 HtmlRendererUtils.getRenderedPartiallyJavascriptArray(facesContext, sheet, sheet), false);
152 writer.writeAttribute(DataAttributes.SELECTIONMODE, sheet.getSelectable(), false);
153 writer.writeAttribute(DataAttributes.FIRST, Integer.toString(sheet.getFirst()), false);
154
155 boolean rowAction = HtmlRendererUtils.renderSheetCommands(sheet, facesContext, writer);
156
157 renderSheet(facesContext, sheet, rowAction, style);
158
159 writer.endElement(HtmlElements.DIV);
160 }
161
162 private void renderSheet(FacesContext facesContext, UISheet sheet, boolean hasClickAction, Style style)
163 throws IOException {
164 final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
165 final ResourceManager resourceManager = ResourceManagerFactory.getResourceManager(facesContext);
166 final String contextPath = facesContext.getExternalContext().getRequestContextPath();
167 final String sheetId = sheet.getClientId(facesContext);
168
169 Measure sheetHeight;
170 if (style.getHeight() == null) {
171
172 LOG.error("no height in parent container, setting to 100");
173 sheetHeight = Measure.valueOf(100);
174 } else {
175 sheetHeight = style.getHeight();
176 }
177 final Measure footerHeight = getFooterHeight(facesContext, sheet);
178 final Measure headerHeight = getHeaderHeight(facesContext, sheet);
179 final String selectable = sheet.getSelectable();
180
181 final Application application = facesContext.getApplication();
182 final SheetState state = sheet.getSheetState(facesContext);
183 final List<Integer> columnWidths = sheet.getWidthList();
184
185 final List<Integer> selectedRows = getSelectedRows(sheet, state);
186 final List<AbstractUIColumn> renderedColumnList = sheet.getRenderedColumns();
187
188 writer.startElement(HtmlElements.INPUT, null);
189 writer.writeIdAttribute(sheetId + WIDTHS_POSTFIX);
190 writer.writeNameAttribute(sheetId + WIDTHS_POSTFIX);
191 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
192 writer.writeAttribute(HtmlAttributes.VALUE, StringUtils.joinWithSurroundingSeparator(columnWidths), false);
193 writer.endElement(HtmlElements.INPUT);
194
195 writer.startElement(HtmlElements.INPUT, null);
196 writer.writeIdAttribute(sheetId + SCROLL_POSTFIX);
197 writer.writeNameAttribute(sheetId + SCROLL_POSTFIX);
198 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
199 Integer[] scrollPosition = sheet.getScrollPosition();
200 if (scrollPosition != null) {
201 String scroll = scrollPosition[0] + ";" + scrollPosition[1];
202 writer.writeAttribute(HtmlAttributes.VALUE, scroll, false);
203 } else {
204 writer.writeAttribute(HtmlAttributes.VALUE, "", false);
205 }
206 writer.endElement(HtmlElements.INPUT);
207
208 if (!UISheet.NONE.equals(selectable)) {
209 writer.startElement(HtmlElements.INPUT, null);
210 writer.writeIdAttribute(sheetId + SELECTED_POSTFIX);
211 writer.writeNameAttribute(sheetId + SELECTED_POSTFIX);
212 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
213 writer.writeAttribute(
214 HtmlAttributes.VALUE, StringUtils.joinWithSurroundingSeparator(selectedRows), true);
215 writer.endElement(HtmlElements.INPUT);
216 }
217
218 ExpandedState expandedState = null;
219 StringBuilder expandedValue = null;
220 if (sheet.isTreeModel()) {
221 expandedState = sheet.getExpandedState();
222 expandedValue = new StringBuilder(",");
223 }
224
225 final boolean showHeader = sheet.isShowHeader();
226 final boolean ie6SelectOneFix = showHeader
227 && ClientProperties.getInstance(facesContext).getUserAgent().isMsie6()
228 && ComponentUtils.findDescendant(sheet, UISelectOne.class) != null;
229
230
231 Style bodyStyle = new Style();
232
233
234
235 Measure tableBodyWidth = sheet.getCurrentWidth().subtractNotNegative(getContentBorder(facesContext, sheet));
236 bodyStyle.setWidth(tableBodyWidth);
237 if (sheet.isPagingVisible()) {
238 sheetHeight = sheetHeight.subtract(footerHeight);
239 }
240 if (ie6SelectOneFix) {
241 bodyStyle.setTop(headerHeight);
242 }
243 if (showHeader) {
244 sheetHeight = sheetHeight.subtract(headerHeight);
245 }
246 bodyStyle.setHeight(sheetHeight);
247
248 if (showHeader) {
249 renderColumnHeaders(
250 facesContext, sheet, writer, resourceManager, contextPath, sheetId, renderedColumnList, tableBodyWidth);
251 }
252
253 writer.startElement(HtmlElements.DIV, null);
254 writer.writeIdAttribute(sheetId + ComponentUtils.SUB_SEPARATOR + "data_div");
255 writer.writeClassAttribute(Classes.create(sheet, "body"));
256
257
258
259
260
261 writer.writeStyleAttribute(bodyStyle);
262 bodyStyle.setHeight(null);
263 bodyStyle.setTop(null);
264 Style sheetBodyStyle = new Style(bodyStyle);
265 if (sheet.getNeedVerticalScrollbar() == null) {
266 LOG.warn("Value of needVerticalScrollbar undefined!");
267 } else {
268 if (sheet.getNeedVerticalScrollbar()) {
269 tableBodyWidth = tableBodyWidth.subtractNotNegative(getVerticalScrollbarWeight(facesContext, sheet));
270 }
271 }
272 sheetBodyStyle.setWidth(tableBodyWidth);
273
274 writer.startElement(HtmlElements.TABLE, null);
275 writer.writeAttribute(HtmlAttributes.CELLSPACING, "0", false);
276 writer.writeAttribute(HtmlAttributes.CELLPADDING, "0", false);
277 writer.writeAttribute(HtmlAttributes.SUMMARY, "", false);
278 writer.writeClassAttribute(Classes.create(sheet, "bodyTable"));
279 writer.writeStyleAttribute(sheetBodyStyle);
280
281 if (columnWidths != null) {
282 writer.startElement(HtmlElements.COLGROUP, null);
283 for (Integer columnWidth : columnWidths) {
284 writer.startElement(HtmlElements.COL, null);
285 writer.writeAttribute(HtmlAttributes.WIDTH, columnWidth);
286 writer.endElement(HtmlElements.COL);
287 }
288 writer.endElement(HtmlElements.COLGROUP);
289 }
290
291
292
293 if (LOG.isDebugEnabled()) {
294 LOG.debug("first = " + sheet.getFirst() + " rows = " + sheet.getRows());
295 }
296
297 final String var = sheet.getVar();
298
299 boolean odd = false;
300 boolean emptySheet = true;
301
302 final int last = sheet.isRowsUnlimited() ? Integer.MAX_VALUE : sheet.getFirst() + sheet.getRows();
303 for (int rowIndex = sheet.getFirst(); rowIndex < last; rowIndex++) {
304 sheet.setRowIndex(rowIndex);
305 if (!sheet.isRowAvailable()) {
306 break;
307 }
308 emptySheet = false;
309 odd = !odd;
310
311 if (LOG.isDebugEnabled()) {
312 LOG.debug("var " + var);
313 LOG.debug("list " + sheet.getValue());
314 }
315
316 if (sheet.isTreeModel()) {
317 final TreePath path = sheet.getPath();
318 if (sheet.isFolder() && expandedState.isExpanded(path)) {
319 expandedValue.append(rowIndex);
320 expandedValue.append(",");
321 }
322 }
323
324 writer.startElement(HtmlElements.TR, null);
325 Markup rowMarkup = odd ? Markup.ODD : Markup.EVEN;
326 final boolean selected = selectedRows.contains(rowIndex);
327 if (selected) {
328 rowMarkup = rowMarkup.add(Markup.SELECTED);
329 }
330 writer.writeClassAttribute(Classes.create(sheet, "row", rowMarkup));
331 if (!sheet.isRowVisible()) {
332 Style rowStyle = new Style();
333 rowStyle.setDisplay(Display.NONE);
334 writer.writeStyleAttribute(rowStyle);
335 }
336 final String parentId = sheet.getRowParentClientId();
337 if (parentId != null) {
338 writer.writeAttribute(DataAttributes.TREEPARENT, parentId, false);
339 }
340
341 int columnIndex = -1;
342 for (UIColumn column : renderedColumnList) {
343 columnIndex++;
344
345 writer.startElement(HtmlElements.TD, column);
346
347 Markup markup = column instanceof SupportsMarkup ? ((SupportsMarkup) column).getMarkup() : Markup.NULL;
348 if (markup == null) {
349 markup = Markup.NULL;
350 }
351 if (columnIndex == 0) {
352 markup = markup.add(Markup.FIRST);
353 }
354 if (hasClickAction) {
355 markup = markup.add(Markup.CLICKABLE);
356 }
357 if (isPure(column)) {
358 markup = markup.add(Markup.PURE);
359 }
360 writer.writeClassAttribute(Classes.create(sheet, "cell", markup));
361 final TextAlign align = TextAlign.parse((String) column.getAttributes().get(Attributes.ALIGN));
362 if (align != null) {
363 Style alignStyle = new Style();
364 alignStyle.setTextAlign(align);
365 writer.writeStyleAttribute(alignStyle);
366 }
367
368 if (column instanceof UIColumnSelector) {
369 final boolean disabled = ComponentUtils.getBooleanAttribute(column, Attributes.DISABLED);
370 writer.startElement(HtmlElements.INPUT, null);
371 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.CHECKBOX, false);
372 writer.writeAttribute(HtmlAttributes.CHECKED, selected);
373 writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
374 writer.writeIdAttribute(sheetId + "_data_row_selector_" + rowIndex);
375 writer.writeClassAttribute(Classes.create(sheet, "columnSelector"));
376 writer.endElement(HtmlElements.INPUT);
377 } else if (column instanceof AbstractUIColumnNode) {
378 RenderUtils.prepareRendererAll(facesContext, column);
379 RenderUtils.encode(facesContext, column);
380 } else {
381 List<UIComponent> children = sheet.getRenderedChildrenOf(column);
382 for (UIComponent grandKid : children) {
383
384 grandKid.getAttributes().put(Attributes.LAYOUT_HEIGHT, HEIGHT_0);
385
386 if (grandKid instanceof LayoutBase) {
387 LayoutBase base = (LayoutBase) grandKid;
388 if (base.getLeft() != null) {
389 base.setLeft(null);
390 }
391 if (base.getTop() != null) {
392 base.setTop(null);
393 }
394 }
395 RenderUtils.prepareRendererAll(facesContext, grandKid);
396 RenderUtils.encode(facesContext, grandKid);
397 }
398 }
399
400 writer.endElement(HtmlElements.TD);
401 }
402
403 writer.startElement(HtmlElements.TD, null);
404 writer.writeClassAttribute(Classes.create(sheet, "cell", Markup.FILLER));
405
406 writer.startElement(HtmlElements.DIV, null);
407 writer.endElement(HtmlElements.DIV);
408 writer.endElement(HtmlElements.TD);
409
410 writer.endElement(HtmlElements.TR);
411 }
412
413 sheet.setRowIndex(-1);
414
415 if (emptySheet && showHeader) {
416 writer.startElement(HtmlElements.TR, null);
417 int columnIndex = -1;
418 for (UIColumn ignored : renderedColumnList) {
419 columnIndex++;
420 writer.startElement(HtmlElements.TD, null);
421 writer.startElement(HtmlElements.DIV, null);
422 Integer divWidth = sheet.getWidthList().get(columnIndex);
423 Style divStyle = new Style();
424 divStyle.setWidth(Measure.valueOf(divWidth));
425 writer.writeStyleAttribute(divStyle);
426 writer.endElement(HtmlElements.DIV);
427 writer.endElement(HtmlElements.TD);
428 }
429 writer.startElement(HtmlElements.TD, null);
430 writer.writeClassAttribute(Classes.create(sheet, "cell", Markup.FILLER));
431
432 writer.startElement(HtmlElements.DIV, null);
433 writer.endElement(HtmlElements.DIV);
434 writer.endElement(HtmlElements.TD);
435 writer.endElement(HtmlElements.TR);
436 }
437
438 writer.endElement(HtmlElements.TABLE);
439 writer.endElement(HtmlElements.DIV);
440
441
442
443 if (sheet.isPagingVisible()) {
444 Style footerStyle = new Style();
445 footerStyle.setWidth(sheet.getCurrentWidth());
446 if (ie6SelectOneFix) {
447 footerStyle.setTop(headerHeight);
448 }
449 writer.startElement(HtmlElements.DIV, sheet);
450 writer.writeClassAttribute(Classes.create(sheet, "footer"));
451 writer.writeStyleAttribute(footerStyle);
452
453
454 final Markup showRowRange = markupForLeftCenterRight(sheet.getShowRowRange());
455 if (showRowRange != Markup.NULL) {
456 UICommand pagerCommand = (UICommand) sheet.getFacet(Facets.PAGER_ROW);
457 if (pagerCommand == null) {
458 pagerCommand = createPagingCommand(application, PageAction.TO_ROW, false);
459 sheet.getFacets().put(Facets.PAGER_ROW, pagerCommand);
460 }
461 final String pagerCommandId = pagerCommand.getClientId(facesContext);
462
463 writer.startElement(HtmlElements.SPAN, null);
464 writer.writeClassAttribute(Classes.create(sheet, "pagingOuter", showRowRange));
465 writer.writeAttribute(HtmlAttributes.TITLE,
466 ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetPagingInfoRowPagingTip"), true);
467 writer.flush();
468 writer.write(createSheetPagingInfo(sheet, facesContext, pagerCommandId, true));
469 writer.endElement(HtmlElements.SPAN);
470 }
471
472
473 final Markup showDirectLinks = markupForLeftCenterRight(sheet.getShowDirectLinks());
474 if (showDirectLinks != Markup.NULL) {
475 writer.startElement(HtmlElements.SPAN, null);
476 writer.writeClassAttribute(Classes.create(sheet, "pagingOuter", showDirectLinks));
477 writer.writeIdAttribute(sheetId + ComponentUtils.SUB_SEPARATOR + "pagingLinks");
478 writeDirectPagingLinks(writer, facesContext, application, sheet);
479 writer.endElement(HtmlElements.SPAN);
480 }
481
482
483 final Markup showPageRange = markupForLeftCenterRight(sheet.getShowPageRange());
484 if (showPageRange != Markup.NULL) {
485 UICommand pagerCommand = (UICommand) sheet.getFacet(Facets.PAGER_PAGE);
486 if (pagerCommand == null) {
487 pagerCommand = createPagingCommand(application, PageAction.TO_PAGE, false);
488 sheet.getFacets().put(Facets.PAGER_PAGE, pagerCommand);
489 }
490 final String pagerCommandId = pagerCommand.getClientId(facesContext);
491
492 writer.startElement(HtmlElements.SPAN, null);
493 writer.writeClassAttribute(Classes.create(sheet, "pagingOuter", showPageRange));
494 writer.writeIdAttribute(sheetId + ComponentUtils.SUB_SEPARATOR + "pagingPages");
495 writer.writeText("");
496
497 boolean atBeginning = sheet.isAtBeginning();
498 link(facesContext, application, atBeginning, PageAction.FIRST, sheet);
499 link(facesContext, application, atBeginning, PageAction.PREV, sheet);
500 writer.startElement(HtmlElements.SPAN, null);
501 writer.writeClassAttribute(Classes.create(sheet, "pagingText"));
502 writer.writeAttribute(HtmlAttributes.TITLE,
503 ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetPagingInfoPagePagingTip"), true);
504 writer.flush();
505 writer.write(createSheetPagingInfo(sheet, facesContext, pagerCommandId, false));
506 writer.endElement(HtmlElements.SPAN);
507 boolean atEnd = sheet.isAtEnd();
508 link(facesContext, application, atEnd, PageAction.NEXT, sheet);
509 link(facesContext, application, atEnd || !sheet.hasRowCount(), PageAction.LAST, sheet);
510 writer.endElement(HtmlElements.SPAN);
511 }
512
513 writer.endElement(HtmlElements.DIV);
514 }
515
516 if (sheet.isTreeModel()) {
517 writer.startElement(HtmlElements.INPUT, sheet);
518 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
519 final String expandedId = sheetId + ComponentUtils.SUB_SEPARATOR + AbstractUIData.SUFFIX_EXPANDED;
520 writer.writeNameAttribute(expandedId);
521 writer.writeIdAttribute(expandedId);
522 writer.writeClassAttribute(Classes.create(sheet, AbstractUIData.SUFFIX_EXPANDED));
523 writer.writeAttribute(HtmlAttributes.VALUE, expandedValue.toString(), false);
524 writer.endElement(HtmlElements.INPUT);
525 }
526 }
527
528
529
530
531
532
533
534
535
536 private boolean isPure(UIColumn column) {
537 for (UIComponent child : column.getChildren()) {
538 if (!(child instanceof UIOut) && !(child instanceof UILink)) {
539 return true;
540 }
541 }
542 return false;
543 }
544
545 private String createSheetPagingInfo(UISheet sheet, FacesContext facesContext, String pagerCommandId, boolean row) {
546 String sheetPagingInfo;
547 if (sheet.getRowCount() != 0) {
548 Locale locale = facesContext.getViewRoot().getLocale();
549 int first = row ? sheet.getFirst() + 1 : sheet.getCurrentPage() + 1;
550 int last = sheet.hasRowCount()
551 ? row ? sheet.getLastRowIndexOfCurrentPage() : sheet.getPages()
552 : -1;
553 final boolean unknown = !sheet.hasRowCount();
554 final String key = "sheetPagingInfo"
555 + (unknown ? "Undefined" : "")
556 + (first == last ? "Single" : "")
557 + (row ? "Row" : "Page")
558 + (first == last ? "" : "s");
559 final String message = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", key);
560 final MessageFormat detail = new MessageFormat(message, locale);
561 Object[] args = {
562 first,
563 last == -1 ? "?" : last,
564 unknown ? "" : sheet.getRowCount(),
565 pagerCommandId + ComponentUtils.SUB_SEPARATOR + "text"
566 };
567 sheetPagingInfo = detail.format(args);
568 } else {
569 sheetPagingInfo =
570 ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago",
571 "sheetPagingInfoEmpty" + (row ? "Row" : "Page"));
572 }
573 return sheetPagingInfo;
574 }
575
576 @Override
577 public void decode(FacesContext facesContext, UIComponent component) {
578 super.decode(facesContext, component);
579
580 UISheet sheet = (UISheet) component;
581
582 String key = sheet.getClientId(facesContext) + WIDTHS_POSTFIX;
583
584 Map requestParameterMap = facesContext.getExternalContext().getRequestParameterMap();
585 if (requestParameterMap.containsKey(key)) {
586 String widths = (String) requestParameterMap.get(key);
587 if (widths.trim().length() > 0) {
588 sheet.getAttributes().put(Attributes.WIDTH_LIST_STRING, widths);
589 }
590 }
591
592 key = sheet.getClientId(facesContext) + SELECTED_POSTFIX;
593 if (requestParameterMap.containsKey(key)) {
594 String selected = (String) requestParameterMap.get(key);
595 if (LOG.isDebugEnabled()) {
596 LOG.debug("selected = " + selected);
597 }
598 List<Integer> selectedRows;
599 try {
600 selectedRows = StringUtils.parseIntegerList(selected);
601 } catch (NumberFormatException e) {
602 LOG.warn(selected, e);
603 selectedRows = Collections.emptyList();
604 }
605
606 sheet.getAttributes().put(Attributes.SELECTED_LIST_STRING, selectedRows);
607 }
608
609 key = sheet.getClientId(facesContext) + SCROLL_POSTFIX;
610 String value = (String) requestParameterMap.get(key);
611 if (value != null) {
612 Integer[] scrollPosition = SheetState.parseScrollPosition(value);
613 if (scrollPosition != null) {
614
615 sheet.getAttributes().put(Attributes.SCROLL_POSITION, scrollPosition);
616 }
617 }
618
619 RenderUtils.decodedStateOfTreeData(facesContext, sheet);
620 }
621
622 private Measure getHeaderHeight(FacesContext facesContext, UISheet sheet) {
623 int rows = sheet.getHeaderGrid().getRowCount();
624 return sheet.isShowHeader()
625 ? getResourceManager().getThemeMeasure(facesContext, sheet, "headerHeight").multiply(rows)
626 : Measure.ZERO;
627 }
628
629 private Measure getRowHeight(FacesContext facesContext, UISheet sheet) {
630 return getResourceManager().getThemeMeasure(facesContext, sheet, "rowHeight");
631 }
632
633 private Measure getFooterHeight(FacesContext facesContext, UISheet sheet) {
634 return sheet.isPagingVisible()
635 ? getResourceManager().getThemeMeasure(facesContext, sheet, "footerHeight")
636 : Measure.ZERO;
637 }
638
639 private Markup markupForLeftCenterRight(String name) {
640 if ("left".equals(name)) {
641 return Markup.LEFT;
642 }
643 if ("center".equals(name)) {
644 return Markup.CENTER;
645 }
646 if ("right".equals(name)) {
647 return Markup.RIGHT;
648 }
649 return Markup.NULL;
650 }
651
652 private String checkPagingAttribute(String name) {
653 if (isNotNone(name)) {
654 return name;
655 } else {
656 if (!"none".equals(name)) {
657 LOG.warn("Illegal value in sheets paging attribute: '" + name + "'");
658 }
659 return "none";
660 }
661 }
662
663 private boolean isNotNone(String value) {
664
665 return "left".equals(value) || "center".equals(value) || "right".equals(value);
666 }
667
668 @Override
669 public boolean getRendersChildren() {
670 return true;
671 }
672
673 private List<Integer> getSelectedRows(UISheet data, SheetState state) {
674 List<Integer> selected = (List<Integer>)
675 data.getAttributes().get(Attributes.SELECTED_LIST_STRING);
676 if (selected == null && state != null) {
677 selected = state.getSelectedRows();
678 }
679 if (selected == null) {
680 selected = Collections.emptyList();
681 }
682 return selected;
683 }
684
685 private void link(FacesContext facesContext, Application application,
686 boolean disabled, PageAction command, UISheet data)
687 throws IOException {
688 UICommand link = createPagingCommand(application, command, disabled);
689
690 data.getFacets().put(command.getToken(), link);
691
692
693 String tip = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago",
694 "sheet" + command.getToken());
695 String image = ResourceManagerUtils.getImageWithPath(facesContext,
696 "image/sheet" + command.getToken() + (disabled ? "Disabled" : "") + ".gif");
697
698 TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
699 writer.startElement(HtmlElements.IMG, null);
700 writer.writeIdAttribute(data.getClientId(facesContext)
701 + ComponentUtils.SUB_SEPARATOR + "pagingPages" + ComponentUtils.SUB_SEPARATOR + command.getToken());
702 Classes pagerClasses = Classes.create(data, "footerPagerButton", disabled ? Markup.DISABLED : null);
703 writer.writeClassAttribute(pagerClasses);
704 writer.writeAttribute(HtmlAttributes.SRC, image, false);
705 writer.writeAttribute(HtmlAttributes.TITLE, tip, true);
706 writer.writeAttribute(HtmlAttributes.ALT, "", false);
707 writer.writeAttribute(DataAttributes.DISABLED, disabled);
708 writer.endElement(HtmlElements.IMG);
709 }
710
711
712
713
714
715 private void renderColumnHeaders(
716 FacesContext facesContext, UISheet sheet, TobagoResponseWriter writer, ResourceManager resourceManager,
717 String contextPath, String sheetId, List<AbstractUIColumn> renderedColumnList, Measure headerWidth)
718 throws IOException {
719
720 final Grid grid = sheet.getHeaderGrid();
721 final List<Integer> columnWidths = sheet.getWidthList();
722
723 LOG.info("*****************************************************");
724 LOG.info("" + grid);
725 LOG.info("*****************************************************");
726
727 writer.startElement(HtmlElements.DIV, sheet);
728 writer.writeClassAttribute(Classes.create(sheet, "headerDiv"));
729 writer.startElement(HtmlElements.TABLE, sheet);
730 writer.writeAttribute(HtmlAttributes.CELLSPACING, "0", false);
731 writer.writeAttribute(HtmlAttributes.CELLPADDING, "0", false);
732 writer.writeAttribute(HtmlAttributes.SUMMARY, "", false);
733 writer.writeClassAttribute(Classes.create(sheet, "headerTable"));
734
735 if (columnWidths != null) {
736 writer.startElement(HtmlElements.COLGROUP, null);
737 for (Integer columnWidth : columnWidths) {
738 writer.startElement(HtmlElements.COL, null);
739 writer.writeAttribute(HtmlAttributes.WIDTH, columnWidth);
740 writer.endElement(HtmlElements.COL);
741 }
742 writer.endElement(HtmlElements.COLGROUP);
743 }
744
745 writer.startElement(HtmlElements.TBODY, sheet);
746 for (int i = 0; i < grid.getRowCount(); i++) {
747 writer.startElement(HtmlElements.TR, null);
748 for (int j = 0; j < grid.getColumnCount(); j++) {
749 final Cell cell = grid.getCell(j, i);
750 if (cell instanceof OriginCell) {
751 writer.startElement(HtmlElements.TD, null);
752 if (cell.getColumnSpan() > 1) {
753 writer.writeAttribute(HtmlAttributes.COLSPAN, cell.getColumnSpan());
754 }
755 if (cell.getRowSpan() > 1) {
756 writer.writeAttribute(HtmlAttributes.ROWSPAN, cell.getRowSpan());
757 }
758
759 final UIComponent cellComponent = (UIComponent) cell.getComponent();
760 final boolean pure = !(cellComponent instanceof UIOut);
761
762 writer.startElement(HtmlElements.DIV, null);
763 writer.writeClassAttribute(Classes.create(sheet, "headerCell"));
764 writer.startElement(HtmlElements.SPAN, null);
765 Style headerStyle = new Style();
766 Measure headerHeight = Measure.valueOf(20).multiply(cell.getRowSpan());
767 if (!pure) {
768 headerHeight = headerHeight.subtract(6);
769 }
770 headerStyle.setHeight(headerHeight);
771 writer.writeStyleAttribute(headerStyle);
772 final AbstractUIColumn column = renderedColumnList.get(j);
773 String sorterImage = null;
774 Markup markup = Markup.NULL;
775 String tip = (String) column.getAttributes().get(Attributes.TIP);
776 if (cell.getColumnSpan() == 1) {
777 final boolean sortable = ComponentUtils.getBooleanAttribute(column, Attributes.SORTABLE);
778 if (sortable) {
779 UICommand sortCommand = (UICommand) column.getFacet(Facets.SORTER);
780 if (sortCommand == null) {
781 final String columnId = column.getClientId(facesContext);
782 final String sorterId = columnId.substring(columnId.lastIndexOf(":") + 1) + "_" + UISheet.SORTER_ID;
783 sortCommand = (UICommand) CreateComponentUtils.createComponent(
784 facesContext, UICommand.COMPONENT_TYPE, RendererTypes.LINK, sorterId);
785 column.getFacets().put(Facets.SORTER, sortCommand);
786 }
787 final CommandMap map = new CommandMap();
788 final Command click = new Command(sortCommand.getClientId(facesContext),
789 null, null, null, new String[]{sheet.getClientId(facesContext)}, null, null, null, null);
790 map.setClick(click);
791 writer.writeAttribute(DataAttributes.COMMANDS, JsonUtils.encode(map), true);
792
793 if (tip == null) {
794 tip = "";
795 } else {
796 tip += " - ";
797 }
798 tip += ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetTipSorting");
799
800 markup = markup.add(Markup.SORTABLE);
801
802 SheetState sheetState = sheet.getSheetState(facesContext);
803 if (column.getId().equals(sheetState.getSortedColumnId())) {
804 String sortTitle;
805 if (sheetState.isAscending()) {
806 sorterImage = contextPath + resourceManager.getImage(facesContext, "image/ascending.gif");
807 sortTitle = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetAscending");
808 markup = markup.add(Markup.ASCENDING);
809 } else {
810 sorterImage = contextPath + resourceManager.getImage(facesContext, "image/descending.gif");
811 sortTitle = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetDescending");
812 markup = markup.add(Markup.DESCENDING);
813 }
814 if (sortTitle != null) {
815 tip += " - " + sortTitle;
816 }
817 }
818 }
819 }
820
821 if (j == 0) {
822 markup = markup.add(Markup.FIRST);
823 }
824 if (pure) {
825 markup = markup.add(Markup.PURE);
826 }
827 writer.writeClassAttribute(Classes.create(sheet, "header", markup));
828 writer.writeAttribute(HtmlAttributes.TITLE, tip, true);
829
830 if (column instanceof UIColumnSelector) {
831 renderColumnSelectorHeader(facesContext, writer, sheet);
832 } else {
833 RenderUtils.encode(facesContext, cellComponent);
834 }
835
836 if (sorterImage != null) {
837 writer.startElement(HtmlElements.IMG, null);
838 writer.writeAttribute(HtmlAttributes.SRC, sorterImage, false);
839 writer.writeAttribute(HtmlAttributes.ALT, "", false);
840 writer.endElement(HtmlElements.IMG);
841 }
842
843 writer.endElement(HtmlElements.SPAN);
844 if (renderedColumnList.get(j).isResizable()) {
845 encodeResizing(writer, sheet, j + cell.getColumnSpan() - 1);
846 }
847 writer.endElement(HtmlElements.DIV);
848
849 writer.endElement(HtmlElements.TD);
850 }
851 }
852
853 writer.startElement(HtmlElements.TD, null);
854 writer.startElement(HtmlElements.DIV, null);
855
856 writer.writeClassAttribute(Classes.create(sheet, "headerCell", Markup.FILLER));
857 writer.endElement(HtmlElements.DIV);
858 writer.endElement(HtmlElements.TD);
859
860 writer.endElement(HtmlElements.TR);
861 }
862 writer.endElement(HtmlElements.TBODY);
863 writer.endElement(HtmlElements.TABLE);
864 writer.endElement(HtmlElements.DIV);
865 }
866
867 private void encodeResizing(TobagoResponseWriter writer, AbstractUISheet sheet, int columnIndex) throws IOException {
868 writer.startElement(HtmlElements.SPAN, null);
869 writer.writeClassAttribute(Classes.create(sheet, "headerResize"));
870 writer.writeAttribute(DataAttributes.COLUMNINDEX, Integer.toString(columnIndex), false);
871 writer.write(" ");
872 writer.endElement(HtmlElements.SPAN);
873 }
874
875 protected void renderColumnSelectorHeader(
876 FacesContext facesContext, TobagoResponseWriter writer, UISheet sheet)
877 throws IOException {
878
879 final UIToolBar toolBar = createToolBar(facesContext, sheet);
880 writer.startElement(HtmlElements.DIV, null);
881 writer.writeClassAttribute(Classes.create(sheet, "toolBar"));
882
883 if (UISheet.MULTI.equals(sheet.getSelectable())) {
884 RenderUtils.prepareRendererAll(facesContext, toolBar);
885 RenderUtils.encode(facesContext, toolBar);
886 }
887
888 writer.endElement(HtmlElements.DIV);
889 }
890
891 private UIToolBar createToolBar(FacesContext facesContext, UISheet sheet) {
892 final Application application = facesContext.getApplication();
893 final UICommand dropDown = (UICommand) CreateComponentUtils.createComponent(
894 facesContext, UICommand.COMPONENT_TYPE, null, "dropDown");
895 final UIMenu menu = (UIMenu) CreateComponentUtils.createComponent(
896 facesContext, UIMenu.COMPONENT_TYPE, RendererTypes.MENU, "menu");
897 FacetUtils.setDropDownMenu(dropDown, menu);
898 final String sheetId = sheet.getClientId(facesContext);
899
900 createMenuItem(facesContext, menu, "sheetMenuSelect", Markup.SHEET_SELECT_ALL, sheetId);
901 createMenuItem(facesContext, menu, "sheetMenuUnselect", Markup.SHEET_DESELECT_ALL, sheetId);
902 createMenuItem(facesContext, menu, "sheetMenuToggleselect", Markup.SHEET_TOGGLE_ALL, sheetId);
903
904 final UIToolBar toolBar = (UIToolBar) application.createComponent(UIToolBar.COMPONENT_TYPE);
905 toolBar.setId(facesContext.getViewRoot().createUniqueId());
906 toolBar.setRendererType("TabGroupToolBar");
907 toolBar.setTransient(true);
908 toolBar.getChildren().add(dropDown);
909 sheet.getFacets().put(Facets.TOOL_BAR, toolBar);
910 return toolBar;
911 }
912
913 private void createMenuItem(
914 final FacesContext facesContext, UIMenu menu, String label, Markup markup, String sheetId) {
915 final String id = markup.toString();
916 final UIMenuCommand menuItem = (UIMenuCommand) CreateComponentUtils.createComponent(
917 facesContext, UIMenuCommand.COMPONENT_TYPE, RendererTypes.MENU_COMMAND, id);
918 menuItem.setLabel(ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", label));
919 menuItem.setMarkup(markup);
920 menuItem.setOnclick("/**/");
921 ComponentUtils.putDataAttributeWithPrefix(menuItem, DataAttributes.SHEETID, sheetId);
922 menu.getChildren().add(menuItem);
923 }
924
925 private void writeDirectPagingLinks(
926 TobagoResponseWriter writer, FacesContext facesContext, Application application, UISheet sheet)
927 throws IOException {
928 UICommand pagerCommand = (UICommand) sheet.getFacet(Facets.PAGER_PAGE);
929 if (pagerCommand == null) {
930 pagerCommand = createPagingCommand(application, PageAction.TO_PAGE, false);
931 sheet.getFacets().put(Facets.PAGER_PAGE, pagerCommand);
932 }
933 String pagerCommandId = pagerCommand.getClientId(facesContext);
934 int linkCount = ComponentUtils.getIntAttribute(sheet, Attributes.DIRECT_LINK_COUNT);
935 linkCount--;
936 ArrayList<Integer> prevs = new ArrayList<Integer>(linkCount);
937 int page = sheet.getCurrentPage() + 1;
938 for (int i = 0; i < linkCount && page > 1; i++) {
939 page--;
940 if (page > 0) {
941 prevs.add(0, page);
942 }
943 }
944
945 ArrayList<Integer> nexts = new ArrayList<Integer>(linkCount);
946 page = sheet.getCurrentPage() + 1;
947 final int pages = sheet.hasRowCount() || sheet.isRowsUnlimited() ? sheet.getPages() : Integer.MAX_VALUE;
948 for (int i = 0; i < linkCount && page < pages; i++) {
949 page++;
950 if (page > 1) {
951 nexts.add(page);
952 }
953 }
954
955 if (prevs.size() > (linkCount / 2)
956 && nexts.size() > (linkCount - (linkCount / 2))) {
957 while (prevs.size() > (linkCount / 2)) {
958 prevs.remove(0);
959 }
960 while (nexts.size() > (linkCount - (linkCount / 2))) {
961 nexts.remove(nexts.size() - 1);
962 }
963 } else if (prevs.size() <= (linkCount / 2)) {
964 while (prevs.size() + nexts.size() > linkCount) {
965 nexts.remove(nexts.size() - 1);
966 }
967 } else {
968 while (prevs.size() + nexts.size() > linkCount) {
969 prevs.remove(0);
970 }
971 }
972
973 String name;
974 int skip = prevs.size() > 0 ? prevs.get(0) : 1;
975 if (skip > 1) {
976 skip -= (linkCount - (linkCount / 2));
977 skip--;
978 name = "...";
979 if (skip < 1) {
980 skip = 1;
981 if (prevs.get(0) == 2) {
982 name = "1";
983 }
984 }
985 writeLinkElement(writer, sheet, name, Integer.toString(skip), pagerCommandId, true);
986 }
987 for (Integer prev : prevs) {
988 name = prev.toString();
989 writeLinkElement(writer, sheet, name, name, pagerCommandId, true);
990 }
991 name = Integer.toString(sheet.getCurrentPage() + 1);
992 writeLinkElement(writer, sheet, name, name, pagerCommandId, false);
993
994 for (Integer next : nexts) {
995 name = next.toString();
996 writeLinkElement(writer, sheet, name, name, pagerCommandId, true);
997 }
998
999 skip = nexts.size() > 0 ? nexts.get(nexts.size() - 1) : pages;
1000 if (skip < pages) {
1001 skip += linkCount / 2;
1002 skip++;
1003 name = "...";
1004 if (skip > pages) {
1005 skip = pages;
1006 if ((nexts.get(nexts.size() - 1)) == skip - 1) {
1007 name = Integer.toString(skip);
1008 }
1009 }
1010 writeLinkElement(writer, sheet, name, Integer.toString(skip), pagerCommandId, true);
1011 }
1012 }
1013
1014 private UICommand createPagingCommand(Application application, PageAction command, boolean disabled) {
1015 UICommand link;
1016 link = (UICommand) application.createComponent(UICommand.COMPONENT_TYPE);
1017 link.setRendererType(RendererTypes.SHEET_PAGE_COMMAND);
1018 link.setRendered(true);
1019 link.setId(command.getToken());
1020 link.getAttributes().put(Attributes.INLINE, Boolean.TRUE);
1021 link.getAttributes().put(Attributes.DISABLED, disabled);
1022 return link;
1023 }
1024
1025 private void writeLinkElement(
1026 TobagoResponseWriter writer, UISheet sheet, String str, String skip, String id, boolean makeLink)
1027 throws IOException {
1028 String type = makeLink ? HtmlElements.A : HtmlElements.SPAN;
1029 writer.startElement(type, null);
1030 writer.writeClassAttribute(Classes.create(sheet, "pagingLink"));
1031 if (makeLink) {
1032 writer.writeIdAttribute(id + ComponentUtils.SUB_SEPARATOR + "link_" + skip);
1033 writer.writeAttribute(HtmlAttributes.HREF, "#", true);
1034 }
1035 writer.flush();
1036 writer.write(str);
1037 writer.endElement(type);
1038 }
1039
1040 private Measure getContentBorder(FacesContext facesContext, UISheet data) {
1041 return getBorderLeft(facesContext, data).add(getBorderRight(facesContext, data));
1042 }
1043
1044 @Override
1045 public Measure getPreferredHeight(FacesContext facesContext, Configurable component) {
1046 final UISheet sheet = (UISheet) component;
1047 final Measure headerHeight = getHeaderHeight(facesContext, sheet);
1048 final Measure rowHeight = getRowHeight(facesContext, sheet);
1049 final Measure footerHeight = getFooterHeight(facesContext, sheet);
1050 int rows = sheet.getRows();
1051 if (rows == 0) {
1052 rows = sheet.getRowCount();
1053 }
1054 if (rows == -1) {
1055 rows = 10;
1056 }
1057
1058 if (LOG.isDebugEnabled()) {
1059 LOG.debug(headerHeight + " " + footerHeight + " " + rowHeight + " " + rows);
1060 }
1061
1062 return headerHeight.add(rowHeight.multiply(rows)).add(footerHeight);
1063 }
1064
1065 @Override
1066 public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
1067
1068 }
1069
1070 @Override
1071 public boolean getPrepareRendersChildren() {
1072 return true;
1073 }
1074
1075 @Override
1076 public void prepareRendersChildren(FacesContext facesContext, UIComponent component) throws IOException {
1077
1078
1079
1080
1081
1082
1083 }
1084 }