/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.server.headlessclient.dataui;
import java.util.Iterator;
import java.util.Locale;
import javax.swing.border.Border;
import org.apache.wicket.ResourceReference;
import org.xhtmlrenderer.css.constants.CSSName;
import com.servoy.j2db.FormController;
import com.servoy.j2db.IForm;
import com.servoy.j2db.IServiceProvider;
import com.servoy.j2db.component.ComponentFactory;
import com.servoy.j2db.persistence.Form;
import com.servoy.j2db.persistence.ISupportScrollbars;
import com.servoy.j2db.persistence.Part;
import com.servoy.j2db.persistence.Solution;
import com.servoy.j2db.server.headlessclient.dataui.TemplateGenerator.TextualCSS;
import com.servoy.j2db.server.headlessclient.dataui.TemplateGenerator.TextualStyle;
import com.servoy.j2db.util.ComponentFactoryHelper;
import com.servoy.j2db.util.IStyleRule;
import com.servoy.j2db.util.IStyleSheet;
import com.servoy.j2db.util.OrientationApplier;
import com.servoy.j2db.util.Pair;
import com.servoy.j2db.util.PersistHelper;
import com.servoy.j2db.util.ServoyStyleSheet;
import com.servoy.j2db.util.Settings;
import com.servoy.j2db.util.Utils;
/**
* Generic superclass of available layout providers for web client. Holds the common
* functionality.
*
* @author gerzse
*/
public abstract class AbstractFormLayoutProvider implements IFormLayoutProvider
{
private final Solution solution;
protected final Form f;
private final String formInstanceName;
private boolean addHeaders;
protected int defaultNavigatorShift;
private final Border border;
protected String orientation;
int viewType;
private final IServiceProvider sp;
private IStyleRule style = null;
private boolean hasImage = false;
public AbstractFormLayoutProvider(IServiceProvider sp, Solution solution, Form f, String formInstanceName)
{
this.sp = sp;
this.solution = solution;
this.f = f;
this.formInstanceName = formInstanceName;
addHeaders = true;
defaultNavigatorShift = 0;
viewType = f.getView();
if (viewType == IForm.LIST_VIEW || viewType == FormController.LOCKED_LIST_VIEW)
{
addHeaders = false;//list views do not have headers
}
if (sp != null && sp.getFlattenedSolution().isInDesign(f) &&
(viewType == FormController.LOCKED_TABLE_VIEW || viewType == IForm.LIST_VIEW || viewType == FormController.LOCKED_LIST_VIEW))
{
viewType = IForm.RECORD_VIEW;
}
if ((viewType == IForm.RECORD_VIEW || viewType == IForm.LOCKED_RECORD_VIEW) && f.getNavigatorID() == Form.NAVIGATOR_DEFAULT)
{
defaultNavigatorShift = WebDefaultRecordNavigator.DEFAULT_WIDTH;
}
// Initially get the border from the form and the background color from the body part.
border = ComponentFactoryHelper.createBorder(f.getBorderType());
Iterator<Part> parts = f.getParts();
while (parts.hasNext())
{
Part p = parts.next();
if (p.getPartType() == Part.HEADER)
{
addHeaders = false;
}
}
// Look into styles.
Pair<IStyleSheet, IStyleRule> pairStyle = ComponentFactory.getCSSPairStyleForForm(sp, f);
if (pairStyle != null)
{
style = pairStyle.getRight();
}
orientation = OrientationApplier.getHTMLContainerOrientation(sp != null ? sp.getLocale() : Locale.getDefault(), solution.getTextOrientation());
}
public String getFormInstanceName()
{
return this.formInstanceName;
}
public int getViewType()
{
return viewType;
}
public boolean needsHeaders()
{
return addHeaders;
}
public void renderOpenFormHTML(StringBuffer html, TextualCSS css)
{
html.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"); //$NON-NLS-1$
html.append("<!-- Servoy webclient page Copyright "); //$NON-NLS-1$
html.append(Utils.formatTime(System.currentTimeMillis(), "yyyy")); //$NON-NLS-1$
html.append(" Servoy -->\n"); //$NON-NLS-1$
html.append("<html xmlns:servoy>\n"); //$NON-NLS-1$
html.append("<head>\n"); //$NON-NLS-1$
html.append("<title>"); //$NON-NLS-1$
html.append((f.getTitleText() != null ? TemplateGenerator.getSafeText(f.getTitleText()) : getFormInstanceName()));
html.append(" - Servoy"); //$NON-NLS-1$
html.append("</title>\n"); //$NON-NLS-1$
html.append("<servoy:head>\n"); //$NON-NLS-1$
html.append("</servoy:head>\n"); //$NON-NLS-1$
html.append("</head>\n"); //$NON-NLS-1$
html.append("<body id='servoy_page'>\n"); //$NON-NLS-1$
html.append("<form id='servoy_dataform'>\n"); //$NON-NLS-1$
html.append("<servoy:panel>\n"); //$NON-NLS-1$
String buildFormID = buildFormID();
html.append("<div servoy:id='servoywebform' id='"); //$NON-NLS-1$
html.append(buildFormID);
html.append("'" + TemplateGenerator.getCssClassForElement(f, new boolean[] { false }, "servoywebform") + "'>\n"); //$NON-NLS-1$
// following two divs are here only because a bug in IE7 made divs that were anchored on all sides break iframe behavior (so dialogs)
html.append("<div id='sfw_"); //$NON-NLS-1$
html.append(buildFormID);
html.append("' style='position: absolute; height: 0px; right: 0px; left: 0px;'/>"); //$NON-NLS-1$
html.append("<div id='sfh_"); //$NON-NLS-1$
html.append(buildFormID);
html.append("' style='position: absolute; bottom: 0px; top: 0px; width: 0px;'/>"); //$NON-NLS-1$
// the 2 divs above are used to keep track of the form's size when browser resizes
// Put CSS properties for background color and border (if any).
TextualStyle formStyle = css.addStyle("#" + buildFormID()); //$NON-NLS-1$
if (style != null && style.getValue(CSSName.BACKGROUND_COLOR.toString()) != null && !f.getTransparent())
{
formStyle.setProperty(CSSName.BACKGROUND_COLOR.toString(), style.getValues(CSSName.BACKGROUND_COLOR.toString()), true);
}
if (border != null)
{
String type = ComponentFactoryHelper.createBorderString(border);
ComponentFactoryHelper.createBorderCSSProperties(type, formStyle);
}
else if (style != null)
{
copyBorderAttributes(style, formStyle);
}
hasImage = addBackgroundImageAttributeIfExists(style, formStyle);
fillFormLayoutCSS(formStyle);
}
private void copyBorderAttributes(IStyleRule source, TextualStyle destination)
{
if (source != null && destination != null)
{
for (String property : ServoyStyleSheet.BORDER_CSS)
{
if (source.hasAttribute(property))
{
destination.setProperty(property, source.getValues(property), true);
}
}
for (String property : ServoyStyleSheet.borderAttributesExtensions)
{
if (source.hasAttribute(property))
{
destination.setProperty(property, source.getValues(property), true);
}
}
}
}
private boolean addBackgroundImageAttributeIfExists(IStyleRule styleRule, TextualStyle textStyle)
{
boolean exists = false;
if (styleRule != null)
{
for (String attName : ServoyStyleSheet.BACKGROUND_IMAGE_CSS)
{
if (styleRule.hasAttribute(attName))
{
textStyle.setProperty(attName, styleRule.getValues(attName), true);
exists = true;
}
}
}
return exists;
}
public void renderCloseFormHTML(StringBuffer html)
{
html.append("</div>\n"); //close form div //$NON-NLS-1$
html.append("</servoy:panel>\n"); //$NON-NLS-1$
html.append("</form>\n"); //$NON-NLS-1$
html.append("</body>\n"); //$NON-NLS-1$
html.append("</html>\n"); //$NON-NLS-1$
}
public void renderOpenPartHTML(StringBuffer html, TextualCSS css, Part part)
{
if (part.getPartType() == Part.BODY)
{
html.append("<div servoy:id='View'>\n"); //$NON-NLS-1$
renderNavigator(html, part);
}
String partID = ComponentFactory.getWebID(f, part);
html.append("<div servoy:id='"); //$NON-NLS-1$
html.append(partID);//Part.getDisplayName(part.getPartType()));
html.append("' id='"); //$NON-NLS-1$
html.append(partID);
String userDefinedClass = "";
if ("true".equals(Settings.getInstance().getProperty("servoy.webclient.pushClassToHTMLElement", "false"))) userDefinedClass = (part.getStyleClass() == null
? "" : part.getStyleClass());
html.append("' class='formpart " + userDefinedClass + "'>\n"); //$NON-NLS-1$
TextualStyle partStyle = css.addStyle('#' + partID);
fillPartStyle(partStyle, part);
}
public void renderClosePartHTML(StringBuffer html, Part part)
{
html.append("</div>\n"); //close part div //$NON-NLS-1$
if (part.getPartType() == Part.BODY)
{
html.append("</div>\n"); //close view div //$NON-NLS-1$
}
}
public void renderOpenTableViewHTML(StringBuffer html, TextualCSS css, Part part)
{
TextualStyle wrapperStyle = new TextualStyle();
fillPartStyle(wrapperStyle, part);
html.append("<div servoy:id='View' "); //$NON-NLS-1$
html.append(StripHTMLTagsConverter.convertMediaReferences(wrapperStyle.toString(), solution.getName(), new ResourceReference("media"), "", false).toString());
html.append(">\n"); //$NON-NLS-1$
}
public void renderCloseTableViewHTML(StringBuffer html)
{
html.append("</div>\n"); //$NON-NLS-1$
}
public void fillPartStyle(TextualStyle partStyle, Part part)
{
Pair<IStyleSheet, IStyleRule> pairStyle = ComponentFactory.getStyleForBasicComponent(sp, part, f);
if (pairStyle != null)
{
addBackgroundImageAttributeIfExists(pairStyle.getRight(), partStyle);
}
fillPartBackground(partStyle, part);
if (part.getPartType() == Part.BODY)
{
partStyle.setProperty("overflow-x", getCSSScrolling(f.getScrollbars(), true)); //$NON-NLS-1$
partStyle.setProperty("overflow-y", getCSSScrolling(f.getScrollbars(), false)); //$NON-NLS-1$
}
else
{
partStyle.setProperty("overflow", "hidden"); //$NON-NLS-1$ //$NON-NLS-2$
}
int spaceUsedOnlyInPrintAbove = 0;
int spaceUsedOnlyInPrintBelow = 0;
Iterator<Part> allParts = f.getParts();
while (allParts.hasNext())
{
Part otherPart = allParts.next();
if (Part.rendersOnlyInPrint(otherPart.getPartType()))
{
int otherPartHeight = otherPart.getHeight() - f.getPartStartYPos(otherPart.getID());
if (part.getPartType() > otherPart.getPartType()) spaceUsedOnlyInPrintAbove += otherPartHeight;
if (part.getPartType() < otherPart.getPartType()) spaceUsedOnlyInPrintBelow += otherPartHeight;
}
}
partStyle.setProperty("position", "absolute"); //$NON-NLS-1$ //$NON-NLS-2$
fillPartLayoutCSS(partStyle, part, spaceUsedOnlyInPrintAbove, spaceUsedOnlyInPrintBelow);
if (pairStyle != null && pairStyle.getLeft() != null && pairStyle.getRight() != null)
{
copyBorderAttributes(pairStyle.getRight(), partStyle);
}
}
/**
* @param defaultNavigatorShift the defaultNavigatorShift to set
*/
public void setDefaultNavigatorShift(int defaultNavigatorShift)
{
this.defaultNavigatorShift = defaultNavigatorShift;
}
private void renderNavigator(StringBuffer html, Part bodyPart)
{
if (defaultNavigatorShift != 0)
{
TextualStyle navigatorStyle = new TextualStyle();
fillPartBackground(navigatorStyle, bodyPart);
navigatorStyle.setProperty("overflow", "auto"); //$NON-NLS-1$ //$NON-NLS-2$
navigatorStyle.setProperty("position", "absolute"); //$NON-NLS-1$ //$NON-NLS-2$
fillNavigatorLayoutCSS(navigatorStyle);
html.append("<div servoy:id='default_navigator' "); //$NON-NLS-1$
html.append(navigatorStyle.toString());
html.append("></div>"); //$NON-NLS-1$
}
}
private String buildFormID()
{
return "form_" + ComponentFactory.stripIllegalCSSChars(getFormInstanceName()); //$NON-NLS-1$
}
private void fillPartBackground(TextualStyle partStyle, Part part)
{
Pair<IStyleSheet, IStyleRule> pairStyle = ComponentFactory.getStyleForBasicComponent(sp, part, f);
if (!hasImage || part.getBackground() != null ||
(pairStyle != null && pairStyle.getRight() != null && pairStyle.getRight().hasAttribute("background-color"))) //$NON-NLS-1$
{
String[] cssValues = ComponentFactory.getPartBackgroundCSSDeclarations(sp, part, f);
if (cssValues != null)
{
// for fallback mechanism
partStyle.setProperty("background-color", cssValues, true);
}
else if (part.getBackground() != null && !f.getTransparent())
{
partStyle.setProperty("background-color", PersistHelper.createColorString(part.getBackground())); //$NON-NLS-1$
}
}
}
public static String getCSSScrolling(int scrollBars, boolean horizontal)
{
if (horizontal)
{
String overflowX = "auto"; //$NON-NLS-1$
if ((scrollBars & ISupportScrollbars.HORIZONTAL_SCROLLBAR_NEVER) == ISupportScrollbars.HORIZONTAL_SCROLLBAR_NEVER) overflowX = "hidden"; //$NON-NLS-1$
else if ((scrollBars & ISupportScrollbars.HORIZONTAL_SCROLLBAR_ALWAYS) == ISupportScrollbars.HORIZONTAL_SCROLLBAR_ALWAYS) overflowX = "scroll"; //$NON-NLS-1$
return overflowX;
}
else
{
String overflowY = "auto"; //$NON-NLS-1$
if ((scrollBars & ISupportScrollbars.VERTICAL_SCROLLBAR_NEVER) == ISupportScrollbars.VERTICAL_SCROLLBAR_NEVER) overflowY = "hidden"; //$NON-NLS-1$
else if ((scrollBars & ISupportScrollbars.VERTICAL_SCROLLBAR_ALWAYS) == ISupportScrollbars.VERTICAL_SCROLLBAR_ALWAYS) overflowY = "scroll"; //$NON-NLS-1$
return overflowY;
}
}
protected abstract void fillFormLayoutCSS(TextualStyle formStyle);
protected abstract void fillPartLayoutCSS(TextualStyle partStyle, Part part, int spaceUsedOnlyInPrintAbove, int spaceUsedOnlyInPrintBelow);
protected abstract void fillNavigatorLayoutCSS(TextualStyle navigatorStyle);
}