diff options
| author | Dirk Fauth | 2022-11-08 05:28:19 +0000 |
|---|---|---|
| committer | Dirk Fauth | 2022-11-08 05:28:19 +0000 |
| commit | 53c3ea7db6d70c4535374a47edfafb67c9467b3d (patch) | |
| tree | 682dfa2b31e273f707e267f75b4004aeab4e50e3 | |
| parent | 8346f341b1c07792083e783d6b8f6d35bb237032 (diff) | |
| download | org.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
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 |
