Skip to content

Content of file ECPAbstractCustomControlSWT.java

/*******************************************************************************
 * 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; } }