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);
         }
     }
