blob: 790d78150505b97539e33702df9a527a8d84e7a2 [file] [log] [blame]
/*******************************************************************************
* 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 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
*
*******************************************************************************/
package org.eclipse.emf.ecp.edit.internal.swt.controls;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.databinding.EMFProperties;
import org.eclipse.emf.databinding.edit.EMFEditObservables;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecp.edit.ECPControlContext;
import org.eclipse.emf.ecp.edit.internal.swt.Activator;
import org.eclipse.emf.ecp.edit.internal.swt.util.CellEditorFactory;
import org.eclipse.emf.ecp.edit.internal.swt.util.ECPCellEditor;
import org.eclipse.emf.ecp.edit.internal.swt.util.SWTControl;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.AdapterFactoryItemDelegator;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableMapCellLabelProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener;
import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
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.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.TableColumn;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* The class describing a table control.
*
* @author Eugen Neufeld
*
*/
public class TableControl extends SWTControl {
private TableViewer tableViewer;
private ComposedAdapterFactory composedAdapterFactory;
private AdapterFactoryItemDelegator adapterFactoryItemDelegator;
private IObservableList list;
/**
* Constructor for a String control.
*
* @param showLabel whether to show a label
* @param itemPropertyDescriptor the {@link IItemPropertyDescriptor} to use
* @param feature the {@link EStructuralFeature} to use
* @param modelElementContext the {@link ECPControlContext} to use
* @param embedded whether this control is embedded in another control
*/
public TableControl(boolean showLabel, IItemPropertyDescriptor itemPropertyDescriptor, EStructuralFeature feature,
ECPControlContext modelElementContext, boolean embedded) {
super(showLabel, itemPropertyDescriptor, feature, modelElementContext, embedded);
}
@Override
protected void bindValue() {
// Not necessary
}
@Override
public Composite createControl(Composite parent) {
composedAdapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
adapterFactoryItemDelegator = new AdapterFactoryItemDelegator(composedAdapterFactory);
EClass clazz = ((EReference) getStructuralFeature()).getEReferenceType();
final Composite parentComposite = new Composite(parent, SWT.NONE);
parentComposite.setLayout(new GridLayout(2, false));
final Composite composite = new Composite(parentComposite, SWT.NONE);
GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING).hint(SWT.DEFAULT, 200)
.applyTo(composite);
composite.setLayout(new FillLayout(SWT.HORIZONTAL | SWT.VERTICAL));
tableViewer = new TableViewer(composite, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION
| SWT.BORDER);
tableViewer.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_swt_table");
tableViewer.getTable().setHeaderVisible(true);
tableViewer.getTable().setLinesVisible(true);
// 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.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
// }
// };
//
// TableViewerEditor.create(tableViewer, actSupport, ColumnViewerEditor.TABBING_HORIZONTAL
// | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR | ColumnViewerEditor.TABBING_VERTICAL
// | ColumnViewerEditor.KEEP_EDITOR_ON_DOUBLE_CLICK | ColumnViewerEditor.KEYBOARD_ACTIVATION);
// create a content provider
ObservableListContentProvider cp = new ObservableListContentProvider();
EObject tempInstance = clazz.getEPackage().getEFactoryInstance().create(clazz);
for (final EStructuralFeature feature : clazz.getEAllStructuralFeatures()) {
IItemPropertyDescriptor itemPropertyDescriptor = adapterFactoryItemDelegator.getPropertyDescriptor(
tempInstance, feature);
final CellEditor cellEditor = CellEditorFactory.INSTANCE.getCellEditor(itemPropertyDescriptor,
tempInstance, tableViewer.getTable());
// create a new column
TableViewerColumn column = new TableViewerColumn(tableViewer, cellEditor.getStyle());
// determine the attribute that should be observed
IObservableMap map = EMFProperties.value(feature).observeDetail(cp.getKnownElements());
column.setLabelProvider(new ObservableMapCellLabelProvider(map) {
@Override
public void update(ViewerCell cell) {
Object element = cell.getElement();
Object value = attributeMaps[0].get(element);
if (ECPCellEditor.class.isInstance(cellEditor)) {
ECPCellEditor ecpCellEditor = (ECPCellEditor) cellEditor;
String text = ecpCellEditor.getFormatedString(value);
cell.setText(text == null ? "" : text);
} else {
cell.setText(value == null ? "" : value.toString()); //$NON-NLS-1$
}
}
});
// set the column title & set the size
column.getColumn().setText(itemPropertyDescriptor.getDisplayName(null));
column.getColumn().setToolTipText(itemPropertyDescriptor.getDescription(null));
column.getColumn().setResizable(false);
column.getColumn().setMoveable(false);
EditingSupport observableSupport = new EditingSupport(tableViewer) {
private EditingState editingState;
private final ColumnViewerEditorActivationListenerHelper activationListener = new ColumnViewerEditorActivationListenerHelper();
/**
*
* Default implementation always returns <code>true</code>.
*
* @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object)
*/
@Override
protected boolean canEdit(Object element) {
return true;
}
/**
* Default implementation always returns <code>null</code> as this will be
* handled by the Binding.
*
* @see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object)
*/
@Override
protected Object getValue(Object element) {
// no op
return null;
}
/**
* Default implementation does nothing as this will be handled by the
* Binding.
*
* @see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object, java.lang.Object)
*/
@Override
protected void setValue(Object element, Object value) {
// no op
}
/**
* Creates a {@link Binding} between the editor and the element to be
* edited. Invokes {@link #doCreateCellEditorObservable(CellEditor)},
* {@link #doCreateElementObservable(Object, ViewerCell)}, and then
* {@link #createBinding(IObservableValue, IObservableValue)}.
*/
@Override
protected void initializeCellEditorValue(CellEditor cellEditor, ViewerCell cell) {
IObservableValue target = doCreateCellEditorObservable(cellEditor);
Assert.isNotNull(target, "doCreateCellEditorObservable(...) did not return an observable"); //$NON-NLS-1$
IObservableValue model = doCreateElementObservable(cell.getElement(), cell);
Assert.isNotNull(model, "doCreateElementObservable(...) did not return an observable"); //$NON-NLS-1$
Binding binding = createBinding(target, model);
Assert.isNotNull(binding, "createBinding(...) did not return a binding"); //$NON-NLS-1$
editingState = new EditingState(binding, target, model);
getViewer().getColumnViewerEditor().addEditorActivationListener(activationListener);
}
@Override
protected CellEditor getCellEditor(Object element) {
return cellEditor;
}
protected Binding createBinding(IObservableValue target, IObservableValue model) {
return getDataBindingContext().bindValue(target, model);
}
protected IObservableValue doCreateElementObservable(Object element, ViewerCell cell) {
return EMFEditObservables.observeValue(getModelElementContext().getEditingDomain(),
(EObject) element, feature);
}
protected IObservableValue doCreateCellEditorObservable(CellEditor cellEditor) {
if (ECPCellEditor.class.isInstance(cellEditor)) {
return ((ECPCellEditor) cellEditor).getValueProperty().observe(cellEditor);
}
return SWTObservables.observeText(cellEditor.getControl(), SWT.FocusOut);
}
@Override
final protected void saveCellEditorValue(CellEditor cellEditor, ViewerCell cell) {
editingState.binding.updateTargetToModel();
}
class ColumnViewerEditorActivationListenerHelper extends ColumnViewerEditorActivationListener {
@Override
public void afterEditorActivated(ColumnViewerEditorActivationEvent event) {
// do nothing
}
@Override
public void afterEditorDeactivated(ColumnViewerEditorDeactivationEvent event) {
editingState.dispose();
editingState = null;
getViewer().getColumnViewerEditor().removeEditorActivationListener(this);
}
@Override
public void beforeEditorActivated(ColumnViewerEditorActivationEvent event) {
// do nothing
}
@Override
public void beforeEditorDeactivated(ColumnViewerEditorDeactivationEvent event) {
// do nothing
}
}
/**
* Maintains references to objects that only live for the length of the edit
* cycle.
*/
class EditingState {
IObservableValue target;
IObservableValue model;
Binding binding;
EditingState(Binding binding, IObservableValue target, IObservableValue model) {
this.binding = binding;
this.target = target;
this.model = model;
}
void dispose() {
binding.dispose();
target.dispose();
model.dispose();
}
}
};
column.setEditingSupport(observableSupport);
}
tableViewer.setContentProvider(cp);
list = EMFEditObservables.observeList(getModelElementContext().getEditingDomain(), getModelElementContext()
.getModelElement(), getStructuralFeature());
tableViewer.setInput(list);
TableColumnLayout layout = new TableColumnLayout();
composite.setLayout(layout);
// IMPORTANT:
// - the minimumWidth and (non)resizable settings of the ColumnWeightData are not supported properly
// - the layout stops resizing columns that have been resized manually by the user (this could be considered a
// feature though)
for (TableColumn col : tableViewer.getTable().getColumns()) {
layout.setColumnData(col, new ColumnWeightData(100));
}
final Composite buttonComposite = new Composite(parentComposite, SWT.NONE);
buttonComposite.setLayout(new FillLayout(SWT.VERTICAL));
createAddRowButton(clazz, buttonComposite);
createRemoveRowButton(clazz, buttonComposite);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
buttonComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 1));
return parentComposite;
}
private void createRemoveRowButton(EClass clazz, final Composite buttonComposite) {
Button removeButton = new Button(buttonComposite, SWT.None);
Image image = Activator.getImage("icons/delete.png"); //$NON-NLS-1$
removeButton.setImage(image);
removeButton.setToolTipText("Remove the selected " + clazz.getInstanceClass().getSimpleName());
removeButton.addSelectionListener(new SelectionAdapter() {
/*
* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
IStructuredSelection selection = (IStructuredSelection) tableViewer.getSelection();
if (selection == null || selection.getFirstElement() == null) {
return;
}
List<EObject> deletionList = new ArrayList<EObject>();
Iterator<?> iterator = selection.iterator();
while (iterator.hasNext()) {
deletionList.add((EObject) iterator.next());
}
EObject modelElement = getModelElementContext().getModelElement();
EditingDomain editingDomain = getModelElementContext().getEditingDomain();
editingDomain.getCommandStack().execute(
RemoveCommand.create(editingDomain, modelElement, getStructuralFeature(), deletionList));
}
});
}
private void createAddRowButton(final EClass clazz, final Composite buttonComposite) {
Button addButton = new Button(buttonComposite, SWT.None);
Image image = Activator.getImage("icons/add.png"); //$NON-NLS-1$
addButton.setImage(image);
addButton.setToolTipText("Add an instance of " + clazz.getInstanceClass().getSimpleName());
addButton.addSelectionListener(new SelectionAdapter() {
/*
* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
addRow(clazz);
}
});
}
@Override
public void dispose() {
composedAdapterFactory.dispose();
}
@Override
public void handleValidation(Diagnostic diagnostic) {
// TODO Auto-generated method stub
}
@Override
public void resetValidation() {
// TODO Auto-generated method stub
}
@Override
public void setEditable(boolean isEditable) {
tableViewer.getTable().setEnabled(false);
}
private void addRow(EClass clazz) {
EObject modelElement = getModelElementContext().getModelElement();
EObject instance = clazz.getEPackage().getEFactoryInstance().create(clazz);
EditingDomain editingDomain = getModelElementContext().getEditingDomain();
editingDomain.getCommandStack().execute(
AddCommand.create(editingDomain, modelElement, getStructuralFeature(), Collections.singleton(instance)));
}
}