blob: dce6498edc374d26b65191af9233ae03d8f865d9 [file] [log] [blame]
/*******************************************************************************
* 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:
* Eugen - initial API and implementation
* Johannes Faltermeier - sorting + drag&drop
* Christian W. Damus - bug 548592
*
******************************************************************************/
package org.eclipse.emf.ecp.view.internal.editor.controls;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import javax.inject.Inject;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.Observables;
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.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecp.common.spi.EMFUtils;
import org.eclipse.emf.ecp.spi.common.ui.CompositeFactory;
import org.eclipse.emf.ecp.spi.common.ui.composites.SelectionComposite;
import org.eclipse.emf.ecp.view.internal.editor.handler.CreateDomainModelReferenceWizard;
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.SimpleControlSWTRenderer;
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.VViewPackage;
import org.eclipse.emf.ecp.view.spi.swt.reporting.RenderingFailedReport;
import org.eclipse.emf.ecp.view.spi.table.model.VTableColumnConfiguration;
import org.eclipse.emf.ecp.view.spi.table.model.VTableControl;
import org.eclipse.emf.ecp.view.spi.table.model.VTableDomainModelReference;
import org.eclipse.emf.ecp.view.spi.table.model.VTablePackage;
import org.eclipse.emf.ecp.view.spi.table.model.VWidthConfiguration;
import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
import org.eclipse.emf.edit.command.DeleteCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.ui.dnd.EditingDomainViewerDropAdapter;
import org.eclipse.emf.edit.ui.dnd.LocalTransfer;
import org.eclipse.emf.edit.ui.dnd.ViewerDragAdapter;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emfforms.spi.common.report.AbstractReport;
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.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.SWTGridDescription;
import org.eclipse.jface.databinding.swt.typed.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.ColumnWeightData;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.TableColumn;
/**
* @author Eugen
*
*/
public class TableColumnsDMRTableControl extends SimpleControlSWTRenderer {
private final EMFDataBindingContext viewModelDBC;
/**
* Default constructor.
*
* @param vElement the view model element to be rendered
* @param viewContext the view context
* @param reportService The {@link ReportService}
* @param emfFormsDatabinding The {@link EMFFormsDatabinding}
* @param emfFormsLabelProvider The {@link EMFFormsLabelProvider}
* @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
*/
@Inject
public TableColumnsDMRTableControl(VControl vElement, ViewModelContext viewContext, ReportService reportService,
EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider,
VTViewTemplateProvider vtViewTemplateProvider) {
super(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider);
viewModelDBC = new EMFDataBindingContext();
}
private ComposedAdapterFactory composedAdapterFactory;
private AdapterFactoryLabelProvider labelProvider;
private AdapterImpl adapter;
private VTableControl tableControl;
private EStructuralFeature structuralFeature;
private EObject eObject;
private TableViewer viewer;
private SWTGridDescription rendererGridDescription;
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTRenderer#createControl(org.eclipse.swt.widgets.Composite)
*/
// BEGIN COMPLEX CODE
@Override
protected Control createControl(final Composite parent) throws DatabindingFailedException {
tableControl = (VTableControl) getViewModelContext().getDomainModel();
final IObservableValue observableValue = getEMFFormsDatabinding()
.getObservableValue(getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
structuralFeature = (EStructuralFeature) observableValue.getValueType();
eObject = (EObject) ((IObserving) observableValue).getObserved();
observableValue.dispose();
final Composite composite = new Composite(parent, SWT.NONE);
composite.setBackgroundMode(SWT.INHERIT_FORCE);
GridLayoutFactory.fillDefaults().numColumns(1).applyTo(composite);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(composite);
final Composite titleComposite = new Composite(composite, SWT.NONE);
titleComposite.setBackgroundMode(SWT.INHERIT_FORCE);
GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).applyTo(titleComposite);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.BEGINNING).grab(true, false).applyTo(titleComposite);
final Label filler = new Label(titleComposite, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(filler);
final Composite buttonComposite = new Composite(titleComposite, SWT.NONE);
buttonComposite.setBackgroundMode(SWT.INHERIT_FORCE);
GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(true).applyTo(buttonComposite);
GridDataFactory.fillDefaults().align(SWT.END, SWT.BEGINNING).grab(false, false).applyTo(buttonComposite);
final Button buttonSort = new Button(buttonComposite, SWT.PUSH);
buttonSort.setText("Sort"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(false, false).applyTo(buttonSort);
final Button buttonAdd = new Button(buttonComposite, SWT.PUSH);
buttonAdd.setText("Add"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(false, false).applyTo(buttonAdd);
final Button buttonRemove = new Button(buttonComposite, SWT.PUSH);
buttonRemove.setText("Remove"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(false, false).applyTo(buttonRemove);
final Composite tableComposite = new Composite(composite, SWT.NONE);
tableComposite.setBackgroundMode(SWT.INHERIT_FORCE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).hint(1, 100)
.applyTo(tableComposite);
final TableColumnLayout layout = new TableColumnLayout();
tableComposite.setLayout(layout);
viewer = new TableViewer(tableComposite);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(viewer.getControl());
viewer.getTable().setHeaderVisible(true);
viewer.getTable().setLinesVisible(true);
final TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
final TableColumn tableColumn = column.getColumn();
final EMFFormsLabelProvider emfFormsLabelProvider = getEMFFormsLabelProvider();// TODO
try {
final IObservableValue labelText = emfFormsLabelProvider.getDisplayName(
getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
final IObservableValue tooltip = emfFormsLabelProvider.getDescription(
getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
viewModelDBC.bindValue(WidgetProperties.text().observe(tableColumn), labelText);
viewModelDBC.bindValue(WidgetProperties.tooltipText().observe(tableColumn), tooltip);
} catch (final NoLabelFoundException e) {
// FIXME Expectations?
getReportService().report(new RenderingFailedReport(e));
tableColumn.setText(e.getMessage());
tableColumn.setToolTipText(e.toString());
}
layout.setColumnData(column.getColumn(), new ColumnWeightData(1, true));
viewer.setLabelProvider(labelProvider);
viewer.setContentProvider(new ObservableListContentProvider());
addDragAndDropSupport(viewer, getEditingDomain(eObject));
final IObservableList list = getEMFFormsDatabinding()
.getObservableList(getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
viewer.setInput(list);
adapter = new TableControlAdapter(parent, viewer);
tableControl.eAdapters().add(adapter);
buttonSort.addSelectionListener(new SortSelectionAdapter());
buttonAdd.addSelectionListener(new AddSelectionAdapter(tableComposite, viewer));
buttonRemove.addSelectionListener(new RemoveSelectionAdapter(viewer));
return composite;
}
// END COMPLEX CODE
/**
* @param viewer
* @param editingDomain
*/
private void addDragAndDropSupport(TableViewer viewer, EditingDomain editingDomain) {
final int dndOperations = DND.DROP_MOVE;
final Transfer[] transfers = new Transfer[] { LocalTransfer.getInstance() };
viewer.addDragSupport(dndOperations, transfers, new ViewerDragAdapter(viewer));
final EditingDomainViewerDropAdapter editingDomainViewerDropAdapter = new EditingDomainViewerDropAdapter(
editingDomain, viewer);
viewer.addDropSupport(dndOperations, transfers, editingDomainViewerDropAdapter);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer#postInit()
*/
@Override
protected void postInit() {
super.postInit();
composedAdapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {
new CustomReflectiveItemProviderAdapterFactory(),
new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE) });
labelProvider = new TableColumnsLabelProvider(composedAdapterFactory);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTRenderer#dispose()
*/
@Override
protected void dispose() {
labelProvider.dispose();
composedAdapterFactory.dispose();
tableControl.eAdapters().remove(adapter);
viewModelDBC.dispose();
super.dispose();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTRenderer#getUnsetText()
*/
@Override
protected String getUnsetText() {
return "No columns set"; //$NON-NLS-1$
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.AbstractControlSWTRenderer#rootDomainModelChanged()
*/
@Override
protected void rootDomainModelChanged() throws DatabindingFailedException {
final IObservableList oldList = (IObservableList) viewer.getInput();
oldList.dispose();
final IObservableList list = getEMFFormsDatabinding().getObservableList(getVElement().getDomainModelReference(),
getViewModelContext().getDomainModel());
// addRelayoutListenerIfNeeded(list, composite);
viewer.setInput(list);
}
/**
* Select and reveal an {@code object} in my table.
*
* @param object an object to reveal
*
* @since 1.22
*/
void reveal(Object object) {
checkRenderer();
if (viewer != null) {
viewer.setSelection(new StructuredSelection(object), true);
}
}
/**
* Adapter set on the {@link VTableControl}.
*
*/
private final class TableControlAdapter extends AdapterImpl {
private final Composite parent;
private final TableViewer viewer;
/**
* @param parent
* @param viewer
*/
private TableControlAdapter(Composite parent, TableViewer viewer) {
this.parent = parent;
this.viewer = viewer;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecore.util.EContentAdapter#notifyChanged(org.eclipse.emf.common.notify.Notification)
*/
@Override
public void notifyChanged(Notification notification) {
super.notifyChanged(notification);
if (notification.getFeature() == VTablePackage.eINSTANCE
.getTableDomainModelReference_ColumnDomainModelReferences()) {
viewer.refresh();
parent.layout();
}
if (VTableDomainModelReference.class.isInstance(notification.getNotifier())) {
updateEObjectAndStructuralFeature();
viewer.refresh();
parent.layout();
}
if (VTableControl.class.isInstance(notification.getNotifier())
&& (VTableDomainModelReference.class.isInstance(notification.getNewValue())
|| VTableDomainModelReference.class
.isInstance(notification.getOldValue()))) {
updateEObjectAndStructuralFeature();
viewer.refresh();
parent.layout();
}
}
private void updateEObjectAndStructuralFeature() {
IObservableValue observableValue;
IObservableList list;
try {
observableValue = getEMFFormsDatabinding()
.getObservableValue(getVElement().getDomainModelReference(),
getViewModelContext().getDomainModel());
list = getEMFFormsDatabinding()
.getObservableList(getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
} catch (final DatabindingFailedException ex) {
getReportService().report(new DatabindingFailedReport(ex));
viewer.setInput(Observables.emptyObservableList());
return;
}
structuralFeature = (EStructuralFeature) observableValue.getValueType();
eObject = (EObject) ((IObserving) observableValue).getObserved();
observableValue.dispose();
viewer.setInput(list);
}
}
/**
* Reacts on remove button clicks.
*
*/
private final class RemoveSelectionAdapter extends SelectionAdapter {
private final TableViewer viewer;
/**
* @param viewer
*/
private RemoveSelectionAdapter(TableViewer viewer) {
this.viewer = viewer;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
final IStructuredSelection selection = IStructuredSelection.class.cast(viewer.getSelection());
/* use a delete command as we are the container and thus may leave a dangling hrefs */
final EditingDomain editingDomain = getEditingDomain(eObject);
editingDomain.getCommandStack().execute(DeleteCommand.create(editingDomain, selection.toList()));
}
}
/**
* Reacts on add button clicks.
*
*/
private final class AddSelectionAdapter extends SelectionAdapter {
private final Composite tableComposite;
private final TableViewer viewer;
/**
* @param tableComposite
* @param viewer
*/
private AddSelectionAdapter(Composite tableComposite, TableViewer viewer) {
this.tableComposite = tableComposite;
this.viewer = viewer;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
final VTableDomainModelReference tableDomainModelReference = VTableDomainModelReference.class
.cast(eObject);
if (tableDomainModelReference == null) {
getReportService()
.report(new AbstractReport("Cannot add column. Table DMR is null.")); //$NON-NLS-1$
return;
}
IValueProperty valueProperty;
try {
valueProperty = getEMFFormsDatabinding()
.getValueProperty(
tableDomainModelReference.getDomainModelReference() == null ? tableDomainModelReference
: tableDomainModelReference.getDomainModelReference(),
getViewModelContext().getDomainModel());
} catch (final DatabindingFailedException ex) {
getReportService().report(new DatabindingFailedReport(ex));
return;
}
final EClass eclass = EReference.class.cast(valueProperty.getValueType()).getEReferenceType();
final Collection<EClass> classes = EMFUtils.getSubClasses(VViewPackage.eINSTANCE
.getDomainModelReference());
// Don't allow to create a plain DMR legacy mode
classes.remove(VViewPackage.Literals.DOMAIN_MODEL_REFERENCE);
final CreateDomainModelReferenceWizard wizard = new CreateDomainModelReferenceWizard(
eObject, structuralFeature, getEditingDomain(eObject), eclass, "New Reference Element", //$NON-NLS-1$
LocalizationServiceHelper.getString(TableColumnsDMRTableControl.class,
"NewModelElementWizard_WizardTitle_AddModelElement"), //$NON-NLS-1$
LocalizationServiceHelper.getString(TableColumnsDMRTableControl.class,
"NewModelElementWizard_PageTitle_AddModelElement"), //$NON-NLS-1$
LocalizationServiceHelper.getString(TableColumnsDMRTableControl.class,
"NewModelElementWizard_PageDescription_AddModelElement"), //$NON-NLS-1$
(VDomainModelReference) IStructuredSelection.class.cast(viewer.getSelection()).getFirstElement());
final SelectionComposite<TreeViewer> helper = CompositeFactory.getSelectModelClassComposite(
new HashSet<EPackage>(),
new HashSet<EPackage>(), classes);
wizard.setCompositeProvider(helper);
final WizardDialog wd = new WizardDialog(Display.getDefault().getActiveShell(), wizard);
wd.open();
tableComposite.layout();
}
}
/**
* Reacts on sort button clicks.
*
* @author jfaltermeier
*
*/
private final class SortSelectionAdapter extends SelectionAdapter {
private boolean down;
/**
* {@inheritDoc}
*
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
down = !down;
// EMF API
@SuppressWarnings("unchecked")
final List<VDomainModelReference> list = new ArrayList<VDomainModelReference>(
(List<VDomainModelReference>) eObject.eGet(structuralFeature, true));
Collections.sort(list, new Comparator<VDomainModelReference>() {
@Override
public int compare(VDomainModelReference o1, VDomainModelReference o2) {
final String label1 = labelProvider.getText(o1);
final String label2 = labelProvider.getText(o2);
int result = label1.compareTo(label2);
if (!down) {
result *= -1;
}
return result;
}
});
final EditingDomain editingDomain = getEditingDomain(eObject);
editingDomain.getCommandStack().execute(
SetCommand.create(editingDomain, eObject, structuralFeature, list));
}
}
@Override
public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
if (rendererGridDescription == null) {
rendererGridDescription = GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 3, this);
rendererGridDescription.getGrid().get(0).setHorizontalGrab(false);
rendererGridDescription.getGrid().get(1).setHorizontalGrab(false);
}
return rendererGridDescription;
}
/**
* The label provider.
*
* @author Johannes Faltermeier
*
*/
private final class TableColumnsLabelProvider extends AdapterFactoryLabelProvider {
private TableColumnsLabelProvider(AdapterFactory adapterFactory) {
super(adapterFactory);
}
@Override
public String getColumnText(Object object, int columnIndex) {
final String text = super.getColumnText(object, columnIndex);
if (columnIndex == 0 && VDomainModelReference.class.isInstance(object)) {
for (final VTableColumnConfiguration configuration : tableControl.getColumnConfigurations()) {
if (!VWidthConfiguration.class.isInstance(configuration)) {
continue;
}
final VWidthConfiguration widthConfiguration = VWidthConfiguration.class.cast(configuration);
if (widthConfiguration.getColumnDomainReference() != object) {
continue;
}
return MessageFormat.format(
"{0} [minWidth={1}, weight={2}]", //$NON-NLS-1$
text,
widthConfiguration.getMinWidth(),
widthConfiguration.getWeight());
}
}
return text;
}
}
}