diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index 978c3f612b..f4034e72a4 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -620,7 +620,7 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
         }
 
         MapFrame map = MainApplication.getMap();
-        if (AutoFilterManager.getInstance().getCurrentAutoFilter() != null) {
+        if (AutoFilterManager.getInstance().getCurrentCombinedFilter() != null) {
             AutoFilterManager.getInstance().drawOSDText(tempG);
         } else if (MainApplication.isDisplayingMapView() && map.filterDialog != null) {
             map.filterDialog.drawOSDText(tempG);
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/src/org/openstreetmap/josm/gui/autofilter/AutoFilterButton.java
+++ b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterButton.java
@@ -35,13 +35,28 @@ public class AutoFilterButton extends JButton {
         super(new JosmAction(filter.getLabel(), null, filter.getDescription(), null, false) {
             @Override
             public synchronized void actionPerformed(ActionEvent e) {
+
                 AutoFilterManager afm = AutoFilterManager.getInstance();
-                if (filter.equals(afm.getCurrentAutoFilter())) {
-                    afm.setCurrentAutoFilter(null);
-                    MainApplication.getMap().filterDialog.getFilterModel().executeFilters(true);
-                } else {
+
+                if (afm.getCurrentAutoFilters().isEmpty()) {
                     afm.setCurrentAutoFilter(filter);
+                } else {
+                    if ((e.getModifiers() & ActionEvent.CTRL_MASK) != 0
+                            || (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0) {
+                        if (afm.getCurrentAutoFilters().contains(filter)) {
+                            afm.removeCurrentAutoFilter(filter);
+                        } else {
+                            afm.addCurrentAutoFilter(filter);
+                        }
+                    } else {
+                        if (afm.getCurrentAutoFilters().size() == 1 && afm.getCurrentAutoFilters().contains(filter)) {
+                            afm.setCurrentAutoFilter(null);
+                        } else {
+                            afm.setCurrentAutoFilter(filter);
+                        }
+                    }
                 }
+
             }
         });
         this.filter = filter;
@@ -54,7 +69,7 @@ public class AutoFilterButton extends JButton {
     protected void paintComponent(Graphics g) {
         if (getModel().isPressed()) {
             g.setColor(PROP_COLOR.get().darker().darker());
-        } else if (getModel().isRollover() || AutoFilterManager.getInstance().getCurrentAutoFilter() == filter) {
+        } else if (getModel().isRollover() || AutoFilterManager.getInstance().getCurrentAutoFilters().contains(filter)) {
             g.setColor(PROP_COLOR.get().darker());
         } else {
             g.setColor(PROP_COLOR.get());
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/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java
+++ b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java
@@ -14,6 +14,7 @@ import java.util.NavigableSet;
 import java.util.Objects;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.data.osm.BBox;
@@ -109,9 +110,10 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
     AutoFilterRule enabledRule;
 
     /**
-     * The currently selected auto filter, if any.
+     * The currently selected auto filters, if any.
+     * If more than one auto filter is active, elements will match if they match at least one of them.
      */
-    private AutoFilter currentAutoFilter;
+    private final List<AutoFilter> currentAutoFilters = new ArrayList<>();
 
     /**
      * Returns the unique instance.
@@ -139,8 +141,8 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
                 && enabledRule.getMinZoomLevel() <= Selector.GeneralSelector.scale2level(map.mapView.getDist100Pixel())) {
             // Retrieve the values from current rule visible on screen
             NavigableSet<Integer> values = getNumericValues();
-            // Make sure current auto filter button remains visible even if no data is found, to allow user to disable it
-            if (currentAutoFilter != null) {
+            // Make sure current auto filter buttons remain visible even if no data is found, to allow user to disable them
+            for (var currentAutoFilter : currentAutoFilters) {
                 values.add(currentAutoFilter.getFilter().value);
             }
             if (!values.equals(buttons.keySet())) {
@@ -184,6 +186,60 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
         }
     }
 
+    /** The combination of multiple {@link CompiledFilter}s */
+    static class CombinedFilter extends Filter implements MatchSupplier {
+
+        private final List<CompiledFilter> filters;
+
+        CombinedFilter(List<CompiledFilter> filters) {
+
+            if (filters == null || filters.isEmpty()) throw new IllegalArgumentException("no filters provided");
+
+            this.filters = filters;
+
+            boolean hiding = filters.get(0).hiding;
+            String key = filters.get(0).rule.getKey();
+            List<String> values = new ArrayList<>();
+
+            for (CompiledFilter filter : filters) {
+                if (hiding != filter.hiding) throw new IllegalArgumentException("non-matching hiding properties");
+                if (!Objects.equals(key, filter.rule.getKey())) throw new IllegalArgumentException("non-matching keys");
+                values.add(filter.rule.formatValue(filter.value));
+            }
+
+            this.hiding = hiding;
+            this.enable = true;
+            this.inverted = true;
+            this.text = key + "~" + String.join("|", values);
+
+        }
+
+        @Override
+        public SearchCompiler.Match get() {
+            return new SearchCompiler.Match() {
+                @Override
+                public boolean match(OsmPrimitive osm) {
+                    return filters.stream().anyMatch(filter -> filter.get().match(osm));
+                }
+            };
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * super.hashCode() + Objects.hash(filters);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (!super.equals(obj) || getClass() != obj.getClass())
+                return false;
+            CombinedFilter other = (CombinedFilter) obj;
+            return Objects.equals(filters, other.filters);
+        }
+    }
+
     static class Match extends SearchCompiler.Match {
         final AutoFilterRule rule;
         final int value;
@@ -226,7 +282,7 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
             String label = enabledRule.formatValue(value);
             AutoFilter autoFilter = new AutoFilter(label, filter.text, filter);
             AutoFilterButton button = new AutoFilterButton(autoFilter);
-            if (autoFilter.equals(currentAutoFilter)) {
+            if (currentAutoFilters.contains(autoFilter)) {
                 button.getModel().setPressed(true);
             }
             maxWidth = Math.max(maxWidth, button.getPreferredSize().width);
@@ -325,13 +381,13 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
     }
 
     private synchronized void updateFiltersFull() {
-        if (currentAutoFilter != null) {
+        if (!currentAutoFilters.isEmpty()) {
             model.executeFilters();
         }
     }
 
     private synchronized void updateFiltersEvent(AbstractDatasetChangedEvent event, boolean affectedOnly) {
-        if (currentAutoFilter != null) {
+        if (!currentAutoFilters.isEmpty()) {
             Collection<? extends OsmPrimitive> prims = event.getPrimitives();
             model.executeFilters(affectedOnly ? FilterModel.getAffectedPrimitives(prims) : prims);
         }
@@ -394,11 +450,25 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
     }
 
     /**
-     * Returns the currently selected auto filter, if any.
-     * @return the currently selected auto filter, or null
+     * Returns the currently selected auto filters, if any.
+     * @return the currently selected auto filters. Can be empty.
      */
-    public synchronized AutoFilter getCurrentAutoFilter() {
-        return currentAutoFilter;
+    public synchronized List<AutoFilter> getCurrentAutoFilters() {
+        return currentAutoFilters;
+    }
+
+    /**
+     * Returns a combination of all {@link #getCurrentAutoFilters()}, if any.
+     * @return a single combined filter, or null
+     */
+    public Filter getCurrentCombinedFilter() {
+        if (currentAutoFilters.isEmpty()) {
+            return null;
+        } else if (currentAutoFilters.size() == 1) {
+            return currentAutoFilters.get(0).getFilter();
+        } else {
+            return new CombinedFilter(currentAutoFilters.stream().map(AutoFilter::getFilter).collect(Collectors.toList()));
+        }
     }
 
     /**
@@ -406,16 +476,40 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
      * @param autoFilter the currently selected auto filter, or null
      */
     public synchronized void setCurrentAutoFilter(AutoFilter autoFilter) {
-        model.clearFilters();
-        currentAutoFilter = autoFilter;
+        currentAutoFilters.clear();
         if (autoFilter != null) {
-            model.addFilter(autoFilter.getFilter());
+            currentAutoFilters.add(autoFilter);
+        }
+        updateModelFilters();
+    }
+
+    public synchronized void addCurrentAutoFilter(AutoFilter autoFilter) {
+        if (!currentAutoFilters.contains(autoFilter)) {
+            currentAutoFilters.add(autoFilter);
+            updateModelFilters();
+        }
+    }
+
+    public synchronized void removeCurrentAutoFilter(AutoFilter autoFilter) {
+        if (currentAutoFilters.contains(autoFilter)) {
+            currentAutoFilters.removeIf(it -> Objects.equals(it, autoFilter));
+            updateModelFilters();
+        }
+    }
+
+    private synchronized void updateModelFilters() {
+        model.clearFilters();
+        if (currentAutoFilters.isEmpty()) {
+			if (MainApplication.getMap() != null) {
+                MainApplication.getMap().filterDialog.getFilterModel().executeFilters(true);
+            }
+        } else {
+            model.addFilter(getCurrentCombinedFilter());
             model.executeFilters();
-            if (model.isChanged()) {
-                OsmDataLayer dataLayer = MainApplication.getLayerManager().getActiveDataLayer();
-                if (dataLayer != null) {
-                    dataLayer.invalidate();
-                }
+            // update the data layer (necessary even if model.isChanged() == false to update the OSDText)
+            OsmDataLayer dataLayer = MainApplication.getLayerManager().getActiveDataLayer();
+            if (dataLayer != null) {
+                dataLayer.invalidate();
             }
         }
     }
@@ -425,8 +519,10 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
      * @param g The graphics to draw that text on.
      */
     public synchronized void drawOSDText(Graphics2D g) {
+        String filterText = Objects.requireNonNull(getCurrentCombinedFilter()).text;
+        String lengthLimitedFilterText = filterText.length() > 18 ? filterText.substring(0, 18) + "…" : filterText;
         model.drawOSDText(g, lblOSD,
-            tr("<h2>Filter active: {0}</h2>", currentAutoFilter.getFilter().text),
+            tr("<h2>Filter active: {0}</h2>", lengthLimitedFilterText),
             tr("</p><p>Click again on filter button to see all objects.</p></html>"));
     }
 
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/src/org/openstreetmap/josm/gui/dialogs/FilterTableModel.java
+++ b/src/org/openstreetmap/josm/gui/dialogs/FilterTableModel.java
@@ -98,7 +98,7 @@ public class FilterTableModel extends AbstractTableModel implements SortableTabl
      * @since 14206
      */
     public void executeFilters(boolean force) {
-        if (AutoFilterManager.getInstance().getCurrentAutoFilter() == null && (force || model.hasFilters())) {
+        if (AutoFilterManager.getInstance().getCurrentCombinedFilter() == null && (force || model.hasFilters())) {
             model.executeFilters();
             updateMap();
         }
@@ -111,7 +111,7 @@ public class FilterTableModel extends AbstractTableModel implements SortableTabl
      * @since 14206
      */
     public void executeFilters(Collection<? extends OsmPrimitive> primitives, boolean force) {
-        if (AutoFilterManager.getInstance().getCurrentAutoFilter() == null && (force || model.hasFilters())) {
+        if (AutoFilterManager.getInstance().getCurrentCombinedFilter() == null && (force || model.hasFilters())) {
             model.executeFilters(primitives);
             updateMap();
         }
