Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java	(revision 17713)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java	(revision 17714)
@@ -17,4 +17,6 @@
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
 
 import javax.swing.AbstractAction;
@@ -49,4 +51,7 @@
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.layer.NoteLayer;
+import org.openstreetmap.josm.gui.util.DocumentAdapter;
+import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
+import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -64,4 +69,5 @@
     private NoteTableModel model;
     private JList<Note> displayList;
+    private final JosmTextField filter = setupFilter();
     private final AddCommentAction addCommentAction;
     private final CloseAction closeAction;
@@ -114,4 +120,5 @@
 
         JPanel pane = new JPanel(new BorderLayout());
+        pane.add(filter, BorderLayout.NORTH);
         pane.add(new JScrollPane(displayList), BorderLayout.CENTER);
 
@@ -222,4 +229,36 @@
     public Note getSelectedNote() {
         return noteData != null ? noteData.getSelectedNote() : null;
+    }
+
+    private JosmTextField setupFilter() {
+        final JosmTextField f = new DisableShortcutsOnFocusGainedTextField();
+        f.setToolTipText(tr("Note filter"));
+        f.getDocument().addDocumentListener(DocumentAdapter.create(ignore -> {
+            String text = f.getText();
+            model.setFilter(note -> matchesNote(text, note));
+        }));
+        return f;
+    }
+
+    static boolean matchesNote(String filter, Note note) {
+        if (filter == null || filter.isEmpty()) {
+            return true;
+        }
+        return Pattern.compile("\\s+").splitAsStream(filter).allMatch(string -> {
+            switch (string) {
+                case "open":
+                    return note.getState() == State.OPEN;
+                case "closed":
+                    return note.getState() == State.CLOSED;
+                case "reopened":
+                    return note.getLastComment().getNoteAction() == NoteComment.Action.REOPENED;
+                case "new":
+                    return note.getId() < 0;
+                case "modified":
+                    return note.getLastComment().isNew();
+                default:
+                    return note.getComments().toString().contains(string);
+            }
+        });
     }
 
@@ -270,24 +309,30 @@
 
     class NoteTableModel extends AbstractListModel<Note> {
-        private final transient List<Note> data;
-
-        /**
-         * Constructs a new {@code NoteTableModel}.
-         */
-        NoteTableModel() {
-            data = new ArrayList<>();
-        }
+        private final transient List<Note> data = new ArrayList<>();
+        private final transient List<Note> filteredData = new ArrayList<>();
+        private transient Predicate<Note> filter;
 
         @Override
         public int getSize() {
-            if (data == null) {
-                return 0;
-            }
-            return data.size();
+            return filteredData.size();
         }
 
         @Override
         public Note getElementAt(int index) {
-            return data.get(index);
+            return filteredData.get(index);
+        }
+
+        public void setFilter(Predicate<Note> filter) {
+            this.filter = filter;
+            filteredData.clear();
+            if (filter == null) {
+                filteredData.addAll(data);
+            } else {
+                data.stream().filter(filter).forEach(filteredData::add);
+            }
+            fireContentsChanged(this, 0, getSize());
+            setTitle(data.isEmpty()
+                    ? tr("Notes")
+                    : tr("Notes: {0}/{1}", filteredData.size(), data.size()));
         }
 
@@ -295,5 +340,5 @@
             data.clear();
             data.addAll(noteList);
-            fireContentsChanged(this, 0, noteList.size());
+            setFilter(filter);
         }
 
@@ -301,5 +346,5 @@
             displayList.clearSelection();
             data.clear();
-            fireIntervalRemoved(this, 0, getSize());
+            setFilter(filter);
         }
     }
Index: /trunk/test/unit/org/openstreetmap/josm/gui/dialogs/NotesDialogTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/dialogs/NotesDialogTest.java	(revision 17713)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/dialogs/NotesDialogTest.java	(revision 17714)
@@ -3,4 +3,6 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.time.Instant;
@@ -32,11 +34,34 @@
     public JOSMTestRules josmTestRules = new JOSMTestRules().preferences();
 
-    @Test
-    void testMultiLineNoteRendering() {
+    private Note createMultiLineNote() {
         Note note = new Note(LatLon.ZERO);
         note.setCreatedAt(Instant.now());
         note.addComment(new NoteComment(Instant.now(), User.createLocalUser(null), "foo\nbar\n\nbaz:\nfoo", null, false));
+        return note;
+    }
+
+    /**
+     * Unit test of {@link NoteRenderer}
+     */
+    @Test
+    void testMultiLineNoteRendering() {
+        Note note = createMultiLineNote();
         assertEquals("0: foo; bar; baz: foo",
                 ((JLabel) new NoteRenderer().getListCellRendererComponent(new JList<>(), note, 0, false, false)).getText());
     }
+
+    /**
+     * Unit test of {@link NotesDialog#matchesNote}
+     */
+    @Test
+    void testMatchesNote() {
+        Note note = createMultiLineNote();
+        assertTrue(NotesDialog.matchesNote(null, note));
+        assertTrue(NotesDialog.matchesNote("", note));
+        assertTrue(NotesDialog.matchesNote("foo", note));
+        assertFalse(NotesDialog.matchesNote("xxx", note));
+        assertFalse(NotesDialog.matchesNote("open", note));
+        assertFalse(NotesDialog.matchesNote("new", note));
+        assertFalse(NotesDialog.matchesNote("reopened", note));
+    }
 }
