/* 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.awt.Color; import java.awt.Dimension; import java.awt.Point; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.xhtmlrenderer.css.constants.CSSName; import com.servoy.j2db.ControllerUndoManager; import com.servoy.j2db.IApplication; import com.servoy.j2db.IDataRendererFactory; import com.servoy.j2db.IFormUIInternal; import com.servoy.j2db.IScriptExecuter; import com.servoy.j2db.component.ComponentFactory; import com.servoy.j2db.dataprocessing.IDisplay; import com.servoy.j2db.dataprocessing.IDisplayData; import com.servoy.j2db.dataui.IServoyAwareBean; import com.servoy.j2db.persistence.Bean; import com.servoy.j2db.persistence.Field; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.GraphicalComponent; import com.servoy.j2db.persistence.IDataProviderLookup; import com.servoy.j2db.persistence.IFormElement; import com.servoy.j2db.persistence.ISupportPrinting; import com.servoy.j2db.persistence.ISupportTabSeq; import com.servoy.j2db.persistence.Part; import com.servoy.j2db.persistence.Portal; import com.servoy.j2db.server.headlessclient.TabIndexHelper; import com.servoy.j2db.server.headlessclient.WebForm; import com.servoy.j2db.ui.IComponent; import com.servoy.j2db.ui.IDataRenderer; import com.servoy.j2db.ui.IFieldComponent; import com.servoy.j2db.ui.IFormUI; import com.servoy.j2db.ui.ILabel; import com.servoy.j2db.util.IStyleRule; import com.servoy.j2db.util.IStyleSheet; import com.servoy.j2db.util.ISupplyFocusChildren; import com.servoy.j2db.util.OrientationApplier; import com.servoy.j2db.util.Pair; import com.servoy.j2db.util.ScopesUtils; import com.servoy.j2db.util.TabSequenceHelper; import com.servoy.j2db.util.Utils; /** * The web implementation of the {@link IDataRendererFactory} * * @author jcompagner */ public class WebDataRendererFactory implements IDataRendererFactory<Component> { public static final int CONTAINER_RESERVATION_GAP = 50; public static final int MAXIMUM_TAB_INDEXES_ON_TABLEVIEW = 500; /** * */ public WebDataRendererFactory() { super(); } /* * (non-Javadoc) * * @see com.servoy.j2db.IDataRendererFactory#completeRenderers(com.servoy.j2db.IApplication, com.servoy.j2db.persistence.Form, * com.servoy.j2db.IScriptExecuter, java.util.Map, int, boolean, com.servoy.j2db.ControllerUndoManager) */ public Map completeRenderers(IApplication application, Form form, IScriptExecuter scriptExecuter, Map emptyDataRenderers, int width, boolean printing, ControllerUndoManager undoManager, TabSequenceHelper<Component> tabSequence) throws Exception { int partHeight = 0; Iterator e2 = form.getParts(); while (e2.hasNext()) { Part part = (Part)e2.next(); Color bg = ComponentFactory.getPartBackground(application, part, form); if (bg != null) { Pair<IStyleSheet, IStyleRule> formStyle = ComponentFactory.getCSSPairStyleForForm(application, form); if (formStyle != null && formStyle.getRight() != null && formStyle.getRight().hasAttribute(CSSName.BACKGROUND_IMAGE.toString())) { bg = null; } } if (bg == null && printing) bg = Color.white; IDataRenderer panel = (IDataRenderer)emptyDataRenderers.get(part); if (panel != null) { int total = Math.abs(part.getHeight() - partHeight); setBasicSettings(panel, bg, new Dimension(width, total), new Point(0, partHeight), printing); } partHeight = part.getHeight(); } //place all the elements return placeElements(application, form, scriptExecuter, emptyDataRenderers, printing, undoManager, tabSequence); } protected Map placeElements(IApplication app, Form form, IScriptExecuter listener, Map emptyDataRenderers, boolean printing, ControllerUndoManager undoManager, TabSequenceHelper<Component> tabSequence) throws Exception { final boolean useAJAX = Utils.getAsBoolean(app.getRuntimeProperties().get("useAJAX")); //$NON-NLS-1$ IDataProviderLookup dataProviderLookup = app.getFlattenedSolution().getDataproviderLookup(app.getFoundSetManager(), form); Map listTocomplete = new HashMap(); Map labelForComponents = new HashMap(); String orientation = OrientationApplier.getHTMLContainerOrientation(app.getLocale(), app.getSolution().getTextOrientation()); boolean leftToRight = !"rtl".equalsIgnoreCase(orientation); //$NON-NLS-1$ boolean isAnchoringEnabled = Utils.getAsBoolean(app.getRuntimeProperties().get("enableAnchors")); //$NON-NLS-1$ // Insets insets = new Insets(0, 0, 0, 0); for (IFormElement obj : Utils.iterate(form.getFormElementsSortedByFormIndex())) { Point l = null; l = (obj).getLocation(); if (l == null) continue;//unknown where to add if (printing && obj instanceof ISupportPrinting) { if (!((ISupportPrinting)obj).getPrintable()) continue; } Iterator it = emptyDataRenderers.values().iterator(); while (it.hasNext()) { WebDataRenderer panel = (WebDataRenderer)it.next(); // Border border = panel.getBorder(); // if (border instanceof EmptyBorder) // { // insets = ((EmptyBorder)border).getBorderInsets(); // } int start = panel.getLocation().y; if (l.y >= start && l.y < start + panel.getSize().height) { org.apache.wicket.Component comp = (org.apache.wicket.Component)ComponentFactory.createComponent(app, form, obj, dataProviderLookup, listener, printing); // Test for a visible bean, then get the real component // if (comp instanceof VisibleBean) // { // comp = ((VisibleBean)comp).getDelegate(); // } if (comp != null) { if (obj instanceof Field) { String name = ((Field)obj).getName(); if (name != null && !"".equals(name)) { labelForComponents.put(name, comp); } } else if (obj instanceof GraphicalComponent && (comp instanceof WebBaseLabel || comp instanceof WebBaseSubmitLink)) { String labelFor = ((GraphicalComponent)obj).getLabelFor(); if (labelFor != null && !"".equals(labelFor)) { labelForComponents.put(comp, labelFor); } } if ((obj instanceof ISupportTabSeq) && (tabSequence != null)) { tabSequence.add(panel, (ISupportTabSeq)obj, comp); } org.apache.wicket.Component newComp = comp; if (newComp instanceof IDisplay) { panel.addDisplayComponent(obj, (IDisplay)newComp); } else if (newComp instanceof WebImageBeanHolder) { WebImageBeanHolder wiBeanHolder = (WebImageBeanHolder)newComp; Object bean = wiBeanHolder.getDelegate(); if (bean instanceof IServoyAwareBean) { IServoyAwareBean ourBean = (IServoyAwareBean)bean; panel.addDisplayComponent(obj, ourBean); } } ((IComponent)comp).setLocation(new Point((l.x /* +insets.left */), (l.y - start))); if (form.getOnRecordEditStartMethodID() > 0 && comp instanceof IFieldComponent) { if (useAJAX && comp instanceof IDisplayData && ((IDisplayData)comp).getDataProviderID() != null && !ScopesUtils.isVariableScope(((IDisplayData)comp).getDataProviderID())) { StartEditOnFocusGainedEventBehavior.addNewBehaviour(comp); } } // For some components, if anchoring is enabled, we need to add a wrapper <div> for anchoring to work: // - some of the fields // - buttons // - beans if (isAnchoringEnabled && (((obj instanceof Field) && WebAnchoringHelper.needsWrapperDivForAnchoring((Field)obj)) || (obj instanceof Bean) || ((obj instanceof GraphicalComponent) && ComponentFactory.isButton((GraphicalComponent)obj)))) { panel.add(WebAnchoringHelper.getWrapperComponent(comp, obj, start, panel.getSize(), leftToRight, false)); } else { panel.add(comp); } } } } } Iterator it = labelForComponents.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Entry)it.next(); Object key = entry.getKey(); if (key instanceof WebBaseLabel || key instanceof WebBaseSubmitLink) { IFieldComponent component = (IFieldComponent)labelForComponents.get(entry.getValue()); if (component != null) { if (key instanceof WebBaseLabel) { ((WebBaseLabel)entry.getKey()).setLabelFor(component); } else { ((WebBaseSubmitLink)entry.getKey()).setLabelFor(component); } (component).addLabelFor((ILabel)entry.getKey()); if (!component.isVisible()) { component.setComponentVisible(component.isVisible()); } if (!component.isEnabled()) { component.setComponentEnabled(component.isEnabled()); } } } } it = emptyDataRenderers.values().iterator(); while (it.hasNext()) { WebDataRenderer panel = (WebDataRenderer)it.next(); panel.createDataAdapter(app, dataProviderLookup, listener, undoManager); } return listTocomplete; } @Override public void prepareRenderers(IApplication application, Form form) { } private void setBasicSettings(IDataRenderer dr, Color bg, Dimension size, Point location, boolean printing) { if (dr != null) { dr.setBackground(bg); // printing is not possible in web // if (printing) // { // dr.setLayout(new SpringLayout()); // } // else // { // dr.setLayout(new AnchorLayout(size)); // } dr.setSize(size); dr.setLocation(location); } } /* * (non-Javadoc) * * @see com.servoy.j2db.IDataRendererFactory#getEmptyDataRenderer(java.lang.String, com.servoy.j2db.IApplication) */ public IDataRenderer getEmptyDataRenderer(String id, String name, IApplication application, boolean showSelection) { return new WebDataRenderer(id, name, application); } /* * (non-Javadoc) * * @see com.servoy.j2db.IDataRendererFactory#createPortalRenderer(com.servoy.j2db.IApplication, com.servoy.j2db.persistence.Portal, * com.servoy.j2db.persistence.Form, com.servoy.j2db.IScriptExecuter, boolean, com.servoy.j2db.ControllerUndoManager) */ public IDataRenderer createPortalRenderer(IApplication app, Portal meta, Form form, IScriptExecuter el, boolean printing, ControllerUndoManager undoManager) throws Exception { return null; } public void extendTabSequence(List<Component> tabSequence, IFormUIInternal containerImpl) { WebForm wf = (WebForm)containerImpl; ISupplyFocusChildren<Component> defaultNav = wf.getDefaultNavigator(); if (defaultNav != null) { Component[] fchilds = defaultNav.getFocusChildren(); for (Component element : fchilds) tabSequence.add(element); } } public void applyTabSequence(List<Component> tabSequence, IFormUIInternal containerImpl) { WebForm wf = (WebForm)containerImpl; wf.setTabSeqComponents(tabSequence); } private int goDownContainer(IWebFormContainer wtp, int goodTabIndex) { int localTabIndex = goodTabIndex; wtp.setTabSequenceIndex(goodTabIndex >= 0 ? localTabIndex++ : ISupportWebTabSeq.SKIP); TabIndexHelper.setUpTabIndexAttributeModifier((Component)wtp, ISupportWebTabSeq.SKIP); IFormUI[] forms = wtp.getChildForms(); if (forms != null) { for (IFormUI form : forms) { if (form != null) localTabIndex = goDownForm((WebForm)form, goodTabIndex >= 0 ? localTabIndex : ISupportWebTabSeq.SKIP); } } if (goodTabIndex >= 0) { localTabIndex = getContainerGapIndex(localTabIndex, goodTabIndex); } return localTabIndex; } private int goDownForm(WebForm wf, int goodTabIndex) { int localTabIndex = goodTabIndex; List<Component> tabSeq = wf.getTabSeqComponents(); Iterator<Component> it = tabSeq.iterator(); while (it.hasNext()) { Component oo = it.next(); if (oo instanceof IWebFormContainer) { localTabIndex = goDownContainer((IWebFormContainer)oo, goodTabIndex >= 0 ? localTabIndex : ISupportWebTabSeq.SKIP); } else if (oo instanceof WebCellBasedView) { WebCellBasedView tableView = (WebCellBasedView)oo; tableView.setTabSequenceIndex(goodTabIndex >= 0 ? localTabIndex : ISupportWebTabSeq.SKIP); localTabIndex += MAXIMUM_TAB_INDEXES_ON_TABLEVIEW; TabIndexHelper.setUpTabIndexAttributeModifier(oo, ISupportWebTabSeq.SKIP); } else { if (goodTabIndex >= 0) { localTabIndex = setMaxTabIndex(oo, localTabIndex); } else { TabIndexHelper.setUpTabIndexAttributeModifier(oo, ISupportWebTabSeq.SKIP); } } } return localTabIndex; } private int setMaxTabIndex(Component comp, int newTabindex) { int oldTabIndex = TabIndexHelper.getTabIndex(comp); int maxIndex = Math.max(newTabindex, oldTabIndex); TabIndexHelper.setUpTabIndexAttributeModifier(comp, maxIndex); return ++maxIndex; } /** * This method is the real tabIndex calculator for all forms in containers. When a container form is made visible, tabIndexes must be recalculated. * Having the tab sequence tree, the tab sequence order is the preorder traversal of the tree. All elements after modified container in tab sequence order will be recalculated. * A small gap for each container is used, so that chances that tabIndex should be changed when visible tab is different are smaller. * Browsers cannot deal with really big numbers of tabIndex (maximum is around 32k). WebCellBasedView is also a special case, some bigger gap is used for that (it will fill tabIndexes itself). */ public void reapplyTabSequence(IFormUIInternal containerImpl, int delta) { WebForm wf = (WebForm)containerImpl; WebForm currentForm = wf; IWebFormContainer currentTabPanel = null; boolean ready = false; int counter = delta + 1; do { List<Component> existingTabSequence = currentForm.getTabSeqComponents(); Iterator<Component> iter = existingTabSequence.iterator(); if (!containerImpl.equals(currentForm)) { while (iter.hasNext()) { Component c = iter.next(); if (c.equals(currentTabPanel)) { if (delta >= 0) { counter = getContainerGapIndex(counter, currentTabPanel.getTabSequenceIndex()); } break; } } } while (iter.hasNext()) { Component comp = iter.next(); if (comp instanceof IWebFormContainer) { counter = goDownContainer((IWebFormContainer)comp, delta >= 0 ? counter : ISupportWebTabSeq.SKIP); } else if (comp instanceof WebCellBasedView) { WebCellBasedView tableView = (WebCellBasedView)comp; tableView.setTabSequenceIndex(delta >= 0 ? counter : ISupportWebTabSeq.SKIP); counter += MAXIMUM_TAB_INDEXES_ON_TABLEVIEW; TabIndexHelper.setUpTabIndexAttributeModifier(comp, ISupportWebTabSeq.SKIP); } else { if (delta >= 0) { counter = setMaxTabIndex(comp, counter); } else { TabIndexHelper.setUpTabIndexAttributeModifier(comp, ISupportWebTabSeq.SKIP); } } } if (delta >= 0) { counter = getContainerGapIndex(counter, delta); } MarkupContainer parent = currentForm.getParent(); while ((parent != null) && !(parent instanceof IWebFormContainer)) parent = parent.getParent(); if (parent != null) { if (parent instanceof WebSplitPane) { ((WebSplitPane)parent).setFormLastTabIndex(currentForm, counter - 1); } currentTabPanel = (IWebFormContainer)parent; MarkupContainer tabParent = ((Component)currentTabPanel).getParent(); while ((tabParent != null) && !(tabParent instanceof WebForm)) tabParent = tabParent.getParent(); if (tabParent != null) { currentForm = (WebForm)tabParent; } else { ready = true; } } else { ready = true; } } while (!ready); } public static int getContainerGapIndex(int realIndex, int offsetIndex) { return Math.max(offsetIndex + CONTAINER_RESERVATION_GAP, realIndex); } }