/*
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;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.Window;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.border.Border;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.JavaMembers;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import com.servoy.base.persistence.constants.IFormConstants;
import com.servoy.base.util.ITagResolver;
import com.servoy.j2db.cmd.ICmdManagerInternal;
import com.servoy.j2db.component.ComponentFactory;
import com.servoy.j2db.dataprocessing.DataAdapterList;
import com.servoy.j2db.dataprocessing.IDisplay;
import com.servoy.j2db.dataprocessing.IFoundSet;
import com.servoy.j2db.dataprocessing.IRecordInternal;
import com.servoy.j2db.dataprocessing.ISaveConstants;
import com.servoy.j2db.dataprocessing.PrototypeState;
import com.servoy.j2db.dataprocessing.TagResolver;
import com.servoy.j2db.persistence.FlattenedForm;
import com.servoy.j2db.persistence.Form;
import com.servoy.j2db.persistence.IRepository;
import com.servoy.j2db.persistence.IScriptProvider;
import com.servoy.j2db.persistence.ISupportScriptProviders;
import com.servoy.j2db.persistence.Part;
import com.servoy.j2db.persistence.ScriptMethod;
import com.servoy.j2db.persistence.ScriptVariable;
import com.servoy.j2db.persistence.StaticContentSpecLoader;
import com.servoy.j2db.scripting.ElementScope;
import com.servoy.j2db.scripting.IScriptSupport;
import com.servoy.j2db.scripting.IScriptable;
import com.servoy.j2db.scripting.IScriptableProvider;
import com.servoy.j2db.scripting.JSApplication.FormAndComponent;
import com.servoy.j2db.scripting.JSEvent;
import com.servoy.j2db.scripting.ScriptObjectRegistry;
import com.servoy.j2db.ui.IComponent;
import com.servoy.j2db.ui.IDataRenderer;
import com.servoy.j2db.ui.ISupportRowStyling;
import com.servoy.j2db.ui.ISupportSecuritySettings;
import com.servoy.j2db.util.ComponentFactoryHelper;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.IStyleRule;
import com.servoy.j2db.util.IStyleSheet;
import com.servoy.j2db.util.Pair;
import com.servoy.j2db.util.ServoyException;
import com.servoy.j2db.util.TabSequenceHelper;
import com.servoy.j2db.util.Utils;
/**
* Representation of a form
*
* @author jblok, jcompagner
*/
public class FormController extends BasicFormController
{
/**
* @author Ovidiu
* this class is an implementations of ISupportSCriptProviders Which always resolves methods and variables based on the Runtime cached FlattenedForm
*/
public static class RuntimeSupportScriptProviders implements ISupportScriptProviders
{
private IApplication application = null;
private Form form = null;
public RuntimeSupportScriptProviders(IApplication application, Form form)
{
this.application = application;
this.form = form;
}
public Iterator<ScriptVariable> getScriptVariables(boolean b)
{
return form.getScriptVariables(b);
}
public Iterator< ? extends IScriptProvider> getScriptMethods(boolean sort)
{
return form.getScriptMethods(sort);
}
public ScriptMethod getScriptMethod(int methodId)
{
return form.getScriptMethod(methodId);
}
/**
* When a JSform changed it first creates a copy before changing . It then uses that copy throughout it's life for any new modifications
* This method is used for updating the scriptLookup with the correct copy .
* For example calling getScriptVariables will return the correct variable list.
*/
public void updateProviderwithCopy(Form originalForm, Form copyForm)
{
if (this.form.getName().equals(originalForm.getName()))
{
if (this.form instanceof FlattenedForm)
{
this.form = application.getFlattenedSolution().getFlattenedForm(copyForm, true);
}
else
{
this.form = copyForm;
}
}
else if (this.form instanceof FlattenedForm)
{
this.form = application.getFlattenedSolution().getFlattenedForm(this.form, true);
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "RSSP[" + form.getName() + "]";
}
}
/*
* _____________________________________________________________ Declaration of attributes
*/
//see IFormConstants for more
public static final int TABLE_VIEW = IFormConstants.VIEW_TYPE_TABLE;
public static final int LOCKED_TABLE_VIEW = IFormConstants.VIEW_TYPE_TABLE_LOCKED;
public static final int LOCKED_LIST_VIEW = IFormConstants.VIEW_TYPE_LIST_LOCKED;
public static final int FORM_EDITOR = Part.BODY;
public static final int FORM_RENDERER = 0;
private int currentViewType = -1;
private IView view; //shows data (trough renderer(s))
private final IDataRenderer[] dataRenderers = new IDataRenderer[Part.PART_ARRAY_SIZE]; //0 position == body_renderer
private Color bgColor = null;
/**
* Some JavaScript related instances
*/
private FormManager fm;
private final IFormUIInternal containerImpl;
/**
* Holds for each data renderer a map with <ISupportTabSeq, Component>. It is recreated each time createDataRenderers() is called. It is needed for setting
* the tab sequence in setView().
*/
private final TabSequenceHelper< ? > tabSequence;
private DesignModeCallbacks designMode;
public FormController(IApplication app, Form form, String namedInstance)
{
super(app, form, namedInstance);
initStyles();
fm = (FormManager)application.getFormManager();
scriptExecuter = new ScriptExecuter(this);
containerImpl = fm.getFormUI(this);
tabSequence = new TabSequenceHelper(containerImpl, application.getDataRenderFactory());
app.getFlattenedSolution().registerLiveForm(form, namedInstance);
}
/*
* (non-Javadoc)
*
* @see com.servoy.j2db.BasicFormController#getBasicFormManager()
*/
@Override
public IBasicFormManager getBasicFormManager()
{
return fm;
}
/**
* @return
*/
@Override
public boolean getDesignMode()
{
return designMode != null;
}
private int previousType = -1;
/**
* @param mode
* @param controllerForm
*/
@Override
public void setDesignMode(DesignModeCallbacks callback)
{
this.designMode = callback;
if (callback != null)
{
application.getFlattenedSolution().setInDesign(form);
if (currentViewType == LOCKED_TABLE_VIEW || currentViewType == LIST_VIEW || currentViewType == LOCKED_LIST_VIEW)
{
previousType = currentViewType;
currentViewType = RECORD_VIEW;
recreateUI();
}
else
{
getFormUI().setDesignMode(callback);
}
}
else
{
application.getFlattenedSolution().setInDesign(null);
if (previousType != -1)
{
currentViewType = previousType;
recreateUI();
previousType = -1;
}
else
{
getFormUI().setDesignMode(callback);
}
}
}
private IStyleSheet stylesheet = null;
private IStyleRule styleRule = null, styleOdd = null, styleEven = null, styleSelected = null, styleHeader = null;
private IStyleRule bodyRule = null;
@Override
public void init()
{
initBorder();
try
{
//create and fill all needed panels
createDataRenderers(form.getView());
super.init();
}
catch (Exception ex)
{
application.reportError(application.getI18NMessage("servoy.formPanel.error.setupForm") + ": " + getName(), ex); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private void initStylesAndBorder()
{
initStyles();
initBorder();
}
private void initBorder()
{
Border border = ComponentFactoryHelper.createBorder(form.getBorderType());
if (stylesheet != null && styleRule != null)
{
if (border == null)
{
border = stylesheet.getBorder(styleRule);
}
bgColor = stylesheet.getBackground(styleRule);
if (bgColor != null)
{
containerImpl.setBackground(bgColor);
}
}
if (border != null)
{
containerImpl.setBorder(border);
}
}
private void initStyles()
{
Pair<IStyleSheet, IStyleRule> pairStyle = ComponentFactory.getCSSPairStyleForForm(application, form);
if (pairStyle != null)
{
stylesheet = pairStyle.getLeft();
}
else
{
stylesheet = null;
}
if (stylesheet != null)
{
String lookupname = "form"; //$NON-NLS-1$
if (form.getStyleClass() != null && !"".equals(form.getStyleClass())) //$NON-NLS-1$
{
String formStyleClass = form.getStyleClass();
lookupname += '.' + formStyleClass;
}
styleRule = pairStyle.getRight();
styleOdd = stylesheet.getCSSRule(lookupname + " " + ISupportRowStyling.CLASS_ODD); //$NON-NLS-1$
styleEven = stylesheet.getCSSRule(lookupname + " " + ISupportRowStyling.CLASS_EVEN); //$NON-NLS-1$
styleSelected = stylesheet.getCSSRule(lookupname + " " + ISupportRowStyling.CLASS_SELECTED); //$NON-NLS-1$
styleHeader = stylesheet.getCSSRule(lookupname + " " + ISupportRowStyling.CLASS_HEADER); //$NON-NLS-1$
}
}
@Override
public boolean recreateUI()
{
// should return false if executing from the form's execute function but
// can happen if it is a none component thing
// so no onfocus/ondatachange/onaction
// of a component on the form but
// onshow/onload that should be allowed
//if (isFormExecutingFunction()) return false;
// hide all visible children; here is an example that explains why it's needed:
// parent form has tabpanel with child1 and child2; child2 is visible (second tab)
// if you recreateUI on parent, child1 would turn out visible after recreateUI without and hide event on child2 if we wouldn't do the notifyVisible below;
// but also when you would afterwards change tab to child2 it's onShow won't be called because it thinks it's still visible which is strange;
List<Runnable> ilr = new ArrayList<Runnable>();
notifyVisibleOnChildren(false, ilr);
Utils.invokeLater(application, ilr);
getFormUI().touch();
Rectangle scrollPosition = null;
if (view != null) scrollPosition = view.getVisibleRect();
getFormUI().setDesignMode(null);
Form f = application.getFlattenedSolution().getForm(form.getName());
form = application.getFlattenedSolution().getFlattenedForm(f);
initStylesAndBorder();
int v = currentViewType;
currentViewType = -1;
setView(v);
if (isFormVisible)
{
// first push the latest data in the regenerated ui.
valueChanged(null);
List<Runnable> invokeLaterRunnables = new ArrayList<Runnable>();
IDataRenderer[] array = getDataRenderers();
for (IDataRenderer dataRenderer : array)
{
if (dataRenderer != null)
{
dataRenderer.notifyVisible(true, invokeLaterRunnables);
}
}
Utils.invokeLater(application, invokeLaterRunnables);
}
if (designMode != null)
{
application.invokeLater(new Runnable()
{
public void run()
{
getFormUI().setDesignMode(designMode);
}
});
}
getFormUI().uiRecreated();
// make sure this form is seen as new.
application.getFlattenedSolution().deregisterLiveForm(form, namedInstance);
application.getFlattenedSolution().registerLiveForm(form, namedInstance);
if (view != null && scrollPosition != null) view.setVisibleRect(scrollPosition);
return true;
}
private void createDataRenderers(int viewType) throws Exception
{
int v = viewType;
if (getDataSource() == null)
{
v = LOCKED_RECORD_VIEW; // form not based on any datasource
}
application.getDataRenderFactory().prepareRenderers(application, form);
Part bodyPart = null;
Map<Part, IDataRenderer> part_panels = new LinkedHashMap<Part, IDataRenderer>();
Iterator<Part> e2 = form.getParts();
while (e2.hasNext())
{
Part part = e2.next();
Color partColor = ComponentFactory.getPartBackground(application, part, form);
//extract the body (bgcolor)
if (part.getPartType() == Part.BODY)
{
bodyPart = part;
bgColor = partColor;
Pair<IStyleSheet, IStyleRule> partStyle = ComponentFactory.getStyleForBasicComponent(application, part, form);
bodyRule = partStyle != null ? partStyle.getRight() : null;
}
if (part.getPartType() == Part.BODY && v == FormController.LOCKED_TABLE_VIEW)
{
continue;//don't create part, view == part
}
IDataRenderer dr = application.getDataRenderFactory().getEmptyDataRenderer(ComponentFactory.getWebID(form, part), part.toString(), application,
(part.getPartType() == Part.BODY));
dr.initDragNDrop(this, form.getPartStartYPos(part.getID()));
dataRenderers[part.getPartType()] = dr;
dr.setName(part.toString());
part_panels.put(part, dr);
if (part.getPartType() == Part.BODY)
{
int onRenderMethodID = form.getOnRenderMethodID();
if (onRenderMethodID > 0)
{
dr.getOnRenderComponent().getRenderEventExecutor().setRenderCallback(Integer.toString(onRenderMethodID),
Utils.parseJSExpressions(form.getInstanceMethodArguments("onRenderMethodID")));
dr.getOnRenderComponent().getRenderEventExecutor().setRenderScriptExecuter(getScriptExecuter());
}
}
dr.setBackground(partColor);
}
tabSequence.clear();
application.getDataRenderFactory().completeRenderers(application, form, scriptExecuter, part_panels, form.getSize().width, false,
containerImpl.getUndoManager(), tabSequence);
if (bodyPart != null && (v == FormController.LOCKED_LIST_VIEW || v == IForm.LIST_VIEW))
{
IDataRenderer dr = application.getDataRenderFactory().getEmptyDataRenderer(ComponentFactory.getWebID(form, bodyPart), bodyPart.toString(),
application, true);
int onRenderMethodID = form.getOnRenderMethodID();
if (onRenderMethodID > 0)
{
dr.getOnRenderComponent().getRenderEventExecutor().setRenderCallback(Integer.toString(onRenderMethodID),
Utils.parseJSExpressions(form.getInstanceMethodArguments("onRenderMethodID")));
dr.getOnRenderComponent().getRenderEventExecutor().setRenderScriptExecuter(getScriptExecuter());
}
//apply bgcolor to renderer
if (bgColor != null)
{
dr.setBackground(bgColor);
}
dataRenderers[FORM_RENDERER] = dr;
dr.setName(bodyPart.toString());
part_panels = new LinkedHashMap<Part, IDataRenderer>();
part_panels.put(bodyPart, dr);
application.getDataRenderFactory().completeRenderers(application, form, scriptExecuter, part_panels, form.getSize().width, false,
containerImpl.getUndoManager(), null);
}
//apply security
int access = application.getFlattenedSolution().getSecurityAccess(form.getUUID());
if (access != -1)
{
boolean b_accessible = ((access & IRepository.ACCESSIBLE) != 0);
if (!b_accessible)
{
for (IDataRenderer dataRenderer : dataRenderers)
{
if (dataRenderer != null)
{
Iterator< ? extends IComponent> componentIterator = dataRenderer.getComponentIterator();
while (componentIterator.hasNext())
{
IComponent c = componentIterator.next();
if (c instanceof ISupportSecuritySettings)
{
if (!b_accessible) ((ISupportSecuritySettings)c).setAccessible(b_accessible);
}
}
}
}
}
}
}
/**
* @return IDataRenderer[]
*/
public IDataRenderer[] getDataRenderers()
{
return dataRenderers;
}
public ITagResolver getTagResolver()
{
for (IDataRenderer element : dataRenderers)
{
if (element != null)
{
return element.getDataAdapterList();
}
}
IRecordInternal state = null;
if (formModel != null)
{
int index = formModel.getSelectedIndex();
if (index != -1)
{
state = formModel.getRecord(index);
}
if (state == null)
{
state = formModel.getPrototypeState();
}
}
else
{
state = new PrototypeState(null);
}
return TagResolver.createResolver(state);
}
/*
* _____________________________________________________________ The methods below belong to interface IDataManipulator
*/
@Override
public ControllerUndoManager getUndoManager()
{
return containerImpl.getUndoManager();
}
@Override
public void destroy()
{
try
{
if (this.designMode != null)
{
setDesignMode(null);
}
containerImpl.destroy();
if (fm != null) fm.removeFormController(this);
fm = null;
unload();
if (view != null) //it may not yet exist
{
try
{
view.destroy();
view = null;
}
catch (Exception e)
{
Debug.error(e);
}
}
deleteRenderers();
hmChildrenJavaMembers = null;
if (scriptExecuter != null)
{
scriptExecuter.destroy();
}
scriptExecuter = null;
super.destroy();
}
catch (Exception e)
{
Debug.error("Destroy error", e); //$NON-NLS-1$
}
finally
{
application.getFlattenedSolution().deregisterLiveForm(form, namedInstance);
}
}
private void deleteRenderers()
{
for (int i = 0; i < dataRenderers.length; i++)
{
if (dataRenderers[i] != null)
{
try
{
dataRenderers[i].destroy();
}
catch (Exception e)
{
Debug.error(e);
}
dataRenderers[i] = null;
}
}
}
@Override
protected void focusFirstField()
{
focusField(null, false);
}
@Override
protected void focusField(String fieldName, final boolean skipReadonly)
{
final Object field = tabSequence.getComponentForFocus(fieldName, skipReadonly);
if (field != null)
{
// do it with invoke later so that other focus events are overridden.
application.invokeLater(new Runnable()
{
public void run()
{
containerImpl.focusField(field);
}
});
}
}
/*
* @see com.servoy.j2db.IForm#refreshView()
*/
public void refreshView()
{
if (formModel != null)
{
int selected[] = null;
if (getView() == RECORD_VIEW || getView() == LOCKED_RECORD_VIEW)
{
int selIdx = formModel.getSelectedIndex();
if (selIdx != -1) selected = new int[] { selIdx };
}
else
{
selected = formModel.getSelectedIndexes();
}
if (selected != null && selected.length > 0)
{
IRecordInternal[] row = new IRecordInternal[selected.length];
for (int i = 0; i < selected.length; i++)
row[i] = formModel.getRecord(selected[i]);
refreshAllPartRenderers(row);
}
else
{
refreshAllPartRenderers(null);
}
if (view != null && view.isDisplayingMoreThanOneRecord())
{
formModel.fireFoundSetChanged();
}
}
}
@Override
protected void refreshAllPartRenderers(IRecordInternal[] records)
{
if (!isFormVisible || application.isShutDown()) return;
// don't do anything yet when there are records but the selection is invalid
if (formModel.getSize() > 0 && (formModel.getSelectedIndex() < 0 || formModel.getSelectedIndex() >= formModel.getSize())) return;
// let the ui know that it will be touched, so that locks can be taken if needed.
getFormUI().touch();
boolean executeOnRecordSelect = false;
IRecordInternal[] state = records;
if (state == null)
{
if (formModel != null)
{
state = new IRecordInternal[] { formModel.getPrototypeState() };
}
else
{
state = new IRecordInternal[] { new PrototypeState(null) };
}
}
if (dataRenderers[FORM_EDITOR] != null && !(records == null && formModel != null && formModel.getRawSize() > 0) && isStateChanged(state))
{
lastState = state;
executeOnRecordSelect = true;
}
for (int i = FORM_RENDERER + 1; i < dataRenderers.length; i++)
{
IDataRenderer dataRenderer = dataRenderers[i];
if (dataRenderer != null)
{
for (IRecordInternal r : state)
dataRenderer.refreshRecord(r);
}
}
if (executeOnRecordSelect)
{
// do this at the end because dataRenderer.refreshRecord(state) will update selection
// for related tabs - and we should execute js code after they have been updated
executeOnRecordSelect();
}
}
/*
* (non-Javadoc)
*
* @see javax.swing.JComponent#requestFocus()
*/
public void requestFocus()
{
if (containerImpl instanceof JComponent)
{
JComponent sf = (JComponent)containerImpl; // swingForm, TODO: remove dependency on smart client
sf.requestFocus();
}
else if (view != null)
{
view.requestFocus();
}
}
@Override
public boolean stopUIEditing(boolean looseFocus)
{
if (view != null && !view.stopUIEditing(looseFocus)) return false; //controller stops editor
IDataRenderer[] array = getDataRenderers();
for (int i = 0; i < array.length; i++)
{
IDataRenderer dataRenderer = array[i];
// skip form editor because that is already done by the view.stopUIEditing()
if (dataRenderer != null && (FormController.FORM_EDITOR != i || view == null))
{
if (!dataRenderer.stopUIEditing(looseFocus)) return false;
}
}
if (looseFocus && form.getOnRecordEditStopMethodID() != 0)
{
//allow beans to store there data via method
IRecordInternal[] records = getApplication().getFoundSetManager().getEditRecordList().getUnmarkedEditedRecords(formModel);
for (IRecordInternal element : records)
{
boolean b = executeOnRecordEditStop(element);
if (!b) return false;
}
}
return true;
}
/*
* _____________________________________________________________ The methods below override methods from superclass <classname>
*/
//this method first overloaded setVisible but setVisible is not always called and had differences between jdks
@Override
public boolean notifyVisible(boolean visible, List<Runnable> invokeLaterRunnables)
{
if (isFormVisible == visible || executingOnLoad) return true;
if (formModel == null)
{
isFormVisible = visible;
return true;
}
boolean notifyVisible = super.notifyVisible(visible, invokeLaterRunnables);
if (notifyVisible)
{
notifyVisibleOnChildren(visible, invokeLaterRunnables);
}
return notifyVisible;
}
private void notifyVisibleOnChildren(boolean visible, List<Runnable> invokeLaterRunnables)
{
IDataRenderer[] array = getDataRenderers();
for (IDataRenderer dataRenderer : array)
{
if (dataRenderer != null)
{
dataRenderer.notifyVisible(visible, invokeLaterRunnables);
}
}
}
/*
* _____________________________________________________________ The methods below belong to this class
*/
@Override
public void propagateFindMode(boolean findMode)
{
if (!findMode)
{
application.getFoundSetManager().getEditRecordList().prepareForSave(true);
}
if (isReadOnly())
{
if (view != null)
{
view.setEditable(findMode);
}
}
for (IDataRenderer dataRenderer : dataRenderers)
{
if (dataRenderer != null)
{
// if (i == FORM_RENDERER || i == FORM_EDITOR)
// {
// if the data renderer itself is a IDisplay then go through that one
// see web cell based view that is used as a tableview and portal.
if (dataRenderer instanceof IDisplay)
{
((IDisplay)dataRenderer).setValidationEnabled(!findMode);
}
else
{
DataAdapterList dal = dataRenderer.getDataAdapterList();
dal.setFindMode(findMode);//disables related data en does getText instead if getValue on fields
dataRenderer.setAllNonFieldsEnabled(!findMode);
}
// }
// else
// {
// //dataRenderers[i].setUIEnabled(!findMode);
// }
}
}
}
@Override
public int getView()
{
return currentViewType;
}
@Override
public IView getViewComponent()
{
return view;
}
//set the view (record,list,table)
@Override
public void setView(int v)
{
int viewType = v;
if (viewType == -1)
{
viewType = getForm().getView();
}
if (viewType == currentViewType)
{
return;//no change
}
if (currentViewType == LOCKED_TABLE_VIEW || currentViewType == LOCKED_LIST_VIEW || currentViewType == LOCKED_RECORD_VIEW)
{
return; // don't let user change
}
currentViewType = viewType;
// always synch the view menu with this call
if (application.getFormManager() instanceof IFormManagerInternal)
{
IBasicFormManager sfm = application.getFormManager();
if (sfm.getCurrentMainShowingFormController() == this)
{
((IFormManagerInternal)sfm).synchViewMenu(viewType);
}
}
boolean formReadOnly = ((FormManager)application.getFormManager()).isFormReadOnly(getName());
//uninstall old view
if (view != null)
{
// if the form manager says this is readonly or the container is currently in readonly
// do revert it here else the readonly flag from the form manager and the containre itself are out of sync
// with the elements that are now created again.
if (formReadOnly || containerImpl.isReadOnly())
{
containerImpl.setReadOnly(false);
}
view.stop();
view = null;
deleteRenderers();
try
{
createDataRenderers(viewType);
}
catch (Exception e)
{
application.reportError(application.getI18NMessage("servoy.formPanel.error.setupForm") + ": " + getName(), e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (bgColor != null)
{
containerImpl.setBackground(bgColor);
}
view = containerImpl.initView(application, this, viewType);
// Do tab sequencing now.
tabSequence.fromAbstractToNamed();
view.start(application);
// make sure that the formscope is created to insert the elements in it.
getFormScope();
if (formScope != null)
{
hmChildrenJavaMembers = new HashMap<String, Object[]>();
Scriptable scope = getFormUI().makeElementsScriptObject(formScope, hmChildrenJavaMembers, dataRenderers, view);
formScope.putWithoutFireChange("elements", scope); //$NON-NLS-1$
}
if (isFormVisible)
{
view.setModel(formModel);
}
view.setRowBGColorScript(form.getRowBGColorCalculation(), form.getInstanceMethodArguments("rowBGColorCalculation")); //$NON-NLS-1$
if (view instanceof ISupportRowStyling) ((ISupportRowStyling)view).setRowStyles(stylesheet, styleOdd, styleEven, styleSelected, styleHeader);
if (formReadOnly)
{
containerImpl.setReadOnly(true);
}
}
@Override
public void showNavigator(List<Runnable> invokeLaterRunnables)
{
if (fm != null && fm.getCurrentMainShowingFormController() == this)//safety for tabpanels (those cannot have a custom controller)
{
ISupportNavigator navigatorSupport = fm.getCurrentContainer();
if (navigatorSupport != null)
{
int form_id = form.getNavigatorID();
if (form_id > 0)
{
FormController currentFC = navigatorSupport.getNavigator();
if (currentFC == null || currentFC.getForm().getID() != form_id)//is already there
{
try
{
Form sliderDef = application.getFlattenedSolution().getForm(form_id);
if (sliderDef != null)
{
if (sliderDef.getID() != form_id && sliderDef.getNavigatorID() >= 0) return;//safety a slider Form CANNOT HAVE A SLIDER
FormController nav = fm.getFormController(sliderDef.getName(), navigatorSupport);
ControllerUndoManager cum = null;
if (nav != null)
{
nav.notifyVisible(true, invokeLaterRunnables);
cum = nav.getUndoManager();
}
if (application.getCmdManager() instanceof ICmdManagerInternal)
{
((ICmdManagerInternal)application.getCmdManager()).setControllerUndoManager(cum);
}
FormController old_nav = navigatorSupport.setNavigator(nav);
if (old_nav != null) old_nav.notifyVisible(false, invokeLaterRunnables);
}
else
{
// Form deleted??
FormController old_nav = navigatorSupport.setNavigator(null);
if (old_nav != null) old_nav.notifyVisible(false, invokeLaterRunnables);
}
}
catch (Exception ex)
{
Debug.error(ex);
}
}
else
{
// Try to lease it extra so it will be added to last used screens.
Form sliderDef = application.getFlattenedSolution().getForm(form_id);
if (sliderDef != null)
{
fm.leaseFormPanel(sliderDef.getName());
currentFC.notifyVisible(true, invokeLaterRunnables);
}
}
}
else if (form_id != Form.NAVIGATOR_IGNORE)//if is ignore leave previous,if not remove
{
FormController old_nav = navigatorSupport.setNavigator(null);
if (old_nav != null) old_nav.notifyVisible(false, invokeLaterRunnables);
}
}
}
}
private ScriptExecuter scriptExecuter;
private static class ScriptExecuter implements IScriptExecuter//this class is made only when swing has a mem leak we don't have one
{
private FormController delegate;
ScriptExecuter(FormController fp)
{
delegate = fp;
}
public Object executeFunction(String cmd, Object[] args, boolean saveData, Object src, boolean focusEvent, String methodKey,
boolean executeWhenFieldValidationFailed)
{
if (delegate != null) return delegate.executeFunction(cmd, args, saveData, src, focusEvent, methodKey, true, executeWhenFieldValidationFailed);
return null;
}
public void setLastKeyModifiers(int modifiers)
{
if (delegate != null)
{
delegate.setLastKeyModifiers(modifiers);
}
}
public FormController getFormController()
{
return delegate;
}
public void destroy()
{
delegate = null;
}
}
/*
* _____________________________________________________________ Java script methods/implementation
*/
public void setLastKeyModifiers(int modifiers)
{
((IScriptSupport)application.getScriptEngine()).setLastKeyModifiers(modifiers);
}
@Override
public void setReadOnly(boolean b)
{
if (b) stopUIEditing(true);
containerImpl.setReadOnly(b);
application.getFormManager().setFormReadOnly(getName(), b);
if (b && containerImpl.getUndoManager() != null) containerImpl.getUndoManager().discardAllEdits();
}
@Override
public void setComponentEnabled(boolean b)
{
containerImpl.setComponentEnabled(b);
application.getFormManager().setFormEnabled(getName(), b);
}
@Override
public int getPartYOffset(int partType)
{
IDataRenderer[] renderers = getDataRenderers();
if (renderers != null && partType < renderers.length && renderers[partType] != null)
{
return renderers[partType].getYOffset();
}
return 0;
}
@Override
protected JSEvent getJSEvent(Object src)
{
JSEvent event = new JSEvent();
event.setType(JSEvent.EventType.form);
event.setFormName(getName());
event.setSource(src);
event.setElementName(src instanceof IComponent ? ((IComponent)src).getName() : null);
return event;
}
/**
* @param src
* @param function
* @param useFormAsEventSourceEventually
*/
@Override
protected FormAndComponent getJSApplicationNames(Object source, Function function, boolean useFormAsEventSourceEventually)
{
Object src = source;
if (src == null)
{
Object window = application.getRuntimeWindowManager().getCurrentWindowWrappedObject();
// if (!(window instanceof Window) || !((Window)window).isVisible())
// {
// window = application.getMainApplicationFrame();
// }
if (window != null)
{
src = ((Window)window).getFocusOwner();
while (src != null && !(src instanceof IComponent))
{
src = ((Component)src).getParent();
}
// Test if this component really comes from the the controllers UI.
if (src instanceof Component)
{
Container container = ((Component)src).getParent();
while (container != null && !(container instanceof IFormUIInternal< ? >))
{
container = container.getParent();
}
if (container != getFormUI())
{
// if not then this is not the trigger element for this form.
src = null;
}
}
}
}
Scriptable thisObject = null;
if (src instanceof IComponent && src instanceof IScriptableProvider)
{
Object esObj = formScope.get("elements", formScope); //$NON-NLS-1$
if (esObj != Scriptable.NOT_FOUND)
{
ElementScope es = (ElementScope)esObj;
String name = ((IComponent)src).getName();
if (name != null && name.length() != 0)
{
Object o = es.get(name, es);
if (o instanceof Scriptable)
{
thisObject = (Scriptable)o;
}
}
if (thisObject == null)
{
if (name == null || name.length() == 0)
{
name = ComponentFactory.WEB_ID_PREFIX + System.currentTimeMillis();
// Check Web components always have a name! Because name can't be set
((IComponent)src).setName(name);
}
try
{
Context.enter();
IScriptable scriptObject = ((IScriptableProvider)src).getScriptObject();
JavaMembers jm = ScriptObjectRegistry.getJavaMembers(scriptObject.getClass(), ScriptableObject.getTopLevelScope(formScope));
thisObject = new NativeJavaObject(formScope, scriptObject, jm);
es.setLocked(false);
es.put(name, es, thisObject);
es.setLocked(true);
}
finally
{
Context.exit();
}
}
}
}
if (src == null && useFormAsEventSourceEventually) src = formScope;
return new FormAndComponent(src, getName());
}
// private static int isExecuting = 0;
// private static LinkedList executeStack = new LinkedList();
private Map<String, Object[]> hmChildrenJavaMembers;
public Map<String, Object[]> getJavaMembers()
{
return hmChildrenJavaMembers;
}
@Override
public Object setUsingAsExternalComponent(boolean visibleExternal) throws ServoyException
{
super.setUsingAsExternalComponent(visibleExternal);
if (visibleExternal)
{
initForJSUsage();
setView(getView());
executeOnLoadMethod();
}
else
{
// make sure that the ui will have pushed all changes
stopUIEditing(true);
}
List<Runnable> invokeLaterRunnables = new ArrayList<Runnable>();
boolean ok = notifyVisible(visibleExternal, invokeLaterRunnables);
Utils.invokeLater(application, invokeLaterRunnables);
if (!ok)
{
//TODO cannot hide...what todo?
}
else if (visibleExternal && getFormUI() instanceof JComponent)
{
// If it is a swing component, test if it has a parent.
JComponent formUI = (JComponent)getFormUI();
if (formUI.getParent() != null)
{
// remove it from the parent
formUI.getParent().remove(formUI);
}
}
// and make sure it is visible when we return it. (FixedCardLayout will set it to none visible when remove)
getFormUI().setComponentVisible(true);
return (visibleExternal ? getFormUI() : null);
}
public void selectNextRecord()
{
if (form.getOnNextRecordCmdMethodID() == 0)
{
if ((application.getFoundSetManager().getEditRecordList().stopEditing(false) & (ISaveConstants.STOPPED + ISaveConstants.AUTO_SAVE_BLOCKED)) == 0)
{
return;
}
IFoundSet fs = getFoundSet();
int nextIndex = fs.getSelectedIndex() + 1;
if (nextIndex >= 0 && nextIndex < fs.getSize())
{
final Component comp = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
fs.setSelectedIndex(nextIndex);
application.invokeLater(new Runnable()
{
public void run()
{
if (getView() == IForm.LOCKED_RECORD_VIEW || getView() == IForm.RECORD_VIEW)
{
if (comp != null) comp.requestFocus();
}
else
{
requestFocus();
}
}
});
}
}
else
{
executeFormMethod(StaticContentSpecLoader.PROPERTY_ONNEXTRECORDCMDMETHODID, null, Boolean.TRUE, true, true);
}
}
public void selectPrevRecord()
{
if (form.getOnPreviousRecordCmdMethodID() == 0)
{
int edittingStoppedFlag = application.getFoundSetManager().getEditRecordList().stopEditing(false);
if (ISaveConstants.STOPPED != edittingStoppedFlag && ISaveConstants.AUTO_SAVE_BLOCKED != edittingStoppedFlag)
{
return;
}
IFoundSet fs = getFoundSet();
int nextIndex = fs.getSelectedIndex() - 1;
if (nextIndex >= 0)
{
final Component comp = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
fs.setSelectedIndex(nextIndex);
application.invokeLater(new Runnable()
{
public void run()
{
if (getView() == IForm.LOCKED_RECORD_VIEW || getView() == IForm.RECORD_VIEW)
{
if (comp != null) comp.requestFocus();
}
else
{
requestFocus();
}
}
});
}
}
else
{
executeFormMethod(StaticContentSpecLoader.PROPERTY_ONPREVIOUSRECORDCMDMETHODID, null, Boolean.TRUE, true, true);
}
}
public IScriptExecuter getScriptExecuter()
{
return scriptExecuter;
}
@Override
public IFormUIInternal getFormUI()
{
return containerImpl;
}
@Override
public String toString()
{
if (formModel != null)
{
return "FormController[form: " + getName() + ", fs size:" + Integer.toString(formModel.getSize()) + ", selected record: " + formModel.getRecord(formModel.getSelectedIndex()) + ",destroyed:" + isDestroyed() + "]"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$
}
else
{
return "FormController[form: " + getName() + ",destroyed:" + isDestroyed() + "]"; //$NON-NLS-1$//$NON-NLS-2$
}
}
/**
*
*/
public void touch()
{
if (!isFormVisible && fm != null)
{
fm.touch(this);
}
}
public void recomputeTabSequence(int baseTabSequenceIndex)
{
application.getDataRenderFactory().reapplyTabSequence(getFormUI(), baseTabSequenceIndex);
}
@Override
public void setTabSequence(Object[] arrayOfElements)
{
tabSequence.setRuntimeTabSequence(arrayOfElements);
}
@Override
public String[] getTabSequence()
{
String[] tabSequenceNames = tabSequence.getNamesInTabSequence();
if (view instanceof IProvideTabSequence)
{
List<String> namesList = ((IProvideTabSequence)view).getTabSeqComponentNames();
if (tabSequenceNames.length != 0)
{
if (!namesList.isEmpty()) tabSequenceNames = Utils.arrayJoin(tabSequenceNames, namesList.toArray(new String[namesList.size()]));
}
else return namesList.toArray(new String[namesList.size()]);
}
return tabSequenceNames;
}
public Serializable getComponentProperty(Object component, String key)
{
return ComponentFactory.getComponentProperty(application, component, key);
}
public IStyleRule getFormStyle()
{
return styleRule;
}
public IStyleRule getBodyStyle()
{
return bodyRule;
}
}