| /*=============================================================================# |
| # 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; |
| } |
| |
| } |