Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDirk Fauth2022-11-08 05:28:19 +0000
committerDirk Fauth2022-11-08 05:28:19 +0000
commit53c3ea7db6d70c4535374a47edfafb67c9467b3d (patch)
tree682dfa2b31e273f707e267f75b4004aeab4e50e3
parent8346f341b1c07792083e783d6b8f6d35bb237032 (diff)
downloadorg.eclipse.nebula.widgets.nattable-53c3ea7db6d70c4535374a47edfafb67c9467b3d.tar.gz
org.eclipse.nebula.widgets.nattable-53c3ea7db6d70c4535374a47edfafb67c9467b3d.tar.xz
org.eclipse.nebula.widgets.nattable-53c3ea7db6d70c4535374a47edfafb67c9467b3d.zip
Bug 581005 - Support mixed filter row
Signed-off-by: Dirk Fauth <dirk.fauth@googlemail.com> Change-Id: I340f4c82b0fcb1c72a1b4711afcff7899a9270bd
-rw-r--r--org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java4
-rw-r--r--org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_603_Filter/_6037_MixedFilterRowExample.java943
-rw-r--r--org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxFilterRowHeaderComposite.java59
-rw-r--r--org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxGlazedListsFilterStrategy.java20
-rw-r--r--org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java5
5 files changed, 1019 insertions, 12 deletions
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java
index e7a5d134..ee9cb59b 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2021 Original authors and others.
+ * Copyright (c) 2012, 2022 Original authors and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -179,7 +179,7 @@ public class FilterRowDataProvider<T> implements IDataProvider, IPersistable {
@Override
public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
- if (ObjectUtils.isNotNull(newValue)) {
+ if (newValue != null && newValue.toString().length() > 0) {
this.filterIndexToObjectMap.put(columnIndex, newValue);
} else {
this.filterIndexToObjectMap.remove(columnIndex);
diff --git a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_603_Filter/_6037_MixedFilterRowExample.java b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_603_Filter/_6037_MixedFilterRowExample.java
new file mode 100644
index 00000000..5c7ded65
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_603_Filter/_6037_MixedFilterRowExample.java
@@ -0,0 +1,943 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Dirk Fauth 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/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.nebula.widgets.nattable.examples._600_GlazedLists._603_Filter;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.bindings.keys.ParseException;
+import org.eclipse.jface.fieldassist.SimpleContentProposalProvider;
+import org.eclipse.jface.fieldassist.TextContentAdapter;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
+import org.eclipse.nebula.widgets.nattable.data.ExtendedReflectiveColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor;
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
+import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
+import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDisplayConverter;
+import org.eclipse.nebula.widgets.nattable.data.convert.DefaultIntegerDisplayConverter;
+import org.eclipse.nebula.widgets.nattable.data.convert.DisplayConverter;
+import org.eclipse.nebula.widgets.nattable.dataset.person.DataModelConstants;
+import org.eclipse.nebula.widgets.nattable.dataset.person.Person.Gender;
+import org.eclipse.nebula.widgets.nattable.dataset.person.PersonService;
+import org.eclipse.nebula.widgets.nattable.dataset.person.PersonWithAddress;
+import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.edit.editor.ComboBoxCellEditor;
+import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor;
+import org.eclipse.nebula.widgets.nattable.edit.editor.IComboBoxDataProvider;
+import org.eclipse.nebula.widgets.nattable.examples.AbstractNatExample;
+import org.eclipse.nebula.widgets.nattable.examples.runner.StandaloneNatExampleRunner;
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.ComboBoxFilterRowHeaderComposite;
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.ComboBoxGlazedListsFilterStrategy;
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.GlazedListsFilterRowComboBoxDataProvider;
+import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataLayer;
+import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataProvider;
+import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowPainter;
+import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowRegularExpressionConverter;
+import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowTextCellEditor;
+import org.eclipse.nebula.widgets.nattable.filterrow.IFilterStrategy;
+import org.eclipse.nebula.widgets.nattable.filterrow.combobox.ComboBoxFilterIconPainter;
+import org.eclipse.nebula.widgets.nattable.filterrow.combobox.ComboBoxFilterRowConfiguration;
+import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxCellEditor;
+import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxDataProvider;
+import org.eclipse.nebula.widgets.nattable.filterrow.config.FilterRowConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.freeze.CompositeFreezeLayer;
+import org.eclipse.nebula.widgets.nattable.freeze.FreezeLayer;
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnLabelAccumulator;
+import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelAccumulator;
+import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.PaddingDecorator;
+import org.eclipse.nebula.widgets.nattable.persistence.command.DisplayPersistenceDialogCommandHandler;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
+import org.eclipse.nebula.widgets.nattable.style.Style;
+import org.eclipse.nebula.widgets.nattable.style.theme.IThemeExtension;
+import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration;
+import org.eclipse.nebula.widgets.nattable.style.theme.ThemeConfiguration;
+import org.eclipse.nebula.widgets.nattable.ui.NatEventData;
+import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
+import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
+import org.eclipse.nebula.widgets.nattable.ui.menu.HeaderMenuConfiguration;
+import org.eclipse.nebula.widgets.nattable.ui.menu.IMenuItemProvider;
+import org.eclipse.nebula.widgets.nattable.ui.menu.IMenuItemState;
+import org.eclipse.nebula.widgets.nattable.ui.menu.MenuItemProviders;
+import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
+import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
+import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
+import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+
+import ca.odell.glazedlists.EventList;
+import ca.odell.glazedlists.FilterList;
+import ca.odell.glazedlists.GlazedLists;
+import ca.odell.glazedlists.SortedList;
+import ca.odell.glazedlists.TransformedList;
+import ca.odell.glazedlists.matchers.CompositeMatcherEditor;
+import ca.odell.glazedlists.matchers.Matcher;
+import ca.odell.glazedlists.matchers.MatcherEditor;
+
+/**
+ * Example showing how to add the filter row to the layer composition of a grid
+ * that contains Excel like filters, text filters and combobox filters.
+ */
+public class _6037_MixedFilterRowExample extends AbstractNatExample {
+
+ private static final String EXCLUDE_LABEL = "EXCLUDE";
+
+ private ArrayList<Serializable> filterExcludes = new ArrayList<>();
+
+ public static void main(String[] args) throws Exception {
+ StandaloneNatExampleRunner.run(new _6037_MixedFilterRowExample());
+ }
+
+ @Override
+ public String getDescription() {
+ return "This example shows the usage of the filter row within a grid that has"
+ + " Excel-like multi-select combobox filters, free text filters and"
+ + " single-selection combobox filters in the filter row";
+ }
+
+ @Override
+ public Control createExampleControl(Composite parent) {
+ Composite container = new Composite(parent, SWT.NONE);
+ container.setLayout(new GridLayout());
+
+ // create a new ConfigRegistry which will be needed for GlazedLists
+ // handling
+ ConfigRegistry configRegistry = new ConfigRegistry();
+
+ // property names of the Person class
+ String[] propertyNames = { "firstName", "lastName", "gender",
+ "married", "birthday", "address.street", "address.housenumber",
+ "address.postalCode", "address.city" };
+
+ // mapping from property to label, needed for column header labels
+ Map<String, String> propertyToLabelMap = new HashMap<>();
+ propertyToLabelMap.put("firstName", "Firstname");
+ propertyToLabelMap.put("lastName", "Lastname");
+ propertyToLabelMap.put("gender", "Gender");
+ propertyToLabelMap.put("married", "Married");
+ propertyToLabelMap.put("birthday", "Birthday");
+ propertyToLabelMap.put("address.street", "Street");
+ propertyToLabelMap.put("address.housenumber", "Housenumber");
+ propertyToLabelMap.put("address.postalCode", "Postal Code");
+ propertyToLabelMap.put("address.city", "City");
+
+ IColumnPropertyAccessor<PersonWithAddress> columnPropertyAccessor =
+ new ExtendedReflectiveColumnPropertyAccessor<>(propertyNames);
+
+ IRowIdAccessor<PersonWithAddress> rowIdAccessor = new IRowIdAccessor<PersonWithAddress>() {
+
+ @Override
+ public Serializable getRowId(PersonWithAddress rowObject) {
+ return rowObject.getId();
+ }
+ };
+
+ final BodyLayerStack<PersonWithAddress> bodyLayerStack =
+ new BodyLayerStack<>(
+ PersonService.getPersonsWithAddress(50),
+ columnPropertyAccessor);
+
+ // add some null and empty values to verify the correct handling
+ bodyLayerStack.getBodyDataLayer().setDataValue(0, 3, "");
+ bodyLayerStack.getBodyDataLayer().setDataValue(0, 5, null);
+ bodyLayerStack.getBodyDataLayer().setDataValue(1, 2, "");
+ bodyLayerStack.getBodyDataLayer().setDataValue(1, 6, null);
+
+ // build the column header layer
+ IDataProvider columnHeaderDataProvider =
+ new DefaultColumnHeaderDataProvider(propertyNames, propertyToLabelMap);
+ DataLayer columnHeaderDataLayer =
+ new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);
+ ILayer columnHeaderLayer =
+ new ColumnHeaderLayer(
+ columnHeaderDataLayer,
+ bodyLayerStack,
+ bodyLayerStack.getSelectionLayer());
+
+ // Create a customized GlazedListsFilterRowComboBoxDataProvider that
+ // distincts the empty string and null from the collected values. This
+ // way null and "" entries in the collection are treated the same way
+ // and there is only a single "empty" entry in the dropdown.
+ GlazedListsFilterRowComboBoxDataProvider<PersonWithAddress> filterRowComboBoxDataProvider =
+ new GlazedListsFilterRowComboBoxDataProvider<PersonWithAddress>(
+ bodyLayerStack.getGlazedListsEventLayer(),
+ bodyLayerStack.getSortedList(),
+ columnPropertyAccessor) {
+
+ @Override
+ protected List<?> collectValues(int columnIndex) {
+ List<?> result = super.collectValues(columnIndex);
+ result = result.stream()
+ .map(x -> (x instanceof String && ((String) x).isEmpty()) ? null : x)
+ .distinct()
+ .collect(Collectors.toList());
+
+ return result;
+ }
+ };
+
+ ComboBoxGlazedListsWithExcludeFilterStrategy<PersonWithAddress> filterStrategy =
+ new ComboBoxGlazedListsWithExcludeFilterStrategy<>(
+ filterRowComboBoxDataProvider,
+ bodyLayerStack.getFilterList(),
+ columnPropertyAccessor,
+ configRegistry);
+
+ // create the ComboBoxFilterRowHeaderComposite without the default
+ // configuration
+ ComboBoxFilterRowHeaderComposite<PersonWithAddress> filterRowHeaderLayer =
+ new ComboBoxFilterRowHeaderComposite<>(
+ filterStrategy,
+ filterRowComboBoxDataProvider,
+ columnHeaderLayer,
+ columnHeaderDataProvider,
+ configRegistry,
+ false);
+
+ // add a default ComboBoxFilterRowConfiguration with an updated editor
+ // that shows a filter icon if a filter is applied
+ IComboBoxDataProvider comboBoxDataProvider = filterRowHeaderLayer.getComboBoxDataProvider();
+ FilterRowComboBoxCellEditor filterEditor = new FilterRowComboBoxCellEditor(comboBoxDataProvider, 10);
+ filterEditor.setShowDropdownFilter(true);
+ filterRowHeaderLayer.addConfiguration(
+ new ComboBoxFilterRowConfiguration(
+ filterEditor,
+ new ComboBoxFilterIconPainter(comboBoxDataProvider, GUIHelper.getImage("filter"), null)));
+
+ // add the specialized configuration to the
+ // ComboBoxFilterRowHeaderComposite
+ filterRowHeaderLayer.addConfiguration(new FilterRowConfiguration());
+
+ // build the row header layer
+ IDataProvider rowHeaderDataProvider =
+ new DefaultRowHeaderDataProvider(bodyLayerStack.getBodyDataProvider());
+ DataLayer rowHeaderDataLayer =
+ new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
+ ILayer rowHeaderLayer =
+ new RowHeaderLayer(
+ rowHeaderDataLayer,
+ bodyLayerStack,
+ bodyLayerStack.getSelectionLayer());
+
+ // build the corner layer
+ IDataProvider cornerDataProvider =
+ new DefaultCornerDataProvider(
+ columnHeaderDataProvider,
+ rowHeaderDataProvider);
+ DataLayer cornerDataLayer =
+ new DataLayer(cornerDataProvider);
+ ILayer cornerLayer =
+ new CornerLayer(
+ cornerDataLayer,
+ rowHeaderLayer,
+ filterRowHeaderLayer);
+
+ // build the grid layer
+ GridLayer gridLayer =
+ new GridLayer(
+ bodyLayerStack,
+ filterRowHeaderLayer,
+ rowHeaderLayer,
+ cornerLayer);
+
+ // turn the auto configuration off as we want to add our header menu
+ // configuration
+ NatTable natTable = new NatTable(container, gridLayer, false);
+
+ // as the autoconfiguration of the NatTable is turned off, we have to
+ // add the DefaultNatTableStyleConfiguration and the ConfigRegistry
+ // manually
+ natTable.setConfigRegistry(configRegistry);
+ natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
+
+ // edit configuration
+ natTable.addConfiguration(new EditConfiguration());
+
+ // header menu configuration
+ natTable.addConfiguration(new HeaderMenuConfiguration(natTable) {
+ @Override
+ protected PopupMenuBuilder createCornerMenu(NatTable natTable) {
+ return super.createCornerMenu(natTable)
+ .withStateManagerMenuItemProvider();
+ }
+ });
+
+ // body menu configuration
+ natTable.addConfiguration(new BodyMenuConfiguration<PersonWithAddress>(
+ natTable,
+ bodyLayerStack,
+ rowIdAccessor,
+ filterStrategy,
+ filterRowHeaderLayer.getFilterRowDataLayer().getFilterRowDataProvider()));
+
+ natTable.configure();
+
+ // The painter instances in a theme configuration are created on demand
+ // to avoid unnecessary painter instances in memory. To change the
+ // default filter row cell painter with the one for the excel like
+ // filter row, we get the painter from the ConfigRegistry after
+ // natTable#configure() and override createPainterInstances() of the
+ // theme configuration.
+ ModernNatTableThemeConfiguration themeConfiguration = new ModernNatTableThemeConfiguration() {
+ @Override
+ public void createPainterInstances() {
+ super.createPainterInstances();
+ this.filterRowCellPainter = configRegistry.getConfigAttribute(
+ CellConfigAttributes.CELL_PAINTER,
+ DisplayMode.NORMAL,
+ GridRegion.FILTER_ROW);
+ }
+ };
+
+ // configure the filter exclude support
+ configureFilterExcludes(rowIdAccessor, filterStrategy, bodyLayerStack, themeConfiguration);
+
+ natTable.setTheme(themeConfiguration);
+
+ natTable.registerCommandHandler(
+ new DisplayPersistenceDialogCommandHandler(natTable));
+
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
+
+ return container;
+ }
+
+ /**
+ * This method is used to configure the filter exclude support. This means
+ * it creates and applies an exclude {@link Matcher}, configures an
+ * {@link IConfigLabelAccumulator} and registers the styling via
+ * {@link IThemeExtension}.
+ *
+ * @param rowIdAccessor
+ * @param filterStrategy
+ * @param bodyLayerStack
+ * @param themeConfiguration
+ */
+ private void configureFilterExcludes(
+ IRowIdAccessor<PersonWithAddress> rowIdAccessor,
+ ComboBoxGlazedListsWithExcludeFilterStrategy<PersonWithAddress> filterStrategy,
+ BodyLayerStack<PersonWithAddress> bodyLayerStack,
+ ThemeConfiguration themeConfiguration) {
+
+ // register the Matcher to the
+ // ComboBoxGlazedListsWithExcludeFilterStrategy
+ Matcher<PersonWithAddress> idMatcher = new Matcher<PersonWithAddress>() {
+ @Override
+ public boolean matches(PersonWithAddress item) {
+ return _6037_MixedFilterRowExample.this.filterExcludes.contains(rowIdAccessor.getRowId(item));
+ }
+ };
+ filterStrategy.addExcludeFilter(idMatcher);
+
+ // register the IConfigLabelAccumulator to the body DataLayer
+ bodyLayerStack.getBodyDataLayer().setConfigLabelAccumulator(new IConfigLabelAccumulator() {
+
+ @Override
+ public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) {
+ if (idMatcher.matches(bodyLayerStack.getBodyDataProvider().getRowObject(rowPosition))) {
+ configLabels.add(EXCLUDE_LABEL);
+ }
+ }
+ });
+
+ // extend the ThemeConfiguration to add styling for the EXCLUDE_LABEL
+ themeConfiguration.addThemeExtension(new IThemeExtension() {
+
+ @Override
+ public void unregisterStyles(IConfigRegistry configRegistry) {
+ configRegistry.unregisterConfigAttribute(
+ CellConfigAttributes.CELL_STYLE,
+ DisplayMode.NORMAL,
+ EXCLUDE_LABEL);
+ }
+
+ @Override
+ public void registerStyles(IConfigRegistry configRegistry) {
+ Style style = new Style();
+ style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, GUIHelper.COLOR_WIDGET_LIGHT_SHADOW);
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.CELL_STYLE,
+ style,
+ DisplayMode.NORMAL,
+ EXCLUDE_LABEL);
+ }
+ });
+ }
+
+ /**
+ * Always encapsulate the body layer stack in an AbstractLayerTransform to
+ * ensure that the index transformations are performed in later commands.
+ *
+ * @param <T>
+ */
+ class BodyLayerStack<T> extends AbstractLayerTransform {
+
+ private final SortedList<T> sortedList;
+ private final FilterList<T> filterList;
+
+ private final ListDataProvider<T> bodyDataProvider;
+ private final DataLayer bodyDataLayer;
+ private final GlazedListsEventLayer<T> glazedListsEventLayer;
+
+ private final SelectionLayer selectionLayer;
+
+ public BodyLayerStack(List<T> values, IColumnPropertyAccessor<T> columnPropertyAccessor) {
+ // wrapping of the list to show into GlazedLists
+ // see http://publicobject.com/glazedlists/ for further information
+ EventList<T> eventList = GlazedLists.eventList(values);
+ TransformedList<T, T> rowObjectsGlazedList = GlazedLists.threadSafeList(eventList);
+
+ // use the SortedList constructor with 'null' for the Comparator
+ // because the Comparator will be set by configuration
+ this.sortedList = new SortedList<>(rowObjectsGlazedList, null);
+ // wrap the SortedList with the FilterList
+ this.filterList = new FilterList<>(this.sortedList);
+
+ this.bodyDataProvider =
+ new ListDataProvider<>(this.filterList, columnPropertyAccessor);
+ this.bodyDataLayer = new DataLayer(getBodyDataProvider());
+ this.bodyDataLayer.setConfigLabelAccumulator(new ColumnLabelAccumulator());
+
+ // layer for event handling of GlazedLists and PropertyChanges
+ this.glazedListsEventLayer =
+ new GlazedListsEventLayer<>(this.bodyDataLayer, this.filterList);
+
+ this.selectionLayer = new SelectionLayer(getGlazedListsEventLayer());
+ ViewportLayer viewportLayer = new ViewportLayer(getSelectionLayer());
+
+ FreezeLayer freezeLayer = new FreezeLayer(this.selectionLayer);
+ CompositeFreezeLayer compositeFreezeLayer =
+ new CompositeFreezeLayer(freezeLayer, viewportLayer, this.selectionLayer);
+
+ setUnderlyingLayer(compositeFreezeLayer);
+ }
+
+ public SelectionLayer getSelectionLayer() {
+ return this.selectionLayer;
+ }
+
+ public SortedList<T> getSortedList() {
+ return this.sortedList;
+ }
+
+ public FilterList<T> getFilterList() {
+ return this.filterList;
+ }
+
+ public ListDataProvider<T> getBodyDataProvider() {
+ return this.bodyDataProvider;
+ }
+
+ public DataLayer getBodyDataLayer() {
+ return this.bodyDataLayer;
+ }
+
+ public GlazedListsEventLayer<T> getGlazedListsEventLayer() {
+ return this.glazedListsEventLayer;
+ }
+ }
+
+ /**
+ * Specialized {@link ComboBoxGlazedListsFilterStrategy} that can be used to
+ * exclude items from filtering. This means you can register a
+ * {@link Matcher} that avoids that matching items get filtered by the
+ * filterrow.
+ *
+ * @param <T>
+ */
+ class ComboBoxGlazedListsWithExcludeFilterStrategy<T> extends ComboBoxGlazedListsFilterStrategy<T> {
+
+ private final CompositeMatcherEditor<T> compositeMatcherEditor;
+
+ protected Map<Matcher<T>, MatcherEditor<T>> excludeMatcherEditor = new HashMap<>();
+
+ public ComboBoxGlazedListsWithExcludeFilterStrategy(
+ FilterRowComboBoxDataProvider<T> comboBoxDataProvider,
+ FilterList<T> filterList,
+ IColumnAccessor<T> columnAccessor,
+ IConfigRegistry configRegistry) {
+ super(comboBoxDataProvider, filterList, columnAccessor, configRegistry);
+
+ // The default MatcherEditor is created and stored as member in the
+ // DefaultGlazedListsFilterStrategy. That MatcherEditor is used for
+ // the default filter operations. To exclude entries from filtering,
+ // we create another CompositeMatcherEditor with an OR mode and set
+ // that one on the FilterList.
+ this.compositeMatcherEditor = new CompositeMatcherEditor<>();
+ this.compositeMatcherEditor.setMode(CompositeMatcherEditor.OR);
+
+ this.compositeMatcherEditor.getMatcherEditors().add(getMatcherEditor());
+
+ this.filterList.setMatcherEditor(this.compositeMatcherEditor);
+ }
+
+ /**
+ * Add a exclude filter to this filter strategy which will always be
+ * applied additionally to any other filter to exclude from filtering.
+ *
+ * @param matcher
+ * the exclude filter to add
+ */
+ public void addExcludeFilter(final Matcher<T> matcher) {
+ // create a new MatcherEditor
+ MatcherEditor<T> matcherEditor = GlazedLists.fixedMatcherEditor(matcher);
+ addExcludeFilter(matcherEditor);
+ }
+
+ /**
+ * Add a exclude filter to this filter strategy which will always be
+ * applied additionally to any other filter to exclude items from
+ * filtering.
+ *
+ * @param matcherEditor
+ * the exclude filter to add
+ */
+ public void addExcludeFilter(final MatcherEditor<T> matcherEditor) {
+ // add the new MatcherEditor to the CompositeMatcherEditor
+ this.filterLock.writeLock().lock();
+ try {
+ this.compositeMatcherEditor.getMatcherEditors().add(matcherEditor);
+ } finally {
+ this.filterLock.writeLock().unlock();
+ }
+
+ this.excludeMatcherEditor.put(matcherEditor.getMatcher(), matcherEditor);
+ }
+
+ /**
+ * Remove the exclude filter from this filter strategy.
+ *
+ * @param matcher
+ * the filter to remove
+ */
+ public void removeExcludeFilter(final Matcher<T> matcher) {
+ MatcherEditor<T> removed = this.excludeMatcherEditor.remove(matcher);
+ if (removed != null) {
+ this.filterLock.writeLock().lock();
+ try {
+ this.compositeMatcherEditor.getMatcherEditors().remove(removed);
+ } finally {
+ this.filterLock.writeLock().unlock();
+ }
+ }
+ }
+
+ /**
+ * Remove the exclude filter from this filter strategy.
+ *
+ * @param matcherEditor
+ * the filter to remove
+ */
+ public void removeExcludeFilter(final MatcherEditor<T> matcherEditor) {
+ removeExcludeFilter(matcherEditor.getMatcher());
+ }
+
+ /**
+ * Removes all applied exclude filters from this filter strategy.
+ */
+ public void clearExcludeFilter() {
+ Collection<MatcherEditor<T>> excludeMatcher = this.excludeMatcherEditor.values();
+ if (!excludeMatcher.isEmpty()) {
+ this.filterLock.writeLock().lock();
+ try {
+ this.compositeMatcherEditor.getMatcherEditors().removeAll(excludeMatcher);
+ } finally {
+ this.filterLock.writeLock().unlock();
+ }
+ this.excludeMatcherEditor.clear();
+ }
+ }
+ }
+
+ /**
+ * The configuration to enable editing of {@link PersonWithAddress} objects.
+ */
+ class EditConfiguration extends AbstractRegistryConfiguration {
+
+ @Override
+ public void configureRegistry(IConfigRegistry configRegistry) {
+ configRegistry.registerConfigAttribute(
+ EditConfigAttributes.CELL_EDITABLE_RULE,
+ IEditableRule.ALWAYS_EDITABLE);
+
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.DISPLAY_CONVERTER,
+ new DefaultIntegerDisplayConverter(),
+ DisplayMode.NORMAL,
+ ColumnLabelAccumulator.COLUMN_LABEL_PREFIX
+ + DataModelConstants.HOUSENUMBER_COLUMN_POSITION);
+
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.DISPLAY_CONVERTER,
+ new DisplayConverter() {
+
+ @Override
+ public Object canonicalToDisplayValue(Object canonicalValue) {
+ return (canonicalValue != null) ? canonicalValue.toString() : "";
+ }
+
+ @Override
+ public Object displayToCanonicalValue(Object displayValue) {
+ try {
+ return Gender.valueOf(displayValue.toString());
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ },
+ DisplayMode.NORMAL,
+ ColumnLabelAccumulator.COLUMN_LABEL_PREFIX
+ + DataModelConstants.GENDER_COLUMN_POSITION);
+ }
+ }
+
+ /**
+ * The configuration to specialize the combobox filter row to mix it with
+ * default filters like free text.
+ */
+ class FilterRowConfiguration extends AbstractRegistryConfiguration {
+
+ @Override
+ public void configureRegistry(IConfigRegistry configRegistry) {
+
+ // #####
+ // Free Edit Text Filter for Firstname column that supports
+ // wildcards
+ // #####
+
+ // register the FilterRowTextCellEditor in the first column which
+ // immediately commits on key press
+ FilterRowTextCellEditor editor = new FilterRowTextCellEditor();
+ SimpleContentProposalProvider contentProposalProvider =
+ new SimpleContentProposalProvider(
+ CustomFilterRowRegularExpressionConverter.EMPTY_LITERAL,
+ CustomFilterRowRegularExpressionConverter.NOT_EMPTY_LITERAL);
+ contentProposalProvider.setFiltering(true);
+ KeyStroke keystroke = null;
+ try {
+ keystroke = KeyStroke.getInstance("Ctrl+Space");
+ } catch (ParseException e) {
+ // should not happen as the string is correct
+ }
+ char[] autoActivationChars = ("<").toCharArray();
+
+ editor.enableContentProposal(
+ new TextContentAdapter(),
+ contentProposalProvider,
+ keystroke,
+ autoActivationChars);
+
+ configRegistry.registerConfigAttribute(
+ EditConfigAttributes.CELL_EDITOR,
+ editor,
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.FIRSTNAME_COLUMN_POSITION);
+
+ // register the FilterRowPainter in the first column to visualize
+ // the free edit filter field as the ComboBoxFilterRowConfiguration
+ // registers the ComboBoxFilterIconPainter as default
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.CELL_PAINTER,
+ new PaddingDecorator(new FilterRowPainter(), 0, 0, 0, 5),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.FIRSTNAME_COLUMN_POSITION);
+
+ // register the FilterRowRegularExpressionConverter in the first
+ // column that converts simple expressions like wildcards to valid
+ // regular expressions
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.DISPLAY_CONVERTER,
+ new CustomFilterRowRegularExpressionConverter(),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.FIRSTNAME_COLUMN_POSITION);
+
+ configRegistry.registerConfigAttribute(
+ FilterRowConfigAttributes.FILTER_DISPLAY_CONVERTER,
+ new DefaultDisplayConverter(),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.FIRSTNAME_COLUMN_POSITION);
+
+ // #####
+ // Fixed value single-selection combobox filter for Gender column
+ // #####
+
+ // register a combo box cell editor for the gender column in the
+ // filter row the label is set automatically to the value of
+ // FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + column
+ // position
+ ICellEditor comboBoxCellEditor = new ComboBoxCellEditor(Arrays.asList(
+ CustomFilterRowRegularExpressionConverter.EMPTY_REGEX,
+ CustomFilterRowRegularExpressionConverter.NOT_EMPTY_REGEX,
+ Gender.FEMALE.toString(),
+ Gender.MALE.toString()));
+ configRegistry.registerConfigAttribute(
+ EditConfigAttributes.CELL_EDITOR,
+ comboBoxCellEditor,
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.GENDER_COLUMN_POSITION);
+
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.CELL_PAINTER,
+ new PaddingDecorator(new FilterRowPainter(), 0, 0, 0, 5),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.GENDER_COLUMN_POSITION);
+
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.DISPLAY_CONVERTER,
+ new CustomFilterRowRegularExpressionConverter(),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.GENDER_COLUMN_POSITION);
+
+ // #####
+ // Free Edit Text Filter for Housenumber column that supports
+ // expressions like greater, lesser, equals
+ // #####
+
+ configRegistry.registerConfigAttribute(
+ EditConfigAttributes.CELL_EDITOR,
+ new FilterRowTextCellEditor(),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.HOUSENUMBER_COLUMN_POSITION);
+
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.CELL_PAINTER,
+ new PaddingDecorator(new FilterRowPainter(), 0, 0, 0, 5),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.HOUSENUMBER_COLUMN_POSITION);
+
+ configRegistry.registerConfigAttribute(
+ FilterRowConfigAttributes.FILTER_DISPLAY_CONVERTER,
+ new DefaultIntegerDisplayConverter(),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.HOUSENUMBER_COLUMN_POSITION);
+
+ // need to register the DefaultDisplayConverter for the housenumber
+ // column as we register a custom display converter to show a label
+ // for the empty entry in the multi-select combobox filter
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.DISPLAY_CONVERTER,
+ new DefaultDisplayConverter(),
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ + DataModelConstants.HOUSENUMBER_COLUMN_POSITION);
+
+ // register a display converter on the filter row in general that
+ // shows a value for an empty entry in the dropdown
+ configRegistry.registerConfigAttribute(
+ CellConfigAttributes.DISPLAY_CONVERTER,
+ new DefaultDisplayConverter() {
+ @Override
+ public Object canonicalToDisplayValue(Object sourceValue) {
+ return (sourceValue != null && !sourceValue.toString().isEmpty())
+ ? sourceValue.toString()
+ : CustomFilterRowRegularExpressionConverter.EMPTY_LITERAL;
+ }
+ },
+ DisplayMode.NORMAL,
+ GridRegion.FILTER_ROW);
+ }
+ }
+
+ /**
+ * Specialization of the {@link FilterRowRegularExpressionConverter} that
+ * additionally parses special literals to corresponding regular
+ * expressions.
+ */
+ class CustomFilterRowRegularExpressionConverter extends FilterRowRegularExpressionConverter {
+
+ static final String EMPTY_LITERAL = "<empty>";
+ static final String EMPTY_REGEX = "^$";
+ static final String NOT_EMPTY_LITERAL = "<not_empty>";
+ static final String NOT_EMPTY_REGEX = "^(?!\\s*$).+";
+
+ @Override
+ public Object displayToCanonicalValue(Object displayValue) {
+ if (displayValue != null) {
+ switch (displayValue.toString()) {
+ case EMPTY_LITERAL:
+ return EMPTY_REGEX;
+ case NOT_EMPTY_LITERAL:
+ return NOT_EMPTY_REGEX;
+ default:
+ return super.displayToCanonicalValue(displayValue);
+ }
+ }
+ return displayValue;
+ }
+
+ @Override
+ public Object canonicalToDisplayValue(Object canonicalValue) {
+ if (canonicalValue != null) {
+ switch (canonicalValue.toString()) {
+ case EMPTY_REGEX:
+ return EMPTY_LITERAL;
+ case NOT_EMPTY_REGEX:
+ return NOT_EMPTY_LITERAL;
+ default:
+ return super.canonicalToDisplayValue(canonicalValue);
+ }
+ }
+ return canonicalValue;
+ }
+ }
+
+ /**
+ * Menu configuration that adds a menu on the body with menu items to
+ * exclude/include items from filtering.
+ *
+ * @param <T>
+ */
+ class BodyMenuConfiguration<T> extends AbstractUiBindingConfiguration {
+
+ private static final String EXCLUDE_MENU_ID = "exclude";
+ private static final String INCLUDE_MENU_ID = "include";
+
+ private final Menu bodyMenu;
+
+ private BodyMenuConfiguration(
+ NatTable natTable,
+ BodyLayerStack<T> bodyLayerStack,
+ IRowIdAccessor<T> rowIdAccessor,
+ IFilterStrategy<T> filterStrategy,
+ FilterRowDataProvider<T> filterRowDataProvider) {
+
+ this.bodyMenu = new PopupMenuBuilder(natTable)
+ .withMenuItemProvider(EXCLUDE_MENU_ID, new IMenuItemProvider() {
+
+ @Override
+ public void addMenuItem(NatTable natTable, Menu popupMenu) {
+ MenuItem excludeRow = new MenuItem(popupMenu, SWT.PUSH);
+ excludeRow.setText("Exclude from filter");
+ excludeRow.setEnabled(true);
+
+ excludeRow.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ int rowPosition = MenuItemProviders.getNatEventData(event).getRowPosition();
+ int rowIndex = natTable.getRowIndexByPosition(rowPosition);
+ T rowObject = bodyLayerStack.getBodyDataProvider().getRowObject(rowIndex);
+ Serializable rowId = rowIdAccessor.getRowId(rowObject);
+ _6037_MixedFilterRowExample.this.filterExcludes.add(rowId);
+ natTable.refresh(false);
+ }
+ });
+ }
+ })
+ .withVisibleState(EXCLUDE_MENU_ID, new IMenuItemState() {
+
+ @Override
+ public boolean isActive(NatEventData natEventData) {
+ int rowPosition = natEventData.getRowPosition();
+ int rowIndex = natTable.getRowIndexByPosition(rowPosition);
+ T rowObject = bodyLayerStack.getBodyDataProvider().getRowObject(rowIndex);
+ Serializable rowId = rowIdAccessor.getRowId(rowObject);
+ return !_6037_MixedFilterRowExample.this.filterExcludes.contains(rowId);
+ }
+ })
+ .withMenuItemProvider(INCLUDE_MENU_ID, new IMenuItemProvider() {
+
+ @Override
+ public void addMenuItem(NatTable natTable, Menu popupMenu) {
+ MenuItem excludeRow = new MenuItem(popupMenu, SWT.PUSH);
+ excludeRow.setText("Include to filter");
+ excludeRow.setEnabled(true);
+
+ excludeRow.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ int rowPosition = MenuItemProviders.getNatEventData(event).getRowPosition();
+ int rowIndex = natTable.getRowIndexByPosition(rowPosition);
+ T rowObject = bodyLayerStack.getBodyDataProvider().getRowObject(rowIndex);
+ Serializable rowId = rowIdAccessor.getRowId(rowObject);
+ _6037_MixedFilterRowExample.this.filterExcludes.remove(rowId);
+ filterStrategy.applyFilter(filterRowDataProvider.getFilterIndexToObjectMap());
+ }
+ });
+ }
+ })
+ .withVisibleState(INCLUDE_MENU_ID, new IMenuItemState() {
+
+ @Override
+ public boolean isActive(NatEventData natEventData) {
+ int rowPosition = natEventData.getRowPosition();
+ int rowIndex = natTable.getRowIndexByPosition(rowPosition);
+ T rowObject = bodyLayerStack.getBodyDataProvider().getRowObject(rowIndex);
+ Serializable rowId = rowIdAccessor.getRowId(rowObject);
+ return _6037_MixedFilterRowExample.this.filterExcludes.contains(rowId);
+ }
+ })
+ .build();
+ }
+
+ @Override
+ public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
+ uiBindingRegistry.registerMouseDownBinding(
+ new MouseEventMatcher(
+ SWT.NONE,
+ GridRegion.BODY,
+ MouseEventMatcher.RIGHT_BUTTON),
+ new PopupMenuAction(this.bodyMenu));
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxFilterRowHeaderComposite.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxFilterRowHeaderComposite.java
index fb3393b8..b7d333c3 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxFilterRowHeaderComposite.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxFilterRowHeaderComposite.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2020 Dirk Fauth and others.
+ * Copyright (c) 2013, 2022 Dirk Fauth and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -23,10 +23,13 @@ import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.edit.EditConstants;
+import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor;
import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataLayer;
import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataProvider;
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.ComboBoxFilterRowConfiguration;
+import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxCellEditor;
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxDataProvider;
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboUpdateEvent;
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.IFilterRowComboUpdateListener;
@@ -37,6 +40,7 @@ import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
import org.eclipse.nebula.widgets.nattable.layer.CompositeLayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.event.RowStructuralRefreshEvent;
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.matchers.CompositeMatcherEditor;
@@ -73,6 +77,12 @@ public class ComboBoxFilterRowHeaderComposite<T> extends CompositeLayer implemen
protected final ComboBoxGlazedListsFilterStrategy<T> filterStrategy;
/**
+ * The {@link IConfigRegistry} needed to retrieve various configurations,
+ * e.g. the filter cell editor per column.
+ */
+ private IConfigRegistry configRegistry;
+
+ /**
* Creates a new ComboBoxFilterRowHeaderComposite based on the given
* informations. Using this constructor will create the
* FilterRowComboBoxDataProvider needed for filtering and add the default
@@ -602,6 +612,8 @@ public class ComboBoxFilterRowHeaderComposite<T> extends CompositeLayer implemen
columnHeaderDataProvider,
configRegistry);
+ this.configRegistry = configRegistry;
+
setAllValuesSelected();
setChildLayer(GridRegion.FILTER_ROW, this.filterRowDataLayer, 0, 1);
@@ -696,6 +708,39 @@ public class ComboBoxFilterRowHeaderComposite<T> extends CompositeLayer implemen
}
@Override
+ public Object getDataValueByPosition(int compositeColumnPosition, int compositeRowPosition) {
+ Object filterValue = super.getDataValueByPosition(compositeColumnPosition, compositeRowPosition);
+
+ // The SELECT_ALL_ITEMS_VALUE is set to the FilterRowDataProvider by
+ // setAllValueSelected(). It is actually ignored in the
+ // ComboBoxGlazedListsFilterStrategy as it technically means "no filter"
+ // and results in having all entries checked in the
+ // FilterRowComboBoxCellEditor. For other filter editors this
+ // replacement is not done and causes incorrect visualization as it
+ // shows the value and implies an active filter. As the
+ // SELECT_ALL_ITEMS_VALUE is propagated in the constructor at a time
+ // where the editors are not yet configured, we remove the value
+ // reactively on accessing it for a consistent view.
+
+ if (compositeRowPosition == 1
+ && !isFilterRowComboBoxCellEditor(compositeColumnPosition)
+ && EditConstants.SELECT_ALL_ITEMS_VALUE.equals(filterValue)) {
+ this.filterRowDataLayer.getFilterRowDataProvider().getFilterIndexToObjectMap().remove(compositeColumnPosition);
+ filterValue = null;
+ }
+
+ return filterValue;
+ }
+
+ private boolean isFilterRowComboBoxCellEditor(int column) {
+ ICellEditor cellEditor = this.configRegistry.getConfigAttribute(
+ EditConfigAttributes.CELL_EDITOR,
+ DisplayMode.NORMAL,
+ this.filterRowDataLayer.getConfigLabelsByPosition(column, 0));
+ return (cellEditor instanceof FilterRowComboBoxCellEditor);
+ }
+
+ @Override
public int getHeight() {
if (this.filterRowVisible) {
return super.getHeight();
@@ -726,11 +771,13 @@ public class ComboBoxFilterRowHeaderComposite<T> extends CompositeLayer implemen
else if (command instanceof ClearFilterCommand
&& command.convertToTargetLayer(this)) {
int columnPosition = ((ClearFilterCommand) command).getColumnPosition();
- this.filterRowDataLayer.setDataValueByPosition(
- columnPosition,
- 0,
- getComboBoxDataProvider().getValues(columnPosition, 0));
- handled = true;
+ if (isFilterRowComboBoxCellEditor(columnPosition)) {
+ this.filterRowDataLayer.setDataValueByPosition(
+ columnPosition,
+ 0,
+ getComboBoxDataProvider().getValues(columnPosition, 0));
+ handled = true;
+ }
} else if (command instanceof ClearAllFiltersCommand) {
setAllValuesSelected();
handled = true;
diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxGlazedListsFilterStrategy.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxGlazedListsFilterStrategy.java
index 59eb93c9..1e87c888 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxGlazedListsFilterStrategy.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/ComboBoxGlazedListsFilterStrategy.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2020 Dirk Fauth and others.
+ * Copyright (c) 2013, 2022 Dirk Fauth and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -28,8 +28,14 @@ import java.util.regex.Pattern;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor;
import org.eclipse.nebula.widgets.nattable.data.convert.IDisplayConverter;
+import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.edit.EditConstants;
+import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor;
+import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataLayer;
+import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxCellEditor;
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.GlazedLists;
@@ -141,9 +147,19 @@ public class ComboBoxGlazedListsFilterStrategy<T> extends DefaultGlazedListsStat
for (Iterator<Map.Entry<Integer, Object>> it = newIndexToObjectMap.entrySet().iterator(); it.hasNext();) {
Entry<Integer, Object> entry = it.next();
Object filterObject = entry.getValue();
+
+ // Check if the filter editor is the combobox and only handle the
+ // collection case with that type of editor. Note that ignoring the
+ // SELECT_ALL_ITEMS_VALUE is needed in any case.
+ ICellEditor cellEditor = this.configRegistry.getConfigAttribute(
+ EditConfigAttributes.CELL_EDITOR,
+ DisplayMode.NORMAL,
+ FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + entry.getKey(), GridRegion.FILTER_ROW);
+ boolean isCombo = (cellEditor instanceof FilterRowComboBoxCellEditor);
+
if (EditConstants.SELECT_ALL_ITEMS_VALUE.equals(filterObject)) {
it.remove();
- } else {
+ } else if (isCombo) {
List<?> dataProviderList = this.comboBoxDataProvider.getValues(entry.getKey(), 0);
// selecting all is transported as String to support lazy
diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java
index 848b249e..e2e08977 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2020 Original authors and others.
+ * Copyright (c) 2012, 2022 Original authors and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -500,7 +500,8 @@ public class DefaultGlazedListsFilterStrategy<T> implements IFilterStrategy<T> {
ThresholdMatcherEditor<?, ?> firstThreshold = (ThresholdMatcherEditor<?, ?>) first;
ThresholdMatcherEditor<?, ?> secondThreshold = (ThresholdMatcherEditor<?, ?>) second;
- result = firstThreshold.getThreshold().equals(secondThreshold.getThreshold())
+ result = (firstThreshold.getThreshold() != null && secondThreshold.getThreshold() != null)
+ && firstThreshold.getThreshold().equals(secondThreshold.getThreshold())
&& firstThreshold.getComparator().equals(secondThreshold.getComparator())
// MatchOperation is not visible and must be a
// references instance, so the 'equals' is not needed

Back to the top