/* * Copyright 2011 PrimeFaces Extensions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id: LayoutRenderer.java 502 2011-11-29 16:18:19Z Zoigln@googlemail.com $ */ package org.primefaces.extensions.component.layout; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.faces.FacesException; import javax.faces.component.UIComponent; import javax.faces.component.UIForm; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.model.DataModel; import org.primefaces.component.menuitem.MenuItem; import org.primefaces.renderkit.CoreRenderer; /** * Renderer for the {@link Layout} component. * * @author Oleg Varaksin / last modified by $Author: Zoigln@googlemail.com $ * @version $Revision: 502 $ * @since 0.2 */ public class LayoutRenderer extends CoreRenderer { private static final String POSITION_NORTH = "north"; private static final String POSITION_SOUTH = "south"; private static final String POSITION_CENTER = "center"; private static final String POSITION_WEST = "west"; private static final String POSITION_EAST = "east"; private static final String POSITION_SEPARATOR = "_"; private static final String MAIN_FORM = "form"; private static final String STYLE_CLASS_PANE = "ui-widget-content ui-corner-top"; private static final String STYLE_CLASS_PANE_HEADER = "ui-widget-header ui-layout-pane-header ui-corner-top"; private static final String STYLE_CLASS_PANE_CONTENT = "ui-layout-pane-content"; @Override public void decode(final FacesContext fc, final UIComponent component) { Layout layout = (Layout) component; layout.setDataModel(null); } @Override public void encodeEnd(final FacesContext fc, final UIComponent component) throws IOException { Layout layout = (Layout) component; Map layoutPanes = pickLayoutPanes(layout); if (layoutPanes.isEmpty() || layoutPanes.get(POSITION_CENTER) == null) { throw new FacesException("Full page layout must have at least one rendered layout pane with 'center' position"); } encodeScript(fc, layout, layoutPanes); encodeMarkup(fc, layout, layoutPanes); } @Override public boolean getRendersChildren() { return true; } @Override public void encodeChildren(final FacesContext fc, final UIComponent component) throws IOException { // nothing to do } protected Map pickLayoutPanes(final Layout layout) { Map<String, UIComponent> layoutPanes = new HashMap<String, UIComponent>(); Iterator<UIComponent> iter = layout.getChildren().iterator(); while (iter.hasNext()) { UIComponent child = (UIComponent) iter.next(); if (child instanceof LayoutPane) { // layout pane on the first level pickLayoutPane(child, layoutPanes); } else if (child instanceof UIForm) { // a form is allowed here layoutPanes.put(MAIN_FORM, child); Iterator<UIComponent> iter2 = child.getChildren().iterator(); while (iter2.hasNext()) { UIComponent child2 = (UIComponent) iter2.next(); if (child2 instanceof LayoutPane) { // layout pane on the first level pickLayoutPane(child2, layoutPanes); } } } } return layoutPanes; } protected void encodeScript(final FacesContext fc, final Layout layout, final Map layoutPanes) throws IOException { ResponseWriter writer = fc.getResponseWriter(); String clientId = layout.getClientId(); String widgetVar = layout.resolveWidgetVar(); writer.write("\n"); writer.startElement("script", null); writer.writeAttribute("id", clientId + "_script", null); writer.writeAttribute("type", "text/javascript", null); // write layout options ... writer.write("var tabLayoutOptions = {resizeWithWindow: false, south__spacing_open: 3"); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_NORTH)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_SOUTH)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_CENTER)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_WEST)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_EAST)); writer.write("};\n"); boolean hasCenterLayoutOptions = hasNestedLayoutOptions((LayoutPane) layoutPanes.get(POSITION_CENTER)); if (hasCenterLayoutOptions) { // write layout options ... writer.write("var centerLayoutOptions = {resizeWhileDragging: false"); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_CENTER + POSITION_SEPARATOR + POSITION_NORTH)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_CENTER + POSITION_SEPARATOR + POSITION_SOUTH)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_CENTER + POSITION_SEPARATOR + POSITION_CENTER)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_CENTER + POSITION_SEPARATOR + POSITION_WEST)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_CENTER + POSITION_SEPARATOR + POSITION_EAST)); writer.write("};\n"); } boolean hasWestLayoutOptions = hasNestedLayoutOptions((LayoutPane) layoutPanes.get(POSITION_WEST)); if (hasWestLayoutOptions) { // write layout options ... writer.write("var westLayoutOptions = {resizeWhileDragging: true"); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_WEST + POSITION_SEPARATOR + POSITION_NORTH)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_WEST + POSITION_SEPARATOR + POSITION_SOUTH)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_WEST + POSITION_SEPARATOR + POSITION_CENTER)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_WEST + POSITION_SEPARATOR + POSITION_WEST)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_WEST + POSITION_SEPARATOR + POSITION_EAST)); writer.write("};\n"); } boolean hasEastLayoutOptions = hasNestedLayoutOptions((LayoutPane) layoutPanes.get(POSITION_EAST)); if (hasEastLayoutOptions) { // write layout options ... writer.write("var eastLayoutOptions = {resizeWhileDragging: true"); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_EAST + POSITION_SEPARATOR + POSITION_NORTH)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_EAST + POSITION_SEPARATOR + POSITION_SOUTH)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_EAST + POSITION_SEPARATOR + POSITION_CENTER)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_EAST + POSITION_SEPARATOR + POSITION_WEST)); writeLayoutPaneOption(fc, writer, layoutPanes.get(POSITION_EAST + POSITION_SEPARATOR + POSITION_EAST)); writer.write("};\n"); } writer.write("$(document).ready(function(){"); writer.write(widgetVar + " = new PrimeFacesExt.widget.Layout('" + clientId + "',"); DataModel dataModel = layout.getDataModel(); if (dataModel == null || dataModel.getRowCount() < 1) { writer.write("-1,"); } else { String viewId = fc.getViewRoot().getViewId(); viewId = viewId.substring(0, viewId.lastIndexOf('.')); if (viewId.startsWith("/")) { viewId = viewId.substring(1); } int indexTab = 0; Iterator<MenuItem> iter = dataModel.iterator(); while (iter.hasNext()) { String url = iter.next().getUrl(); url = url.substring(0, url.lastIndexOf('.')); if (url.startsWith("/")) { url = url.substring(1); } if (viewId.equals(url)) { break; } ++indexTab; } writer.write(indexTab + ","); } if (layoutPanes.get(POSITION_NORTH) != null) { writer.write(((LayoutPane) layoutPanes.get(POSITION_NORTH)).getSize() + ","); } else { writer.write("0,"); } writer.write("tabLayoutOptions,"); writer.write(hasCenterLayoutOptions ? "centerLayoutOptions," : "null,"); writer.write(hasWestLayoutOptions ? "westLayoutOptions," : "null,"); writer.write(hasEastLayoutOptions ? "eastLayoutOptions," : "null,"); if (layout.getTogglerTipClose() != null) { writer.write("\"" + layout.getTogglerTipClose() + "\","); } else { writer.write("'Close',"); } if (layout.getTogglerTipOpen() != null) { writer.write("\"" + layout.getTogglerTipOpen() + "\","); } else { writer.write("'Open',"); } if (layout.getResizerTip() != null) { writer.write("\"" + layout.getResizerTip() + "\""); } else { writer.write("'Resize'"); } writer.write(");"); writer.write(widgetVar + ".buildOuterTabsLayout();"); if (dataModel != null && dataModel.getRowCount() > 0) { writer.write("$('#" + getEscapedClientId(clientId) + "-layout-tabbuttons').find('.ui-tab').corner('top 6px');"); } writer.write("});"); writer.endElement("script"); writer.write("\n"); } protected void encodeMarkup(final FacesContext fc, final Layout layout, final Map layoutPanes) throws IOException { ResponseWriter writer = fc.getResponseWriter(); String clientId = layout.getClientId(); LayoutPane layoutPane = (LayoutPane) layoutPanes.get(POSITION_NORTH); if (layoutPane != null && layoutPane.isRendered()) { writer.startElement("div", null); writer.writeAttribute("id", clientId + "-layout-outer-north", null); writer.writeAttribute("class", "layout-outer-north", null); renderChildren(fc, layoutPane); writer.endElement("div"); } writer.startElement("div", null); writer.writeAttribute("id", clientId + "-layout-outer-center", null); writer.writeAttribute("class", "layout-outer-center", null); DataModel dataModel = layout.getDataModel(); if (dataModel != null && dataModel.getRowCount() > 0) { // render tabs writer.startElement("ul", null); writer.writeAttribute("id", clientId + "-layout-tabbuttons", null); writer.writeAttribute("class", "layout-tabbuttons", null); writer.writeAttribute("style", "display: none;", null); Iterator<MenuItem> iter = dataModel.iterator(); while (iter.hasNext()) { MenuItem mi = iter.next(); writer.startElement("li", null); if (!iter.hasNext()) { writer.writeAttribute("class", "ui-tab last-tab", null); } else { writer.writeAttribute("class", "ui-tab", null); } writer.startElement("a", null); // build complete URL ExternalContext ec = fc.getExternalContext(); StringBuilder url = new StringBuilder(); String scheme = ec.getRequestScheme(); int port = ec.getRequestServerPort(); url.append(scheme); // http, https url.append("://"); url.append(ec.getRequestServerName()); if ((scheme.equals("http") && port != 80) || (scheme.equals("https") && port != 443)) { url.append(':'); url.append(port); } String actionURL = fc.getApplication().getViewHandler().getActionURL(fc, mi.getUrl()); url.append(ec.encodeActionURL(actionURL)); // write URL writer.writeURIAttribute("href", "#" + url.toString(), null); // render inner table = tab icon + label writer.startElement("table", null); writer.startElement("tr", null); writer.startElement("td", null); writer.writeAttribute("class", "ui-icon " + mi.getIcon(), null); writer.endElement("td"); writer.startElement("td", null); writer.write((String) mi.getValue()); writer.endElement("td"); writer.endElement("tr"); writer.endElement("table"); writer.endElement("a"); writer.endElement("li"); } writer.endElement("ul"); } writer.startElement("div", null); writer.writeAttribute("id", clientId + "-layout-tabpanels", null); writer.writeAttribute("class", "layout-tabpanels", null); writer.startElement("div", null); writer.writeAttribute("class", "ui-layout-tab", null); UIForm form = (UIForm) layoutPanes.get(MAIN_FORM); if (form != null) { form.encodeBegin(fc); } // render current tab panel pane by pane encodePane(fc, writer, layoutPanes, POSITION_SOUTH); encodePane(fc, writer, layoutPanes, POSITION_CENTER); encodePane(fc, writer, layoutPanes, POSITION_WEST); encodePane(fc, writer, layoutPanes, POSITION_EAST); if (form != null) { form.encodeEnd(fc); } writer.endElement("div"); writer.endElement("div"); writer.endElement("div"); } protected void encodePane(final FacesContext fc, final ResponseWriter writer, final Map layoutPanes, final String position) throws IOException { LayoutPane layoutPane = (LayoutPane) layoutPanes.get(position); if (layoutPane == null) { return; } writer.startElement("div", null); // render class attribute if (layoutPane.isExistNestedPanes() || layoutPane.isStatusbar()) { writer.writeAttribute("class", "ui-layout-" + layoutPane.getPosition(), null); } else { writer.writeAttribute("class", "ui-layout-" + layoutPane.getPosition() + " " + STYLE_CLASS_PANE, null); } // render stuff inside pane(s) if (layoutPane.isExistNestedPanes()) { encodePane(fc, writer, layoutPanes, position + POSITION_SEPARATOR + POSITION_NORTH); encodePane(fc, writer, layoutPanes, position + POSITION_SEPARATOR + POSITION_CENTER); encodePane(fc, writer, layoutPanes, position + POSITION_SEPARATOR + POSITION_SOUTH); encodePane(fc, writer, layoutPanes, position + POSITION_SEPARATOR + POSITION_EAST); encodePane(fc, writer, layoutPanes, position + POSITION_SEPARATOR + POSITION_WEST); } else { encodePaneHeader(fc, writer, layoutPane); encodePaneContent(fc, writer, layoutPane); } writer.endElement("div"); } protected void encodePaneHeader(final FacesContext fc, final ResponseWriter writer, final LayoutPane layoutPane) throws IOException { UIComponent header = layoutPane.getFacet("header"); if (header != null) { writer.startElement("div", null); if (layoutPane.getStyleClassHeader() != null) { writer.writeAttribute("class", STYLE_CLASS_PANE_HEADER + " " + layoutPane.getStyleClassHeader(), null); } else { writer.writeAttribute("class", STYLE_CLASS_PANE_HEADER, null); } if (layoutPane.getStyleHeader() != null) { writer.writeAttribute("style", layoutPane.getStyleHeader(), null); } header.encodeAll(fc); writer.endElement("div"); } } protected void encodePaneContent(final FacesContext fc, final ResponseWriter writer, final LayoutPane layoutPane) throws IOException { writer.startElement("div", null); String styleClass = STYLE_CLASS_PANE_CONTENT; if (layoutPane.isStatusbar()) { styleClass = styleClass + " ui-state-default statusbar"; } if (layoutPane.getStyleClassContent() != null) { writer.writeAttribute("class", styleClass + " " + layoutPane.getStyleClassContent(), null); } else { writer.writeAttribute("class", styleClass, null); } if (layoutPane.getStyleContent() != null) { writer.writeAttribute("style", layoutPane.getStyleContent(), null); } renderChildren(fc, layoutPane); writer.endElement("div"); } private void pickLayoutPane(final UIComponent child, final Map<String, UIComponent> layoutPanes) { if (!child.isRendered()) { return; } String position = ((LayoutPane) child).getPosition(); layoutPanes.put(position, child); boolean hasSubPanes = false; Iterator<UIComponent> iter = child.getChildren().iterator(); while (iter.hasNext()) { UIComponent subChild = (UIComponent) iter.next(); if (subChild instanceof LayoutPane) { if (!subChild.isRendered()) { continue; } // layout pane on the second level layoutPanes.put(position + POSITION_SEPARATOR + ((LayoutPane) subChild).getPosition(), subChild); hasSubPanes = true; } } if (hasSubPanes && layoutPanes.get(position + POSITION_SEPARATOR + POSITION_CENTER) == null) { throw new FacesException("Rendered 'center' layout pane inside of '" + position + "' layout pane is missing"); } if (hasSubPanes) { ((LayoutPane) child).setExistNestedPanes(true); } } private void writeLayoutPaneOption(final FacesContext fc, final ResponseWriter writer, final Object objPane) throws IOException { if (objPane == null) { return; } LayoutPane pane = (LayoutPane) objPane; writer.write(", " + pane.getPosition() + "__resizable: " + pane.isResizable()); writer.write(", " + pane.getPosition() + "__closable: " + pane.isClosable()); writer.write(", " + pane.getPosition() + "__initClosed: " + pane.isInitClosed()); if (pane.getSize() != null) { writer.write(", " + pane.getPosition() + "__size: " + pane.getSize()); } if (pane.getMinSize() != null) { writer.write(", " + pane.getPosition() + "__minSize: " + pane.getMinSize()); } if (pane.getMaxSize() != null) { writer.write(", " + pane.getPosition() + "__maxSize: " + pane.getMaxSize()); } if (pane.getMinWidth() != null) { writer.write(", " + pane.getPosition() + "__minWidth: " + pane.getMinWidth()); } if (pane.getMaxWidth() != null) { writer.write(", " + pane.getPosition() + "__maxWidth: " + pane.getMaxWidth()); } if (pane.getMinHeight() != null) { writer.write(", " + pane.getPosition() + "__minHeight: " + pane.getMinHeight()); } if (pane.getMaxHeight() != null) { writer.write(", " + pane.getPosition() + "__maxHeight: " + pane.getMaxHeight()); } } private boolean hasNestedLayoutOptions(final LayoutPane layoutPane) { if (layoutPane == null || !layoutPane.isExistNestedPanes()) { return false; } return true; } }