| /******************************************************************************* |
| * 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.typed.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<String, String>() { |
| |
| @Override |
| public String convert(String value) { |
| final String labelText = 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; |
| } |
| } |