blob: 929819e5967cba7bb3690d8819499fbd99b02aa8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2015 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.client.ui.form.fields.wrappedform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.scout.rt.client.extension.ui.form.fields.IFormFieldExtension;
import org.eclipse.scout.rt.client.extension.ui.form.fields.wrappedform.IWrappedFormFieldExtension;
import org.eclipse.scout.rt.client.extension.ui.form.fields.wrappedform.WrappedFormFieldChains.WrappedFormFieldInnerFormChangedChain;
import org.eclipse.scout.rt.client.ui.form.FormEvent;
import org.eclipse.scout.rt.client.ui.form.FormListener;
import org.eclipse.scout.rt.client.ui.form.IForm;
import org.eclipse.scout.rt.client.ui.form.IFormFieldVisitor;
import org.eclipse.scout.rt.client.ui.form.fields.AbstractFormField;
import org.eclipse.scout.rt.client.ui.form.fields.CompositeFieldUtility;
import org.eclipse.scout.rt.client.ui.form.fields.IFormField;
import org.eclipse.scout.rt.client.ui.form.fields.groupbox.IGroupBox;
import org.eclipse.scout.rt.platform.BEANS;
import org.eclipse.scout.rt.platform.Order;
import org.eclipse.scout.rt.platform.annotations.ConfigOperation;
import org.eclipse.scout.rt.platform.annotations.ConfigProperty;
import org.eclipse.scout.rt.platform.classid.ClassId;
import org.eclipse.scout.rt.platform.exception.ExceptionHandler;
import org.eclipse.scout.rt.platform.exception.ProcessingException;
import org.w3c.dom.Element;
@ClassId("535cfd11-39cf-4804-beef-2bc1bc3d34cc")
public abstract class AbstractWrappedFormField<FORM extends IForm> extends AbstractFormField implements IWrappedFormField<FORM> {
private FORM m_innerForm;
private boolean m_manageInnerFormLifeCycle;
private P_InnerFormPropertyChangeListener m_innerFormPropertyListener;
private P_InnerFormSubtreePropertyChangeListener m_innerFormSubtreePropertyListener;
private P_InnerFormListener m_innerFormListener;
public AbstractWrappedFormField() {
this(true);
}
public AbstractWrappedFormField(boolean callInitializer) {
super(callInitializer);
}
@Override
protected boolean getConfiguredLabelVisible() {
return false;
}
@Override
protected boolean getConfiguredStatusVisible() {
return false;
}
/**
* If set, an instance of this form class is created on field initialization and is then set as inner form. The form's
* life cycle is managed automatically by the field (i.e. it is started and closed).
*/
@ConfigProperty(ConfigProperty.FORM)
@Order(200)
protected Class<? extends IForm> getConfiguredInnerForm() {
return null;
}
/**
* @return {@code true} if the inner form should request the initial focus once loaded, {@code false} otherwise.
* Default is {@code false}.
*/
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(210)
protected boolean getConfiguredInitialFocusEnabled() {
return false;
}
@Override
protected double getConfiguredGridWeightY() {
return 1;
}
@Override
protected boolean getConfiguredGridUseUiHeight() {
return true;
}
@Override
protected boolean execIsSaveNeeded() {
return getInnerForm() != null && getInnerForm().isSaveNeeded();
}
@Override
protected void execMarkSaved() {
if (getInnerForm() != null) {
getInnerForm().markSaved();
}
}
@Override
protected boolean execIsEmpty() {
if (getInnerForm() != null) {
return getInnerForm().isEmpty();
}
return super.execIsEmpty();
}
@SuppressWarnings("unchecked")
@Override
protected void initConfig() {
super.initConfig();
m_innerFormPropertyListener = new P_InnerFormPropertyChangeListener();
m_innerFormSubtreePropertyListener = new P_InnerFormSubtreePropertyChangeListener();
m_innerFormListener = new P_InnerFormListener();
setInitialFocusEnabled(getConfiguredInitialFocusEnabled());
if (getConfiguredInnerForm() != null) {
try {
setInnerForm((FORM) getConfiguredInnerForm().newInstance(), true);
}
catch (Exception e) {
BEANS.get(ExceptionHandler.class).handle(new ProcessingException("error creating instance of class '" + getConfiguredInnerForm().getName() + "'.", e));
}
}
}
@Override
protected void disposeFieldInternal() {
super.disposeFieldInternal();
// Remove listeners, close the form if life cycle is not externally managed
uninstallInnerForm();
}
@Override
public void setEnabledGranted(boolean b) {
super.setEnabledGranted(b);
if (getInnerForm() != null) {
getInnerForm().setEnabledGranted(b);
}
}
@Override
public void setEnabled(boolean b) {
super.setEnabled(b);
if (getInnerForm() != null) {
getInnerForm().setAllEnabled(b);
}
}
@Override
public final FORM getInnerForm() {
return m_innerForm;
}
@Override
public void setInnerForm(FORM form) {
setInnerForm(form, true);
}
@Override
public void setInnerForm(FORM form, boolean manageFormLifeCycle) {
if (m_innerForm == form) {
return;
}
// TODO [7.0] BSH: Add assertion to ensure Form is not started yet; currently, that cannot be done because of AbstractPageField.
// --> Assertions.assertFalse(form != null && form.isFormStarted(), "Inner Form must not be started yet [wrappedFormField=%s, innerForm=%s]", this, form)
// TODO [7.0] BSH: Check if the above to-do would better be solved using "form.isShowing()". But what about forms that are started and contained in wrapper field 1 and are added to wrapper field 2?
// Example: Would work with "isFormStarted()": Would _not_ work with "isFormStarted()", but would work with "isShowing()".
// ---------------------------------- ---------------------------------------------------------------------------
// TestForm form = new TestForm(); TestForm form = new TestForm()
// form.setHandler(form.new TestHandler()); form.setHandler(form.new TestHandler())
// form.start(); form.setShowOnStart(false) // <--
// getWrappedFormField().setInnerForm(form, false); form.start()
// getWrappedFormField().setInnerForm(form, false)
FORM oldInnerForm = m_innerForm;
uninstallInnerForm();
m_innerForm = form;
m_manageInnerFormLifeCycle = manageFormLifeCycle;
installInnerForm();
propertySupport.setProperty(PROP_INNER_FORM, m_innerForm);
calculateVisibleInternal();
checkSaveNeeded();
checkEmpty();
if (m_innerForm != null) {
fireSubtreePropertyChange(new PropertyChangeEvent(m_innerForm.getRootGroupBox(), IFormField.PROP_PARENT_FIELD, null, null));
if (m_manageInnerFormLifeCycle && m_innerForm.isFormStartable()) { // TODO [7.0] BSH: Remove 'started check' once assertion is in place
m_innerForm.start();
}
}
// Inform parent form (update layout etc.)
if (getForm() != null) {
getForm().structureChanged(this);
}
interceptInnerFormChanged(oldInnerForm, m_innerForm);
}
@Override
public boolean isInitialFocusEnabled() {
return propertySupport.getPropertyBool(PROP_INITIAL_FOCUS_ENABLED);
}
@Override
public void setInitialFocusEnabled(boolean initialFocusEnabled) {
propertySupport.setPropertyBool(PROP_INITIAL_FOCUS_ENABLED, initialFocusEnabled);
}
protected void installInnerForm() {
if (m_innerForm == null) {
return;
}
m_innerForm.setShowOnStart(false);
m_innerForm.setWrapperFieldInternal(this);
m_innerForm.getRootGroupBox().setBorderVisible(false);
m_innerForm.getRootGroupBox().updateKeyStrokes();
m_innerForm.addPropertyChangeListener(m_innerFormPropertyListener);
m_innerForm.getRootGroupBox().addSubtreePropertyChangeListener(m_innerFormSubtreePropertyListener);
m_innerForm.addFormListener(m_innerFormListener);
}
protected void uninstallInnerForm() {
if (m_innerForm == null) {
return;
}
fireSubtreePropertyChange(new PropertyChangeEvent(m_innerForm.getRootGroupBox(), IFormField.PROP_PARENT_FIELD, null, null));
m_innerForm.removePropertyChangeListener(m_innerFormPropertyListener);
m_innerForm.getRootGroupBox().removeSubtreePropertyChangeListener(m_innerFormSubtreePropertyListener);
m_innerForm.removeFormListener(m_innerFormListener);
m_innerForm.setWrapperFieldInternal(null);
if (m_manageInnerFormLifeCycle && !m_innerForm.isFormClosed()) {
m_innerForm.doClose();
}
}
@Override
public boolean acceptVisitor(IFormFieldVisitor visitor, int level, int fieldIndex, boolean includeThis) {
Collection<IFormField> innerFormRootGroupBox = null;
FORM innerForm = getInnerForm();
if (innerForm != null) {
IGroupBox rootGroupBox = innerForm.getRootGroupBox();
if (rootGroupBox != null) {
innerFormRootGroupBox = Collections.<IFormField> singletonList(rootGroupBox);
}
}
IFormField thisField = null;
if (includeThis) {
thisField = this;
}
return CompositeFieldUtility.applyFormFieldVisitor(visitor, thisField, innerFormRootGroupBox, level, fieldIndex);
}
@Override
public boolean visitFields(IFormFieldVisitor visitor) {
return acceptVisitor(visitor, 0, 0, true);
}
@Override
public void loadFromXml(Element x) {
super.loadFromXml(x);
if (getInnerForm() != null) {
getInnerForm().loadFromXml(x);
}
}
@Override
public void storeToXml(Element x) {
super.storeToXml(x);
if (getInnerForm() != null) {
getInnerForm().storeToXml(x);
}
}
// group box is only visible when it has at least one visible item
protected void handleFieldVisibilityChanged() {
calculateVisibleInternal();
}
/**
* Method invoked once the inner Form is changed.
*
* @param oldInnerForm
* the old inner {@link IForm}; might be <code>null</code>.
* @param newInnerForm
* the new inner {@link IForm}; might be <code>null</code>.
*/
@ConfigOperation
protected void execInnerFormChanged(FORM oldInnerForm, FORM newInnerForm) {
}
/**
* Implementation of PropertyChangeListener Proxy on all attached fields (not groups)
*/
private class P_InnerFormPropertyChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName().equals(IFormField.PROP_VISIBLE)) {
// fire group box visibility
handleFieldVisibilityChanged();
}
else if (e.getPropertyName().equals(IFormField.PROP_SAVE_NEEDED)) {
checkSaveNeeded();
}
else if (e.getPropertyName().equals(IFormField.PROP_EMPTY)) {
checkEmpty();
}
}
}// end private class
private class P_InnerFormSubtreePropertyChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent e) {
fireSubtreePropertyChange(e);
}
}// end private class
private class P_InnerFormListener implements FormListener {
@Override
public void formChanged(FormEvent e) {
if (e.getType() == FormEvent.TYPE_CLOSED && m_manageInnerFormLifeCycle) {
setInnerForm(null, true);
}
else if (e.getType() == FormEvent.TYPE_LOAD_COMPLETE && !m_manageInnerFormLifeCycle) {
propertySupport.setPropertyAlwaysFire(PROP_INNER_FORM, m_innerForm);
}
}
}// end private class
protected final void interceptInnerFormChanged(FORM oldInnerForm, FORM newInnerForm) {
List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions = getAllExtensions();
WrappedFormFieldInnerFormChangedChain<FORM> chain = new WrappedFormFieldInnerFormChangedChain<>(extensions);
chain.execInnerFormChanged(oldInnerForm, newInnerForm);
}
protected static class LocalWrappedFormFieldExtension<FORM extends IForm, OWNER extends AbstractWrappedFormField<FORM>> extends LocalFormFieldExtension<OWNER> implements IWrappedFormFieldExtension<FORM, OWNER> {
public LocalWrappedFormFieldExtension(OWNER owner) {
super(owner);
}
@Override
public void execInnerFormChanged(WrappedFormFieldInnerFormChangedChain<FORM> chain, FORM oldInnerForm, FORM newInnerForm) {
getOwner().execInnerFormChanged(oldInnerForm, newInnerForm);
}
}
@Override
protected IWrappedFormFieldExtension<FORM, ? extends AbstractWrappedFormField<FORM>> createLocalExtension() {
return new LocalWrappedFormFieldExtension<>(this);
}
}