org.eclipse.emf.ecp.view.internal.control.multireference.MultiReferenceSWTRenderer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.ecp.view.internal.control.multireference.MultiReferenceSWTRenderer.java

Source

/*******************************************************************************
 * 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 v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Eugen Neufeld - initial API and implementation
 * Lucas Koehler - use data binding services
 ******************************************************************************/
package org.eclipse.emf.ecp.view.internal.control.multireference;

import java.util.Collection;
import java.util.List;

import javax.inject.Inject;

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.emf.common.command.Command;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecp.edit.internal.swt.controls.TableViewerColumnBuilder;
import org.eclipse.emf.ecp.edit.spi.DeleteService;
import org.eclipse.emf.ecp.edit.spi.EMFDeleteServiceImpl;
import org.eclipse.emf.ecp.edit.spi.ReferenceService;
import org.eclipse.emf.ecp.view.model.common.edit.provider.CustomReflectiveItemProviderAdapterFactory;
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.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.spi.swt.reporting.RenderingFailedReport;
import org.eclipse.emf.ecp.view.spi.util.swt.ImageRegistryService;
import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
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.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.ObservableListContentProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TableViewerEditor;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Label;
import org.eclipse.swt.widgets.TableColumn;
import org.osgi.framework.FrameworkUtil;

/**
 * Renderer for MultiReferenceControl.
 *
 * @author Eugen Neufeld
 *
 */
@SuppressWarnings("restriction")
public class MultiReferenceSWTRenderer extends AbstractControlSWTRenderer<VControl> {

    private final ImageRegistryService imageRegistryService;

    /**
     * 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}
     * @param imageRegistryService The {@link ImageRegistryService}
     */
    @Inject
    public MultiReferenceSWTRenderer(VControl vElement, ViewModelContext viewContext, ReportService reportService,
            EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider,
            VTViewTemplateProvider vtViewTemplateProvider, ImageRegistryService imageRegistryService) {
        super(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider,
                vtViewTemplateProvider);
        this.imageRegistryService = imageRegistryService;
        viewModelDBC = new EMFDataBindingContext();
    }

    private Label validationIcon;
    private AdapterFactoryLabelProvider labelProvider;
    private ComposedAdapterFactory composedAdapterFactory;
    private TableViewer tableViewer;
    private final EMFDataBindingContext viewModelDBC;

    /**
     * {@inheritDoc}
     *
     * @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#getGridDescription(org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription)
     */
    @Override
    public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
        return GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 1, this);
    }

    /**
     * {@inheritDoc}
     *
     * @see org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer#renderControl(org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell,
     *      org.eclipse.emf.ecp.view.spi.swt.Composite)
     */
    @Override
    protected Control renderControl(SWTGridCell cell, Composite parent)
            throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
        if (cell.getRow() != 0 || cell.getColumn() != 0 || cell.getRenderer() != this) {
            throw new IllegalArgumentException("Wrong parameter passed!"); //$NON-NLS-1$
        }

        final Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(new GridLayout(1, false));
        composite.setBackgroundMode(SWT.INHERIT_FORCE);

        try {
            createTitleComposite(composite);
        } catch (final DatabindingFailedException ex) {
            getReportService().report(new RenderingFailedReport(ex));
            return createErrorLabel(parent, ex);
        }

        createLabelProvider();

        final Composite controlComposite = createControlComposite(composite);
        try {
            createContent(controlComposite);
        } catch (final DatabindingFailedException ex) {
            getReportService().report(new RenderingFailedReport(ex));
            return createErrorLabel(parent, ex);
        }

        return composite;
    }

    /**
     * Creates the composite which will be the parent for the table.
     *
     * @param composite
     *            the parent composite
     * @return the table composite
     */
    protected Composite createControlComposite(final Composite composite) {
        final Composite controlComposite = new Composite(composite, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).hint(1, getTableHeightHint())
                .applyTo(controlComposite);
        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(controlComposite);
        return controlComposite;
    }

    /**
     * Returns the height for the table that will be created.
     *
     * @return the height hint
     */
    protected int getTableHeightHint() {
        return 300;
    }

    /**
     * Gives access to the tableViewer used to display the attributes.
     *
     * @return the viewer
     */
    protected TableViewer getTableViewer() {
        return tableViewer;
    }

    /**
     * Creates an error label for the given {@link Exception}.
     *
     * @param parent The parent of the {@link Label}
     * @param ex The {@link Exception} causing the error
     * @return The error {@link Label}
     */
    protected Control createErrorLabel(Composite parent, final Exception ex) {
        final Label errorLabel = new Label(parent, SWT.NONE);
        errorLabel.setText(ex.getMessage());
        return errorLabel;
    }

    private void createLabelProvider() {
        composedAdapterFactory = new ComposedAdapterFactory(
                new AdapterFactory[] { new CustomReflectiveItemProviderAdapterFactory(),
                        new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
        labelProvider = new AdapterFactoryLabelProvider(composedAdapterFactory);
        labelProvider.setFireLabelUpdateNotifications(true);
    }

    /**
     * {@inheritDoc}
     *
     * @see org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer#dispose()
     */
    @Override
    protected void dispose() {
        composedAdapterFactory.dispose();
        labelProvider.dispose();
        viewModelDBC.dispose();
        super.dispose();
    }

    private void createTitleComposite(Composite composite)
            throws NoPropertyDescriptorFoundExeption, DatabindingFailedException {
        final Composite titleComposite = new Composite(composite, SWT.NONE);
        titleComposite.setBackgroundMode(SWT.INHERIT_FORCE);
        GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING).applyTo(titleComposite);
        GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(false).applyTo(titleComposite);

        final Label filler = new Label(titleComposite, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING).applyTo(filler);

        // VALIDATION
        // // set the size of the label to the size of the image
        validationIcon = createValidationIcon(titleComposite);
        GridDataFactory.fillDefaults().hint(16, 17).grab(false, false).applyTo(validationIcon);

        final Composite buttonComposite = new Composite(titleComposite, SWT.NONE);
        GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(true).applyTo(buttonComposite);
        GridDataFactory.fillDefaults().grab(true, false).align(SWT.END, SWT.FILL).applyTo(buttonComposite);

        final IObservableValue observableValue = getEMFFormsDatabinding().getObservableValue(
                getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
        final EObject eObject = (EObject) ((IObserving) observableValue).getObserved();
        final EStructuralFeature structuralFeature = (EStructuralFeature) observableValue.getValueType();
        observableValue.dispose();

        final Button btnAddExisting = new Button(buttonComposite, SWT.PUSH);
        GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(btnAddExisting);
        btnAddExisting.setImage(getImage("icons/link.png")); //$NON-NLS-1$
        btnAddExisting.setToolTipText(LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
                MessageKeys.MultiReferenceSWTRenderer_addExistingTooltip));
        btnAddExisting.addSelectionListener(new SelectionAdapter() {

            /**
             * {@inheritDoc}
             *
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);
                handleAddExisting(tableViewer, eObject, structuralFeature);
            }

        });

        final Button btnAddNew = new Button(buttonComposite, SWT.PUSH);
        GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(btnAddNew);
        btnAddNew.setImage(getImage("icons/link_add.png")); //$NON-NLS-1$
        btnAddNew.setToolTipText(LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
                MessageKeys.MultiReferenceSWTRenderer_addNewTooltip));
        btnAddNew.addSelectionListener(new SelectionAdapter() {

            /**
             * {@inheritDoc}
             *
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);
                handleAddNew(tableViewer, eObject, structuralFeature);
            }

        });

        final Button btnDelete = new Button(buttonComposite, SWT.PUSH);
        GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(btnDelete);
        btnDelete.setImage(getImage("icons/unset_reference.png")); //$NON-NLS-1$
        btnDelete.setToolTipText(LocalizationServiceHelper.getString(MultiReferenceSWTRenderer.class,
                MessageKeys.MultiReferenceSWTRenderer_deleteTooltip));
        btnDelete.addSelectionListener(new SelectionAdapter() {

            /**
             * {@inheritDoc}
             *
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);
                handleDelete(tableViewer, eObject, structuralFeature);
            }

        });
        if (getVElement().isReadonly()) {
            btnAddExisting.setEnabled(false);
            btnAddNew.setEnabled(false);
            btnDelete.setEnabled(false);
        }

    }

    /**
     * Returns an {@link Image} from the image registry.
     *
     * @param path
     *            the path to the image
     * @return the image
     */
    protected Image getImage(String path) {
        return imageRegistryService.getImage(FrameworkUtil.getBundle(MultiReferenceSWTRenderer.class), path);
    }

    private void createContent(Composite composite) throws DatabindingFailedException {
        tableViewer = new TableViewer(composite, SWT.MULTI | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
        tableViewer.getTable().setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_multireference"); //$NON-NLS-1$
        tableViewer.getTable().setHeaderVisible(true);
        tableViewer.getTable().setLinesVisible(true);

        final ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(
                tableViewer) {
            @Override
            protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
                return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
                        || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION
                        || event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED
                                && event.keyCode == SWT.CR
                        || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
            }
        };

        TableViewerEditor.create(tableViewer, null, actSupport,
                ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
                        | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);
        ColumnViewerToolTipSupport.enableFor(tableViewer);

        final ECPTableViewerComparator comparator = new ECPTableViewerComparator();
        tableViewer.setComparator(comparator);

        final ObservableListContentProvider cp = new ObservableListContentProvider();

        final EMFFormsLabelProvider labelService = getEMFFormsLabelProvider();

        final TableViewerColumn column = TableViewerColumnBuilder.create().setResizable(false).setMoveable(false)
                .setStyle(SWT.NONE).build(tableViewer);

        final IObservableValue textObservableValue = WidgetProperties.text().observe(column.getColumn());
        final IObservableValue tooltipObservableValue = WidgetProperties.tooltipText().observe(column.getColumn());
        try {
            viewModelDBC.bindValue(textObservableValue, labelService.getDisplayName(
                    getVElement().getDomainModelReference(), getViewModelContext().getDomainModel()));

            viewModelDBC.bindValue(tooltipObservableValue, labelService.getDescription(
                    getVElement().getDomainModelReference(), getViewModelContext().getDomainModel()));
        } catch (final NoLabelFoundException e) {
            // FIXME Expectations?
            getReportService().report(new RenderingFailedReport(e));
        }

        column.getColumn()
                .addSelectionListener(getSelectionAdapter(tableViewer, comparator, column.getColumn(), 0));

        tableViewer.setLabelProvider(labelProvider);
        tableViewer.setContentProvider(cp);
        final IObservableList list = getEMFFormsDatabinding()
                .getObservableList(getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
        tableViewer.setInput(list);

        final TableColumnLayout layout = new TableColumnLayout();
        composite.setLayout(layout);
        layout.setColumnData(column.getColumn(), new ColumnWeightData(1, false));

        tableViewer.addDoubleClickListener(new IDoubleClickListener() {

            @Override
            public void doubleClick(DoubleClickEvent event) {
                final EObject selectedObject = (EObject) IStructuredSelection.class.cast(event.getSelection())
                        .getFirstElement();
                handleDoubleClick(selectedObject);
            }

        });
    }

    private SelectionAdapter getSelectionAdapter(final TableViewer tableViewer,
            final ECPTableViewerComparator comparator, final TableColumn column, final int index) {
        final SelectionAdapter selectionAdapter = new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                comparator.setColumn(index);
                final int dir = comparator.getDirection();
                tableViewer.getTable().setSortDirection(dir);
                tableViewer.getTable().setSortColumn(column);
                tableViewer.refresh();
            }
        };
        return selectionAdapter;
    }

    /**
     * Method for handling a double click.
     *
     * @param selectedObject the selected {@link EObject}
     */
    protected void handleDoubleClick(EObject selectedObject) {
        final ReferenceService referenceService = getViewModelContext().getService(ReferenceService.class);
        referenceService.openInNewContext(selectedObject);
    }

    /**
     * Method for adding an existing element.
     *
     * @param tableViewer the {@link TableViewer}
     * @param eObject The {@link EObject} to add to
     * @param structuralFeature The corresponding {@link EStructuralFeature}
     */
    protected void handleAddExisting(TableViewer tableViewer, EObject eObject,
            EStructuralFeature structuralFeature) {
        final ReferenceService referenceService = getViewModelContext().getService(ReferenceService.class);
        referenceService.addExistingModelElements(eObject, (EReference) structuralFeature);
    }

    /**
     * Method for adding a new element.
     *
     * @param tableViewer the {@link TableViewer}
     * @param eObject The {@link EObject} to add to
     * @param structuralFeature The corresponding {@link EStructuralFeature}
     */
    protected void handleAddNew(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {
        final ReferenceService referenceService = getViewModelContext().getService(ReferenceService.class);
        referenceService.addNewModelElements(eObject, (EReference) structuralFeature);

    }

    /**
     * Method for deleting elements.
     *
     * @param tableViewer the {@link TableViewer}
     * @param eObject The {@link EObject} to delete from
     * @param structuralFeature The corresponding {@link EStructuralFeature}
     */
    protected void handleDelete(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {

        @SuppressWarnings("unchecked")
        final List<Object> deletionList = IStructuredSelection.class.cast(tableViewer.getSelection()).toList();
        final EditingDomain editingDomain = getEditingDomain(eObject);

        /* assured by #isApplicable */
        final EReference reference = EReference.class.cast(structuralFeature);

        if (reference.isContainment()) {
            DeleteService deleteService = getViewModelContext().getService(DeleteService.class);
            if (deleteService == null) {
                /*
                 * #getService(Class<?>) will report to the reportservice if it could not be found
                 * Use Default
                 */
                deleteService = new EMFDeleteServiceImpl();
            }
            deleteService.deleteElements(deletionList);
        } else {
            removeElements(editingDomain, eObject, reference, deletionList);
        }
    }

    private void removeElements(EditingDomain editingDomain, Object source, EStructuralFeature feature,
            Collection<Object> toRemove) {
        final Command removeCommand = RemoveCommand.create(editingDomain, source, feature, toRemove);
        if (removeCommand.canExecute()) {
            if (editingDomain.getCommandStack() == null) {
                removeCommand.execute();
            } else {
                editingDomain.getCommandStack().execute(removeCommand);
            }
        }
    }

    /**
     * The {@link ViewerComparator} for this table which allows 3 states for sort order:
     * none, up and down.
     *
     * @author Eugen Neufeld
     *
     */
    private class ECPTableViewerComparator extends ViewerComparator {
        private int propertyIndex;
        private static final int NONE = 0;
        private int direction = NONE;

        public ECPTableViewerComparator() {
            propertyIndex = 0;
            direction = NONE;
        }

        public int getDirection() {
            switch (direction) {
            case 0:
                return SWT.NONE;
            case 1:
                return SWT.UP;
            case 2:
                return SWT.DOWN;
            default:
                return SWT.NONE;
            }

        }

        public void setColumn(int column) {
            if (column == propertyIndex) {
                // Same column as last sort; toggle the direction
                direction = (direction + 1) % 3;
            } else {
                // New column; do an ascending sort
                propertyIndex = column;
                direction = 1;
            }
        }

        @Override
        public int compare(Viewer viewer, Object e1, Object e2) {
            if (direction == 0) {
                return 0;
            }
            int rc = 0;
            final EObject object1 = (EObject) e1;
            final EObject object2 = (EObject) e2;
            final EStructuralFeature feat1 = object1.eClass().getEAllStructuralFeatures().get(propertyIndex);
            final EStructuralFeature feat2 = object2.eClass().getEAllStructuralFeatures().get(propertyIndex);

            final Object value1 = object1.eGet(feat1);
            final Object value2 = object2.eGet(feat2);

            if (value1 == null) {
                rc = 1;
            } else if (value2 == null) {
                rc = -1;
            } else {
                rc = value1.toString().compareTo(value2.toString());
            }
            // If descending order, flip the direction
            if (direction == 2) {
                rc = -rc;
            }
            return rc;
        }
    }
}