blob: 14d4a45d48c050ac95a8315b7e7ee36bcbba9a3f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2016 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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Clemens Elflein - initial API and implementation
* Alexandra Buzila - refactoring
******************************************************************************/
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.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> {
/**
* 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);
final Spinner lowerBound = new Spinner(main, SWT.BORDER);
lowerBound.setMaximum(Integer.MAX_VALUE);
lowerBound.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, true));
final Spinner upperBound = new Spinner(main, SWT.BORDER);
upperBound.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, true));
upperBound.setMinimum(-1);
upperBound.setMaximum(Integer.MAX_VALUE);
final Button 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(getClass(), 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(getVElement().getDiagnostic().getHighestSeverity()));
validationIcon.setToolTipText(getVElement().getDiagnostic().getMessage());
setValidationColor(editControl, getValidationBackgroundColor(getVElement().getDiagnostic()
.getHighestSeverity()));
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer#rootDomainModelChanged()
*/
@Override
protected void rootDomainModelChanged() throws DatabindingFailedException {
// TODO change implementation to use databinding
}
}