Commit 5d6e73cb authored by Alexander Diewald's avatar Alexander Diewald
Browse files

Initial implementation: Provide control to apply value to multiple rows.

- Extend the IAnnotationValueProvider interface to include a method that returns a list of context menu entries with associated actions. The default implementation returns an empty list (implemented in the default interface method). This allows existing annotation value providers to add some default context menu actions by implementing an additional interface while existing AnnotationValueProviders do not need to be adjusted.
- Add an additional descriptor class "AnnotationInstSpec" that allows to uniquely identify the Annotation (& instanceKey) shown as columns in the annotation view. It has been abstracted from the class ColumnHandle.
- Add a method to the AnnotationLabelProvider that allows to query the Annotation for which it has been constructed.
- Extend the GenericAnnotationView to display a context menu for "annotation columns". The content of the context menu is specified by the AnnoationvalueProvider.
- Implement a context menu entry in the interface DefaultAnnotationContextActions that is implemented by the EnergyConsumptionValueProvider.
refs 2361
parent 52a950e2
......@@ -123,4 +123,20 @@ public class AnnotationLabelProvider extends LabelProviderBase {
}
return super.getBackground(element);
}
/**
* Returns the type (class) of the {@link IAnnotatedSpecification} that is handled by
* {@code this} {@link AnnotationLabelProvider}.
*/
public Class<? extends IAnnotatedSpecification> getAnnotationType() {
return clazz;
}
/**
* Returns the instance key (if any exists) of the {@link IAnnotatedSpecification} that is
* handled by {@code this} {@link AnnotationLabelProvider}.
*/
public String getInstanceKey() {
return instanceKey;
}
}
......@@ -18,6 +18,7 @@ $Id$
package org.fortiss.tooling.base.ui.annotation.view.generic;
import org.fortiss.tooling.base.annotation.AnnotationEntry;
import org.fortiss.tooling.base.annotation.valueprovider.AnnotationInstSpec;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
/**
......@@ -28,9 +29,10 @@ import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
* @author barner
* @author $Author$
* @version $Rev$
* @ConQAT.Rating GREEN Hash: A222EC5AE109DD8443DE2EE117292545
* @ConQAT.Rating YELLOW Hash: 7A023A8AF4AD6C6766060D8D64620E17
*/
public class ColumnHandle implements Comparable<ColumnHandle> {
public class ColumnHandle<T extends IAnnotatedSpecification> extends AnnotationInstSpec<T>
implements Comparable<ColumnHandle<T>> {
/**
* Character used to separate the annotation name from the instance name for multi-instance
* annotations.
......@@ -49,22 +51,16 @@ public class ColumnHandle implements Comparable<ColumnHandle> {
*/
private IAnnotatedSpecification annotatedSpecification;
/**
* Instance key identifying the particular instance of an {@link IAnnotatedSpecification} from
* {@code entry} to be displayed in the column represented by this {@link ColumnHandle}.
*/
private String instanceKey;
/** Flag if this {@link ColumnHandle} represents a {@link CreateAnnotationInstanceColumn}. */
private boolean isCreateColumn;
/** Constructs a new {@link ColumnHandle}. */
public ColumnHandle(AnnotationEntry entry, IAnnotatedSpecification annotatedSpecification,
String instanceKey, boolean isCreateColumn) {
@SuppressWarnings("unchecked")
public ColumnHandle(AnnotationEntry entry, T annotatedSpecification, String instanceKey,
boolean isCreateColumn) {
super((Class<T>)annotatedSpecification.getClass(), instanceKey);
this.entry = entry;
this.annotatedSpecification = annotatedSpecification;
this.instanceKey = instanceKey;
this.isCreateColumn = isCreateColumn;
}
......@@ -84,15 +80,6 @@ public class ColumnHandle implements Comparable<ColumnHandle> {
return annotatedSpecification;
}
/**
* Returns the instance key identifying the particular instance of an
* {@link IAnnotatedSpecification} from {@code entry} to be displayed in the column
* represented by this {@link ColumnHandle}.
*/
public String getInstanceKey() {
return instanceKey;
}
/**
* Returns a flag if this {@link ColumnHandle} represents a
* {@link CreateAnnotationInstanceColumn}.
......@@ -123,7 +110,7 @@ public class ColumnHandle implements Comparable<ColumnHandle> {
/** {@inheritDoc} */
@Override
public int compareTo(ColumnHandle other) {
public int compareTo(ColumnHandle<T> other) {
String columnName = getColumnName();
String annotationName;
......
......@@ -27,19 +27,28 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiConsumer;
import org.conqat.lib.commons.collections.Pair;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
......@@ -49,17 +58,21 @@ import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.fortiss.tooling.base.annotation.AnnotationEntry;
import org.fortiss.tooling.base.annotation.IAnnotationValueService;
import org.fortiss.tooling.base.annotation.valueprovider.AnnotationInstSpec;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.model.element.IModelElement;
import org.fortiss.tooling.base.ui.annotation.editingsupport.EditingSupportFactory;
import org.fortiss.tooling.base.ui.annotation.editingsupport.ElementCommentEditingSupport;
import org.fortiss.tooling.base.ui.annotation.editingsupport.ElementNameEditingSupport;
import org.fortiss.tooling.base.ui.annotation.labelprovider.AnnotationLabelProvider;
import org.fortiss.tooling.base.ui.annotation.labelprovider.ElementCommentLabelProvider;
import org.fortiss.tooling.base.ui.annotation.labelprovider.ElementNameLabelProvider;
import org.fortiss.tooling.base.ui.annotation.view.AnnotationViewPartBase;
......@@ -79,7 +92,7 @@ import org.fortiss.tooling.base.ui.annotation.view.generic.filter.AnnotationFilt
* @author eder, diewald, barner
* @author $Author$
* @version $Rev$
* @ConQAT.Rating GREEN Hash: 71F556F903C111B8280FD04616898B7E
* @ConQAT.Rating YELLOW Hash: 7A192862443127A75F619AC15433D8CD
*/
public class GenericAnnotationView extends AnnotationViewPartBase {
/** Root composite of {@link GenericAnnotationView}. */
......@@ -88,6 +101,13 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
/** The table viewer used to implement the view. */
private TableViewer tableViewer;
/** Caches the currently selected item in the table to make it available to listeners. */
IStructuredSelection selectedTableItem;
/** Associates the columns of the annotation view with the providers responsible for them. */
Map<AnnotationInstSpec<? extends IAnnotatedSpecification>, IAnnotationValueProvider<IAnnotatedSpecification>> annotationCtxEntries =
new HashMap<>();
/** Row and column filter for annotations. */
private AnnotationFilter annotationFilter;
......@@ -204,7 +224,7 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
// Sorted set of {@link ColumnHandle} used to instantiate this {@link
// GenericAnnotationView}'s columns in the right order.
Set<ColumnHandle> sortedColumnHandles = new TreeSet<ColumnHandle>();
Set<ColumnHandle<?>> sortedColumnHandles = new TreeSet<ColumnHandle<?>>();
// Dispose all columns of the table viewer, except the leading fixed columns, which e.g.
// hold the model elements' names and comments.
......@@ -224,8 +244,9 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
if(allowsDynamicInstances || !isSingleInstanceAnnotation) {
for(String instanceKey : entry.getInstanceKeys(specClass)) {
ColumnHandle columnHandle =
new ColumnHandle(entry, spec, instanceKey, false);
ColumnHandle<IAnnotatedSpecification> columnHandle =
new ColumnHandle<IAnnotatedSpecification>(entry, spec,
instanceKey, false);
if(annotationFilter.passesColumnFilter(columnHandle)) {
sortedColumnHandles.add(columnHandle);
}
......@@ -237,8 +258,9 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
// - "create" column for multi-instance annotations for which instances can be
// created dynamically
if(allowsDynamicInstances || isSingleInstanceAnnotation) {
ColumnHandle columnHandle =
new ColumnHandle(entry, spec, null, allowsDynamicInstances);
ColumnHandle<IAnnotatedSpecification> columnHandle =
new ColumnHandle<IAnnotatedSpecification>(entry, spec, null,
allowsDynamicInstances);
if(annotationFilter.passesColumnFilter(columnHandle)) {
sortedColumnHandles.add(columnHandle);
......@@ -248,7 +270,7 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
}
// Instantiate columns
for(ColumnHandle columnHandle : sortedColumnHandles) {
for(ColumnHandle<?> columnHandle : sortedColumnHandles) {
createAnnotationColumn(columnHandle);
}
......@@ -369,6 +391,83 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
table.addListener(SWT.EraseItem, rowHeightListener);
}
/**
* Constructs a {@link Listener} such that a context menu is created upon a right-click on
* entries in the Annotation View.
*/
private void addContextMenuListener(Table table) {
table.addListener(SWT.MouseDown, new Listener() {
@Override
public void handleEvent(Event event) {
TableItem[] selection = table.getSelection();
if(selection.length != 0 && (event.button == 3)) {
Menu contextMenu = new Menu(table);
table.setMenu(contextMenu);
ViewerCell cell = tableViewer.getCell(new Point(event.x, event.y));
if(cell == null) {
return;
}
CellLabelProvider labelProv =
tableViewer.getLabelProvider(cell.getColumnIndex());
if(labelProv instanceof AnnotationLabelProvider) {
@SuppressWarnings("unchecked") Class<IAnnotatedSpecification> annotationType =
(Class<IAnnotatedSpecification>)((AnnotationLabelProvider)labelProv)
.getAnnotationType();
String instanceKey = ((AnnotationLabelProvider)labelProv).getInstanceKey();
AnnotationInstSpec<IAnnotatedSpecification> spec =
new AnnotationInstSpec<IAnnotatedSpecification>(annotationType,
instanceKey);
addContextMenuEntries(contextMenu, annotationCtxEntries.get(spec), spec);
}
contextMenu.setVisible(true);
}
}
});
}
/**
* Adds a {@link Listener} to the given {@link Menu} entry such that the given
* {@link BiConsumer} is executed upon selection.
*/
private
<T extends IAnnotatedSpecification, S extends IModelElement>
void
addContextMenuEntryListener(
MenuItem entry,
BiConsumer<AnnotationInstSpec<IAnnotatedSpecification>, AnnotationEntry> action,
AnnotationInstSpec<IAnnotatedSpecification> specificationType) {
entry.addSelectionListener(new SelectionAdapter() {
AnnotationInstSpec<IAnnotatedSpecification> spec = specificationType;
@Override
public void widgetSelected(SelectionEvent e) {
if(selectedTableItem.getFirstElement() instanceof AnnotationEntry) {
AnnotationEntry entry = (AnnotationEntry)selectedTableItem.getFirstElement();
setUpdateEnabled(false);
action.accept(spec, entry);
setUpdateEnabled(true);
}
}
});
}
/**
* Adds context menu entries to the given {@link Menu} whose name and associated action is
* determined by the {@link IAnnotationValueProvider}.
*/
private void addContextMenuEntries(Menu contextMenu,
IAnnotationValueProvider<IAnnotatedSpecification> valueProvider,
AnnotationInstSpec<IAnnotatedSpecification> annotationClass) {
for(Pair<String, BiConsumer<AnnotationInstSpec<IAnnotatedSpecification>, AnnotationEntry>> ctxMenuEntry : valueProvider
.getContextMenuEntries()) {
MenuItem mItem = new MenuItem(contextMenu, SWT.None);
mItem.setText(ctxMenuEntry.getFirst());
addContextMenuEntryListener(mItem, ctxMenuEntry.getSecond(), annotationClass);
}
}
/** {@inheritDoc} */
@Override
public void createPartControl(Composite parent) {
......@@ -421,6 +520,15 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
}
});
tableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
selectedTableItem = (IStructuredSelection)event.getSelection();
}
});
addContextMenuListener(table);
// Rows (= components) are filtered using a specialized ViewerFilter.
// Since the data model is an array of AnnotationEntries, columns (= annotations)
// cannot be filtered using such a filter, and require the reconstruction of the
......@@ -463,7 +571,8 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
* Creates a column in the table viewer displaying the annotation identified by the given
* {@link ColumnHandle}.
*/
protected void createAnnotationColumn(ColumnHandle columnHandle) {
protected void createAnnotationColumn(
ColumnHandle<? extends IAnnotatedSpecification> columnHandle) {
TableColumn tableColumn = null;
if(columnHandle.isCreateColumn()) {
......@@ -475,13 +584,16 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
columnHandle.getAnnotatedSpecification(), columnHandle.getEntry());
tableColumn = column.getColumn();
} else {
Class<? extends IAnnotatedSpecification> annotationClass =
columnHandle.getAnnotatedSpecification().getClass();
@SuppressWarnings("unchecked") Class<IAnnotatedSpecification> annotationClass =
(Class<IAnnotatedSpecification>)columnHandle.getAnnotatedSpecification()
.getClass();
// Add new column
try {
TableViewerColumn column =
createAnnotationTableViewerColumn(columnHandle, annotationClass);
@SuppressWarnings("unchecked") TableViewerColumn column =
createAnnotationTableViewerColumn(
(ColumnHandle<IAnnotatedSpecification>)columnHandle,
annotationClass);
tableColumn = column.getColumn();
String specName = columnHandle.getColumnName();
......@@ -527,8 +639,10 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
* Creates a {@link TableViewerColumn}, i.e., instantiate matching {@link EditingSupport} and
* {@link LabelProvider} for the given annotation.
*/
private <T extends IAnnotatedSpecification> TableViewerColumn
createAnnotationTableViewerColumn(ColumnHandle columnHandle, Class<T> annotationClass)
private
<T extends IAnnotatedSpecification>
TableViewerColumn
createAnnotationTableViewerColumn(ColumnHandle<T> columnHandle, Class<T> annotationClass)
throws Exception {
TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE);
......@@ -550,6 +664,9 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
column.setEditingSupport(editingSupport);
column.setLabelProvider(columnLabelProvider);
annotationCtxEntries.put(columnHandle, valueProvider);
// addContextMenuEntries(valueProvider, annotationClass);
return column;
}
......
/*--------------------------------------------------------------------------+
$Id: codetemplates.xml 1 2011-01-01 00:00:01Z hoelzl $
| |
| Copyright 2016 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.annotation.valueprovider;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
/**
* Container class to uniquely specify an {@link IAnnotatedSpecification} including its instnaceKey
* of multi-valued {@link IAnnotatedSpecification}s.
*
* @author diewald
* @author $Author: hoelzl $
* @version $Rev: 18709 $
* @ConQAT.Rating YELLOW Hash: 41A961F3E903456E5DD7A9867994DA8C
*/
public class AnnotationInstSpec<T extends IAnnotatedSpecification> {
/**
* {@link IAnnotatedSpecification} from {@code entry} to be displayed in the column
* represented by this {@link AnnotationInstSpec}.
*/
private Class<T> annotatedSpecificationClass;
/**
* Instance key identifying the particular instance of an {@link IAnnotatedSpecification} from
* {@code entry} to be displayed in the column represented by this {@link AnnotationInstSpec}.
*/
private String instanceKey;
/** Constructor. */
public AnnotationInstSpec(Class<T> annotatedSpecificationClass, String instanceKey) {
this.annotatedSpecificationClass = annotatedSpecificationClass;
this.instanceKey = instanceKey;
}
/**
* Returns the {@link IAnnotatedSpecification} from {@code entry} to be displayed in the
* column represented by this {@link AnnotationInstSpec}.
*/
public Class<T> getAnnotatedSpecificationType() {
return annotatedSpecificationClass;
}
/**
* Returns the instance key identifying the particular instance of an
* {@link IAnnotatedSpecification} from {@code entry} to be displayed in the column
* represented by this {@link AnnotationInstSpec}.
*/
public String getInstanceKey() {
return instanceKey;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return annotatedSpecificationClass.hashCode() +
(instanceKey != null ? instanceKey.hashCode() : 0);
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if(other instanceof AnnotationInstSpec) {
return this.hashCode() == other.hashCode();
}
return super.equals(other);
}
}
/*--------------------------------------------------------------------------+
$Id: codetemplates.xml 1 2011-01-01 00:00:01Z hoelzl $
| |
| Copyright 2016 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.annotation.valueprovider;
import static org.fortiss.tooling.kernel.utils.EcoreUtils.getChildrenWithType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
import org.conqat.lib.commons.collections.Pair;
import org.fortiss.tooling.base.ToolingBaseActivator;
import org.fortiss.tooling.base.annotation.AnnotationEntry;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
import org.fortiss.tooling.kernel.service.IPersistencyService;
import org.fortiss.tooling.kernel.utils.LoggingUtils;
/**
* Interface defining default implementations for the context menu of
* {@link IAnnotatedSpecification}s.
*
* @author diewald
* @author $Author: hoelzl $
* @version $Rev: 18709 $
* @ConQAT.Rating YELLOW Hash: 5A3C99F6C95BD4E0AFE4B8B2D35C0752
*/
public interface DefaultAnnotationContextActions<T extends IAnnotatedSpecification> extends
IAnnotationValueProvider<T> {
/** {@inheritDoc} */
@Override
public default List<Pair<String, BiConsumer<AnnotationInstSpec<T>, AnnotationEntry>>>
getContextMenuEntries() {
List<Pair<String, BiConsumer<AnnotationInstSpec<T>, AnnotationEntry>>> retList =
new ArrayList<>();
// Propagates values from the selected entry to all entries in the same ITopLevelElement
// with the same type.
BiConsumer<AnnotationInstSpec<T>, AnnotationEntry> propagateToAll =
(AnnotationInstSpec<T> spec, AnnotationEntry entry) -> {
Class<T> specType = spec.getAnnotatedSpecificationType();
Object value = entry.getSpecificationValue(specType);
ITopLevelElement topElement =
IPersistencyService.getInstance().getTopLevelElementFor(
entry.getModelElement());
Collection<T> sameAnnotations =
getChildrenWithType(topElement.getRootModelElement(), specType);
for(T sameAnn : sameAnnotations) {
// Apply the value for each annotation of the same type in the smae
// IToplevelElement.
Runnable run =
() -> {
try {
setAnnotationValue(value, sameAnn);
} catch(Exception ex) {
LoggingUtils
.error(ToolingBaseActivator.getDefault(),
"Could not set the value of the annotation " +
sameAnn.getName() +
" when trying to propagate the value form the selected annotation. Reason:\n" +
ex.getLocalizedMessage());
}
};
topElement.runAsCommand(run);
}
};
// Add the propagate action to the list of context menu entries.
retList.add(new Pair<String, BiConsumer<AnnotationInstSpec<T>, AnnotationEntry>>(
"Propagate value", propagateToAll));
return retList;
}
}
......@@ -18,10 +18,14 @@ $Id$
package org.fortiss.tooling.base.annotation.valueprovider;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import org.conqat.lib.commons.collections.Pair;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.fortiss.tooling.base.annotation.AnnotationEntry;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.model.element.IModelElement;
import org.fortiss.tooling.kernel.service.base.IEObjectAware;
......@@ -64,7 +68,7 @@ import org.fortiss.tooling.kernel.service.base.IEObjectAware;
* @author eder, diewald, barner
* @author $Author$
* @version $Rev$
* @ConQAT.Rating GREEN Hash: 7A66671C1108A72220B941052B8CCA48
* @ConQAT.Rating YELLOW Hash: F64D6AE408B10240BAF4A43B9D79827C
*/
public interface IAnnotationValueProvider<T extends IAnnotatedSpecification> extends
IEObjectAware<IModelElement> {
......@@ -190,4 +194,19 @@ public interface IAnnotationValueProvider<T extends IAnnotatedSpecification> ext
* </p>
*/