blob: 92b4587f4fa6738536383ed901f7f28b0498f6f7 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2012, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.rtm.base.ui.rexpr;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CommandStack;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.databinding.IEMFObservable;
import org.eclipse.emf.databinding.edit.EMFEditProperties;
import org.eclipse.emf.databinding.edit.IEMFEditListProperty;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.MoveCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableValueEditingSupport;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ecommons.emf.core.IContext;
import org.eclipse.statet.ecommons.emf.core.databinding.IEMFEditContext;
import org.eclipse.statet.ecommons.emf.core.databinding.IEMFEditPropertyContext;
import org.eclipse.statet.ecommons.emf.ui.forms.EFProperty;
import org.eclipse.statet.ecommons.emf.ui.forms.IEFFormPage;
import org.eclipse.statet.ecommons.ui.components.ButtonGroup;
import org.eclipse.statet.ecommons.ui.content.ElementSourceSelectionProvider;
import org.eclipse.statet.ecommons.ui.util.SelectionTransferDragAdapter;
import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils;
import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils.TableComposite;
import org.eclipse.statet.rtm.base.ui.editors.RtFormToolkit;
import org.eclipse.statet.rtm.base.util.RExprTypes;
import org.eclipse.statet.rtm.rtdata.types.RTypedExpr;
public class RExprListProperty extends EFProperty implements ButtonGroup.IActions<RTypedExpr> {
private class RExprEditing extends ObservableValueEditingSupport<RTypedExpr, RTypedExpr, String> {
private CellEditor cellEditor;
public RExprEditing() {
super(RExprListProperty.this.widget.viewer, RExprListProperty.this.getDataBindingContext());
}
@Override
protected CellEditor getCellEditor(final Object element) {
if (element instanceof RTypedExpr) {
if (this.cellEditor == null) {
this.cellEditor= createRExprCellEditor();
}
return this.cellEditor;
}
return null;
}
protected CellEditor createRExprCellEditor() {
final TextCellEditor editor= new TextCellEditor((Composite) getViewer().getControl());
// editor.setValidator(new RIdentifierCellValidator());
return editor;
}
@Override
protected IObservableValue<String> doCreateCellEditorObservable(final CellEditor cellEditor) {
return WidgetProperties.text(SWT.Modify)
.observe(((TextCellEditor)this.cellEditor).getControl());
}
@Override
protected Binding createBinding(final IObservableValue<String> target, final IObservableValue<RTypedExpr> model) {
return getDataBindingContext().bindValue(
target,
model,
new UpdateValueStrategy<String, @Nullable RTypedExpr>(UpdateValueStrategy.POLICY_CONVERT)
.setConverter(new String2RTypedExprConverter(RExprListProperty.this.types.getDefaultTypeKey())),
new UpdateValueStrategy<@Nullable RTypedExpr, String>()
.setConverter(new RExpr2StringConverter()) );
}
@Override
protected IObservableValue<RTypedExpr> doCreateElementObservable(final RTypedExpr element, final ViewerCell cell) {
return new AbstractObservableValue<RTypedExpr>() {
RTypedExpr value= element;
@Override
public Object getValueType() {
return RTypedExpr.class;
}
@Override
protected RTypedExpr doGetValue() {
return this.value;
}
@Override
protected void doSetValue(final @Nullable RTypedExpr value) {
final int index= getIndex(value);
final CommandStack commandStack= getEditingDomain().getCommandStack();
final IEMFObservable emfObservable= (IEMFObservable) RExprListProperty.this.modelObservable;
Command command;
if (value == null) {
if (index >= 0) {
RExprListProperty.this.modelObservable.remove(index);
}
return;
}
else {
if (index >= 0) {
command= SetCommand.create(getEditingDomain(),
emfObservable.getObserved(), getEFeature(),
value, index );
// this.modelObservable.set(index, value);
}
else {
// command= AddCommand.create(this.context.getEditingDomain(),
// emfObservable.getObserved(), emfObservable.getStructuralFeature(),
// value );
RExprListProperty.this.modelObservable.add(value);
return;
}
}
commandStack.execute(command);
}
};
}
}
private final IRExprTypesUIProvider provider;
private final RExprTypes types;
private List<RExprTypeUIAdapter> typeUIAdapters;
private TableComposite widget;
private ButtonGroup<RTypedExpr> buttonGroup;
private IObservableList<RTypedExpr> modelObservable;
public RExprListProperty(final String label, final String tooltip,
final EClass eClass, final EStructuralFeature eFeature,
final IRExprTypesUIProvider provider) {
super(label, tooltip, eClass, eFeature);
this.provider= provider;
this.types= provider.getTypes(getEClass(), getEFeature());
}
protected IRExprTypesUIProvider getProvider() {
return this.provider;
}
@Override
public void create(final Composite parent, final IEFFormPage page) {
final IRExprTypesUIProvider provider= getProvider();
this.typeUIAdapters= provider.getUIAdapters(this.types, getEClass(), getEFeature() );
final RtFormToolkit toolkit= (RtFormToolkit) page.getToolkit();
toolkit.createPropLabel(parent, getLabel(), getTooltip(), 3);
this.widget= toolkit.createPropSingleColumnTable(parent, 6, 25);
this.widget.viewer.setContentProvider(new ObservableListContentProvider());
final ViewerColumn column= this.widget.getViewerColumn(0);
column.setLabelProvider(new RExprLabelProvider(this.typeUIAdapters));
this.buttonGroup= new ButtonGroup<>(parent, this, true);
this.buttonGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
customizeButtonGroup(this.buttonGroup);
}
protected void customizeButtonGroup(final ButtonGroup<RTypedExpr> buttonGroup) {
buttonGroup.addAddButton(null);
buttonGroup.addDeleteButton(null);
buttonGroup.addSeparator();
buttonGroup.addUpButton(null);
buttonGroup.addDownButton(null);
}
@Override
public Control getControl() {
return this.widget;
}
public TableViewer getViewer() {
return this.widget.viewer;
}
@Override
public RTypedExpr edit(final int command, final RTypedExpr item, final Object parent) {
switch(command) {
case ButtonGroup.ADD_NEW:
return new RTypedExpr(this.types.getDefaultTypeKey(), ""); //$NON-NLS-1$
case ButtonGroup.EDIT:
return item;
default:
return null;
}
}
@Override
public void updateState(final IStructuredSelection selection) {
}
@Override
public void bind(final IEMFEditContext context) {
super.bind(context);
final IEMFEditListProperty emfProperty= EMFEditProperties.list(getEditingDomain(),
getEFeature() );
this.modelObservable= emfProperty.observeDetail(getBaseObservable());
this.widget.viewer.setInput(this.modelObservable);
this.buttonGroup.connectTo(this.widget.viewer, this.modelObservable, null);
final ViewerColumn column= this.widget.getViewerColumn(0);
column.setEditingSupport(new RExprEditing());
ViewerUtils.installDefaultEditBehaviour2(this.widget.viewer);
final ElementSourceSelectionProvider extSelectionProvider= new ElementSourceSelectionProvider(
this.widget.viewer, this );
{ final int operations= (DND.DROP_DEFAULT | DND.DROP_COPY | DND.DROP_MOVE);
final Transfer[] transfers= new Transfer[] { LocalSelectionTransfer.getTransfer() };
this.widget.viewer.addDragSupport(operations, transfers, new SelectionTransferDragAdapter(
extSelectionProvider ));
this.widget.viewer.addDropSupport(operations, transfers, new RExprViewerDropAdapter(
this.widget.viewer, this.typeUIAdapters, this ) {
@Override
protected boolean canMove(final IContext source, final Object input) {
final IEMFEditPropertyContext sourcePropertyContext= source.getAdapter(IEMFEditPropertyContext.class);
return (sourcePropertyContext != null
&& getEditingDomain() == sourcePropertyContext.getEditingDomain());
}
@Override
protected int getIndex(final Object element) {
return RExprListProperty.this.modelObservable.indexOf(element);
}
@Override
protected void insertExprs(final List<RTypedExpr> exprs, final int index, final int time) {
if (index >= 0) {
RExprListProperty.this.modelObservable.addAll(index, exprs);
}
else {
RExprListProperty.this.modelObservable.addAll(exprs);
}
}
@Override
protected void moveExprs(final IContext source, final List<RTypedExpr> exprs, final int index, final int time) {
final IEMFEditPropertyContext sourceContext= (IEMFEditPropertyContext) source;
final EObject owner= (EObject) ((IObserving) RExprListProperty.this.modelObservable).getObserved();
final IObservable sourceObservable= sourceContext.getPropertyObservable();
if (owner == null || sourceObservable == null) {
return;
}
Command command;
if (sourceObservable == RExprListProperty.this.modelObservable) {
if (exprs.size() == 1) {
final RTypedExpr expr= exprs.get(0);
final int oldIndex= getIndex(expr);
if (oldIndex < 0) {
return;
}
command= MoveCommand.create(getEditingDomain(),
owner, getEFeature(), expr, (oldIndex < index) ? index - 1 : index );
}
else {
final CompoundCommand compound= new CompoundCommand();
final List<RTypedExpr> after= new ArrayList<>(exprs.size());
for (final RTypedExpr expr : exprs) {
final int oldIndex= getIndex(expr);
if (oldIndex < 0) {
continue;
}
if (oldIndex < index) {
compound.append(MoveCommand.create(getEditingDomain(),
owner, getEFeature(), expr, index - 1 ));
}
else {
after.add(expr);
}
}
for (final RTypedExpr expr : after) {
compound.append(MoveCommand.create(getEditingDomain(),
owner, getEFeature(), expr, index ));
}
command= compound;
}
}
else {
final EObject oldOwner= (EObject) ((IObserving) sourceObservable).getObserved();
final CompoundCommand compound= new CompoundCommand();
compound.append(AddCommand.create(getEditingDomain(),
owner, getEFeature(), exprs));
compound.append(RemoveCommand.create(getEditingDomain(),
oldOwner, sourceContext.getEFeature(), exprs));
command= compound;
}
getEditingDomain().getCommandStack().execute(command);
}
});
}
if (!this.modelObservable.isEmpty()) {
this.widget.viewer.setSelection(new StructuredSelection(this.modelObservable.get(0)));
}
else {
this.buttonGroup.updateState();
}
}
protected int getIndex(final Object element) {
for (int i= 0; i < this.modelObservable.size(); i++) {
if (this.modelObservable.get(i) == element) {
return i;
}
}
return this.modelObservable.indexOf(element);
}
@Override
public IObservableList getPropertyObservable() {
return this.modelObservable;
}
}