Ticket #3475: filters_dialogs.diff

File filters_dialogs.diff, 29.2 KB (added by Petr Dlouhý <petr.dlouhy@…>, 3 years ago)
Line 
1Index: src/org/openstreetmap/josm/data/osm/Filter.java
2===================================================================
3--- src/org/openstreetmap/josm/data/osm/Filter.java     (revision 0)
4+++ src/org/openstreetmap/josm/data/osm/Filter.java     (revision 0)
5@@ -0,0 +1,52 @@
6+package org.openstreetmap.josm.data.osm;
7+
8+import org.openstreetmap.josm.actions.search.SearchAction;
9+import org.openstreetmap.josm.actions.search.SearchAction.SearchSetting;
10+import org.openstreetmap.josm.actions.search.SearchAction.SearchMode;
11+
12+/**
13+ *
14+ * @author Petr_DlouhÜ
15+ */
16+public class Filter extends SearchSetting {
17+   private final String version = "1";
18+   public String filterName = "";
19+   public Boolean filtered = false;
20+   public Boolean disabled = true;
21+   public Boolean inverted = false;
22+   public Boolean applyForChildren = true;
23+   public Filter() {
24+       super("", SearchMode.add, false, false);
25+   }
26+   public Filter(String text, SearchMode mode, boolean caseSensitive, boolean regexSearch, String filterName) {
27+       super(text, mode, caseSensitive, regexSearch);
28+       this.filterName = filterName;
29+   }
30+
31+   public Filter(String prefText){
32+      super("", SearchMode.add, false, false);
33+      String[] prfs = prefText.split(";");
34+      if(prfs.length != 10 && !prfs[0].equals(version))
35+         throw new Error("Incompatible filter preferences");
36+      text = prfs[1];
37+      if(prfs[2].equals("replace")) mode = SearchMode.replace;
38+      if(prfs[2].equals("add")) mode = SearchMode.add;
39+      if(prfs[2].equals("remove")) mode = SearchMode.remove;
40+      if(prfs[2].equals("in_selection")) mode = SearchMode.in_selection;
41+      caseSensitive = Boolean.parseBoolean(prfs[3]);
42+      regexSearch = Boolean.parseBoolean(prfs[4]);
43+      filterName = prfs[5];
44+      filtered = Boolean.parseBoolean(prfs[6]);
45+      disabled = Boolean.parseBoolean(prfs[7]);
46+      inverted = Boolean.parseBoolean(prfs[8]);
47+      applyForChildren = Boolean.parseBoolean(prfs[9]);
48+
49+   } 
50+
51+   public String getPrefString(){
52+      return version + ";" +
53+          text + ";" + mode + ";" + caseSensitive + ";" + regexSearch + ";" +
54+          filterName + ";" + filtered + ";" + disabled + ";" +
55+          inverted + ";" + applyForChildren;
56+   }
57+}
58Index: src/org/openstreetmap/josm/data/osm/Filters.java
59===================================================================
60--- src/org/openstreetmap/josm/data/osm/Filters.java    (revision 0)
61+++ src/org/openstreetmap/josm/data/osm/Filters.java    (revision 0)
62@@ -0,0 +1,193 @@
63+package org.openstreetmap.josm.data.osm;
64+
65+import static org.openstreetmap.josm.tools.I18n.tr;
66+
67+import javax.swing.table.AbstractTableModel;
68+
69+import java.util.LinkedList;
70+import java.util.List;
71+import java.util.Collection;
72+import java.util.Map;
73+
74+import org.openstreetmap.josm.data.osm.Filter;
75+import org.openstreetmap.josm.Main;
76+import org.openstreetmap.josm.actions.search.SearchAction.Function;
77+import org.openstreetmap.josm.actions.search.SearchAction;
78+
79+/**
80+ *
81+ * @author Petr_DlouhÜ
82+ */
83+public class Filters extends AbstractTableModel{
84+
85+   public Filters(){
86+      loadPrefs();
87+   }
88+
89+   private List<Filter> filters = new LinkedList<Filter>();
90+   public void filter(){
91+      Collection<OsmPrimitive> seld = new LinkedList<OsmPrimitive> ();
92+      Collection<OsmPrimitive> self = new LinkedList<OsmPrimitive> ();
93+      Main.main.getCurrentDataSet().setFiltered();
94+      Main.main.getCurrentDataSet().setDisabled();
95+      for (Filter flt : filters){
96+            if(flt.filtered){
97+               SearchAction.getSelection(flt, self, new Function(){
98+                  public Boolean isSomething(OsmPrimitive o){
99+                     return o.isFiltered();
100+                  }
101+               });
102+            }
103+            if(flt.disabled) {
104+               SearchAction.getSelection(flt, seld, new Function(){
105+                  public Boolean isSomething(OsmPrimitive o){
106+                     return o.isDisabled();
107+                  }
108+               });
109+            }
110+      }
111+      Main.main.getCurrentDataSet().setFiltered(self);
112+      Main.main.getCurrentDataSet().setDisabled(seld);
113+      Main.map.mapView.repaint();
114+   }
115+
116+   private void loadPrefs(){
117+      Map<String,String> prefs = Main.pref.getAllPrefix("filters.filter");
118+      for (String value : prefs.values()) {
119+         Filter filter = new Filter(value);
120+         if(filter!=null)
121+            filters.add(filter);
122+      }
123+   }
124+
125+   private void savePrefs(){
126+      Map<String,String> prefs = Main.pref.getAllPrefix("filters.filter");
127+      for (String key : prefs.keySet()) {
128+         String[] sts = key.split("\\.");
129+         if (sts.length != 3)throw new Error("Incompatible filter preferences");
130+         Main.pref.put("filters.filter." + sts[2], null);
131+      }
132+
133+      int i = 0;
134+      for (Filter flt : filters){
135+         Main.pref.put("filters.filter." + i++, flt.getPrefString());
136+      }
137+   }
138+
139+   private void savePref(int i){
140+      if(i >= filters.size())
141+         Main.pref.put("filters.filter." + i, null);
142+      else
143+         Main.pref.put("filters.filter." + i, filters.get(i).getPrefString());
144+   }
145+
146+   public void addFilter(Filter f){
147+      filters.add(f);
148+      savePref(filters.size()-1);
149+      filter();
150+      fireTableRowsInserted(filters.size()-1, filters.size()-1);
151+   }
152+
153+   public void moveDownFilter(int i){
154+      if(i >= filters.size()-1) return;
155+      filters.add(i+1, filters.remove(i));
156+      savePref(i);
157+      savePref(i+1);
158+      filter();
159+      fireTableRowsUpdated(i, i+1);
160+   }
161+
162+   public void moveUpFilter(int i){
163+      if(i == 0) return;
164+      filters.add(i-1, filters.remove(i));
165+      savePref(i);
166+      savePref(i-1);
167+      filter();
168+      fireTableRowsUpdated(i-1, i);
169+   }
170+
171+   public void removeFilter(int i){
172+      filters.remove(i);
173+      savePrefs();
174+      filter();
175+      fireTableRowsDeleted(i, i);
176+   }
177+
178+   public void setFilter(int i, Filter f){
179+      filters.set(i, f);
180+      savePref(i);
181+      filter();
182+      fireTableRowsUpdated(i, i);
183+   }
184+
185+   public Filter getFilter(int i){
186+      return filters.get(i);
187+   }
188+
189+   public int getRowCount(){
190+      return filters.size();
191+   }
192+
193+   public int getColumnCount(){
194+      return 6;
195+   }
196+
197+   public String getColumnName(int column){
198+      String[] names = { tr("F"), tr("D"), tr("Name"), tr("C"), tr("I"), tr("M") };
199+      return names[column];
200+   }
201+
202+   public Class getColumnClass(int column){
203+      Class[] classes = { Boolean.class, Boolean.class, String.class, Boolean.class, Boolean.class, String.class };
204+      return classes[column];
205+   }
206+
207+   public boolean isCellEditable(int row, int column){
208+      if(column < 5)return true;
209+      return false;
210+   }
211+
212+   public void setValueAt(Object aValue, int row, int column){
213+      Filter f = filters.get(row);
214+      switch(column){
215+         case 0: f.filtered = (Boolean)aValue;
216+                 savePref(row);
217+                 filter();
218+                 return;
219+         case 1: f.disabled = (Boolean)aValue;
220+                 savePref(row);
221+                 filter();
222+                 return;
223+         case 2: f.filterName = (String)aValue;
224+                 savePref(row);
225+                 return;
226+         case 3: f.applyForChildren = (Boolean)aValue;
227+                 savePref(row);
228+                 filter();
229+                 return;
230+         case 4: f.inverted = (Boolean)aValue;
231+                 savePref(row);
232+                 filter();
233+                 return;
234+      }
235+   }
236+
237+   public Object getValueAt(int row, int column){
238+      Filter f = filters.get(row);
239+      switch(column){
240+         case 0: return f.filtered;
241+         case 1: return f.disabled;
242+         case 2: return f.filterName;
243+         case 3: return f.applyForChildren;
244+         case 4: return f.inverted;
245+         case 5:
246+                 switch(f.mode){
247+                    case replace: return "∅";
248+                    case add: return "∪";
249+                    case remove: return "∖";
250+                    case in_selection: return "∩";
251+                 }
252+      }
253+      return null;
254+   }
255+}
256Index: src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java
257===================================================================
258--- src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java    (revision 0)
259+++ src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java    (revision 0)
260@@ -0,0 +1,189 @@
261+// License: GPL. Copyright 2007 by Immanuel Scholz and others
262+package org.openstreetmap.josm.gui.dialogs;
263+
264+import static org.openstreetmap.josm.tools.I18n.marktr;
265+import static org.openstreetmap.josm.tools.I18n.tr;
266+import static org.openstreetmap.josm.tools.I18n.trn;
267+
268+import javax.swing.JPanel;
269+import javax.swing.JTable;
270+import javax.swing.table.JTableHeader;
271+import javax.swing.ListSelectionModel;
272+import javax.swing.JPopupMenu;
273+import javax.swing.table.AbstractTableModel;
274+import javax.swing.JScrollPane;
275+
276+import java.awt.FlowLayout;
277+import java.awt.BorderLayout;
278+import java.awt.GridLayout;
279+import java.awt.event.KeyEvent;
280+import java.awt.event.ActionListener;
281+import java.awt.event.ActionEvent;
282+import java.awt.event.MouseEvent;
283+
284+import org.openstreetmap.josm.Main;
285+import org.openstreetmap.josm.gui.SideButton;
286+import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
287+import org.openstreetmap.josm.gui.layer.DataChangeListener;
288+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
289+import org.openstreetmap.josm.gui.layer.Layer;
290+import org.openstreetmap.josm.data.osm.Filters;
291+import org.openstreetmap.josm.data.osm.Filter;
292+import org.openstreetmap.josm.tools.Shortcut;
293+import org.openstreetmap.josm.data.osm.DataSet;
294+import org.openstreetmap.josm.actions.search.SearchAction;
295+
296+/**
297+ *
298+ * @author Petr_DlouhÜ
299+ */
300+public class FilterDialog extends ToggleDialog implements DataChangeListener, LayerChangeListener {
301+    private JTable userTable;
302+    private Filters filters = new Filters();
303+    private SideButton addButton;
304+    private SideButton editButton;
305+    private SideButton deleteButton;
306+    private SideButton upButton;
307+    private SideButton downButton;
308+    private JPopupMenu popupMenu;
309+
310+    public FilterDialog(){
311+       super(tr("Filter"), "filter", tr("Filter objects and hide/disable them."),
312+               Shortcut.registerShortcut("subwindow:filter", tr("Toggle: {0}", tr("Filter")), KeyEvent.VK_F, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 162);
313+
314+       Layer.listeners.add(this);
315+       build();
316+    }
317+
318+    protected JPanel buildButtonRow() {
319+        JPanel pnl = new JPanel(new GridLayout(1, 4));
320+
321+        addButton = new SideButton(marktr("Add"), "add", "SelectionList", tr("Add filter."),
322+              new ActionListener(){
323+                 public void actionPerformed(ActionEvent evt){
324+                    Filter filter = (Filter)SearchAction.showSearchDialog(new Filter());
325+                    if(filter != null){
326+                       filters.addFilter(filter);
327+                       filters.filter();
328+                    }
329+                 }
330+              });
331+        pnl.add(addButton);
332+
333+        editButton = new SideButton(marktr("Edit"), "edit", "SelectionList", tr("Edit filter."),
334+              new ActionListener(){
335+                 public void actionPerformed(ActionEvent evt){
336+                    int index = userTable.getSelectionModel().getMinSelectionIndex();
337+                    if(index < 0) return;
338+                    Filter f = filters.getFilter(index);
339+                    Filter filter = (Filter)SearchAction.showSearchDialog(f);
340+                    if(filter != null){
341+                       filters.setFilter(index, filter);
342+                       filters.filter();
343+                    }
344+                 }
345+              });
346+        pnl.add(editButton);
347+
348+        deleteButton = new SideButton(marktr("Delete"), "delete", "SelectionList", tr("Delete filter."),
349+              new ActionListener(){
350+                 public void actionPerformed(ActionEvent evt){
351+                    int index = userTable.getSelectionModel().getMinSelectionIndex();
352+                    if(index < 0) return;
353+                    filters.removeFilter(index);
354+                 }
355+              });
356+        pnl.add(deleteButton);
357+
358+        upButton = new SideButton(marktr("Up"), "up", "SelectionList", tr("Move filter up."),
359+              new ActionListener(){
360+                 public void actionPerformed(ActionEvent evt){
361+                    int index = userTable.getSelectionModel().getMinSelectionIndex();
362+                    if(index < 0) return;
363+                    filters.moveUpFilter(index);
364+                    userTable.getSelectionModel().setSelectionInterval(index-1, index-1);
365+                 }
366+              });
367+        pnl.add(upButton);
368+       
369+        downButton = new SideButton(marktr("Down"), "down", "SelectionList", tr("Move filter down."),
370+              new ActionListener(){
371+                 public void actionPerformed(ActionEvent evt){
372+                    int index = userTable.getSelectionModel().getMinSelectionIndex();
373+                    if(index < 0) return;
374+                    filters.moveDownFilter(index);
375+                    userTable.getSelectionModel().setSelectionInterval(index+1, index+1);
376+                 }
377+              });
378+        pnl.add(downButton);
379+        return pnl;
380+    }
381+
382+    protected String[] columnToolTips = {
383+        tr("Filter elements"),
384+        tr("Disable elements"),
385+        tr("Apply also for children"),
386+        tr("Inverse filter"),
387+        null,
388+        tr("Filter mode")
389+    };
390+
391+    protected void build() {
392+        JPanel pnl = new JPanel();
393+        pnl.setLayout(new BorderLayout());
394+        userTable = new JTable(filters){
395+            protected JTableHeader createDefaultTableHeader() {
396+                return new JTableHeader(columnModel) {
397+                   public String getToolTipText(MouseEvent e) {
398+                       String tip = null;
399+                       java.awt.Point p = e.getPoint();
400+                       int index = columnModel.getColumnIndexAtX(p.x);
401+                       int realIndex = columnModel.getColumn(index).getModelIndex();
402+                       return columnToolTips[realIndex];
403+                   }
404+               };
405+           }
406+        };   
407
408+        userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
409+
410+        userTable.getColumnModel().getColumn(0).setMaxWidth(1);
411+        userTable.getColumnModel().getColumn(1).setMaxWidth(1);
412+        userTable.getColumnModel().getColumn(3).setMaxWidth(1);
413+        userTable.getColumnModel().getColumn(4).setMaxWidth(1);
414+        userTable.getColumnModel().getColumn(5).setMaxWidth(1);
415+
416+        userTable.getColumnModel().getColumn(0).setResizable(false);
417+        userTable.getColumnModel().getColumn(1).setResizable(false);
418+        userTable.getColumnModel().getColumn(3).setResizable(false);
419+        userTable.getColumnModel().getColumn(4).setResizable(false);
420+        userTable.getColumnModel().getColumn(5).setResizable(false);
421+
422+        pnl.add(new JScrollPane(userTable), BorderLayout.CENTER);
423+
424+        // -- the button row
425+        pnl.add(buildButtonRow(), BorderLayout.SOUTH);
426+        /*userTable.addMouseListener(new DoubleClickAdapter());*/
427+        add(pnl, BorderLayout.CENTER);
428+    }
429+
430+    public void layerRemoved(Layer a) {
431+        if (a instanceof OsmDataLayer) {
432+            ((OsmDataLayer)a).listenerDataChanged.remove(this);
433+        }
434+    }
435+
436+    public void layerAdded(Layer a) {
437+        if (a instanceof OsmDataLayer) {
438+            ((OsmDataLayer)a).listenerDataChanged.add(this);
439+        }
440+    }
441+
442+   public void activeLayerChange(Layer oldLayer, Layer newLayer) {
443+      filters.filter();
444+   }
445+
446+   public void dataChanged(OsmDataLayer l){
447+      filters.filter();
448+   }
449+}
450Index: src/org/openstreetmap/josm/actions/search/SearchAction.java
451===================================================================
452--- src/org/openstreetmap/josm/actions/search/SearchAction.java (revision 2116)
453+++ src/org/openstreetmap/josm/actions/search/SearchAction.java (working copy)
454@@ -24,13 +24,14 @@
455 import org.openstreetmap.josm.gui.ExtendedDialog;
456 import org.openstreetmap.josm.tools.GBC;
457 import org.openstreetmap.josm.tools.Shortcut;
458+import org.openstreetmap.josm.data.osm.Filter;
459 
460 public class SearchAction extends JosmAction{
461 
462     public static final int SEARCH_HISTORY_SIZE = 10;
463 
464     public static enum SearchMode {
465-        replace, add, remove
466+        replace, add, remove, in_selection
467     }
468 
469     public static final LinkedList<SearchSetting> searchHistory = new LinkedList<SearchSetting>();
470@@ -56,33 +57,46 @@
471         }
472         SearchSetting s = lastSearch;
473         if (s == null) {
474-            s = new SearchSetting("", false, false, SearchMode.replace);
475+            s = new SearchSetting("", SearchMode.replace, false, false);
476         }
477-        showSearchDialog(s);
478+        SearchSetting se = showSearchDialog(s);
479+        if(se != null) searchWithHistory(se);
480     }
481 
482-    public void showSearchDialog(SearchSetting initialValues) {
483-        JLabel label = new JLabel(tr("Please enter a search string."));
484+    public static SearchSetting showSearchDialog(SearchSetting initialValues) {
485+        JLabel label = new JLabel( initialValues instanceof Filter ? tr("Please enter a filter string.") : tr("Please enter a search string."));
486         final JTextField input = new JTextField(initialValues.text);
487         input.selectAll();
488         input.requestFocusInWindow();
489         JRadioButton replace = new JRadioButton(tr("replace selection"), initialValues.mode == SearchMode.replace);
490         JRadioButton add = new JRadioButton(tr("add to selection"), initialValues.mode == SearchMode.add);
491         JRadioButton remove = new JRadioButton(tr("remove from selection"), initialValues.mode == SearchMode.remove);
492+        JRadioButton in_selection = new JRadioButton(tr("find in selection"), initialValues.mode == SearchMode.in_selection);
493         ButtonGroup bg = new ButtonGroup();
494         bg.add(replace);
495         bg.add(add);
496         bg.add(remove);
497+        bg.add(in_selection);
498 
499         JCheckBox caseSensitive = new JCheckBox(tr("case sensitive"), initialValues.caseSensitive);
500         JCheckBox regexSearch   = new JCheckBox(tr("regular expression"), initialValues.regexSearch);
501 
502         JPanel left = new JPanel(new GridBagLayout());
503+
504+        JTextField finput = null;
505+        if(initialValues instanceof Filter){
506+           JLabel fLabel = new JLabel(tr("Please enter a filter name."));
507+           finput = new JTextField(((Filter)initialValues).filterName);
508+           left.add(fLabel, GBC.eop());
509+           left.add(finput, GBC.eop().fill(GBC.HORIZONTAL));
510+        }
511+
512         left.add(label, GBC.eop());
513         left.add(input, GBC.eop().fill(GBC.HORIZONTAL));
514         left.add(replace, GBC.eol());
515         left.add(add, GBC.eol());
516-        left.add(remove, GBC.eop());
517+        left.add(remove, GBC.eol());
518+        left.add(in_selection, GBC.eop());
519         left.add(caseSensitive, GBC.eol());
520         left.add(regexSearch, GBC.eol());
521 
522@@ -119,21 +133,27 @@
523         p.add(right);
524         ExtendedDialog dialog = new ExtendedDialog(
525                 Main.parent,
526-                tr("Search"),
527-                new String[] {tr("Start Search"), tr("Cancel")}
528+                initialValues instanceof Filter ? tr("Filter") : tr("Search"),
529+                new String[] {
530+                   initialValues instanceof Filter ? tr("Make filter") : tr("Start Search"),
531+                tr("Cancel")}
532         );
533         dialog.setButtonIcons(new String[] {"dialogs/search.png", "cancel.png"});
534         dialog.setContent(p);
535         dialog.showDialog();
536         int result = dialog.getValue();
537 
538-        if(result != 1) return;
539+        if(result != 1) return null;
540 
541         // User pressed OK - let's perform the search
542         SearchMode mode = replace.isSelected() ? SearchAction.SearchMode.replace
543-                : (add.isSelected() ? SearchAction.SearchMode.add : SearchAction.SearchMode.remove);
544-        SearchSetting setting = new SearchSetting(input.getText(), caseSensitive.isSelected(), regexSearch.isSelected(), mode);
545-        searchWithHistory(setting);
546+                : (add.isSelected() ? SearchAction.SearchMode.add
547+                : (remove.isSelected() ? SearchAction.SearchMode.remove : SearchAction.SearchMode.in_selection));
548+        if(initialValues instanceof Filter){
549+           return new Filter(input.getText(), mode, caseSensitive.isSelected(), regexSearch.isSelected(), finput.getText());
550+        } else {
551+           return new SearchSetting(input.getText(), mode, caseSensitive.isSelected(), regexSearch.isSelected());
552+        }
553     }
554 
555     /**
556@@ -150,67 +170,48 @@
557             searchHistory.removeLast();
558         }
559         lastSearch = s;
560-        search(s.text, s.mode, s.caseSensitive, s.regexSearch);
561+        search(s);
562     }
563 
564     public static void searchWithoutHistory(SearchSetting s) {
565         lastSearch = s;
566-        search(s.text, s.mode, s.caseSensitive, s.regexSearch);
567+        search(s);
568     }
569 
570-    public static void search(String search, SearchMode mode, boolean caseSensitive, boolean regexSearch) {
571-        // FIXME: This is confusing. The GUI says nothing about loading primitives from an URL. We'd like to *search*
572-        // for URLs in the current data set.
573-        // Disabling until a better solution is in place
574-        //
575-        //        if (search.startsWith("http://") || search.startsWith("ftp://") || search.startsWith("https://")
576-        //                || search.startsWith("file:/")) {
577-        //            SelectionWebsiteLoader loader = new SelectionWebsiteLoader(search, mode);
578-        //            if (loader.url != null && loader.url.getHost() != null) {
579-        //                Main.worker.execute(loader);
580-        //                return;
581-        //            }
582-        //        }
583+    public interface Function{
584+       public Boolean isSomething(OsmPrimitive o);
585+    }
586+
587+    public static Integer getSelection(SearchSetting s, Collection<OsmPrimitive> sel, Function f) {
588+        Integer foundMatches = 0;
589         try {
590-            Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
591-            SearchCompiler.Match matcher = SearchCompiler.compile(search, caseSensitive, regexSearch);
592-            int foundMatches = 0;
593+            String searchText = s.text;
594+            if(s instanceof Filter){
595+               searchText = ((Filter)s).inverted ? "-" : "";
596+               searchText = searchText + "(" + ((Filter)s).text + ")" + (((Filter)s).applyForChildren ? ("| child (" + ((Filter)s).text + ")"): "");
597+            }
598+            /*System.out.println(searchText);  */
599+            SearchCompiler.Match matcher = SearchCompiler.compile(searchText, s.caseSensitive, s.regexSearch);
600+            foundMatches = 0;
601             for (OsmPrimitive osm : Main.main.getCurrentDataSet().allNonDeletedCompletePrimitives()) {
602-                if (mode == SearchMode.replace) {
603+                if (s.mode == SearchMode.replace) {
604                     if (matcher.match(osm)) {
605                         sel.add(osm);
606                         ++foundMatches;
607                     } else {
608                         sel.remove(osm);
609                     }
610-                } else if (mode == SearchMode.add && !osm.isSelected() && matcher.match(osm)) {
611+                } else if (s.mode == SearchMode.add && !f.isSomething(osm) && matcher.match(osm)) {
612                     sel.add(osm);
613                     ++foundMatches;
614-                } else if (mode == SearchMode.remove && osm.isSelected() && matcher.match(osm)) {
615+                } else if (s.mode == SearchMode.remove && f.isSomething(osm) && matcher.match(osm)) {
616                     sel.remove(osm);
617                     ++foundMatches;
618+                } else if (s.mode == SearchMode.in_selection &&  f.isSomething(osm)&& !matcher.match(osm)) {
619+                    sel.remove(osm);
620+                    ++foundMatches;
621                 }
622             }
623-            Main.main.getCurrentDataSet().setSelected(sel);
624-            if (foundMatches == 0) {
625-                String msg = null;
626-                if (mode == SearchMode.replace) {
627-                    msg = tr("No match found for ''{0}''", search);
628-                } else if (mode == SearchMode.add) {
629-                    msg = tr("Nothing added to selection by searching for ''{0}''", search);
630-                } else if (mode == SearchMode.remove) {
631-                    msg = tr("Nothing removed from selection by searching for ''{0}''", search);
632-                }
633-                Main.map.statusLine.setHelpText(msg);
634-                JOptionPane.showMessageDialog(
635-                        Main.parent,
636-                        msg,
637-                        tr("Warning"),
638-                        JOptionPane.WARNING_MESSAGE
639-                );
640-            } else {
641-                Main.map.statusLine.setHelpText(tr("Found {0} matches", foundMatches));
642-            }
643         } catch (SearchCompiler.ParseError e) {
644             JOptionPane.showMessageDialog(
645                     Main.parent,
646@@ -220,15 +221,64 @@
647 
648             );
649         }
650+        return foundMatches;
651     }
652+
653+    public static void search(String search, SearchMode mode, boolean caseSensitive, boolean regexSearch) {
654+       search(new SearchSetting(search, mode, caseSensitive, regexSearch));
655+    }
656 
657+    public static void search(SearchSetting s) {
658+        // FIXME: This is confusing. The GUI says nothing about loading primitives from an URL. We'd like to *search*
659+        // for URLs in the current data set.
660+        // Disabling until a better solution is in place
661+        //
662+        //        if (search.startsWith("http://") || search.startsWith("ftp://") || search.startsWith("https://")
663+        //                || search.startsWith("file:/")) {
664+        //            SelectionWebsiteLoader loader = new SelectionWebsiteLoader(search, mode);
665+        //            if (loader.url != null && loader.url.getHost() != null) {
666+        //                Main.worker.execute(loader);
667+        //                return;
668+        //            }
669+        //        }
670+         
671+       Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
672+       int foundMatches = getSelection(s, sel, new Function(){
673+          public Boolean isSomething(OsmPrimitive o){
674+             return o.isSelected();
675+          }
676+       });
677+       Main.main.getCurrentDataSet().setSelected(sel);
678+       if (foundMatches == 0) {
679+           String msg = null;
680+           if (s.mode == SearchMode.replace) {
681+               msg = tr("No match found for ''{0}''", s.text);
682+           } else if (s.mode == SearchMode.add) {
683+               msg = tr("Nothing added to selection by searching for ''{0}''", s.text);
684+           } else if (s.mode == SearchMode.remove) {
685+               msg = tr("Nothing removed from selection by searching for ''{0}''", s.text);
686+           } else if (s.mode == SearchMode.in_selection) {
687+               msg = tr("Nothing find in selection by searching for ''{0}''", s.text);
688+           }
689+           Main.map.statusLine.setHelpText(msg);
690+           JOptionPane.showMessageDialog(
691+                   Main.parent,
692+                   msg,
693+                   tr("Warning"),
694+                   JOptionPane.WARNING_MESSAGE
695+           );
696+       } else {
697+           Main.map.statusLine.setHelpText(tr("Found {0} matches", foundMatches));
698+       }
699+    }
700+
701     public static class SearchSetting {
702-        String text;
703-        SearchMode mode;
704-        boolean caseSensitive;
705-        boolean regexSearch;
706+        public String text;
707+        public SearchMode mode;
708+        public boolean caseSensitive;
709+        public boolean regexSearch;
710 
711-        public SearchSetting(String text, boolean caseSensitive, boolean regexSearch, SearchMode mode) {
712+        public SearchSetting(String text, SearchMode mode, boolean caseSensitive, boolean regexSearch) {
713             super();
714             this.caseSensitive = caseSensitive;
715             this.regexSearch = regexSearch;
716Index: src/org/openstreetmap/josm/gui/MapFrame.java
717===================================================================
718--- src/org/openstreetmap/josm/gui/MapFrame.java        (revision 2116)
719+++ src/org/openstreetmap/josm/gui/MapFrame.java        (working copy)
720@@ -28,6 +28,7 @@
721 import org.openstreetmap.josm.gui.dialogs.PropertiesDialog;
722 import org.openstreetmap.josm.gui.dialogs.RelationListDialog;
723 import org.openstreetmap.josm.gui.dialogs.SelectionListDialog;
724+import org.openstreetmap.josm.gui.dialogs.FilterDialog;
725 import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
726 import org.openstreetmap.josm.gui.dialogs.UserListDialog;
727 import org.openstreetmap.josm.tools.Destroyable;
728@@ -103,6 +104,7 @@
729         addToggleDialog(new PropertiesDialog(this));
730         addToggleDialog(new HistoryDialog());
731         addToggleDialog(new SelectionListDialog());
732+        addToggleDialog(new FilterDialog());
733         addToggleDialog(new UserListDialog());
734         addToggleDialog(conflictDialog = new ConflictDialog());
735         addToggleDialog(new CommandStackDialog(this));
736Index: images/dialogs/filter.png
737===================================================================
738Cannot display: file marked as a binary type.
739svn:mime-type = application/octet-stream
740
741Property changes on: images/dialogs/filter.png
742___________________________________________________________________
743Added: svn:mime-type
744   + application/octet-stream