Skip to content

Content of file SWTControl.java

/*******************************************************************************
 * Copyright (c) 2011-2013 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
 *
 *******************************************************************************/
package org.eclipse.emf.ecp.edit.internal.swt.util;

import java.util.Collections;
import java.util.List;

import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.databinding.edit.EMFEditObservables;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecp.edit.internal.swt.Activator;
import org.eclipse.emf.ecp.edit.spi.ECPAbstractControl;
import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
import org.eclipse.emf.ecp.view.spi.renderer.RenderingResultRow;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
import org.eclipse.emfforms.spi.swt.core.ui.SWTValidationHelper;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridLayout;
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;

/**
 * This class defines a SWTCOntrol which is an abstract class defining an {@link ECPAbstractControl} for SWT.
 *
 * @author Eugen Neufeld
 *
 */
@Deprecated
public abstract class SWTControl extends ECPAbstractControl implements ECPControlSWT {

	/**
	 * RAP theming variable to set.
	 */
	protected static final String CUSTOM_VARIANT = "org.eclipse.rap.rwt.customVariant";//$NON-NLS-1$
	/**
	 * The icon for a validation error.
	 */
	protected static final String VALIDATION_ERROR_ICON = "icons/validation_error.png";//$NON-NLS-1$

	private static final String ICONS_UNSET_FEATURE = "icons/unset_feature.png"; //$NON-NLS-1$

	/**
	 * The label for the validation icon.
	 */
	protected Label validationLabel;

	private IObservableValue modelValue;
	private Binding binding;

	private Composite controlComposite;
	private Composite parentComposite;
	private StackLayout sl;
	private Label unsetLabel;

	/**
	 *
	 * {@inheritDoc}
	 *
	 * @see org.eclipse.emf.ecp.edit.internal.swt.util.ECPControlSWT#createControls(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	public List<RenderingResultRow<Control>> createControls(final Composite parent) {
		final IItemPropertyDescriptor itemPropertyDescriptor = getItemPropertyDescriptor(getFirstSetting());
		if (itemPropertyDescriptor == null) {
			return null;
		}
		parent.addDisposeListener(new DisposeListener() {

			@Override
			public void widgetDisposed(DisposeEvent e) {
				dispose();
			}
		});
		createValidationIcon(parent);
		final List<RenderingResultRow<Control>> list = Collections.singletonList(SWTRenderingHelper.INSTANCE
			.getResultRowFactory().createRenderingResultRow(
				validationLabel, createControl(parent)));

		applyValidation(getControl().getDiagnostic());

		// TODO remove asap
		backwardCompatibleHandleValidation();

		return list;

	}

	/**
	 * This method is called to render the control on a parent.
	 *
	 * @param parent the {@link Composite} which is the parent
	 * @return the created {@link Composite}
	 */
	public Composite createControl(final Composite parent) {

		final Composite dataControl = createDataControl(parent);

		setHelpTooltips();

		// init
		setEditable(isEditable());

		binding = bindValue();

		// write initial values to model (if they differ from the default value of the model-element)
		if (!getFirstStructuralFeature().isUnsettable()
			&& !getFirstSetting().isSet()) {
			if (binding != null) {
				binding.updateTargetToModel();
			}
		}

		return dataControl;
	}

	private void createValidationIcon(Composite composite) {
		if (!isEmbedded()) {
			validationLabel = new Label(composite, SWT.NONE);
			validationLabel.setBackground(composite.getBackground());
			// set the size of the label to the size of the image
			// GridDataFactory.fillDefaults().hint(16, 17).applyTo(validationLabel);
		}
	}

	private Composite createDataControl(Composite composite) {
		final Composite innerComposite = new Composite(composite, SWT.NONE);
		innerComposite.setBackground(composite.getBackground());
		// GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING).applyTo(innerComposite);
		GridLayoutFactory.fillDefaults().numColumns(1).applyTo(innerComposite);
		createContentControl(innerComposite);
		return innerComposite;
	}

	private void setHelpTooltips() {
		final Control[] controls = getControlsForTooltip();
		if (controls != null) {
			for (final Control control : controls) {
				control.setToolTipText(getHelpText());
			}
		}
	}

	/**
	 * Method for retrieving all controls which should have the help text as their tooltip.
	 *
	 * @return the array of the controls to set a tooltip to
	 */
	protected abstract Control[] getControlsForTooltip();

	/**
	 * Helper for creating the unset stacklayout and creating the control's composite.
	 *
	 * @param composite the parent {@link Composite} to create the control onto
	 */
	protected void createContentControl(Composite composite) {
		parentComposite = new Composite(composite, SWT.NONE);
		parentComposite.setBackground(composite.getBackground());
		GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(parentComposite);
		sl = new StackLayout();
		parentComposite.setLayout(sl);
		controlComposite = new Composite(parentComposite, SWT.NONE);
		controlComposite.setBackground(parentComposite.getBackground());

		// 1 column for control, 1 for default unset button
		GridLayoutFactory.fillDefaults().numColumns(1).spacing(2, 0)
			.applyTo(controlComposite);

		unsetLabel = new Label(parentComposite, SWT.NONE);
		unsetLabel.setBackground(composite.getBackground());
		unsetLabel.setForeground(composite.getShell().getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
		unsetLabel.setAlignment(SWT.CENTER);
		unsetLabel.setText(getUnsetLabelText());
		unsetLabel.addMouseListener(new MouseListener() {

			@Override
			public void mouseUp(MouseEvent e) {
				sl.topControl = controlComposite;
				parentComposite.layout(true);
				if (binding != null) {
					binding.updateTargetToModel();
				} else {
					// TODO editingdomain is missing
					final Setting firstSetting = getFirstSetting();
					final Object currentUnsetValue = firstSetting.get(true);
					firstSetting.set(currentUnsetValue);
				}
			}

			@Override
			public void mouseDown(MouseEvent e) {
				// nothing to do
			}

			@Override
			public void mouseDoubleClick(MouseEvent e) {
				// nothing to do
			}
		});
		int numControls = 1;
		fillControlComposite(controlComposite);

		if (!isEmbedded() && getFirstStructuralFeature().isUnsettable()) {
			Button unsetButton = getCustomUnsetButton();
			if (unsetButton == null) {
				numControls++;
				unsetButton = new Button(controlComposite, SWT.PUSH);
				unsetButton.setToolTipText(getUnsetButtonTooltip());
				unsetButton.setImage(Activator.getImage(ICONS_UNSET_FEATURE));
			}
			unsetButton.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					final Setting firstSetting = getFirstSetting();
					final EditingDomain editingDomain = getEditingDomain(firstSetting);
					editingDomain.getCommandStack()
						.execute(
							new SetCommand(editingDomain, firstSetting.getEObject(), firstSetting
								.getEStructuralFeature(), SetCommand.UNSET_VALUE));

					showUnsetLabel();
				}
			});
			unsetButton.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_unset"); //$NON-NLS-1$
		}
		if (numControls != 1) {
			// 1 column for control, 1 for default unset button
			((GridLayout) controlComposite.getLayout()).numColumns = numControls;
		}

		// INFO margin needed for controlDecorator
		// .extendedMargins(10, 0, 0, 0)

		if (!getFirstStructuralFeature().isUnsettable()
			|| getFirstSetting().isSet()) {
			sl.topControl = controlComposite;
		} else {
			sl.topControl = unsetLabel;
		}

		parentComposite.layout();
	}

	/**
	 *
	 */
	protected void showUnsetLabel() {
		sl.topControl = unsetLabel;
		parentComposite.layout();
	}

	/**
	 * This method must be overridden by concrete classes. Here the widget displaying the data is added to the
	 * composite.
	 *
	 * @param controlComposite the {@link Composite} to add the widget to
	 */
	protected abstract void fillControlComposite(Composite controlComposite);

	/**
	 * The default unset button will be displayed to the right of the control's composite. Concrete classes may override
	 * this method to include an own unset button in their composite rather than using the default positioning.
	 *
	 * @return The custom unset button of the concrete class
	 */
	protected Button getCustomUnsetButton() {
		return null;
	}

	/**
	 * The model value used for databinding. It is either the set one or the calculated.
	 *
	 * @return the {@link IObservableValue}
	 */
	protected IObservableValue getModelValue() {
		if (modelValue != null) {
			return modelValue;
		}
		final Setting setting = getFirstSetting();
		modelValue = EMFEditObservables.observeValue(getEditingDomain(setting),
			setting.getEObject(), setting.getEStructuralFeature());
		return modelValue;
	}

	/**
	 * Allows the user to set the {@link IObservableValue} to use in the control during databinding.
	 *
	 * @param modelValue the set {@link IObservableValue}
	 */
	public void setObservableValue(IObservableValue modelValue) {
this.modelValue = modelValue; } /** * A helper method which creates a button for an action on a composite. * * @param action the action to create a button for * @param composite the composite to create the button onto * @return the created button */ protected Button createButtonForAction(final Action action, final Composite composite) { final Button selectButton = new Button(composite, SWT.PUSH); selectButton.setImage(Activator.getImage(action)); selectButton.setEnabled(!getControl().isReadonly()); selectButton.setToolTipText(action.getToolTipText()); selectButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { action.run(); composite.layout(); } }); return selectButton; } /** * Triggers the control to perform the databinding. * * @return The {@link Binding} */ protected abstract Binding bindValue(); /** * Returns the help information. * * @return The help text */ protected String getHelpText() { return getItemPropertyDescriptor(getFirstSetting()).getDescription(null); } /** * Returns the validation icon matching the given severity. * * @param severity the severity of the {@link org.eclipse.emf.common.util.Diagnostic} * @return the icon to be displayed, or <code>null</code> when no icon is to be displayed */ protected Image getValidationIcon(int severity) { return SWTValidationHelper.INSTANCE.getValidationIcon(severity); } /** * Returns the background color for a control with the given validation severity. * * @param severity severity the severity of the {@link org.eclipse.emf.common.util.Diagnostic} * @return the color to be used as a background color */ protected Color getValidationBackgroundColor(int severity) { return SWTValidationHelper.INSTANCE.getValidationBackgroundColor(severity); } /** * Returns the string for the unset label. * * @return The unset label text */ protected abstract String getUnsetLabelText(); /** * Returns the string for the unset button tooltip. * * @return The unset button tooltip */ protected abstract String getUnsetButtonTooltip(); /** * Method for retrieving a {@link Color} based on the predefined SWT id. * * @param color the SWT id of the color * @return the Color or black if the id was incorrect */ protected final Color getSystemColor(int color) { if (parentComposite == null) { return Display.getDefault().getSystemColor(color); } return parentComposite.getShell().getDisplay().getSystemColor(color); } /** * Whether this control should be editable. * * @return true if the {@link IItemPropertyDescriptor#canSetProperty(Object)} returns true, false otherwise */ @Deprecated protected boolean isEditable() { final Setting firstSetting = getFirstSetting(); return getItemPropertyDescriptor(firstSetting).canSetProperty(firstSetting.getEObject()); } @Override public void dispose() { validationLabel = null; if (modelValue != null) { modelValue.dispose(); modelValue = null; } if (binding != null) { binding.dispose(); binding = null; } if (controlComposite != null) { controlComposite.dispose(); controlComposite = null; } if (parentComposite != null) { parentComposite.dispose(); parentComposite = null; } sl = null; if (unsetLabel != null) { unsetLabel.dispose(); unsetLabel = null; } super.dispose(); } /** * Helper method to keep the old validation. * * @since 1.2 */ @Override protected void backwardCompatibleHandleValidation() { final VDiagnostic diagnostic = getControl().getDiagnostic(); if (diagnostic == null) { return; } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { if (getControl() == null) { return; } resetValidation(); for (final Object object : diagnostic.getDiagnostics()) { handleValidation((Diagnostic) object); } } }); } }