Skip to content
Snippets Groups Projects
Commit dcaf54f4 authored by Simon Barner's avatar Simon Barner
Browse files

- Enable filtering of rows (component names) and columns (annotation names)

refs 1841
parent 37081f17
No related branches found
No related tags found
No related merge requests found
......@@ -134,7 +134,10 @@ public class CreateAnnotationInstanceColumn extends ViewerColumn {
@Override
public void widgetDisposed(DisposeEvent e) {
crButton.dispose();
if(crButton != null) {
crButton.dispose();
crButton = null;
}
}
});
}
......
......@@ -28,11 +28,22 @@ import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
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.layout.FillLayout;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.model.element.IModelElement;
import org.fortiss.tooling.base.ui.annotation.AnnotationEntry;
......@@ -63,11 +74,31 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
protected TableViewer tableViewer;
/**
* Character used to separte the annotation name from the instance name for multi-instance
* Character used to separate the annotation name from the instance name for multi-instance
* annotations.
*/
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;
/** Flag whether columns should be filtered. */
boolean filterColumns;
/**
* Last set of {@link AnnotationEntry}s used to {@link #update(Collection)} the
* {@link GenericAnnotationView}, i.e. during the construction of the, or because of a model
* change. The reference is required to update view after a change of the column (annotation)
* filter.
*/
Collection<AnnotationEntry> lastAnnotationEntries;
/**
* Data required to identify a column displaying a particular {@link IAnnotatedSpecification} in
* a column of the {@link GenericAnnotationView}. Used to sort columns (see
......@@ -276,9 +307,73 @@ 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;
/** (De-)activates the filter */
public void setActive(boolean active) {
this.active = active;
}
/** {@inheritDoc} */
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if(!active) {
return true;
}
if(!(element instanceof AnnotationEntry)) {
return true;
}
AnnotationEntry annotationEntry = ((AnnotationEntry)element);
if(!(annotationEntry.getModelElement() instanceof INamedElement)) {
return true;
}
return passesFilter(((INamedElement)annotationEntry.getModelElement()).getName(),
filterPattern);
}
}
/**
* Returns 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;
}
// Null-input cannot be accepted by a non-null filter
if(input == null) {
return false;
}
return input.toLowerCase().contains(filterString.toLowerCase());
}
/** Updates the table after a filter change */
private void update() {
// Full update, i.e. including reconstructions of columns is always required due to the
// following scenario:
// - Filter is set for annotation names.
// - Filter scope is changed to component names -> annotation filter needs to be undone
if(lastAnnotationEntries != null) {
update(lastAnnotationEntries);
}
}
/** {@inheritDoc} */
@Override
protected void update(Collection<AnnotationEntry> annotationEntries) {
// Remember last annotation entries (required to re-draw table after changing the
// column/annotation filter
lastAnnotationEntries = annotationEntries;
// Minimize flickering while updating the table
tableViewer.getTable().setRedraw(false);
......@@ -304,7 +399,12 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
!entry.getInstanceKeys(spec.getClass()).isEmpty()) {
for(String instanceKey : entry.getInstanceKeys(spec.getClass())) {
sortedColumnHandles.add(new ColumnHandle(entry, spec, instanceKey, false));
ColumnHandle columnHandle =
new ColumnHandle(entry, spec, instanceKey, false);
if(!filterColumns ||
passesFilter(getColumnName(columnHandle), filterPattern)) {
sortedColumnHandles.add(columnHandle);
}
}
}
......@@ -314,8 +414,13 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
// created dynamically
if(entry.getInstanceKeys(spec.getClass()).isEmpty() ||
entry.allowsDynamicAnnotationInstances(spec.getClass())) {
sortedColumnHandles.add(new ColumnHandle(entry, spec, null, entry
.allowsDynamicAnnotationInstances(spec.getClass())));
ColumnHandle columnHandle =
new ColumnHandle(entry, spec, null,
entry.allowsDynamicAnnotationInstances(spec.getClass()));
if(!filterColumns || passesFilter(getColumnName(columnHandle), filterPattern)) {
sortedColumnHandles.add(columnHandle);
}
}
}
......@@ -334,21 +439,134 @@ 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);
// Radio buttons to select between row and column filtering
Composite radioButtonComposite = new Composite(filterComposite, SWT.NULL);
radioButtonComposite.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
radioButtonComposite.setLayout(new RowLayout());
final Button filterComponentNamesButton = new Button(radioButtonComposite, SWT.RADIO);
filterComponentNamesButton.setText("Components names");
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(radioButtonComposite, 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();
}
});
}
/** {@inheritDoc} */
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
Composite c = new Composite(parent, SWT.NULL);
c.setLayout(new FillLayout(SWT.HORIZONTAL));
Composite rootComposite = new Composite(parent, SWT.NULL);
rootComposite.setLayout(new GridLayout(1, false));
tableViewer = new TableViewer(c, SWT.BORDER | SWT.FULL_SELECTION);
tableViewer = new TableViewer(rootComposite, SWT.BORDER | SWT.FULL_SELECTION);
Table table = tableViewer.getTable();
table.setHeaderVisible(true);
table.setLinesVisible(true);
table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
createModelElementColumn();
tableViewer.setComparator(new HierarchicalNameComparator());
createFilter(rootComposite);
}
/** Creates the (leading) column which displays the model elements */
......
/*--------------------------------------------------------------------------+
$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. |
+--------------------------------------------------------------------------*/
// Based on http://stackoverflow.com/a/10048884, provided by Stackoverflow user "seand".
// (follow link for license)
package org.fortiss.tooling.base.ui.annotation.view;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
/**
* Listener to react to focus in/out events of {@link Text} controls than can originate from both
* keyboard and mouse events.
*/
public abstract class TextFocusListener implements Listener {
/** Flag if {@link Text} currently has the focus */
private boolean hasFocus = false;
/** Flag required to obtain focus via mouse events */
private boolean hadFocusOnMousedown = false;
/** Action to be performed on focus in events */
protected abstract void focusIn(Text text, Event e);
/** Action to be performed on focus out events */
protected abstract void focusOut(Text text, Event e);
/** {@inheritDoc} */
@Override
public void handleEvent(Event e) {
if(!(e.widget instanceof Text)) {
return;
}
Text text = (Text)e.widget;
switch(e.type) {
case SWT.FocusIn: {
// Covers the case where the user focuses by keyboard.
focusIn(text, e);
// The case where the user focuses by mouse click is special because Eclipse,
// for some reason, fires SWT.FocusIn before SWT.MouseDown, and on mouse down
// it cancels the selection. So we set a variable to keep track of whether the
// control is focused (can't rely on isFocusControl() because sometimes it's
// wrong), and we make it asynchronous so it will get set AFTER SWT.MouseDown is
// fired.
text.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
hasFocus = true;
}
});
break;
}
case SWT.FocusOut: {
hasFocus = false;
focusOut(text, e);
break;
}
case SWT.MouseDown: {
// Set the variable which is used in SWT.MouseUp.
hadFocusOnMousedown = hasFocus;
break;
}
case SWT.MouseUp: {
if(!hadFocusOnMousedown) {
focusIn(text, e);
}
break;
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment