Commit 2b78e0f7 authored by Simon Barner's avatar Simon Barner

- Implement MultiSelectionEditing support that currently supports the...

- Implement MultiSelectionEditing support that currently supports the selection of multiple values for EReferences (based on OPAL's DualList)
- Rename ComboBoxLabelValue mapping -> LabelValueMapping
- Update AnnotationViewPartBase also on ADD_MANY / REMOVE_MANY events
refs 1841
parent 3334d7dc
......@@ -4,4 +4,5 @@
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="build"/>
<classpathentry kind="lib" path="lib/opal-1.0.0.jar" sourcepath="lib/opal-1.0.0-src.jar"/>
</classpath>
......@@ -40,3 +40,5 @@ Export-Package: org.fortiss.tooling.base.ui,
org.fortiss.tooling.base.ui.tablecell,
org.fortiss.tooling.base.ui.utils,
org.fortiss.tooling.base.ui.widget
Bundle-ClassPath: .,
lib/opal-1.0.0.jar
......@@ -55,7 +55,7 @@ public class ComboBoxEditingSupport extends AnnotationEditingSupportBase {
* Translates between labels shown in this {@link ComboBoxEditingSupport}, and the underlying
* actual values (e.g., required for {@link EReference}s.
*/
private ComboBoxLabelValueMapping comboBoxLabelValueMapping;
private LabelValueMapping comboBoxLabelValueMapping;
/**
* Input-choice based on an {@link EStructuralFeature} (i.e., {@link EEnum} or
......@@ -127,13 +127,13 @@ public class ComboBoxEditingSupport extends AnnotationEditingSupportBase {
}
/**
* Creates a {@link ComboBoxLabelValueMapping} for the annotation shown in this
* Creates a {@link LabelValueMapping} for the annotation shown in this
* {@link ComboBoxEditingSupport}.
*/
public ComboBoxLabelValueMapping createComboBoxLabelValueMapping(EObject modelElement,
public LabelValueMapping createComboBoxLabelValueMapping(EObject modelElement,
IAnnotatedSpecification specification) {
return new ComboBoxLabelValueMapping(eStructuralFeatureDescriptor, specification,
return new LabelValueMapping(eStructuralFeatureDescriptor, specification,
modelElement, stringInputChoice);
}
......
......@@ -39,19 +39,21 @@ import org.eclipse.emf.ecore.EReference;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.ui.annotation.valueprovider.EStructuralFeatureDescriptor;
import org.fortiss.tooling.base.ui.annotation.valueprovider.EStructuralFeatureDescriptor.EReferenceScope;
import org.fortiss.tooling.kernel.model.IIdLabeled;
import org.fortiss.tooling.kernel.model.INamedElement;
import org.fortiss.tooling.kernel.model.IProjectRootElement;
/**
* Translates between a set of labels shown and the corresponding actual values (e.g., required for
* {@link EReference}s.
* Translates between the labels shown in a GUI and actual model elements (=values) (e.g.,
* required to integrate {@link EReference}s into {@link ComboBoxEditingSupport} or
* {@link MultiSelectionEditingSupport}. This class ensures that its label is unique.
*
* @author barner
* @author $Author$
* @version $Rev$
* @ConQAT.Rating RED Hash:
*/
public class ComboBoxLabelValueMapping {
public class LabelValueMapping {
/** Map: label -> actual value */
private Map<String, Object> labelToValueMap;
......@@ -60,10 +62,10 @@ public class ComboBoxLabelValueMapping {
Collection<String> stringLabels;
/**
* Constructs a {@link ComboBoxLabelValueMapping}. {@code eStructuralFeatureDescriptor} may be
* Constructs a {@link LabelValueMapping}. {@code eStructuralFeatureDescriptor} may be
* {@code null} in which case this translator acts as the identity-function.
*/
public ComboBoxLabelValueMapping(EStructuralFeatureDescriptor eStructuralFeatureDescriptor,
public LabelValueMapping(EStructuralFeatureDescriptor eStructuralFeatureDescriptor,
IAnnotatedSpecification specification, EObject modelElement,
Collection<String> stringLabels) {
......@@ -78,7 +80,7 @@ public class ComboBoxLabelValueMapping {
Set<String> enumValues = new TreeSet<String>();
for(EEnumLiteral e : ((EEnum)eType).getELiterals()) {
enumValues.add(e.getName());
labelToValueMap.put(e.getName(), e.getInstance());
addLabelValuePair(e.getName(), e.getInstance());
}
} else if(eType instanceof EClass) {
......@@ -125,18 +127,18 @@ public class ComboBoxLabelValueMapping {
label = label.substring(i + 1);
}
}
labelToValueMap.put(label, v);
addLabelValuePair(label, v);
} else {
labelToValueMap.put(computeFullyQualifiedName((INamedElement)v), v);
addLabelValuePair(computeFullyQualifiedName((INamedElement)v), v);
}
} else {
labelToValueMap.put(v.toString(), v);
addLabelValuePair(v.toString(), v);
}
}
}
}
/** Returns the set of labels managed by this {@link ComboBoxLabelValueMapping}. */
/** Returns the set of labels managed by this {@link LabelValueMapping}. */
public Collection<String> getLabels() {
if(labelToValueMap == null) {
return stringLabels;
......@@ -145,6 +147,28 @@ public class ComboBoxLabelValueMapping {
return labelToValueMap.keySet();
}
/**
* Adds a label / value pair to this {@link LabelValueMapping}. This methods ensures that the
* label set managed by this {@link LabelValueMapping} unique.
*/
private void addLabelValuePair(String label, Object value) {
// Append IDs for IIdLabeled objects
if(value instanceof IIdLabeled) {
label += " [Id=" + ((IIdLabeled)value).getId() + "]";
}
// Be extra careful and add unique suffix if required.
String prefix = new String(label);
int i = 1;
while(labelToValueMap.keySet().contains(label)) {
label = prefix + " (" + i + ")";
i++;
}
label = prefix;
labelToValueMap.put(label, value);
}
/** Translates a value into the corresponding label. */
public String getLabelForValue(Object value) {
if(labelToValueMap == null) {
......
/*--------------------------------------------------------------------------+
$Id$
| |
| Copyright 2014 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.ui.annotation.editingsupport;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.DialogCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.mihalis.opal.itemSelector.DLItem;
import org.mihalis.opal.itemSelector.DualList;
/**
* {@link CellEditor} that opens a dialog for the selection of multiple elements.
*
* @author barner
* @author $Author$
* @version $Rev$
* @ConQAT.Rating RED Hash: D8DFB89FBEC85495151411C232708CEA
*
*/
public class MultiSelectionCellEditor extends DialogCellEditor {
/** Mapping: label shown in GUI -> actual value. */
private LabelValueMapping labelValueMapping;
/** The collection of elements selected using this {@link MultiSelectionCellEditor}. */
private Collection<?> selectedElements = CollectionUtils.emptyList();
/** Constructors a new MultiSelectionCellEditor */
public MultiSelectionCellEditor(Composite parent) {
super(parent);
}
/**
* Updates the data shown in this {@link MultiSelectionCellEditor}.
*
* @param labelValueMapping
* Mapping: label shown in GUI -> actual value.
* @param selectedElements
* Current collection of selected elements
*/
public void updateData(LabelValueMapping labelValueMapping, Collection<?> selectedElements) {
this.labelValueMapping = labelValueMapping;
this.selectedElements = selectedElements;
}
/**
* Returns the set of selected elements. In case the user presses cancel in the
* {@link MultiSelectionDialog} wrapped by this {@link MultiSelectionCellEditor}, {@code null}
* is returned.
*/
public Collection<?> getSelectedElements() {
return selectedElements;
}
/** {@inheritDoc} */
@Override
protected Object openDialogBox(Control cellEditorWindow) {
MultiSelectionDialog selectionDialog = new MultiSelectionDialog(null);
selectionDialog.open();
return selectedElements;
}
/** Shows a dialog to that allows to select multiple values. */
private class MultiSelectionDialog extends Dialog {
/** Element selection dialog */
DualList dl = null;
/** Constructs a new {@link MultiSelectionDialog}. */
public MultiSelectionDialog(Shell parentShell) {
super(parentShell);
}
/**
* Creates a dummy image required to work around a layout bug in {@link DualList} if used
* without any image.
*/
private Image createDummyImage(Display display) {
Image image = new Image(display, 1, 1);
GC gc = new GC(image);
gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
gc.fillRectangle(new Rectangle(0, 0, 1, 1));
gc.dispose();
return image;
}
/** {@inheritDoc} */
@Override
protected Composite createDialogArea(Composite parent) {
/* Generate the dialog and its layout */
Composite area = (Composite)super.createDialogArea(parent);
area.setLayout(new FillLayout());
Set<String> labels = new HashSet<String>();
labels.addAll(labelValueMapping.getLabels());
Image dummyImage = createDummyImage(getShell().getDisplay());
dl = new DualList(area, SWT.NONE);
for(Object selectedItem : selectedElements) {
String label = labelValueMapping.getLabelForValue(selectedItem);
DLItem item = new DLItem(label, dummyImage);
dl.add(item);
dl.selectDoNotFireEvent(0);
dl.remove(item);
labels.remove(label);
}
for(String label : labels) {
DLItem item = new DLItem(label, dummyImage);
dl.add(item);
}
return area;
}
/** {@inheritDoc} */
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText("Select multiple elements");
}
/** {@inheritDoc} */
@Override
protected Point getInitialSize() {
return new Point(600, 600);
}
/** Returns the set of selected elements */
private Collection<Object> getSelectedItems() {
Collection<Object> rval = new ArrayList<Object>();
for(DLItem item : dl.getSelection()) {
if(item != null) {
rval.add(labelValueMapping.getValueForLabel(item.getText()));
}
}
return rval;
}
/** {@inheritDoc} */
@Override
protected void okPressed() {
// In case the user presses ok, update the corresponding field of
// MultiSelectionCellEditor that wraps this MultiSelectionDialog
selectedElements = getSelectedItems();
super.okPressed();
}
/** {@inheritDoc} */
@Override
protected void cancelPressed() {
selectedElements = null;
super.okPressed();
}
}
}
/*--------------------------------------------------------------------------+
$Id$
| |
| Copyright 2014 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.ui.annotation.editingsupport;
import java.util.Collection;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.swt.widgets.Composite;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.ui.annotation.AnnotationEntry;
import org.fortiss.tooling.base.ui.annotation.valueprovider.EStructuralFeatureDescriptor;
import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
import org.fortiss.tooling.kernel.service.IPersistencyService;
/**
* {@link EditingSupport} that supports the selection of multiple values.
*
* @author barner
* @author $Author$
* @version $Rev$
* @ConQAT.Rating RED Hash: F7D5CB86794EB52A041ABC1398FA7338
*
*/
public class MultiSelectionEditingSupport extends AnnotationEditingSupportBase {
/** The dialog used to select multiple elements. */
private MultiSelectionCellEditor multiSelectionEditDialog;
/** The underlying annotation */
IAnnotatedSpecification specification;
/** Specifications {@link EStructuralFeatureDescriptor}. */
private EStructuralFeatureDescriptor eStructuralFeatureDescriptor;
/** Constructor */
public MultiSelectionEditingSupport(ColumnViewer viewer,
Class<? extends IAnnotatedSpecification> clazz, IAnnotatedSpecification specification,
String instanceKey, EStructuralFeatureDescriptor eStructuralFeatureDescriptor) {
super(viewer, clazz, instanceKey);
this.specification = specification;
this.eStructuralFeatureDescriptor = eStructuralFeatureDescriptor;
multiSelectionEditDialog =
new MultiSelectionCellEditor((Composite)getViewer().getControl());
}
/** {@inheritDoc} */
@Override
protected CellEditor getCellEditor(Object element) {
if(!(element instanceof AnnotationEntry)) {
return null;
}
AnnotationEntry entry = (AnnotationEntry)element;
// Only can return a cell editor if the value of the underlying IAnnotatedSpecification is a
// collection
Object value = entry.getSpecificationValue(specClass, instanceKey);
if(!(value instanceof Collection<?>)) {
return null;
}
LabelValueMapping clvm =
new LabelValueMapping(eStructuralFeatureDescriptor, specification,
((AnnotationEntry)element).getModelElement(), null);
multiSelectionEditDialog.updateData(clvm, (Collection<?>)value);
return multiSelectionEditDialog;
}
//
/** {@inheritDoc} */
@Override
protected boolean canEdit(Object element) {
return true;
}
//
/** {@inheritDoc} */
@Override
protected Object getValue(Object element) {
return getLabel(element);
}
/**
* Returns a {@link String} label for the given annotation (identified by its
* {@link AnnotationEntry}).
*/
public String getLabel(Object element) {
if(element instanceof AnnotationEntry) {
AnnotationEntry entry = (AnnotationEntry)element;
Object value = entry.getSpecificationValue(specClass, instanceKey);
if(value instanceof Collection<?>) {
return "[" + ((Collection<?>)value).size() + " selected]";
}
}
return "[0 selected]";
}
/** {@inheritDoc} */
@Override
protected void setValue(Object element, final Object value) {
// Writes back into the model the collection of selected elements
if(element instanceof AnnotationEntry && value instanceof Collection<?>) {
final AnnotationEntry entry = (AnnotationEntry)element;
ITopLevelElement modelContext =
IPersistencyService.INSTANCE.getTopLevelElementFor(entry.getModelElement());
modelContext.runAsCommand(new Runnable() {
@Override
public void run() {
try {
entry.setSpecificationValue(value, specClass);
} catch(Exception e) {
// Ignore
}
}
});
}
}
}
......@@ -25,7 +25,8 @@ import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.model.element.IDerivedAnnotation;
import org.fortiss.tooling.base.ui.annotation.AnnotationEntry;
import org.fortiss.tooling.base.ui.annotation.editingsupport.ComboBoxEditingSupport;
import org.fortiss.tooling.base.ui.annotation.editingsupport.ComboBoxLabelValueMapping;
import org.fortiss.tooling.base.ui.annotation.editingsupport.LabelValueMapping;
import org.fortiss.tooling.base.ui.annotation.editingsupport.MultiSelectionEditingSupport;
import org.fortiss.tooling.base.ui.annotation.view.IAnnotationViewPart;
/**
......@@ -82,12 +83,14 @@ public class AnnotationLabelProvider extends LabelProviderBase {
}
if(value != null) {
if(editingSupport instanceof ComboBoxEditingSupport) {
ComboBoxLabelValueMapping comboBoxLabelValueMapping =
LabelValueMapping comboBoxLabelValueMapping =
((ComboBoxEditingSupport)editingSupport)
.createComboBoxLabelValueMapping(
annotationEntry.getModelElement(),
annotationEntry.getSpecification(specClass));
return comboBoxLabelValueMapping.getLabelForValue(value);
} else if(editingSupport instanceof MultiSelectionEditingSupport) {
return ((MultiSelectionEditingSupport)editingSupport).getLabel(element);
}
return value.toString();
......
......@@ -30,12 +30,12 @@ import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.EditingSupport;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.model.element.IModelElement;
import org.fortiss.tooling.base.ui.annotation.editingsupport.ComboBoxEditingSupport;
import org.fortiss.tooling.base.ui.annotation.editingsupport.MultiSelectionEditingSupport;
/**
* Base class for {@link IAnnotationValueProvider}s that manages {@link EStructuralFeature}s
......@@ -302,12 +302,6 @@ public abstract class EStructuralFeatureValueProviderBase<T extends IAnnotatedSp
EStructuralFeature structuralFeature = getEStructuralFeature(specification, instanceKey);
if(structuralFeature.getUpperBound() > 1 ||
structuralFeature.getUpperBound() == ETypedElement.UNBOUNDED_MULTIPLICITY) {
throw new Exception(
"EStructuralValueProvider has not been implemented for structural feature multiplicity > 1.");
}
EClassifier eType = null;
try {
eType = structuralFeature.getEType();
......@@ -315,14 +309,31 @@ public abstract class EStructuralFeatureValueProviderBase<T extends IAnnotatedSp
// Ignore
}
if(((!(eType instanceof EEnum)) && (!(eType instanceof EClass))) ||
((eType instanceof EClass) && (structuralFeatureDescriptorMap.get(instanceKey)
.getEReferenceScope() == null))) {
if(structuralFeature.getUpperBound() == 0) {
throw new Exception(
"EStructuralValueProvider: feature multiplicity == 0 is not supported.");
}
if(structuralFeature.getUpperBound() == 1) {
return super.createEditingSupport(viewer, clazz, specification, instanceKey);
if(((!(eType instanceof EEnum)) && (!(eType instanceof EClass))) ||
((eType instanceof EClass) && (structuralFeatureDescriptorMap.get(instanceKey)
.getEReferenceScope() == null))) {
return super.createEditingSupport(viewer, clazz, specification, instanceKey);
}
return new ComboBoxEditingSupport(viewer, clazz, specification, instanceKey,
structuralFeatureDescriptorMap.get(instanceKey));
}
if(eType instanceof EClass) {
return new MultiSelectionEditingSupport(viewer, clazz, specification, instanceKey,
structuralFeatureDescriptorMap.get(instanceKey));
}
return new ComboBoxEditingSupport(viewer, clazz, specification, instanceKey,
structuralFeatureDescriptorMap.get(instanceKey));
throw new Exception(
"EStructuralValueProvider has been implemented for EReferences only if feature multiplicity > 1.");
}
/**
......
......@@ -96,14 +96,15 @@ public abstract class AnnotationViewPartBase extends ViewPart implements ISelect
// Otherwise, the addition of a new model elements results in a cascade of updates (one
// for the model element, and one for each of the annotations that have been registered
// for it)
if(notification.getEventType() == Notification.ADD &&