diff --git a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/view/GenericAnnotationView.java b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/view/GenericAnnotationView.java index ffee2e86c1024589ad4bfb811a06dde527f8693d..6c1ae64c45e368f4a3756cb6048ae4fa3388a6ff 100644 --- a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/view/GenericAnnotationView.java +++ b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/view/GenericAnnotationView.java @@ -17,7 +17,10 @@ $Id$ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.base.ui.annotation.view; +import static org.fortiss.tooling.kernel.utils.EcoreUtils.pickInstanceOf; + import java.util.Collection; +import java.util.HashSet; import java.util.Set; import java.util.TreeSet; @@ -30,6 +33,7 @@ import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CCombo; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; @@ -50,6 +54,7 @@ import org.fortiss.tooling.base.model.element.IModelElement; import org.fortiss.tooling.base.ui.annotation.AnnotationEntry; import org.fortiss.tooling.base.ui.annotation.editingsupport.ElementNameEditingSupport; import org.fortiss.tooling.base.ui.annotation.valueprovider.IAnnotationValueProvider; +import org.fortiss.tooling.base.ui.widget.ExtendedCCombo; import org.fortiss.tooling.kernel.model.INamedElement; import org.fortiss.tooling.kernel.model.IProjectRootElement; @@ -81,23 +86,11 @@ public class GenericAnnotationView extends AnnotationViewPartBase { */ private final static String MULTIINSTANCE_NAME_SEPARATOR = ":"; - /** Widget for editing of filter text. */ - private Text filterText; - - /** Filter hint text. */ - private static final String FILTER_HINT_TEXT = "type filter text"; - - /** Filter pattern */ - private String filterPattern; + /** Row an column filter for annotations */ + AnnotationFilter annotationFilter; - /** Flag whether columns should be filtered. */ - boolean filterColumns; - - /** - * Flag whether the displayed rows should be restricted to the currently selected model element - * type. - */ - boolean restrictToSelectedModelElementType; + /** Widget for filter options */ + private AnnotationFilterWidget filterWidget; /** * Last set of {@link AnnotationEntry}s used to {@link #update(Collection)} the @@ -228,39 +221,14 @@ public class GenericAnnotationView extends AnnotationViewPartBase { */ private class HierarchicalNameComparator extends ViewerComparator { - /** Computes the level of an {@link IModelElement} in the tree */ - private int getLevel(EObject o) { - int level = 0; - while(o.eContainer() != null) { - level++; - o = o.eContainer(); - } - - return level; - } - - /** - * Returns the ancestor of the {@link IModelElement} {@code modelElement} that is - * {@code levelsUp} levels above model element in the hierarchy, or {@code null}, in case - * {@code levelsUp} > depth(modelElement}. - */ - private EObject getAncestor(EObject o, int levelsUp) { - while(o.eContainer() != null && levelsUp > 0) { - o = o.eContainer(); - levelsUp--; - } - return o; - - } - /** {@inheritDoc} */ @Override public int compare(Viewer viewer, Object o1, Object o2) { if(o1 instanceof AnnotationEntry && o2 instanceof AnnotationEntry) { IModelElement modelElement1 = ((AnnotationEntry)o1).getModelElement(); IModelElement modelElement2 = ((AnnotationEntry)o2).getModelElement(); - int modelElement1Level = getLevel(modelElement1); - int modelElement2Level = getLevel(modelElement2); + int modelElement1Level = getModelElementLevel(modelElement1); + int modelElement2Level = getModelElementLevel(modelElement2); EObject e1 = modelElement1; EObject e2 = modelElement2; @@ -269,7 +237,8 @@ public class GenericAnnotationView extends AnnotationViewPartBase { // Element1 is higher than element2: Get ancestor of element2 that is at the // level as the element1. EObject ancestor = - getAncestor(modelElement2, modelElement2Level - modelElement1Level); + getModelElementAncestor(modelElement2, modelElement2Level - + modelElement1Level); if(modelElement1 == ancestor) { // Element1 is offspring of element2 @@ -282,7 +251,8 @@ public class GenericAnnotationView extends AnnotationViewPartBase { // Element2 is higher than element1: Get ancestor of element1 that is at the // level as the element2. EObject ancestor = - getAncestor(modelElement1, modelElement1Level - modelElement2Level); + getModelElementAncestor(modelElement1, modelElement1Level - + modelElement2Level); if(modelElement2 == ancestor) { // Element2 is offspring of element1 @@ -315,16 +285,45 @@ public class GenericAnnotationView extends AnnotationViewPartBase { } } - // Based on org.fortiss.af3.scheduling.ui.specificationsEditor.ComponentResourceTableBackend - /** Filters rows of the {@link GenericAnnotationView} based on the components' names. */ - private class ComponentNamesRowFilter extends ViewerFilter { - /** Flag if the filter is active */ - private boolean active = true; + /** + * Row and column filter for the {@link GenericAnnotationView}. Row filtering is realized by + * inheriting {@link ViewerFilter}, whereas for column filtering is supported by + * {@link AnnotationFilter#passesColumnFilter(ColumnHandle)} that is evaluated in + * {@link GenericAnnotationView#update(Collection)}. + */ + private class AnnotationFilter extends ViewerFilter { + /** Filter hint text. */ + private static final String FILTER_HINT_TEXT = "type filter text"; - /** (De-)activates the filter */ - public void setActive(boolean active) { - this.active = active; - } + /** Hierarchy filter option: all model elements from any level */ + private static final String HIERARCHY_LEVELS_ALL = "Show all levels"; + + /** Hierarchy filter option: all model elements from current level */ + private static final String HIERARCHY_LEVELS_CURRENT = "Show current level"; + + /** Hierarchy filter option: currently selected model element and its offspring */ + private static final String HIERARCHY_LEVELS_SELECTED_SUBMODEL = "Show selected sub-model"; + + /** Current model element / annotation name filter pattern */ + private String filterPattern; + + /** Flag whether columns / annotation names should be filtered. */ + private boolean filterColumnName; + + /** Flag whether rows / model element names should be filtered */ + private boolean filterRowName; + + /** + * Flag whether the displayed rows should be restricted to the currently selected model + * element type. + */ + boolean restrictToSelectedModelElementType; + + /** Row filter option based on hierarchy level of model element. */ + private String hierarchyLevelFilter; + + /** Column filter option based on annotation type */ + private Class<? extends IAnnotatedSpecification> annotationTypeFilter; /** {@inheritDoc} */ @Override @@ -338,38 +337,464 @@ public class GenericAnnotationView extends AnnotationViewPartBase { return true; } - return (!active || passesFilter( - ((INamedElement)annotationEntry.getModelElement()).getName(), filterPattern)) && - (!restrictToSelectedModelElementType || getCurrentlySelectedObject() == null || - getCurrentlySelectedObject() instanceof IProjectRootElement || annotationEntry - .getModelElement().getClass() - .equals(getCurrentlySelectedObject().getClass())); + return passesRowFilter(annotationEntry); + } + + /** + * Returns {@code true} if a given {@code input} passes an case-insensitive filter specified + * by {@code filterString}. + */ + private boolean passesNameFilter(String input) { + // Null-filter accepts every input + if(filterPattern == null || filterPattern.equals(FILTER_HINT_TEXT)) { + return true; + } + + // Null-input cannot be accepted by a non-null filter + if(input == null) { + return false; + } + return input.toLowerCase().contains(filterPattern.toLowerCase()); + } + + /** + * Returns {@code true} if given annotation entry matches the currently selected model + * element (or if this particular filter option is turned off). + */ + private boolean passesSelectedElementTypeFilter(AnnotationEntry annotationEntry) { + if(getCurrentlySelectedObject() == null || + getCurrentlySelectedObject() instanceof IProjectRootElement) { + return true; + } + + int currentlySelectedObjectLevel = getModelElementLevel(getCurrentlySelectedObject()); + int modelElementLevel = getModelElementLevel(annotationEntry.getModelElement()); + + if(hierarchyLevelFilter.equals(HIERARCHY_LEVELS_CURRENT)) { + if((getModelElementLevel(annotationEntry.getModelElement()) != currentlySelectedObjectLevel) || + (getCurrentlySelectedObject().eContainer() != getModelElementAncestor( + annotationEntry.getModelElement(), modelElementLevel - + (currentlySelectedObjectLevel - 1)))) { + return false; + } + } + if(hierarchyLevelFilter.equals(HIERARCHY_LEVELS_SELECTED_SUBMODEL)) { + if(modelElementLevel < currentlySelectedObjectLevel) { + return false; + } + + if((modelElementLevel > currentlySelectedObjectLevel) && + ((getCurrentlySelectedObject() != getModelElementAncestor( + annotationEntry.getModelElement(), modelElementLevel - + currentlySelectedObjectLevel))) || + (getCurrentlySelectedObject() != getModelElementAncestor( + annotationEntry.getModelElement(), modelElementLevel - + (currentlySelectedObjectLevel - 0)))) { + return false; + } + + } + + return !restrictToSelectedModelElementType || + annotationEntry.getModelElement().getClass() + .equals(getCurrentlySelectedObject().getClass()); + } + + /** Returns true if the given specification passes the current annotation type filter */ + private boolean passesAnnotationTypeFilter(IAnnotatedSpecification spec) { + return annotationTypeFilter == null || spec.getClass().equals(annotationTypeFilter); + } + + /** + * Returns {@code true} if the column represented by the given column is visible in the + * view. + */ + public boolean passesColumnFilter(ColumnHandle columnHandle) { + return (!filterColumnName || passesNameFilter(getColumnName(columnHandle))) && + passesSelectedElementTypeFilter(columnHandle.getEntry()) && + passesAnnotationTypeFilter(columnHandle.getAnnotatedSpecification()); + } + + /** + * Returns {@code true} if the rows represented by {@code annotationEntry} are visible + * in the view. + */ + public boolean passesRowFilter(AnnotationEntry annotationEntry) { + if(!(annotationEntry.getModelElement() instanceof INamedElement)) { + return true; + } + + return (!filterRowName || passesNameFilter(((INamedElement)annotationEntry + .getModelElement()).getName())) && + passesSelectedElementTypeFilter(annotationEntry); + } + + /** Sets the model element / annotation name filter pattern. */ + public void setNameFilterPattern(String filterPattern) { + this.filterPattern = filterPattern; + } + + /** Sets the flag whether rows should be filtered by name. */ + public void setFilterRowName(boolean filterRowName) { + this.filterRowName = filterRowName; + } + + /** Sets the flag whether columns should be filtered by name. */ + public void setColumnRowName(boolean filterColumnName) { + this.filterColumnName = filterColumnName; + } + + /** Sets the flag whether the view should be filtered by the selected model element type. */ + public void + setRestrictToSelectedModelElementType(boolean restrictToSelectedModelElementType) { + this.restrictToSelectedModelElementType = restrictToSelectedModelElementType; + } + + /** Sets the model element hierarchy level filter option. */ + public void setHierarchyLevelFilter(String hierarchyLevelFilter) { + this.hierarchyLevelFilter = hierarchyLevelFilter; + } + + /** Sets the annotation type filter option. */ + public void setAnnotationTypeFilter( + Class<? extends IAnnotatedSpecification> annotationTypeFilter) { + this.annotationTypeFilter = annotationTypeFilter; } } - /** - * Returns {@code true} if a given {@code input} passes an case-insensitive filter specified by - * {@code filterString}. - */ - private static boolean passesFilter(String input, String filterString) { - // Null-filter accepts every input - if(filterString == null || filterString.equals(FILTER_HINT_TEXT)) { - return true; + /** Widget to set content filters for the {@link GenericAnnotationView}. */ + private class AnnotationFilterWidget extends Composite { + /** Annotation type filter: all annotations */ + private static final String ANNOTATION_TYPE_ANY = "Show all annotations"; + + /** Combo box Column filter option based on annotation type */ + private ExtendedCCombo<Class<? extends IAnnotatedSpecification>> filterAnnotationTypeCombo; + + /** Constructs a new {@link AnnotationFilterWidget} */ + public AnnotationFilterWidget(Composite parent, int style, final GenericAnnotationView view) { + super(parent, style); + + GridLayout layout = new GridLayout(4, false); + layout.horizontalSpacing = 50; + setLayout(layout); + + // Row 1: name filter scope / labels + Composite nameFilterScopeComposite = createNameFilterScopeComposite(this, view); + nameFilterScopeComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true, + 1, 1)); + + final Label modelElementTypefilterLabel = new Label(this, SWT.NULL); + modelElementTypefilterLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, + true, 1, 1)); + modelElementTypefilterLabel.setText("Filter model element type:"); + + final Label hierarchyLevelFilterLabel = new Label(this, SWT.NULL); + hierarchyLevelFilterLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true, + 1, 1)); + hierarchyLevelFilterLabel.setText("Filter model element hierarchy level:"); + + final Label annotationTypeFilterLabel = new Label(this, SWT.NULL); + annotationTypeFilterLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true, + 1, 1)); + annotationTypeFilterLabel.setText("Filter annotation type:"); + + // Row 2: controls + Text nameFilterText = createNameFilterText(this, view); + nameFilterText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true, 1, 1)); + + Button modelElementTypeFilterButton = + createModelElementTypeFilterOptionsCheckButton(this, view); + modelElementTypeFilterButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, + true, 1, 1)); + + CCombo hierarchyLevelFilterCombo = + createHierarchyLevelFilterOptionsComboBox(this, view); + hierarchyLevelFilterCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true, + 1, 1)); + + ExtendedCCombo<Class<? extends IAnnotatedSpecification>> annotationTypeFilterCombo = + createAnnotationTypeFilterOptionsComboBox(this, view); + annotationTypeFilterCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true, + 1, 1)); } - // Null-input cannot be accepted by a non-null filter - if(input == null) { - return false; + /** + * Creates the {@link Text} input box for setting the pattern to filter the + * {@link GenericAnnotationView} for model element or annotation names + */ + private Text createNameFilterText(Composite parent, final GenericAnnotationView view) { + // Text input field for filter pattern + final Text nameFilterText = + new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL); + view.getAnnotationFilter(); + nameFilterText.setText(AnnotationFilter.FILTER_HINT_TEXT); + nameFilterText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY)); + + // Observe changes of filter pattern + nameFilterText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + String filterPattern = nameFilterText.getText(); + if(filterPattern.isEmpty()) { + nameFilterText.setText(AnnotationFilter.FILTER_HINT_TEXT); + nameFilterText.selectAll(); + filterPattern = AnnotationFilter.FILTER_HINT_TEXT; + } + if(filterPattern.equals(AnnotationFilter.FILTER_HINT_TEXT)) { + nameFilterText.setForeground(Display.getCurrent().getSystemColor( + SWT.COLOR_DARK_GRAY)); + } else { + nameFilterText.setForeground(Display.getCurrent().getSystemColor( + SWT.COLOR_WIDGET_FOREGROUND)); + } + view.getAnnotationFilter().setNameFilterPattern(filterPattern); + view.update(); + } + }); + + // Select the filter pattern if it is equal to the FILTER_HINT_TEXT + TextFocusListener textSelectionOnFocusListener = new TextFocusListener() { + + @Override + protected void focusOut(Text text, Event e) { + text.clearSelection(); + + } + + @Override + protected void focusIn(Text text, Event e) { + if(text.getText().equals(AnnotationFilter.FILTER_HINT_TEXT) && + (e.type != SWT.MouseUp || text.getSelectionCount() == 0)) { + text.selectAll(); + } + + } + }; + nameFilterText.addListener(SWT.FocusIn, textSelectionOnFocusListener); + nameFilterText.addListener(SWT.FocusOut, textSelectionOnFocusListener); + nameFilterText.addListener(SWT.MouseDown, textSelectionOnFocusListener); + nameFilterText.addListener(SWT.MouseUp, textSelectionOnFocusListener); + + return nameFilterText; + } + + /** + * Creates a {@link Composite} that contains radio buttons to set the scope of the name + * filter (model element names or annotation names). + */ + private Composite createNameFilterScopeComposite(Composite parent, + final GenericAnnotationView view) { + Composite nameFilterScopeComposite = new Composite(parent, SWT.NULL); + nameFilterScopeComposite.setLayout(new RowLayout()); + + // Radio buttons to select between row and column name filtering + final Label filterLabel = new Label(nameFilterScopeComposite, SWT.NULL); + filterLabel.setText("Filter "); + + final Button filterComponentNamesButton = + new Button(nameFilterScopeComposite, SWT.RADIO); + filterComponentNamesButton.setText("component"); + filterComponentNamesButton.setSelection(true); + view.getAnnotationFilter().setFilterRowName(true); + + filterComponentNamesButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + // widgetSelected() is also fired when the selection is removed from the radio + // button, so check, if this is the button that just obtained the selection + if(!((Button)e.getSource()).getSelection()) { + return; + } + view.getAnnotationFilter().setFilterRowName(true); + view.getAnnotationFilter().setColumnRowName(false); + view.update(); + } + }); + + final Button filterAnnotationNamesButton = + new Button(nameFilterScopeComposite, SWT.RADIO); + filterAnnotationNamesButton.setText("annotation names:"); + filterAnnotationNamesButton.setSelection(false); + + filterAnnotationNamesButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + // widgetSelected() is also fired when the selection is removed from the radio + // button, so check, if this is the button that just obtained the selection + if(!((Button)e.getSource()).getSelection()) { + return; + } + view.getAnnotationFilter().setFilterRowName(false); + view.getAnnotationFilter().setColumnRowName(true); + view.update(); + } + }); + + return nameFilterScopeComposite; + } + + /** + * Returns a {@link Button} for setting the options for filtering the + * {@link GenericAnnotationView} for the currently selected model element type. + * + * @param view + * {@link GenericAnnotationView} to be filtered. + */ + private Button createModelElementTypeFilterOptionsCheckButton(Composite parent, + final GenericAnnotationView view) { + + // Check box to restrict view to currently selected model element type + final Button filterModelElementTypeButton = new Button(parent, SWT.CHECK); + + filterModelElementTypeButton.setText("Show only selected model element type."); + filterModelElementTypeButton.setSelection(false); + + filterModelElementTypeButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + // widgetSelected() is also fired when the selection is removed from the radio + // button, so check, if this is the button that just obtained the selection + view.getAnnotationFilter().setRestrictToSelectedModelElementType( + ((Button)e.getSource()).getSelection()); + view.update(); + } + }); + + return filterModelElementTypeButton; + } + + /** + * Returns a {@link CCombo} for setting the options for filtering the + * {@link GenericAnnotationView} for the model element hierarchy level. + */ + private CCombo createHierarchyLevelFilterOptionsComboBox(Composite parent, + final GenericAnnotationView view) { + + // Combo box to select component hierarchy level filter + final CCombo filterHierarchyLevelCombo = new CCombo(parent, SWT.READ_ONLY); + + filterHierarchyLevelCombo.add(AnnotationFilter.HIERARCHY_LEVELS_ALL); + filterHierarchyLevelCombo.add(AnnotationFilter.HIERARCHY_LEVELS_CURRENT); + filterHierarchyLevelCombo.add(AnnotationFilter.HIERARCHY_LEVELS_SELECTED_SUBMODEL); + filterHierarchyLevelCombo.select(0); + + view.getAnnotationFilter().setHierarchyLevelFilter(filterHierarchyLevelCombo.getText()); + + filterHierarchyLevelCombo.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + @Override + public void widgetSelected(SelectionEvent e) { + view.getAnnotationFilter().setHierarchyLevelFilter( + filterHierarchyLevelCombo.getText()); + view.update(); + } + }); + + return filterHierarchyLevelCombo; + } + + /** + * Returns an {@link ExtendedCCombo} for setting the options for filtering the + * {@link GenericAnnotationView} for the annotation type. + */ + private ExtendedCCombo<Class<? extends IAnnotatedSpecification>> + createAnnotationTypeFilterOptionsComboBox(Composite parent, + final GenericAnnotationView view) { + + // // Combo box to select annotation type filter + filterAnnotationTypeCombo = + new ExtendedCCombo<Class<? extends IAnnotatedSpecification>>(parent, + SWT.READ_ONLY); + updateAnnotationTypeFilterOptionsComboBox(lastAnnotationEntries, view); + + filterAnnotationTypeCombo.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + @Override + public void widgetSelected(SelectionEvent e) { + view.getAnnotationFilter().setAnnotationTypeFilter( + filterAnnotationTypeCombo.getObject(filterAnnotationTypeCombo + .getSelectionIndex())); + view.update(); + } + }); + + return filterAnnotationTypeCombo; + } + + /** + * Update the combobox that can be used to configure the annotation type filter based on the + * annotations contained in the current set of annotation entries / model elements (before + * filtering). + */ + public void updateAnnotationTypeFilterOptionsComboBox( + Collection<AnnotationEntry> annotationEntries, GenericAnnotationView view) { + String currentSelection = filterAnnotationTypeCombo.getText(); + filterAnnotationTypeCombo.removeAll(); + filterAnnotationTypeCombo.add(ANNOTATION_TYPE_ANY, null); + + if(annotationEntries != null) { + // Set needed to ensure that each IAnnotatedSpecification which may be contributed + // by more than one AnnotationEntry is added exactly once to the ComboBox. + Set<Class<? extends IAnnotatedSpecification>> annotatedSpecificationClasses = + new HashSet<Class<? extends IAnnotatedSpecification>>(); + + for(AnnotationEntry annotationEntry : annotationEntries) { + for(IAnnotatedSpecification spec : pickInstanceOf( + IAnnotatedSpecification.class, annotationEntry.getSpecificationsList())) { + if(!annotatedSpecificationClasses.contains(spec.getClass())) { + filterAnnotationTypeCombo.add( + "Show " + + annotationEntry.getSpecificationAnnotationName(spec + .getClass()), spec.getClass()); + annotatedSpecificationClasses.add(spec.getClass()); + } + } + } + } + + // If possible, maintain selected element + int index = filterAnnotationTypeCombo.indexOf(currentSelection); + if(index == -1) { + filterAnnotationTypeCombo.select(0); + view.getAnnotationFilter().setAnnotationTypeFilter( + filterAnnotationTypeCombo.getObject(0)); + } else { + filterAnnotationTypeCombo.select(index); + } + + // Adjust size to new content + filterAnnotationTypeCombo.pack(true); } - return input.toLowerCase().contains(filterString.toLowerCase()); } - /** Returns {@code true} if the column represented by the given column is visible in the view. */ - private boolean passesFilter(ColumnHandle columnHandle, String filterPattern) { - if(!filterColumns) { - return true; + /** Returns the view's {@link AnnotationFilter}. */ + private AnnotationFilter getAnnotationFilter() { + return annotationFilter; + } + + /** Computes the level of an {@link IModelElement} in the tree */ + private int getModelElementLevel(EObject o) { + int level = 0; + while(o.eContainer() != null) { + level++; + o = o.eContainer(); + } + + return level; + } + + /** + * Returns the ancestor of the {@link IModelElement} {@code modelElement} that is + * {@code levelsUp} levels above model element in the hierarchy, or {@code null}, in case + * {@code levelsUp} > depth(modelElement}. + */ + private EObject getModelElementAncestor(EObject o, int levelsUp) { + while(o.eContainer() != null && levelsUp > 0) { + o = o.eContainer(); + levelsUp--; } - return passesFilter(getColumnName(columnHandle), filterPattern); + return o; + } /** Updates the table after a filter change */ @@ -386,6 +811,8 @@ public class GenericAnnotationView extends AnnotationViewPartBase { /** {@inheritDoc} */ @Override protected void update(Collection<AnnotationEntry> annotationEntries) { + filterWidget.updateAnnotationTypeFilterOptionsComboBox(annotationEntries, this); + // Remember last annotation entries (required to re-draw table after changing the // column/annotation filter lastAnnotationEntries = annotationEntries; @@ -422,7 +849,7 @@ public class GenericAnnotationView extends AnnotationViewPartBase { for(String instanceKey : entry.getInstanceKeys(spec.getClass())) { ColumnHandle columnHandle = new ColumnHandle(entry, spec, instanceKey, false); - if(passesFilter(columnHandle, filterPattern)) { + if(annotationFilter.passesColumnFilter(columnHandle)) { sortedColumnHandles.add(columnHandle); } } @@ -438,7 +865,7 @@ public class GenericAnnotationView extends AnnotationViewPartBase { new ColumnHandle(entry, spec, null, entry.allowsDynamicAnnotationInstances(spec.getClass())); - if(passesFilter(columnHandle, filterPattern)) { + if(annotationFilter.passesColumnFilter(columnHandle)) { sortedColumnHandles.add(columnHandle); } @@ -456,138 +883,6 @@ public class GenericAnnotationView extends AnnotationViewPartBase { tableViewer.refresh(); } - /** Creates the UI controls for the column and row filter. */ - private void createFilter(Composite rootComposite) { - // 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 - // table, with an implementation of the filter in update(Set<AnnotationEntry>). - final ComponentNamesRowFilter rowfilter = new ComponentNamesRowFilter(); - rowfilter.setActive(true); - tableViewer.addFilter(rowfilter); - - // Composite for filter GUI controls - Composite filterComposite = new Composite(rootComposite, SWT.NULL); - filterComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - filterComposite.setLayout(new GridLayout(2, false)); - - // Text input field for filter pattern - filterText = - new Text(filterComposite, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL); - filterText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true, 1, 1)); - filterText.setText(FILTER_HINT_TEXT); - filterText.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY)); - - // Observe changes of filter pattern - filterText.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - filterPattern = filterText.getText(); - if(filterPattern.isEmpty()) { - filterText.setText(FILTER_HINT_TEXT); - filterText.selectAll(); - filterPattern = FILTER_HINT_TEXT; - } - if(filterPattern.equals(FILTER_HINT_TEXT)) { - filterText.setForeground(Display.getCurrent().getSystemColor( - SWT.COLOR_DARK_GRAY)); - } else { - filterText.setForeground(Display.getCurrent().getSystemColor( - SWT.COLOR_WIDGET_FOREGROUND)); - } - update(); - } - }); - - // Select the filter pattern if it is equal to the FILTER_HINT_TEXT - TextFocusListener textSelectionOnFocusListener = new TextFocusListener() { - - @Override - protected void focusOut(Text text, Event e) { - text.clearSelection(); - - } - - @Override - protected void focusIn(Text text, Event e) { - if(text.getText().equals(FILTER_HINT_TEXT) && - (e.type != SWT.MouseUp || text.getSelectionCount() == 0)) { - text.selectAll(); - } - - } - }; - filterText.addListener(SWT.FocusIn, textSelectionOnFocusListener); - filterText.addListener(SWT.FocusOut, textSelectionOnFocusListener); - filterText.addListener(SWT.MouseDown, textSelectionOnFocusListener); - filterText.addListener(SWT.MouseUp, textSelectionOnFocusListener); - - // Filter options - Composite filterOptionsComposite = new Composite(filterComposite, SWT.NULL); - filterOptionsComposite - .setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - filterOptionsComposite.setLayout(new RowLayout()); - - // Radio buttons to select between row and column filtering - final Label filterLabel = new Label(filterOptionsComposite, SWT.NULL); - filterLabel.setText("Filter "); - - final Button filterComponentNamesButton = new Button(filterOptionsComposite, SWT.RADIO); - filterComponentNamesButton.setText("component"); - filterComponentNamesButton.setSelection(true); - filterComponentNamesButton.addSelectionListener(new SelectionAdapter() { - - @Override - public void widgetSelected(SelectionEvent e) { - // widgetSelected() is also fired when the selection is removed from the radio - // button, so check, if this is the button that just obtained the selection - if(!((Button)e.getSource()).getSelection()) { - return; - } - rowfilter.setActive(true); - filterColumns = false; - update(); - } - }); - - final Button filterAnnotationNamesButton = new Button(filterOptionsComposite, SWT.RADIO); - filterAnnotationNamesButton.setText("annotation names."); - filterAnnotationNamesButton.setSelection(false); - filterAnnotationNamesButton.addSelectionListener(new SelectionAdapter() { - - @Override - public void widgetSelected(SelectionEvent e) { - // widgetSelected() is also fired when the selection is removed from the radio - // button, so check, if this is the button that just obtained the selection - if(!((Button)e.getSource()).getSelection()) { - return; - } - rowfilter.setActive(false); - filterColumns = true; - update(); - } - }); - - final Label spacerLabel = new Label(filterOptionsComposite, SWT.NULL); - spacerLabel.setText(" "); - - // Check box to restrict view to currently selected model element type - final Button filterModelElementType = new Button(filterOptionsComposite, SWT.CHECK); - filterModelElementType.setText("Show only selected model element type."); - filterModelElementType.setSelection(false); - filterModelElementType.addSelectionListener(new SelectionAdapter() { - - @Override - public void widgetSelected(SelectionEvent e) { - // widgetSelected() is also fired when the selection is removed from the radio - // button, so check, if this is the button that just obtained the selection - restrictToSelectedModelElementType = ((Button)e.getSource()).getSelection(); - update(); - } - }); - - } - /** {@inheritDoc} */ @Override public void createPartControl(Composite parent) { @@ -605,7 +900,17 @@ public class GenericAnnotationView extends AnnotationViewPartBase { tableViewer.setComparator(new HierarchicalNameComparator()); - createFilter(rootComposite); + // 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 + // table, with an implementation of the filter in update(Set<AnnotationEntry>). + annotationFilter = new AnnotationFilter(); + tableViewer.addFilter(annotationFilter); + + // Add filter widget + filterWidget = new AnnotationFilterWidget(rootComposite, SWT.NULL, this); + filterWidget.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + } /** Creates the (leading) column which displays the model elements */