Ticket #24482: autofilter_all.patch
| File autofilter_all.patch, 33.3 KB (added by , 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 { 620 620 } 621 621 622 622 MapFrame map = MainApplication.getMap(); 623 if (AutoFilterManager.getInstance().getCurrent AutoFilter() != null) {623 if (AutoFilterManager.getInstance().getCurrentCombinedFilter() != null) { 624 624 AutoFilterManager.getInstance().drawOSDText(tempG); 625 625 } else if (MainApplication.isDisplayingMapView() && map.filterDialog != null) { 626 626 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 { 35 35 super(new JosmAction(filter.getLabel(), null, filter.getDescription(), null, false) { 36 36 @Override 37 37 public synchronized void actionPerformed(ActionEvent e) { 38 38 39 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()) { 43 42 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 } 44 58 } 59 45 60 } 46 61 }); 47 62 this.filter = filter; … … public class AutoFilterButton extends JButton { 54 69 protected void paintComponent(Graphics g) { 55 70 if (getModel().isPressed()) { 56 71 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)) { 58 73 g.setColor(PROP_COLOR.get().darker()); 59 74 } else { 60 75 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 3a7ccb67a2..e59bc85f18 100644
a b import java.util.ArrayList; 8 8 import java.util.Arrays; 9 9 import java.util.Collection; 10 10 import java.util.Collections; 11 import java.util.HashMap; 11 12 import java.util.List; 12 13 import java.util.Map; 13 14 import java.util.NavigableSet; 14 15 import java.util.Objects; 15 import java.util. TreeMap;16 import java.util.OptionalInt; 16 17 import java.util.TreeSet; 17 import java.util.function.Consumer; 18 import java.util.stream.Collectors; 19 import java.util.stream.IntStream; 18 20 19 21 import org.openstreetmap.josm.actions.mapmode.MapMode; 20 22 import org.openstreetmap.josm.data.osm.BBox; … … public final class AutoFilterManager 63 65 implements ZoomChangeListener, MapModeChangeListener, DataSetListener, PreferenceChangedListener, LayerChangeListener { 64 66 65 67 /** 66 * Property to determine sif the auto filter feature is enabled.68 * Property to determine if the auto filter feature is enabled. 67 69 */ 68 70 public static final BooleanProperty PROP_AUTO_FILTER_ENABLED = new BooleanProperty("auto.filter.enabled", true); 69 71 72 /** 73 * Property to determine if the auto filter feature completely hides elements instead of just disabling them. 74 * Equivalent to {@link Filter#hiding} 75 */ 76 public static final BooleanProperty PROP_AUTO_FILTER_HIDING = new BooleanProperty("auto.filter.hiding", false); 77 78 70 79 /** 71 80 * Property to determine the current auto filter rule. 72 81 */ … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 80 89 /** 81 90 * The buttons currently displayed in map view. 82 91 */ 83 private final Map< Integer, AutoFilterButton> buttons = new TreeMap<>();92 private final Map<OptionalInt, AutoFilterButton> buttons = new HashMap<>(); 84 93 85 94 /** 86 95 * The list of registered auto filter rules. … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 103 112 AutoFilterRule enabledRule; 104 113 105 114 /** 106 * The currently selected auto filter, if any. 115 * The currently selected auto filters, if any. 116 * If more than one auto filter is active, elements will match if they match at least one of them. 107 117 */ 108 private AutoFilter currentAutoFilter;118 private final List<AutoFilter> currentAutoFilters = new ArrayList<>(); 109 119 110 120 /** 111 121 * Returns the unique instance. … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 133 143 && enabledRule.getMinZoomLevel() <= Selector.GeneralSelector.scale2level(map.mapView.getDist100Pixel())) { 134 144 // Retrieve the values from current rule visible on screen 135 145 NavigableSet<Integer> values = getNumericValues(); 136 // Make sure current auto filter button remains visible even if no data is found, to allow user to disable it 137 if (currentAutoFilter != null) { 138 values.add(currentAutoFilter.getFilter().value); 146 // Make sure current auto filter buttons remain visible even if no data is found, to allow user to disable them 147 for (var currentAutoFilter : currentAutoFilters) { 148 if (currentAutoFilter.getFilter().value != null) { 149 values.add(currentAutoFilter.getFilter().value); 150 } 139 151 } 140 if (!values.equals(buttons.keySet())) { 152 if (!values.equals(buttons.keySet().stream() 153 .filter(it -> it.isPresent() && it.getAsInt() != Integer.MIN_VALUE) 154 .map(OptionalInt::getAsInt).collect(Collectors.toSet()))) { 141 155 removeAllButtons(); 142 156 addNewButtons(values); 143 157 } … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 146 160 147 161 static class CompiledFilter extends Filter implements MatchSupplier { 148 162 final AutoFilterRule rule; 149 final intvalue;163 final Integer value; 150 164 151 CompiledFilter(AutoFilterRule rule, int value) {165 CompiledFilter(AutoFilterRule rule, Integer value, boolean hiding) { 152 166 this.rule = rule; 153 167 this.value = value; 168 this.hiding = hiding; 154 169 this.enable = true; 155 170 this.inverted = true; 156 171 this.text = rule.getKey() + "=" + rule.formatValue(value); … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 173 188 if (!super.equals(obj) || getClass() != obj.getClass()) 174 189 return false; 175 190 CompiledFilter other = (CompiledFilter) obj; 176 return Objects.equals(rule, other.rule) && value == other.value; 191 return Objects.equals(rule, other.rule) && Objects.equals(value, other.value); 192 } 193 } 194 195 /** The combination of multiple {@link CompiledFilter}s */ 196 static class CombinedFilter extends Filter implements MatchSupplier { 197 198 private final List<CompiledFilter> filters; 199 200 CombinedFilter(List<CompiledFilter> filters) { 201 202 if (filters == null || filters.isEmpty()) throw new IllegalArgumentException("no filters provided"); 203 204 this.filters = filters; 205 206 boolean hiding = filters.get(0).hiding; 207 String key = filters.get(0).rule.getKey(); 208 List<String> values = new ArrayList<>(); 209 210 for (CompiledFilter filter : filters) { 211 if (hiding != filter.hiding) throw new IllegalArgumentException("non-matching hiding properties"); 212 if (!Objects.equals(key, filter.rule.getKey())) throw new IllegalArgumentException("non-matching keys"); 213 values.add(filter.rule.formatValue(filter.value)); 214 } 215 216 this.hiding = hiding; 217 this.enable = true; 218 this.inverted = true; 219 this.text = key + "~" + String.join("|", values); 220 221 } 222 223 @Override 224 public SearchCompiler.Match get() { 225 return new SearchCompiler.Match() { 226 @Override 227 public boolean match(OsmPrimitive osm) { 228 return filters.stream().anyMatch(filter -> filter.get().match(osm)); 229 } 230 }; 231 } 232 233 @Override 234 public int hashCode() { 235 return 31 * super.hashCode() + Objects.hash(filters); 236 } 237 238 @Override 239 public boolean equals(Object obj) { 240 if (this == obj) 241 return true; 242 if (!super.equals(obj) || getClass() != obj.getClass()) 243 return false; 244 CombinedFilter other = (CombinedFilter) obj; 245 return Objects.equals(filters, other.filters); 177 246 } 178 247 } 179 248 180 249 static class Match extends SearchCompiler.Match { 181 250 final AutoFilterRule rule; 182 final intvalue;251 final Integer value; 183 252 184 Match(AutoFilterRule rule, intvalue) {253 Match(AutoFilterRule rule, Integer value) { 185 254 this.rule = rule; 186 255 this.value = value; 187 256 } 188 257 189 258 @Override 190 259 public boolean match(OsmPrimitive osm) { 191 return rule.getTagValuesForPrimitive(osm).anyMatch(v -> v == value); 260 IntStream values = rule.getTagValuesForPrimitive(osm, false); 261 if (value != null) { 262 return values.anyMatch(v -> v == value); 263 } else { 264 return values.findAny().isEmpty(); 265 } 192 266 } 193 267 194 268 @Override … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 196 270 if (this == o) return true; 197 271 if (o == null || getClass() != o.getClass()) return false; 198 272 Match match = (Match) o; 199 return value == match.value&&273 return Objects.equals(value, match.value) && 200 274 Objects.equals(rule, match.rule); 201 275 } 202 276 … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 214 288 int maxWidth = 16; 215 289 final AutoFilterButton keyButton = AutoFilterButton.forOsmKey(enabledRule.getKey()); 216 290 addButton(keyButton, Integer.MIN_VALUE, i++); 217 for (final Integer value : values.descendingSet()) { 218 CompiledFilter filter = new CompiledFilter(enabledRule, value); 291 var valueList = new ArrayList<>(values.descendingSet()); 292 if (enabledRule.getNoValueFilter()) { 293 valueList.add(null); 294 } 295 for (final Integer value : valueList) { 296 CompiledFilter filter = new CompiledFilter(enabledRule, value, PROP_AUTO_FILTER_HIDING.get()); 219 297 String label = enabledRule.formatValue(value); 220 298 AutoFilter autoFilter = new AutoFilter(label, filter.text, filter); 221 299 AutoFilterButton button = new AutoFilterButton(autoFilter); 222 if ( autoFilter.equals(currentAutoFilter)) {300 if (currentAutoFilters.contains(autoFilter)) { 223 301 button.getModel().setPressed(true); 224 302 } 225 maxWidth = Math.max(maxWidth, button.getPreferredSize().width);226 303 addButton(button, value, i++); 304 maxWidth = Math.max(maxWidth, button.getPreferredSize().width); 227 305 } 228 306 for (AutoFilterButton b : buttons.values()) { 229 307 b.setSize(b == keyButton ? b.getPreferredSize().width : maxWidth, 20); … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 231 309 MainApplication.getMap().mapView.validate(); 232 310 } 233 311 234 private void addButton(AutoFilterButton button, intvalue, int i) {312 private void addButton(AutoFilterButton button, Integer value, int i) { 235 313 MapView mapView = MainApplication.getMap().mapView; 236 buttons.put(value , button);314 buttons.put(value == null ? OptionalInt.empty() : OptionalInt.of(value), button); 237 315 mapView.add(button).setLocation(3, 60 + 22*i); 238 316 } 239 317 … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 252 330 } 253 331 BBox bbox = MainApplication.getMap().mapView.getState().getViewArea().getLatLonBoundsBox().toBBox(); 254 332 NavigableSet<Integer> values = new TreeSet<>(); 255 Consumer<OsmPrimitive> consumer = o -> enabledRule.getTagValuesForPrimitive(o).forEach(values::add); 256 ds.searchNodes(bbox).forEach(consumer); 257 ds.searchWays(bbox).forEach(consumer); 258 ds.searchRelations(bbox).forEach(consumer); 333 for (var primitiveList : List.of(ds.searchNodes(bbox), ds.searchWays(bbox), ds.searchRelations(bbox))) { 334 // add all values that are directly mentioned 335 primitiveList.forEach(o -> enabledRule.getTagValuesForPrimitive(o, true).forEach(values::add)); 336 // only add integer values from value ranges, not fractional values 337 primitiveList.forEach(o -> enabledRule.getTagValuesForPrimitive(o, false) 338 .filter(v -> !enabledRule.formatValue(v).contains(".")) 339 .forEach(values::add)); 340 341 } 259 342 return values; 260 343 } 261 344 … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 267 350 @Override 268 351 public void dataChanged(DataChangedEvent event) { 269 352 updateFiltersFull(); 353 updateButtons(); 270 354 } 271 355 272 356 @Override … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 313 397 } 314 398 315 399 private synchronized void updateFiltersFull() { 316 if ( currentAutoFilter != null) {400 if (!currentAutoFilters.isEmpty()) { 317 401 model.executeFilters(); 318 402 } 319 403 } 320 404 321 405 private synchronized void updateFiltersEvent(AbstractDatasetChangedEvent event, boolean affectedOnly) { 322 if ( currentAutoFilter != null) {406 if (!currentAutoFilters.isEmpty()) { 323 407 Collection<? extends OsmPrimitive> prims = event.getPrimitives(); 324 408 model.executeFilters(affectedOnly ? FilterModel.getAffectedPrimitives(prims) : prims); 325 409 } … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 382 466 } 383 467 384 468 /** 385 * Returns the currently selected auto filter , if any.386 * @return the currently selected auto filter , or null469 * Returns the currently selected auto filters, if any. 470 * @return the currently selected auto filters. Can be empty. 387 471 */ 388 public synchronized AutoFilter getCurrentAutoFilter() { 389 return currentAutoFilter; 472 public synchronized List<AutoFilter> getCurrentAutoFilters() { 473 return currentAutoFilters; 474 } 475 476 /** 477 * Returns a combination of all {@link #getCurrentAutoFilters()}, if any. 478 * @return a single combined filter, or null 479 */ 480 public Filter getCurrentCombinedFilter() { 481 if (currentAutoFilters.isEmpty()) { 482 return null; 483 } else if (currentAutoFilters.size() == 1) { 484 return currentAutoFilters.get(0).getFilter(); 485 } else { 486 return new CombinedFilter(currentAutoFilters.stream().map(AutoFilter::getFilter).collect(Collectors.toList())); 487 } 390 488 } 391 489 392 490 /** … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 394 492 * @param autoFilter the currently selected auto filter, or null 395 493 */ 396 494 public synchronized void setCurrentAutoFilter(AutoFilter autoFilter) { 397 model.clearFilters(); 398 currentAutoFilter = autoFilter; 495 currentAutoFilters.clear(); 399 496 if (autoFilter != null) { 400 model.addFilter(autoFilter.getFilter()); 497 currentAutoFilters.add(autoFilter); 498 } 499 updateModelFilters(); 500 } 501 502 public synchronized void addCurrentAutoFilter(AutoFilter autoFilter) { 503 if (!currentAutoFilters.contains(autoFilter)) { 504 currentAutoFilters.add(autoFilter); 505 updateModelFilters(); 506 } 507 } 508 509 public synchronized void removeCurrentAutoFilter(AutoFilter autoFilter) { 510 if (currentAutoFilters.contains(autoFilter)) { 511 currentAutoFilters.removeIf(it -> Objects.equals(it, autoFilter)); 512 updateModelFilters(); 513 } 514 } 515 516 private synchronized void updateModelFilters() { 517 model.clearFilters(); 518 if (currentAutoFilters.isEmpty()) { 519 if (MainApplication.getMap() != null) { 520 MainApplication.getMap().filterDialog.getFilterModel().executeFilters(true); 521 } 522 } else { 523 model.addFilter(getCurrentCombinedFilter()); 401 524 model.executeFilters(); 402 if (model.isChanged()) { 403 OsmDataLayer dataLayer = MainApplication.getLayerManager().getActiveDataLayer(); 404 if (dataLayer != null) { 405 dataLayer.invalidate(); 406 } 525 // update the data layer (necessary even if model.isChanged() == false to update the OSDText) 526 OsmDataLayer dataLayer = MainApplication.getLayerManager().getActiveDataLayer(); 527 if (dataLayer != null) { 528 dataLayer.invalidate(); 407 529 } 408 530 } 409 531 } … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 413 535 * @param g The graphics to draw that text on. 414 536 */ 415 537 public synchronized void drawOSDText(Graphics2D g) { 538 String filterText = Objects.requireNonNull(getCurrentCombinedFilter()).text; 539 String lengthLimitedFilterText = filterText.length() > 18 ? filterText.substring(0, 18) + "…" : filterText; 416 540 model.drawOSDText(g, lblOSD, 417 tr("<h2>Filter active: {0}</h2>", currentAutoFilter.getFilter().text),541 tr("<h2>Filter active: {0}</h2>", lengthLimitedFilterText), 418 542 tr("</p><p>Click again on filter button to see all objects.</p></html>")); 419 543 } 420 544 … … implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc 437 561 enableAutoFilterRule((AutoFilterRule) null); 438 562 resetCurrentAutoFilter(); 439 563 } 440 } else if (e.getKey().equals(PROP_AUTO_FILTER_RULE.getKey())) { 564 } else if (e.getKey().equals(PROP_AUTO_FILTER_RULE.getKey()) 565 || e.getKey().equals(PROP_AUTO_FILTER_HIDING.getKey())) { 441 566 enableAutoFilterRule(PROP_AUTO_FILTER_RULE.get()); 442 567 resetCurrentAutoFilter(); 443 568 updateButtons(); -
src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java
diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java index 9c35c387e9..0710d4a03b 100644
a b 2 2 package org.openstreetmap.josm.gui.autofilter; 3 3 4 4 import java.text.DecimalFormat; 5 import java.util.Arrays; 6 import java.util.Locale; 7 import java.util.Objects; 8 import java.util.Optional; 5 import java.util.*; 9 6 import java.util.function.Function; 10 7 import java.util.function.IntFunction; 11 8 import java.util.function.ToIntFunction; 12 9 import java.util.regex.Matcher; 13 10 import java.util.regex.Pattern; 14 11 import java.util.stream.IntStream; 12 import java.util.stream.Stream; 15 13 16 14 import org.openstreetmap.josm.data.osm.OsmPrimitive; 17 15 import org.openstreetmap.josm.data.osm.OsmUtils; 18 16 import org.openstreetmap.josm.data.preferences.BooleanProperty; 19 17 import org.openstreetmap.josm.tools.Logging; 20 18 19 import static java.util.stream.Collectors.toList; 20 21 21 /** 22 22 * An auto filter rule determines how auto filter can be built from visible map data. 23 23 * Several rules can be registered, but only one rule is active at the same time. … … public class AutoFilterRule { 42 42 43 43 private IntFunction<String> valueFormatter = Integer::toString; 44 44 45 private boolean noValueFilter = false; 46 47 /** The union of {@link #key} and the keys provided by {@link #setExtraKeys(List)}. */ 48 private List<String> allKeys; 49 45 50 /** 46 51 * Constructs a new {@code AutoFilterRule}. 47 52 * @param key the OSM key on which the rule applies … … public class AutoFilterRule { 50 55 public AutoFilterRule(String key, int minZoomLevel) { 51 56 this.key = key; 52 57 this.minZoomLevel = minZoomLevel; 58 this.allKeys = List.of(key); 53 59 } 54 60 55 61 /** … … public class AutoFilterRule { 68 74 return minZoomLevel; 69 75 } 70 76 77 /** 78 * Returns true if there should be a filter button for OSM primitives which have no value for the key. 79 */ 80 public boolean getNoValueFilter() { 81 return noValueFilter; 82 } 83 71 84 /** 72 85 * Formats the numeric value 73 86 * @param value the numeric value to format 74 87 * @return the formatted value 75 88 */ 76 public String formatValue( intvalue) {77 return value Formatter.apply(value);89 public String formatValue(Integer value) { 90 return value == null ? "∅" : valueFormatter.apply(value); 78 91 } 79 92 80 93 /** … … public class AutoFilterRule { 111 124 return this; 112 125 } 113 126 127 /** 128 * Adds a filter button for OSM primitives which have no value for the key. 129 */ 130 public AutoFilterRule enableNoValueFilter() { 131 this.noValueFilter = true; 132 return this; 133 } 134 135 /** 136 * Sets extra OSM keys on which the rule applies in addition to the primary key ({@link #getKey()}). 137 * This allows a filter to look at the values of more than one key at the same time. 138 * @param extraKeys the list of extra keys, may be empty 139 * @return {@code this} 140 * @throws NullPointerException if {@code extraKeys} is null 141 */ 142 public AutoFilterRule setExtraKeys(List<String> extraKeys) { 143 Objects.requireNonNull(extraKeys); 144 this.allKeys = Stream.concat(Stream.of(key), extraKeys.stream()).collect(toList()); 145 return this; 146 } 147 114 148 /** 115 149 * Returns the numeric values for the given OSM primitive 116 * @param osm the primitive 150 * 151 * @param osm the primitive 152 * @param directValuesOnly whether "inner" values from ranges (such as 6 and 7 for 5-8) should be omitted 117 153 * @return a stream of numeric values 118 154 */ 119 public IntStream getTagValuesForPrimitive(OsmPrimitive osm) { 155 public IntStream getTagValuesForPrimitive(OsmPrimitive osm, boolean directValuesOnly) { 156 if (osm.isDeleted()) return IntStream.empty(); 157 if (allKeys.size() == 1) { 158 IntStream values = getTagValuesForPrimitive(osm, key, directValuesOnly); 159 if (values != null) return values; 160 } else { 161 Set<Integer> allValues = new HashSet<>(); 162 for (String k : allKeys) { 163 IntStream values = getTagValuesForPrimitive(osm, k, directValuesOnly); 164 if (values != null) { values.forEach(allValues::add); } 165 } 166 if (!allValues.isEmpty()) return allValues.stream().mapToInt(it -> it).sorted(); 167 } 168 return Boolean.TRUE.equals(PROP_AUTO_FILTER_DEFAULTS.get()) ? defaultValueSupplier.apply(osm) : IntStream.empty(); 169 } 170 171 private IntStream getTagValuesForPrimitive(OsmPrimitive osm, String key, boolean directValuesOnly) { 120 172 String value = osm.get(key); 121 173 if (value != null) { 122 174 Pattern p = Pattern.compile("(-?[0-9]+)-(-?[0-9]+)"); … … public class AutoFilterRule { 125 177 if (m.matches()) { 126 178 int a = valueExtractor.applyAsInt(m.group(1)); 127 179 int b = valueExtractor.applyAsInt(m.group(2)); 128 return IntStream.rangeClosed(Math.min(a, b), Math.max(a, b)); 180 if (directValuesOnly && a != b) { 181 return IntStream.of(a, b).sorted(); 182 } else { 183 return IntStream.rangeClosed(Math.min(a, b), Math.max(a, b)); 184 } 129 185 } else { 130 186 try { 131 187 return IntStream.of(valueExtractor.applyAsInt(v)); … … public class AutoFilterRule { 136 192 } 137 193 }); 138 194 } 139 return Boolean.TRUE.equals(PROP_AUTO_FILTER_DEFAULTS.get()) ? defaultValueSupplier.apply(osm) : IntStream.empty();195 return null; 140 196 } 141 197 142 198 /** … … public class AutoFilterRule { 156 212 new AutoFilterRule("layer", 16) 157 213 .setDefaultValueSupplier(AutoFilterRule::defaultLayer), 158 214 new AutoFilterRule("level", 17) 215 .setExtraKeys(List.of("repeat_on")) 216 .enableNoValueFilter() 159 217 // #17109, support values like 0.5 or 1.5 - level values are multiplied by 2 when parsing, values are divided by 2 for formatting 160 218 .setValueExtractor(s -> (int) (Double.parseDouble(s) * 2.)) 161 219 .setValueFormatter(v -> DecimalFormat.getInstance(Locale.ROOT).format(v / 2.)), -
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 98 98 * @since 14206 99 99 */ 100 100 public void executeFilters(boolean force) { 101 if (AutoFilterManager.getInstance().getCurrent AutoFilter() == null && (force || model.hasFilters())) {101 if (AutoFilterManager.getInstance().getCurrentCombinedFilter() == null && (force || model.hasFilters())) { 102 102 model.executeFilters(); 103 103 updateMap(); 104 104 } … … public class FilterTableModel extends AbstractTableModel implements SortableTabl 111 111 * @since 14206 112 112 */ 113 113 public void executeFilters(Collection<? extends OsmPrimitive> primitives, boolean force) { 114 if (AutoFilterManager.getInstance().getCurrent AutoFilter() == null && (force || model.hasFilters())) {114 if (AutoFilterManager.getInstance().getCurrentCombinedFilter() == null && (force || model.hasFilters())) { 115 115 model.executeFilters(primitives); 116 116 updateMap(); 117 117 } -
src/org/openstreetmap/josm/gui/preferences/display/DrawingPreference.java
diff --git a/src/org/openstreetmap/josm/gui/preferences/display/DrawingPreference.java b/src/org/openstreetmap/josm/gui/preferences/display/DrawingPreference.java index c75c5fcfe9..f9dd0bf99d 100644
a b public class DrawingPreference extends DefaultTabPreferenceSetting { 56 56 private final JCheckBox inactive = new JCheckBox(tr("Draw inactive layers in other color")); 57 57 private final JCheckBox discardableKeys = new JCheckBox(tr("Display discardable keys")); 58 58 private final JCheckBox autoFilters = new JCheckBox(tr("Use auto filters")); 59 private final JCheckBox autoFiltersHiding = new JCheckBox(tr("Hide auto-filtered elements")); 60 59 61 private final JLabel lblRule = new JLabel(tr("Rule")); 60 62 private final JosmComboBox<AutoFilterRule> autoFilterRules = new JosmComboBox<>( 61 63 AutoFilterManager.getInstance().getAutoFilterRules().toArray(new AutoFilterRule[] {})); … … public class DrawingPreference extends DefaultTabPreferenceSetting { 149 151 autoFilters.addActionListener(e -> { 150 152 lblRule.setEnabled(autoFilters.isSelected()); 151 153 autoFilterRules.setEnabled(autoFilters.isSelected()); 154 autoFiltersHiding.setEnabled(autoFilters.isSelected()); 152 155 }); 153 156 autoFilterRules.setToolTipText("Rule defining which tag will provide automatic filters, below a certain zoom level"); 154 157 autoFilterRules.setSelectedItem(AutoFilterManager.getInstance().getAutoFilterRule(AutoFilterManager.PROP_AUTO_FILTER_RULE.get())); 158 autoFiltersHiding.setToolTipText(tr("Completely hide elements which do not match the automatic filter instead of merely disabling them")); 159 autoFiltersHiding.setSelected(AutoFilterManager.PROP_AUTO_FILTER_HIDING.get()); 155 160 156 161 JLabel performanceLabel = new JLabel(tr("Options that affect drawing performance")); 157 162 … … public class DrawingPreference extends DefaultTabPreferenceSetting { 183 188 panel.add(autoFilters, GBC.eop().insets(20, 0, 0, 0)); 184 189 panel.add(lblRule, GBC.std().insets(40, 0, 0, 0)); 185 190 panel.add(autoFilterRules, GBC.eop().fill(GBC.HORIZONTAL).insets(5, 0, 0, 0)); 191 panel.add(autoFiltersHiding, GBC.eop().insets(40, 0, 0, 0)); 186 192 187 193 ExpertToggleAction.addVisibilitySwitcher(performanceLabel); 188 194 ExpertToggleAction.addVisibilitySwitcher(useAntialiasing); … … public class DrawingPreference extends DefaultTabPreferenceSetting { 213 219 Config.getPref().putBoolean("draw.helper-line", drawHelperLine.isSelected()); 214 220 Config.getPref().putBoolean("display.discardable-keys", discardableKeys.isSelected()); 215 221 AutoFilterManager.PROP_AUTO_FILTER_ENABLED.put(autoFilters.isSelected()); 222 AutoFilterManager.PROP_AUTO_FILTER_HIDING.put(autoFiltersHiding.isSelected()); 216 223 AutoFilterManager.PROP_AUTO_FILTER_RULE.put(((AutoFilterRule) autoFilterRules.getSelectedItem()).getKey()); 217 224 int vn = Config.getPref().getInt("mappaint.node.virtual-size", 8); 218 225 if (virtualNodes.isSelected()) { -
test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterRuleTest.java
diff --git a/test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterRuleTest.java b/test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterRuleTest.java index d68b4bcaec..3d1914dcb6 100644
a b import org.junit.jupiter.api.Test; 22 22 @I18n 23 23 class AutoFilterRuleTest { 24 24 /** 25 * Unit test of {@link AutoFilterRule#getTagValuesForPrimitive }.25 * Unit test of {@link AutoFilterRule#getTagValuesForPrimitive(OsmPrimitive, boolean)}. 26 26 */ 27 27 @Test 28 28 void testTagValuesForPrimitive() { … … class AutoFilterRuleTest { 36 36 assertTagValuesForPrimitive(level, "way level=6-9", 12, 13, 14, 15, 16, 17, 18); 37 37 assertTagValuesForPrimitive(level, "way level=10;12-13", 20, 24, 25, 26); 38 38 assertTagValuesForPrimitive(level, "way level=0;0.5;1;1.5;2;2.5;3", 0, 1, 2, 3, 4, 5, 6); 39 assertTagValuesForPrimitive(level, "node level=0 repeat_on=1;2", 0, 2, 4); 40 assertTagValuesForPrimitive(level, "way level=4 repeat_on=4;5 layer=1", 8, 10); 39 41 assertEquals("0 0.5 1 1.5 2 2.5 3", 40 42 IntStream.of(0, 1, 2, 3, 4, 5, 6).mapToObj(level::formatValue).collect(Collectors.joining(" "))); 41 43 } 42 44 43 45 /** 44 * Unit test of {@link AutoFilterRule#getTagValuesForPrimitive } to deal with {@code %} of key {@code incline}.46 * Unit test of {@link AutoFilterRule#getTagValuesForPrimitive(OsmPrimitive, boolean)} to deal with {@code %} of key {@code incline}. 45 47 */ 46 48 @Test 47 49 void testTagValuesForPrimitiveInclineUnit() { … … class AutoFilterRuleTest { 52 54 } 53 55 54 56 /** 55 * Unit test of {@link AutoFilterRule#getTagValuesForPrimitive } provides sensible defaults, see #17496.57 * Unit test of {@link AutoFilterRule#getTagValuesForPrimitive(OsmPrimitive, boolean)} provides sensible defaults, see #17496. 56 58 */ 57 59 @Test 58 60 void testTagValuesForPrimitivesDefaults() { … … class AutoFilterRuleTest { 69 71 70 72 private void assertTagValuesForPrimitive(AutoFilterRule rule, String assertion, int... expected) { 71 73 final OsmPrimitive primitive = OsmUtils.createPrimitive(assertion); 72 final int[] actual = rule.getTagValuesForPrimitive(primitive ).toArray();74 final int[] actual = rule.getTagValuesForPrimitive(primitive, false).toArray(); 73 75 assertArrayEquals(expected, actual); 74 76 } 75 77
