blob: 07bf3634418da82afed94332e35e06a1cc897f96 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2015 EclipseSource Muenchen GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eugen Neufeld - initial API and implementation
* Stefan Dirix - Add ControlProcessorService
*******************************************************************************/
package org.eclipse.emf.ecp.view.spi.custom.swt;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.property.value.IValueProperty;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.databinding.edit.EMFEditObservables;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.edit.spi.ECPAbstractControl;
import org.eclipse.emf.ecp.edit.spi.ECPControlFactory;
import org.eclipse.emf.ecp.view.internal.custom.swt.Activator;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
import org.eclipse.emf.ecp.view.spi.custom.model.ECPCustomControlChangeListener;
import org.eclipse.emf.ecp.view.spi.custom.model.ECPHardcodedReferences;
import org.eclipse.emf.ecp.view.spi.custom.model.VCustomControl;
import org.eclipse.emf.ecp.view.spi.custom.model.VCustomDomainModelReference;
import org.eclipse.emf.ecp.view.spi.custom.model.impl.VCustomDomainModelReferenceImpl;
import org.eclipse.emf.ecp.view.spi.model.LabelAlignment;
import org.eclipse.emf.ecp.view.spi.model.VControl;
import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
import org.eclipse.emf.ecp.view.spi.model.VFeaturePathDomainModelReference;
import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
import org.eclipse.emf.ecp.view.spi.swt.reporting.RenderingFailedReport;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.AdapterFactoryItemDelegator;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedReport;
import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
import org.eclipse.emfforms.spi.core.services.label.NoLabelFoundException;
import org.eclipse.emfforms.spi.localization.LocalizationServiceHelper;
import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.databinding.viewers.ViewerSupport;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.osgi.framework.Bundle;
/**
* Extend this class in order to provide an own implementation of an {@link ECPAbstractCustomControlSWT}.
*
* @author Eugen Neufeld
* @since 1.2
*
*/
@SuppressWarnings("deprecation")
public abstract class ECPAbstractCustomControlSWT {
/**
* Variant constant for indicating RAP controls.
*
* @since 1.3
*/
protected static final String CUSTOM_VARIANT = "org.eclipse.rap.rwt.customVariant"; //$NON-NLS-1$
/**
* Constant for an validation error image.
*/
public static final int VALIDATION_ERROR_IMAGE = 0;
/**
* Constant for an add image.
*/
public static final int ADD_IMAGE = 1;
/**
* Constant for an delete image.
*/
public static final int DELETE_IMAGE = 2;
/**
* Constant for a help image.
*/
public static final int HELP_IMAGE = 3;
private final SWTCustomControlHelper swtHelper = new SWTCustomControlHelper();
private ECPControlFactory controlFactory;
private final Map<EStructuralFeature, ECPAbstractControl> controlMap = new LinkedHashMap<EStructuralFeature, ECPAbstractControl>();
private final Map<VDomainModelReference, Adapter> adapterMap = new LinkedHashMap<VDomainModelReference, Adapter>();
private ViewModelContext viewModelContext;
private ComposedAdapterFactory composedAdapterFactory;
private AdapterFactoryItemDelegator adapterFactoryItemDelegator;
private VCustomControl customControl;
private DataBindingContext dataBindingContext;
private final EMFDataBindingContext viewModelDBC = new EMFDataBindingContext();
/**
* Called by the framework to trigger an initialization.
*
* @param customControl the {@link VCustomControl} to use
* @param viewModelContext the {@link ViewModelContext} to use
* @since 1.3
*/
public final void init(VCustomControl customControl, ViewModelContext viewModelContext) {
this.customControl = customControl;
this.viewModelContext = viewModelContext;
controlFactory = Activator.getDefault().getECPControlFactory();
composedAdapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {
new ReflectiveItemProviderAdapterFactory(),
new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
adapterFactoryItemDelegator = new AdapterFactoryItemDelegator(composedAdapterFactory);
postInit();
}
/**
* This method is called after the initialization. Custom controls can overwrite this to execute specific
* initialization steps.
*
* @since 1.3
*/
protected void postInit() {
if (!VCustomDomainModelReference.class.isInstance(customControl.getDomainModelReference())) {
return;
}
final VCustomDomainModelReference customDomainModelReference = VCustomDomainModelReference.class
.cast(customControl.getDomainModelReference());
final ECPHardcodedReferences hardcodedReferences = loadObject(customDomainModelReference.getBundleName(),
customDomainModelReference.getClassName());
if (!customDomainModelReference.isControlChecked()) {
// read stuff from control
final Set<VDomainModelReference> controlReferences = new LinkedHashSet<VDomainModelReference>();
controlReferences.addAll(hardcodedReferences.getNeededDomainModelReferences());
controlReferences.addAll(customDomainModelReference.getDomainModelReferences());
customDomainModelReference.getDomainModelReferences().clear();
customDomainModelReference.getDomainModelReferences().addAll(controlReferences);
customDomainModelReference.setControlChecked(true);
}
}
private static ECPHardcodedReferences loadObject(String bundleName, String clazz) {
final Bundle bundle = Platform.getBundle(bundleName);
if (bundle == null) {
new ClassNotFoundException(String.format(LocalizationServiceHelper.getString(
VCustomDomainModelReferenceImpl.class, "BundleNotFound_ExceptionMessage"), clazz, bundleName)); //$NON-NLS-1$
return null;
}
try {
final Class<?> loadClass = bundle.loadClass(clazz);
if (!ECPHardcodedReferences.class.isAssignableFrom(loadClass)) {
return null;
}
return ECPHardcodedReferences.class.cast(loadClass.newInstance());
} catch (final ClassNotFoundException ex) {
return null;
} catch (final InstantiationException ex) {
return null;
} catch (final IllegalAccessException ex) {
return null;
}
}
/**
* Is called by the framework to trigger a dispose of the control.
*/
public final void dispose() {
if (composedAdapterFactory != null) {
composedAdapterFactory.dispose();
}
if (dataBindingContext != null) {
dataBindingContext.dispose();
}
viewModelDBC.dispose();
customControl = null;
if (adapterMap != null) {
for (final VDomainModelReference domainModelReference : adapterMap.keySet()) {
final Setting setting = getFirstSetting(domainModelReference);
setting.getEObject().eAdapters().remove(adapterMap.get(domainModelReference));
}
adapterMap.clear();
}
if (controlMap != null) {
for (final ECPAbstractControl control : controlMap.values()) {
control.dispose();
}
controlMap.clear();
}
viewModelContext = null;
Activator.getDefault().ungetECPControlFactory();
controlFactory = null;
disposeCustomControl();
}
/**
* This method is called during dispose and allows to dispose necessary objects.
*
* @since 1.3
*/
protected abstract void disposeCustomControl();
/**
* Return the {@link ViewModelContext}.
*
* @return the {@link ViewModelContext} of this control
* @since 1.3
*/
protected final ViewModelContext getViewModelContext() {
return viewModelContext;
}
/**
* Return the {@link VCustomControl}.
*
* @return the {@link VCustomControl} of this control
* @since 1.3
*/
protected final VCustomControl getCustomControl() {
return customControl;
}
/**
* Returns a {@link DataBindingContext} for this control.
*
* @return the {@link DataBindingContext}
* @since 1.3
*/
protected final DataBindingContext getDataBindingContext() {
if (dataBindingContext == null) {
dataBindingContext = new EMFDataBindingContext();
}
return dataBindingContext;
}
private void handleCreatedControls(Diagnostic diagnostic) {
if (diagnostic.getData() == null) {
return;
}
if (diagnostic.getData().size() < 2) {
return;
}
if (!(diagnostic.getData().get(1) instanceof EStructuralFeature)) {
return;
}
final EStructuralFeature feature = (EStructuralFeature) diagnostic.getData().get(1);
final ECPAbstractControl ecpControl = controlMap.get(feature);
if (ecpControl == null) {
return;
}
ecpControl.handleValidation(diagnostic);
}
/**
* This is called so that an error can be shown by the user.
*
* @since 1.3
*/
protected abstract void handleContentValidation();
/**
* This is a helper method which provides an {@link SWTCustomControlHelper}. It allows to get an image based on the
* constants defined in {@link ECPAbstractCustomControlSWT}.
*
* @return the {@link SWTCustomControlHelper} to use to retrieve images.
* @since 1.3
*/
protected final SWTCustomControlHelper getHelper() {
return swtHelper;
}
private Image getImage(int imageType) {
switch (imageType) {
case VALIDATION_ERROR_IMAGE:
return Activator.getImage("icons/validation_error.png"); //$NON-NLS-1$
case HELP_IMAGE:
return Activator.getImage("icons/help.png"); //$NON-NLS-1$
case ADD_IMAGE:
return Activator.getImage("icons/add.png"); //$NON-NLS-1$
case DELETE_IMAGE:
return Activator.getImage("icons/unset_reference.png"); //$NON-NLS-1$
default:
return null;
}
}
/**
* Whether the label for this control should be rendered on the left of the control. This is the case if the
* {@link VControl#getLabelAlignment()} is set to {@link LabelAlignment#LEFT} or {@link LabelAlignment#DEFAULT}.
*
* @return <code>true</code> if label should be on the left, <code>false</code> otherwise
* @since 1.7
*/
protected boolean hasLeftLabelAlignment() {
return getCustomControl().getLabelAlignment() == LabelAlignment.LEFT
|| getCustomControl().getLabelAlignment() == LabelAlignment.DEFAULT;
}
/**
* Create the {@link Control} displaying the label of the current {@link VControl}.
*
* @param parent the {@link Composite} to render onto
* @return the created {@link Control} or null
* @throws NoPropertyDescriptorFoundExeption thrown if the {@link org.eclipse.emf.ecore.EStructuralFeature
* EStructuralFeature} of the {@link VControl} doesn't have a registered {@link IItemPropertyDescriptor}
* @since 1.3
*/
protected final Control createLabel(final Composite parent)
throws NoPropertyDescriptorFoundExeption {
Label label = null;
labelRender: if (hasLeftLabelAlignment()) {
IValueProperty valueProperty;
try {
valueProperty = getViewModelContext().getService(EMFFormsDatabinding.class)
.getValueProperty(getCustomControl().getDomainModelReference(),
getViewModelContext().getDomainModel());
} catch (final DatabindingFailedException ex) {
Activator.getDefault().getReportService().report(new DatabindingFailedReport(ex));
break labelRender;
}
final EStructuralFeature structuralFeature = (EStructuralFeature) valueProperty.getValueType();
label = new Label(parent, SWT.NONE);
label.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_label"); //$NON-NLS-1$
label.setBackground(parent.getBackground());
try {
viewModelDBC.bindValue(
WidgetProperties.text().observe(label),
getViewModelContext().getService(EMFFormsLabelProvider.class)
.getDisplayName(getCustomControl().getDomainModelReference(),
getViewModelContext().getDomainModel()),
null, new UpdateValueStrategy() {
/**
* {@inheritDoc}
*
* @see org.eclipse.core.databinding.UpdateValueStrategy#convert(java.lang.Object)
*/
@Override
public Object convert(Object value) {
final String labelText = (String) super.convert(value);
String extra = ""; //$NON-NLS-1$
if (structuralFeature.getLowerBound() > 0) {
extra = "*"; //$NON-NLS-1$
}
return labelText + extra;
}
});
viewModelDBC.bindValue(
WidgetProperties.tooltipText().observe(label),
getViewModelContext().getService(EMFFormsLabelProvider.class)
.getDescription(getCustomControl().getDomainModelReference(),
getViewModelContext().getDomainModel()));
} catch (final NoLabelFoundException e) {
Activator.getDefault().getReportService().report(new RenderingFailedReport(e));
label.setText(e.getMessage());
label.setToolTipText(e.toString());
}
}
return label;
}
/**
* Creates a Label which is used to display the validation icon.
*
* @param composite the {@link Composite} to render onto
* @return the created Label
* @since 1.3
*/
protected final Label createValidationIcon(Composite composite) {
final Label validationLabel = new Label(composite, SWT.NONE);
validationLabel.setBackground(composite.getBackground());
return validationLabel;
}
/**
* Creates a binding for a {@link StructuredViewer} based on a {@link ECPCustomControlFeature} and the array of
* {@link IValueProperty IValueProperties} for labels.
*
* @param customControlFeature the {@link ECPCustomControlFeature} to use
* @param viewer the {@link StructuredViewer} to bind
* @param labelProperties the array if {@link IValueProperty IValueProperties} to use for labels
*/
protected final void createViewerBinding(VDomainModelReference customControlFeature, StructuredViewer viewer,
IValueProperty[] labelProperties) {
try {
final IObservableList list = getViewModelContext().getService(EMFFormsDatabinding.class)
.getObservableList(customControlFeature, getViewModelContext().getDomainModel());
ViewerSupport.bind(viewer, list, labelProperties);
} catch (final DatabindingFailedException ex) {
Activator.getDefault().getReportService().report(new DatabindingFailedReport(ex));
}
}
/**
* Return an {@link IObservableList} based on a {@link VDomainModelReference}.
*
* @param domainModelReference the {@link VDomainModelReference} to use
* @return the {@link IObservableList}
* @since 1.3
* @deprecated This method is deprecated and must not be used anymore. Use the
* {@link org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding
* databinding service} instead.
*/
@Deprecated
protected final IObservableList getObservableList(VDomainModelReference domainModelReference) {
throw new UnsupportedOperationException(
"This method is deprecated and must not be used anymore. Use the databinding service instead."); //$NON-NLS-1$
}
/**
* Returns the {@link EditingDomain} for the provided {@link Setting}.
*
* @param setting the provided {@link Setting}
* @return the {@link EditingDomain} of this {@link Setting}
* @since 1.3
*/
protected final EditingDomain getEditingDomain(Setting setting) {
return AdapterFactoryEditingDomain.getEditingDomainFor(setting.getEObject());
}
/**
* @param modelFeature the {@link VDomainModelReference} to get the Setting for
* @return the setting or throws an {@link IllegalStateException} if too many or too few elements are found.
*/
private Setting getFirstSetting(VDomainModelReference modelFeature) {
IObservableValue observableValue;
try {
observableValue = getViewModelContext().getService(EMFFormsDatabinding.class)
.getObservableValue(modelFeature, getViewModelContext().getDomainModel());
} catch (final DatabindingFailedException ex) {
Activator.getDefault().getReportService().report(new DatabindingFailedReport(ex));
throw new IllegalStateException("The databinding failed due to an incorrect VDomainModelReference: " //$NON-NLS-1$
+ ex.getMessage());
}
final InternalEObject internalEObject = (InternalEObject) ((IObserving) observableValue).getObserved();
final EStructuralFeature structuralFeature = (EStructuralFeature) observableValue.getValueType();
observableValue.dispose();
return internalEObject.eSetting(structuralFeature);
}
/**
* Use this method to get an {@link ECPControl} which can be used inside the {@link ECPCustomControl}.
*
* @param clazz the {@link Class} of the {@link ECPControl} to retrieve
* @param domainModelReference the {@link VDomainModelReference} to retrieve a control for
* @param <T> the type of the control to retrieve
* @return the {@link ECPControl} that is fitting the most for the {@link ECPCustomControlFeature}. Can also be
* null.
* @since 1.3
*/
protected final <T extends ECPAbstractControl> T getControl(Class<T> clazz,
VDomainModelReference domainModelReference) {
final T createControl = controlFactory.createControl(clazz, getViewModelContext().getDomainModel(),
domainModelReference);
final VControl vControl = VViewFactory.eINSTANCE.createControl();
final VDomainModelReference modelReference = EcoreUtil.copy(domainModelReference);
vControl.setDomainModelReference(modelReference);
vControl.setDiagnostic(VViewFactory.eINSTANCE.createDiagnostic());
createControl.init(getViewModelContext(), vControl);
IValueProperty valueProperty;
try {
valueProperty = getViewModelContext().getService(EMFFormsDatabinding.class)
.getValueProperty(domainModelReference, getViewModelContext().getDomainModel());
} catch (final DatabindingFailedException ex) {
Activator.getDefault().getReportService().report(new DatabindingFailedReport(ex));
return null;
}
final EStructuralFeature structuralFeature = (EStructuralFeature) valueProperty.getValueType();
controlMap.put(structuralFeature, createControl);
return createControl;
}
/**
* Return the {@link IItemPropertyDescriptor} describing this {@link Setting}.
*
* @param setting the {@link Setting} to use for identifying the {@link IItemPropertyDescriptor}.
* @return the {@link IItemPropertyDescriptor}
* @since 1.3
*/
protected final IItemPropertyDescriptor getItemPropertyDescriptor(Setting setting) {
return adapterFactoryItemDelegator.getPropertyDescriptor(setting.getEObject(),
setting.getEStructuralFeature());
}
private String getHelp(VDomainModelReference domainModelReference) {
if (!getResolvedDomainModelReferences().contains(domainModelReference)) {
throw new IllegalArgumentException("The feature must have been registered before!"); //$NON-NLS-1$
}
return getItemPropertyDescriptor(getFirstSetting(domainModelReference)).getDescription(null);
}
private String getLabel(VDomainModelReference domainModelReference) {
if (!getResolvedDomainModelReferences().contains(domainModelReference)) {
throw new IllegalArgumentException("The feature must have been registered before!"); //$NON-NLS-1$
}
return getItemPropertyDescriptor(getFirstSetting(domainModelReference)).getDisplayName(null);
}
/**
* Returns a list of all {@link VDomainModelReference VDomainModelReferences} which were already resolved and thus
* can be used for binding etc.
*
* @return the List of {@link VDomainModelReference VDomainModelReferences}
* @since 1.3
*/
protected final List<VDomainModelReference> getResolvedDomainModelReferences() {
// final VHardcodedDomainModelReference hardcodedDomainModelReference = VHardcodedDomainModelReference.class
// .cast(getCustomControl().getDomainModelReference());
// return hardcodedDomainModelReference.getDomainModelReferences();
final VDomainModelReference domainModelReference = getCustomControl().getDomainModelReference();
if (VCustomDomainModelReference.class.isInstance(domainModelReference)) {
return VCustomDomainModelReference.class.cast(domainModelReference).getDomainModelReferences();
}
return Collections.singletonList(domainModelReference);
}
/**
* Finds the {@link VDomainModelReference} which provides a specific {@link EStructuralFeature}.
*
* @param feature the {@link EStructuralFeature} to find the {@link VDomainModelReference} for
* @return the {@link VDomainModelReference} or null
* @since 1.3
*/
protected final VDomainModelReference getResolvedDomainModelReference(EStructuralFeature feature) {
for (final VDomainModelReference domainModelReference : getResolvedDomainModelReferences()) {
if (VFeaturePathDomainModelReference.class.isInstance(domainModelReference)) {
final VFeaturePathDomainModelReference ref = VFeaturePathDomainModelReference.class
.cast(domainModelReference);
if (ref.getDomainModelEFeature() == feature) {
return ref;
}
}
}
return null;
}
/**
* Method for enabling databinding on the reference/attribute of the referenced object.
* Throws an {@link IllegalStateException} if the {@link VDomainModelReference} doesn't resolve to exactly one
* {@link Setting}.
*
*
* @param modelFeature the {@link VDomainModelReference} to bind
* @param targetValue the target observerable
* @param targetToModel update strategy target to model
* @param modelToTarget update strategy model to target
* @return the resulting binding
* @since 1.3
*/
protected final Binding bindTargetToModel(VDomainModelReference modelFeature, IObservableValue targetValue,
UpdateValueStrategy targetToModel,
UpdateValueStrategy modelToTarget) {
final Setting setting = getFirstSetting(modelFeature);
final IObservableValue modelValue = EMFEditObservables.observeValue(
getEditingDomain(setting),
setting.getEObject(), setting.getEStructuralFeature());
return getDataBindingContext().bindValue(targetValue, modelValue, targetToModel,
modelToTarget);
}
/**
* Provides the {@link EditingDomain} for this custom control.
*
* @return the {@link EditingDomain} for this control
* @since 1.3
*/
protected final EditingDomain getEditingDomain() {
return getEditingDomain(getFirstSetting(getCustomControl().getDomainModelReference()));
}
/**
* Returns the current set value of the feature.
* Throws an {@link IllegalStateException} if the {@link VDomainModelReference} doesn't resolve to exactly one
* {@link Setting}.
*
* @param modelReference the {@link VDomainModelReference} to get the value for
* @return the value
* @since 1.3
*/
protected final Object getValue(VDomainModelReference modelReference) {
final Setting setting = getFirstSetting(modelReference);
return setting.get(false);
}
/**
* Sets the value of the feature to the new value.
* Throws an {@link IllegalStateException} if the {@link VDomainModelReference} doesn't resolve to exactly one
* {@link Setting}.
*
* @param modelReference the {@link VDomainModelReference} to get the value for
* @param newValue the value to be set
* @since 1.3
*/
protected final void setValue(VDomainModelReference modelReference, Object newValue) {
// FIXME needed?
// if (!isEditable) {
// throw new UnsupportedOperationException(
// "Set value is not supported on ECPCustomControlFeatures that are not editable.");
// }
final Setting setting = getFirstSetting(modelReference);
setting.set(newValue);
}
/**
*
* Registers a change listener on the referenced object. {@link ECPCustomControlChangeListener#notifyChanged()} will
* be called when a change on the referenced object is noticed.
*
* Throws an {@link IllegalStateException} if the {@link VDomainModelReference} doesn't resolve to exactly one
* {@link Setting}.
*
* @param modelReference the {@link VDomainModelReference} to register a listener for
* @param changeListener the change listener to register
* @since 1.3
*/
protected final void registerChangeListener(VDomainModelReference modelReference,
final ECPCustomControlChangeListener changeListener) {
if (adapterMap.containsKey(modelReference)) {
return;
}
final Setting setting = getFirstSetting(modelReference);
final Adapter newAdapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
if (msg.isTouch()) {
return;
}
if (msg.getFeature().equals(setting.getEStructuralFeature())) {
super.notifyChanged(msg);
changeListener.notifyChanged();
}
}
};
setting.getEObject().eAdapters().add(newAdapter);
adapterMap.put(modelReference, newAdapter);
}
/**
* The {@link SWTCustomControlHelper} allows the retrieval of SWT specific elements.
*
* @author Eugen Neufeld
*
*/
public final class SWTCustomControlHelper {
/**
* Allows to get an {@link Image} based on the constants defined in {@link ECPAbstractCustomControlSWT}.
*
* @param imageType the image type to retrieve
* @return the retrieved Image or null if an unknown imageType was provided
*/
public Image getImage(int imageType) {
return ECPAbstractCustomControlSWT.this.getImage(imageType);
}
/**
* This return a text providing a long helpful description of the feature. Can be used for example in a ToolTip.
*
* @param domainModelReference the {@link VDomainModelReference} to retrieve the help text for
* @return the String containing the helpful description or null if no description is found
* @since 1.3
*/
public String getHelp(VDomainModelReference domainModelReference) {
return ECPAbstractCustomControlSWT.this.getHelp(domainModelReference);
}
/**
* This return a text providing a short label of the feature. Can be used for example as a label in front of the
* edit field.
*
* @param domainModelReference the {@link VDomainModelReference} to retrieve the text for
* @return the String containing the label null if no label is found
* @since 1.3
*/
public String getLabel(VDomainModelReference domainModelReference) {
return ECPAbstractCustomControlSWT.this.getLabel(domainModelReference);
}
}
/**
* Returns the GridDescription for this Renderer.
*
* @return the GridDescription
* @since 1.6
*/
public abstract SWTGridDescription getGridDescription();
/**
* Renders the control.
*
* @param cell the {@link SWTGridCell} of the control to render
* @param parent the {@link Composite} to render on
* @return the rendered {@link Control}
* @throws NoRendererFoundException this is thrown when a renderer cannot be found
* @throws NoPropertyDescriptorFoundExeption this is thrown when no property descriptor can be found
* @since 1.6
*/
public abstract Control renderControl(SWTGridCell cell, Composite parent) throws NoRendererFoundException,
NoPropertyDescriptorFoundExeption;
/**
* Called by the framework to apply validation changes.
*
* @since 1.3
*/
public final void applyValidation() {
if (getCustomControl() == null || getCustomControl().getDiagnostic() == null) {
return;
}
for (final Object diagnostic : getCustomControl().getDiagnostic().getDiagnostics()) {
handleCreatedControls((Diagnostic) diagnostic);
}
handleContentValidation();
}
/**
* Applies the current readOnlyState.
*
* @param controls the controls provided
* @since 1.3
*/
public final void applyReadOnly(Map<SWTGridCell, Control> controls) {
if (setEditable(!getCustomControl().isEffectivelyReadonly())) {
for (final SWTGridCell gridCell : controls.keySet()) {
setControlEnabled(gridCell, controls.get(gridCell), !getCustomControl().isEffectivelyReadonly());
}
}
}
/**
* Override this to control which and how controls should be enabled/disabled.
*
* @param gridCell the {@link SWTGridCell} to enable/disable
* @param control the {@link Control} to enable/disable
* @param enabled true if the control should be enabled false otherwise
* @since 1.6
*/
protected void setControlEnabled(SWTGridCell gridCell, Control control, boolean enabled) {
// ignore labels as they are readonly per definition
if (Label.class.isInstance(control)) {
return;
}
control.setEnabled(enabled);
}
/**
* Applies the current enable state.
*
* @param controls the controls
* @since 1.3
*/
public final void applyEnable(Map<SWTGridCell, Control> controls) {
if (setEditable(getCustomControl().isEffectivelyEnabled())) {
for (final SWTGridCell gridCell : controls.keySet()) {
setControlEnabled(gridCell, controls.get(gridCell), getCustomControl().isEffectivelyEnabled());
}
}
}
/**
* Allows custom controls to call specific code for setting controls editable or not. The result indicates whether
* to call the default editable
*
* @param editable if the current state is editable
* @return true if default editable modification should be executed
* @since 1.3
*/
protected boolean setEditable(boolean editable) {
return true;
}
/**
* Creates a simple grid.
*
* @param rows the number of rows
* @param columns the number of columns
* @return the {@link GridDescription}
* @since 1.6
*/
protected final SWTGridDescription createSimpleGrid(int rows, int columns) {
return GridDescriptionFactory.INSTANCE.createSimpleGrid(rows, columns, null);
}
/**
* <p>
* Indicates if the given custom control takes the responsibility to call a possibly existing
* {@link org.eclipse.emfforms.spi.swt.core.EMFFormsControlProcessorService EMFFormsControlProcessorService} itself.
* </p>
* <p>
* The default implementation returns {@code false}.
* </p>
*
* @return
* {@code true} if the custom control can handle the
* {@link org.eclipse.emfforms.spi.swt.core.EMFFormsControlProcessorService EMFFormsControlProcessorService}
* itself, {@code false} otherwise.
* @since 1.8
*/
protected boolean canHandleControlProcessor() {
return false;
}
}