Skip to content

Content of file TypedElementBoundsRenderer.java

/*******************************************************************************
 * Copyright (c) 2011-2019 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:
 * Clemens Elflein - initial API and implementation
 * Alexandra Buzila - refactoring
 * Christian W. Damus - bug 548592
 ******************************************************************************/
package org.eclipse.emfforms.internal.editor.ecore.controls;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.databinding.edit.EMFEditObservables;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
import org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer;
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.renderer.NoPropertyDescriptorFoundExeption;
import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
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.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Spinner; /** * * The Renderer allowing the user to test the bounds with two Combos. * * @author Clemens Elflein */ public class TypedElementBoundsRenderer extends AbstractControlSWTRenderer<VControl> { private Spinner lowerBound; private Spinner upperBound; private Button unbounded; /** * Default constructor. * * @param vElement the view model element to be rendered * @param viewContext the view context * @param emfFormsDatabinding The {@link EMFFormsDatabinding} * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider} * @param reportService The {@link ReportService} * @param vtViewTemplateProvider The {@link VTViewTemplateProvider} */ @Inject public TypedElementBoundsRenderer(VControl vElement, ViewModelContext viewContext, ReportService reportService, EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider, VTViewTemplateProvider vtViewTemplateProvider) { super(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider); } private SWTGridDescription rendererGridDescription; @Override public final SWTGridDescription getGridDescription(SWTGridDescription gridDescription) { if (rendererGridDescription == null) { final boolean showLabel = getVElement().getLabelAlignment() != LabelAlignment.NONE; final int columns = showLabel ? 3 : 2; rendererGridDescription = GridDescriptionFactory.INSTANCE.createEmptyGridDescription(); rendererGridDescription.setRows(1); rendererGridDescription.setColumns(columns); final List<SWTGridCell> grid = new ArrayList<SWTGridCell>(); if (columns == 3) { final SWTGridCell labelCell = createLabelCell(grid.size()); grid.add(labelCell); } final SWTGridCell validationCell = createValidationCell(grid.size()); grid.add(validationCell); final SWTGridCell controlCel = createControlCell(grid.size()); grid.add(controlCel); rendererGridDescription.setGrid(grid); } return rendererGridDescription; } private SWTGridCell createLabelCell(int column) { final SWTGridCell labelCell = new SWTGridCell(0, column, this); labelCell.setHorizontalGrab(false); labelCell.setVerticalGrab(false); labelCell.setHorizontalFill(false); labelCell.setHorizontalAlignment(SWTGridCell.Alignment.BEGINNING); labelCell.setVerticalFill(false); labelCell.setVerticalAlignment(SWTGridCell.Alignment.CENTER); labelCell.setRenderer(this); return labelCell; } private SWTGridCell createValidationCell(int column) { final SWTGridCell validationCell = new SWTGridCell(0, column, this); validationCell.setHorizontalGrab(false); validationCell.setVerticalGrab(false); validationCell.setHorizontalFill(false); validationCell.setHorizontalAlignment(SWTGridCell.Alignment.CENTER); validationCell.setVerticalFill(false); validationCell.setVerticalAlignment(SWTGridCell.Alignment.CENTER); validationCell.setRenderer(this); validationCell.setPreferredSize(16, 17); return validationCell; } private SWTGridCell createControlCell(int column) { final SWTGridCell controlCell = new SWTGridCell(0, column, this); controlCell.setHorizontalGrab(true); controlCell.setVerticalGrab(false); controlCell.setHorizontalFill(true); controlCell.setVerticalFill(false); controlCell.setVerticalAlignment(SWTGridCell.Alignment.CENTER); controlCell.setRenderer(this); return controlCell; } @Override protected final Control renderControl(SWTGridCell gridCell, Composite parent) throws NoRendererFoundException, NoPropertyDescriptorFoundExeption { int controlIndex = gridCell.getColumn(); if (getVElement().getLabelAlignment() == LabelAlignment.NONE) { controlIndex++; } switch (controlIndex) { case 0: return createBoundsLabel(parent); case 1: return createValidationIcon(parent); case 2: return createControl(parent); default: throw new IllegalArgumentException( String .format( "The provided SWTGridCell (%1$s) cannot be used by this (%2$s) renderer.", gridCell.toString(), //$NON-NLS-1$ toString())); } } private Control createBoundsLabel(Composite parent) { final Label label = new Label(parent, SWT.NONE); label.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_label"); //$NON-NLS-1$ label.setText(getLocalizedString(Messages.TypedElementBoundsRenderer_Bounds)); return label; } private Control createControl(Composite parent) { final Composite main = new Composite(parent, SWT.NONE); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(main); GridDataFactory.fillDefaults().grab(true, false) .align(SWT.FILL, SWT.BEGINNING).applyTo(main); lowerBound = new Spinner(main, SWT.BORDER); lowerBound.setMaximum(Integer.MAX_VALUE); lowerBound.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, true)); upperBound = new Spinner(main, SWT.BORDER); upperBound.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, true)); upperBound.setMinimum(-1); upperBound.setMaximum(Integer.MAX_VALUE); unbounded = new Button(main, SWT.CHECK); unbounded.setText(getLocalizedString(Messages.TypedElementBoundsRenderer_Unbounded)); createDataBindings(lowerBound, upperBound, unbounded); applyValidation(); return main; } private String getLocalizedString(String string) { return LocalizationServiceHelper.getString(TypedElementBoundsRenderer.class, string); } private void createDataBindings(final Spinner lowerBound, final Spinner upperBound, Button unbounded) { final EObject domainObject = getViewModelContext().getDomainModel(); final DataBindingContext dbc = getDataBindingContext(); final ISWTObservableValue lowerBoundSelectionTargetValue = WidgetProperties.selection().observe(lowerBound); final IObservableValue lowerBoundModelValue = EMFEditObservables.observeValue(getEditingDomain(domainObject), domainObject, EcorePackage.eINSTANCE.getETypedElement_LowerBound()); dbc.bindValue(lowerBoundSelectionTargetValue, lowerBoundModelValue); final IObservableValue upperBoundModelValue = EMFEditObservables.observeValue(getEditingDomain(domainObject), domainObject, EcorePackage.eINSTANCE.getETypedElement_UpperBound()); final ISWTObservableValue upperBoundSelectionTargetValue = WidgetProperties.selection().observe(upperBound); dbc.bindValue(upperBoundSelectionTargetValue, upperBoundModelValue); bindUpperAndLowerBounds(dbc, lowerBoundModelValue, upperBoundModelValue); /* Disable the upperBound spinner when it's value is set to -1 */ final ISWTObservableValue upperBoundEnabledTargetValue = WidgetProperties.enabled().observe(upperBound); dbc.bindValue(upperBoundEnabledTargetValue, upperBoundModelValue, null, new UpdateValueStrategy() { @Override public Object convert(Object value) { // model to target if (!Integer.class.isInstance(value)) { return null; } return (Integer) value != -1; } }); /* The unbounded checkbox is selected, when the upperBound value is -1 (and vice versa) */ final ISWTObservableValue unboundedSelectionTargetValue = WidgetProperties.selection().observe(unbounded); dbc.bindValue(unboundedSelectionTargetValue, upperBoundModelValue, new UpdateValueStrategy() { @Override public Object convert(Object value) { // target to model final Boolean unbounded = (Boolean) value; if (!unbounded) { return Math.max(1, lowerBound.getSelection()); } return -1; } }, new UpdateValueStrategy() { @Override public Object convert(Object value) { // model to target if (!Integer.class.isInstance(value)) { return null; } return (Integer) value == -1; } }); } /** * Make sure that the upperBound cannot be lower than the lowerBound and that the lowerBound cannot be higher * than the upperBound. */ private void bindUpperAndLowerBounds(final DataBindingContext dbc, final IObservableValue lowerBoundModelValue, final IObservableValue upperBoundModelValue) { dbc.bindValue(upperBoundModelValue, lowerBoundModelValue, new UpdateValueStrategy() { @Override public Object convert(Object value) { // upper value to lower value if (!Integer.class.isInstance(value)) { return null; } final Integer upperValue = (Integer) value; final Integer lowerValue = (Integer) lowerBoundModelValue.getValue(); if (upperValue < lowerValue && upperValue != -1) { return upperValue; } return lowerValue; } @Override protected IStatus doSet(IObservableValue observableValue, Object value) { if (observableValue.getValue().equals(value)) { return Status.OK_STATUS; } return super.doSet(observableValue, value); } }, new UpdateValueStrategy() { @Override public Object convert(Object value) { // lower value to upper value if (!Integer.class.isInstance(value)) { return null; } final Integer lowerValue = (Integer) value; final Integer upperValue = (Integer) upperBoundModelValue.getValue(); if (upperValue >= 0 && upperValue < lowerValue) { return lowerValue; } return upperValue; } @Override protected IStatus doSet(IObservableValue observableValue, Object value) { if (observableValue.getValue().equals(value)) { return Status.OK_STATUS; } return super.doSet(observableValue, value); } }); } private void setValidationColor(Control control, Color validationColor) { control.setBackground(validationColor); } @Override protected final void applyValidation() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { applyInnerValidation(); } }); } private void applyInnerValidation() { Label validationIcon; Control editControl; switch (getControls().size()) { case 2: validationIcon = Label.class.cast(getControls().get( new SWTGridCell(0, 0, TypedElementBoundsRenderer.this))); editControl = getControls().get(new SWTGridCell(0, 1, TypedElementBoundsRenderer.this)); break; case 3: validationIcon = Label.class.cast(getControls().get( new SWTGridCell(0, 1, TypedElementBoundsRenderer.this))); editControl = getControls().get(new SWTGridCell(0, 2, TypedElementBoundsRenderer.this)); break; default: // TODO log error ; return; } // triggered due to another validation rule before this control is rendered if (validationIcon == null || editControl == null) { return; } // validation rule triggered after the control was disposed if (validationIcon.isDisposed()) { return; } // no diagnostic set if (getVElement().getDiagnostic() == null) { return; } validationIcon.setImage(getValidationIcon()); validationIcon.setToolTipText(getVElement().getDiagnostic().getMessage()); setValidationColor(editControl, getValidationBackgroundColor()); } /** * {@inheritDoc} * * @see org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer#rootDomainModelChanged() */ @Override protected void rootDomainModelChanged() throws DatabindingFailedException { // TODO change implementation to use databinding } /** * Reveal the given {@code feature} in my controls. * * @param feature the feature to reveal (either the upper or the lower bound) * * @since 1.22 */ public void reveal(EStructuralFeature feature) { if (feature == EcorePackage.Literals.ETYPED_ELEMENT__UPPER_BOUND) { if (upperBound != null && upperBound.isEnabled()) { scrollToReveal(upperBound); } else if (unbounded != null && unbounded.isEnabled()) { scrollToReveal(unbounded); } } else if (feature == EcorePackage.Literals.ETYPED_ELEMENT__LOWER_BOUND) { if (lowerBound != null && lowerBound.isEnabled()) { scrollToReveal(lowerBound); } } } }