Ticket #24482: autofilter_04_multi.patch

File autofilter_04_multi.patch, 13.8 KB (added by tordanik, 6 months ago)
  • src/org/openstreetmap/josm/gui/MapView.java

    diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
    index 978c3f612b..f4034e72a4 100644
    a b LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    620620        }
    621621
    622622        MapFrame map = MainApplication.getMap();
    623         if (AutoFilterManager.getInstance().getCurrentAutoFilter() != null) {
     623        if (AutoFilterManager.getInstance().getCurrentCombinedFilter() != null) {
    624624            AutoFilterManager.getInstance().drawOSDText(tempG);
    625625        } else if (MainApplication.isDisplayingMapView() && map.filterDialog != null) {
    626626            map.filterDialog.drawOSDText(tempG);
  • src/org/openstreetmap/josm/gui/autofilter/AutoFilterButton.java

    diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterButton.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterButton.java
    index d12685529b..ec14133f2c 100644
    a b public class AutoFilterButton extends JButton {  
    3535        super(new JosmAction(filter.getLabel(), null, filter.getDescription(), null, false) {
    3636            @Override
    3737            public synchronized void actionPerformed(ActionEvent e) {
     38
    3839                AutoFilterManager afm = AutoFilterManager.getInstance();
    39                 if (filter.equals(afm.getCurrentAutoFilter())) {
    40                     afm.setCurrentAutoFilter(null);
    41                     MainApplication.getMap().filterDialog.getFilterModel().executeFilters(true);
    42                 } else {
     40
     41                if (afm.getCurrentAutoFilters().isEmpty()) {
    4342                    afm.setCurrentAutoFilter(filter);
     43                } else {
     44                    if ((e.getModifiers() & ActionEvent.CTRL_MASK) != 0
     45                            || (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0) {
     46                        if (afm.getCurrentAutoFilters().contains(filter)) {
     47                            afm.removeCurrentAutoFilter(filter);
     48                        } else {
     49                            afm.addCurrentAutoFilter(filter);
     50                        }
     51                    } else {
     52                        if (afm.getCurrentAutoFilters().size() == 1 && afm.getCurrentAutoFilters().contains(filter)) {
     53                            afm.setCurrentAutoFilter(null);
     54                        } else {
     55                            afm.setCurrentAutoFilter(filter);
     56                        }
     57                    }
    4458                }
     59
    4560            }
    4661        });
    4762        this.filter = filter;
    public class AutoFilterButton extends JButton {  
    5469    protected void paintComponent(Graphics g) {
    5570        if (getModel().isPressed()) {
    5671            g.setColor(PROP_COLOR.get().darker().darker());
    57         } else if (getModel().isRollover() || AutoFilterManager.getInstance().getCurrentAutoFilter() == filter) {
     72        } else if (getModel().isRollover() || AutoFilterManager.getInstance().getCurrentAutoFilters().contains(filter)) {
    5873            g.setColor(PROP_COLOR.get().darker());
    5974        } else {
    6075            g.setColor(PROP_COLOR.get());
  • src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java

    diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java
    index 5643cf12ca..ba5514dc03 100644
    a b import java.util.NavigableSet;  
    1414import java.util.Objects;
    1515import java.util.TreeMap;
    1616import java.util.TreeSet;
     17import java.util.stream.Collectors;
    1718
    1819import org.openstreetmap.josm.actions.mapmode.MapMode;
    1920import org.openstreetmap.josm.data.osm.BBox;
    implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc  
    109110    AutoFilterRule enabledRule;
    110111
    111112    /**
    112      * The currently selected auto filter, if any.
     113     * The currently selected auto filters, if any.
     114     * If more than one auto filter is active, elements will match if they match at least one of them.
    113115     */
    114     private AutoFilter currentAutoFilter;
     116    private final List<AutoFilter> currentAutoFilters = new ArrayList<>();
    115117
    116118    /**
    117119     * Returns the unique instance.
    implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc  
    139141                && enabledRule.getMinZoomLevel() <= Selector.GeneralSelector.scale2level(map.mapView.getDist100Pixel())) {
    140142            // Retrieve the values from current rule visible on screen
    141143            NavigableSet<Integer> values = getNumericValues();
    142             // Make sure current auto filter button remains visible even if no data is found, to allow user to disable it
    143             if (currentAutoFilter != null) {
     144            // Make sure current auto filter buttons remain visible even if no data is found, to allow user to disable them
     145            for (var currentAutoFilter : currentAutoFilters) {
    144146                values.add(currentAutoFilter.getFilter().value);
    145147            }
    146148            if (!values.equals(buttons.keySet())) {
    implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc  
    184186        }
    185187    }
    186188
     189    /** The combination of multiple {@link CompiledFilter}s */
     190    static class CombinedFilter extends Filter implements MatchSupplier {
     191
     192        private final List<CompiledFilter> filters;
     193
     194        CombinedFilter(List<CompiledFilter> filters) {
     195
     196            if (filters == null || filters.isEmpty()) throw new IllegalArgumentException("no filters provided");
     197
     198            this.filters = filters;
     199
     200            boolean hiding = filters.get(0).hiding;
     201            String key = filters.get(0).rule.getKey();
     202            List<String> values = new ArrayList<>();
     203
     204            for (CompiledFilter filter : filters) {
     205                if (hiding != filter.hiding) throw new IllegalArgumentException("non-matching hiding properties");
     206                if (!Objects.equals(key, filter.rule.getKey())) throw new IllegalArgumentException("non-matching keys");
     207                values.add(filter.rule.formatValue(filter.value));
     208            }
     209
     210            this.hiding = hiding;
     211            this.enable = true;
     212            this.inverted = true;
     213            this.text = key + "~" + String.join("|", values);
     214
     215        }
     216
     217        @Override
     218        public SearchCompiler.Match get() {
     219            return new SearchCompiler.Match() {
     220                @Override
     221                public boolean match(OsmPrimitive osm) {
     222                    return filters.stream().anyMatch(filter -> filter.get().match(osm));
     223                }
     224            };
     225        }
     226
     227        @Override
     228        public int hashCode() {
     229            return 31 * super.hashCode() + Objects.hash(filters);
     230        }
     231
     232        @Override
     233        public boolean equals(Object obj) {
     234            if (this == obj)
     235                return true;
     236            if (!super.equals(obj) || getClass() != obj.getClass())
     237                return false;
     238            CombinedFilter other = (CombinedFilter) obj;
     239            return Objects.equals(filters, other.filters);
     240        }
     241    }
     242
    187243    static class Match extends SearchCompiler.Match {
    188244        final AutoFilterRule rule;
    189245        final int value;
    implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc  
    226282            String label = enabledRule.formatValue(value);
    227283            AutoFilter autoFilter = new AutoFilter(label, filter.text, filter);
    228284            AutoFilterButton button = new AutoFilterButton(autoFilter);
    229             if (autoFilter.equals(currentAutoFilter)) {
     285            if (currentAutoFilters.contains(autoFilter)) {
    230286                button.getModel().setPressed(true);
    231287            }
    232288            maxWidth = Math.max(maxWidth, button.getPreferredSize().width);
    implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc  
    325381    }
    326382
    327383    private synchronized void updateFiltersFull() {
    328         if (currentAutoFilter != null) {
     384        if (!currentAutoFilters.isEmpty()) {
    329385            model.executeFilters();
    330386        }
    331387    }
    332388
    333389    private synchronized void updateFiltersEvent(AbstractDatasetChangedEvent event, boolean affectedOnly) {
    334         if (currentAutoFilter != null) {
     390        if (!currentAutoFilters.isEmpty()) {
    335391            Collection<? extends OsmPrimitive> prims = event.getPrimitives();
    336392            model.executeFilters(affectedOnly ? FilterModel.getAffectedPrimitives(prims) : prims);
    337393        }
    implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc  
    394450    }
    395451
    396452    /**
    397      * Returns the currently selected auto filter, if any.
    398      * @return the currently selected auto filter, or null
     453     * Returns the currently selected auto filters, if any.
     454     * @return the currently selected auto filters. Can be empty.
    399455     */
    400     public synchronized AutoFilter getCurrentAutoFilter() {
    401         return currentAutoFilter;
     456    public synchronized List<AutoFilter> getCurrentAutoFilters() {
     457        return currentAutoFilters;
     458    }
     459
     460    /**
     461     * Returns a combination of all {@link #getCurrentAutoFilters()}, if any.
     462     * @return a single combined filter, or null
     463     */
     464    public Filter getCurrentCombinedFilter() {
     465        if (currentAutoFilters.isEmpty()) {
     466            return null;
     467        } else if (currentAutoFilters.size() == 1) {
     468            return currentAutoFilters.get(0).getFilter();
     469        } else {
     470            return new CombinedFilter(currentAutoFilters.stream().map(AutoFilter::getFilter).collect(Collectors.toList()));
     471        }
    402472    }
    403473
    404474    /**
    implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc  
    406476     * @param autoFilter the currently selected auto filter, or null
    407477     */
    408478    public synchronized void setCurrentAutoFilter(AutoFilter autoFilter) {
    409         model.clearFilters();
    410         currentAutoFilter = autoFilter;
     479        currentAutoFilters.clear();
    411480        if (autoFilter != null) {
    412             model.addFilter(autoFilter.getFilter());
     481            currentAutoFilters.add(autoFilter);
     482        }
     483        updateModelFilters();
     484    }
     485
     486    public synchronized void addCurrentAutoFilter(AutoFilter autoFilter) {
     487        if (!currentAutoFilters.contains(autoFilter)) {
     488            currentAutoFilters.add(autoFilter);
     489            updateModelFilters();
     490        }
     491    }
     492
     493    public synchronized void removeCurrentAutoFilter(AutoFilter autoFilter) {
     494        if (currentAutoFilters.contains(autoFilter)) {
     495            currentAutoFilters.removeIf(it -> Objects.equals(it, autoFilter));
     496            updateModelFilters();
     497        }
     498    }
     499
     500    private synchronized void updateModelFilters() {
     501        model.clearFilters();
     502        if (currentAutoFilters.isEmpty()) {
     503                        if (MainApplication.getMap() != null) {
     504                MainApplication.getMap().filterDialog.getFilterModel().executeFilters(true);
     505            }
     506        } else {
     507            model.addFilter(getCurrentCombinedFilter());
    413508            model.executeFilters();
    414             if (model.isChanged()) {
    415                 OsmDataLayer dataLayer = MainApplication.getLayerManager().getActiveDataLayer();
    416                 if (dataLayer != null) {
    417                     dataLayer.invalidate();
    418                 }
     509            // update the data layer (necessary even if model.isChanged() == false to update the OSDText)
     510            OsmDataLayer dataLayer = MainApplication.getLayerManager().getActiveDataLayer();
     511            if (dataLayer != null) {
     512                dataLayer.invalidate();
    419513            }
    420514        }
    421515    }
    implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc  
    425519     * @param g The graphics to draw that text on.
    426520     */
    427521    public synchronized void drawOSDText(Graphics2D g) {
     522        String filterText = Objects.requireNonNull(getCurrentCombinedFilter()).text;
     523        String lengthLimitedFilterText = filterText.length() > 18 ? filterText.substring(0, 18) + "…" : filterText;
    428524        model.drawOSDText(g, lblOSD,
    429             tr("<h2>Filter active: {0}</h2>", currentAutoFilter.getFilter().text),
     525            tr("<h2>Filter active: {0}</h2>", lengthLimitedFilterText),
    430526            tr("</p><p>Click again on filter button to see all objects.</p></html>"));
    431527    }
    432528
  • src/org/openstreetmap/josm/gui/dialogs/FilterTableModel.java

    diff --git a/src/org/openstreetmap/josm/gui/dialogs/FilterTableModel.java b/src/org/openstreetmap/josm/gui/dialogs/FilterTableModel.java
    index 595dbd9a9c..fc8553f65e 100644
    a b public class FilterTableModel extends AbstractTableModel implements SortableTabl  
    9898     * @since 14206
    9999     */
    100100    public void executeFilters(boolean force) {
    101         if (AutoFilterManager.getInstance().getCurrentAutoFilter() == null && (force || model.hasFilters())) {
     101        if (AutoFilterManager.getInstance().getCurrentCombinedFilter() == null && (force || model.hasFilters())) {
    102102            model.executeFilters();
    103103            updateMap();
    104104        }
    public class FilterTableModel extends AbstractTableModel implements SortableTabl  
    111111     * @since 14206
    112112     */
    113113    public void executeFilters(Collection<? extends OsmPrimitive> primitives, boolean force) {
    114         if (AutoFilterManager.getInstance().getCurrentAutoFilter() == null && (force || model.hasFilters())) {
     114        if (AutoFilterManager.getInstance().getCurrentCombinedFilter() == null && (force || model.hasFilters())) {
    115115            model.executeFilters(primitives);
    116116            updateMap();
    117117        }