source: josm/trunk/src/org/openstreetmap/josm/data/osm/FilterModel.java@ 12400

Last change on this file since 12400 was 12400, checked in by Don-vip, 7 years ago

see #14929 - Automatic filters on numeric tag values (level, layer, maxspeed, voltage)

File size: 13.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Graphics2D;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.HashSet;
11import java.util.LinkedList;
12import java.util.List;
13import java.util.Set;
14import java.util.Stack;
15
16import javax.swing.JOptionPane;
17
18import org.openstreetmap.josm.Main;
19import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
20import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry;
21import org.openstreetmap.josm.gui.layer.OsmDataLayer;
22import org.openstreetmap.josm.gui.widgets.OSDLabel;
23import org.openstreetmap.josm.tools.Utils;
24
25/**
26 * The model that is used both for auto and manual filters.
27 * @since 12400
28 */
29public class FilterModel {
30
31 /**
32 * number of primitives that are disabled but not hidden
33 */
34 private int disabledCount;
35 /**
36 * number of primitives that are disabled and hidden
37 */
38 private int disabledAndHiddenCount;
39 /**
40 * true, if the filter state (normal / disabled / hidden) of any primitive has changed in the process
41 */
42 private boolean changed;
43
44 /**
45 * Constructs a new {@code FilterTableModel}.
46 */
47 public FilterModel() {
48 }
49
50 private final transient List<Filter> filters = new LinkedList<>();
51 private final transient FilterMatcher filterMatcher = new FilterMatcher();
52
53 private void updateFilterMatcher() {
54 filterMatcher.reset();
55 for (Filter filter : filters) {
56 try {
57 filterMatcher.add(filter);
58 } catch (ParseError e) {
59 Main.error(e);
60 JOptionPane.showMessageDialog(
61 Main.parent,
62 tr("<html>Error in filter <code>{0}</code>:<br>{1}",
63 Utils.escapeReservedCharactersHTML(Utils.shortenString(filter.text, 80)),
64 Utils.escapeReservedCharactersHTML(e.getMessage())),
65 tr("Error in filter"),
66 JOptionPane.ERROR_MESSAGE);
67 filter.enable = false;
68 }
69 }
70 }
71
72 /**
73 * Initializes the model from preferences.
74 * @param prefEntry preference key
75 */
76 public void loadPrefs(String prefEntry) {
77 List<FilterPreferenceEntry> entries = Main.pref.getListOfStructs(prefEntry, null, FilterPreferenceEntry.class);
78 if (entries != null) {
79 for (FilterPreferenceEntry e : entries) {
80 filters.add(new Filter(e));
81 }
82 updateFilterMatcher();
83 }
84 }
85
86 /**
87 * Saves the model to preferences.
88 * @param prefEntry preferences key
89 */
90 public void savePrefs(String prefEntry) {
91 Collection<FilterPreferenceEntry> entries = new ArrayList<>();
92 for (Filter flt : filters) {
93 entries.add(flt.getPreferenceEntry());
94 }
95 Main.pref.putListOfStructs(prefEntry, entries, FilterPreferenceEntry.class);
96 }
97
98 /**
99 * Runs the filters on the current edit data set.
100 */
101 public void executeFilters() {
102 DataSet ds = Main.getLayerManager().getEditDataSet();
103 changed = false;
104 if (ds == null) {
105 disabledAndHiddenCount = 0;
106 disabledCount = 0;
107 changed = true;
108 } else {
109 final Collection<OsmPrimitive> deselect = new HashSet<>();
110
111 ds.beginUpdate();
112 try {
113
114 final Collection<OsmPrimitive> all = ds.allNonDeletedCompletePrimitives();
115
116 changed = FilterWorker.executeFilters(all, filterMatcher);
117
118 disabledCount = 0;
119 disabledAndHiddenCount = 0;
120 // collect disabled and selected the primitives
121 for (OsmPrimitive osm : all) {
122 if (osm.isDisabled()) {
123 disabledCount++;
124 if (osm.isSelected()) {
125 deselect.add(osm);
126 }
127 if (osm.isDisabledAndHidden()) {
128 disabledAndHiddenCount++;
129 }
130 }
131 }
132 disabledCount -= disabledAndHiddenCount;
133 } finally {
134 ds.endUpdate();
135 }
136
137 if (!deselect.isEmpty()) {
138 ds.clearSelection(deselect);
139 }
140 }
141 if (changed) {
142 updateMap();
143 }
144 }
145
146 /**
147 * Runs the filter on a list of primitives that are part of the edit data set.
148 * @param primitives The primitives
149 */
150 public void executeFilters(Collection<? extends OsmPrimitive> primitives) {
151 DataSet ds = Main.getLayerManager().getEditDataSet();
152 if (ds == null)
153 return;
154
155 changed = false;
156 List<OsmPrimitive> deselect = new ArrayList<>();
157
158 ds.beginUpdate();
159 try {
160 for (int i = 0; i < 2; i++) {
161 for (OsmPrimitive primitive: primitives) {
162
163 if (i == 0 && primitive instanceof Node) {
164 continue;
165 }
166
167 if (i == 1 && !(primitive instanceof Node)) {
168 continue;
169 }
170
171 if (primitive.isDisabled()) {
172 disabledCount--;
173 }
174 if (primitive.isDisabledAndHidden()) {
175 disabledAndHiddenCount--;
176 }
177 changed |= FilterWorker.executeFilters(primitive, filterMatcher);
178 if (primitive.isDisabled()) {
179 disabledCount++;
180 }
181 if (primitive.isDisabledAndHidden()) {
182 disabledAndHiddenCount++;
183 }
184
185 if (primitive.isSelected() && primitive.isDisabled()) {
186 deselect.add(primitive);
187 }
188 }
189 }
190 } finally {
191 ds.endUpdate();
192 }
193
194 if (!deselect.isEmpty()) {
195 ds.clearSelection(deselect);
196 }
197 if (changed) {
198 updateMap();
199 }
200 }
201
202 private static void updateMap() {
203 OsmDataLayer editLayer = Main.getLayerManager().getEditLayer();
204 if (editLayer != null) {
205 editLayer.invalidate();
206 }
207 }
208
209 /**
210 * Clears all filtered flags from all primitives in the dataset
211 */
212 public void clearFilterFlags() {
213 DataSet ds = Main.getLayerManager().getEditDataSet();
214 if (ds != null) {
215 FilterWorker.clearFilterFlags(ds.allPrimitives());
216 }
217 disabledCount = 0;
218 disabledAndHiddenCount = 0;
219 }
220
221 /**
222 * Removes all filters from this model.
223 */
224 public void clearFilters() {
225 filters.clear();
226 updateFilterMatcher();
227 }
228
229 /**
230 * Adds a new filter to the filter list.
231 * @param filter The new filter
232 * @return true (as specified by {@link Collection#add})
233 */
234 public boolean addFilter(Filter filter) {
235 filters.add(filter);
236 updateFilterMatcher();
237 return true;
238 }
239
240 /**
241 * Moves down the filter in the given row.
242 * @param rowIndex The filter row
243 * @return true if the filter has been moved down
244 */
245 public boolean moveDownFilter(int rowIndex) {
246 if (rowIndex >= filters.size() - 1)
247 return false;
248 filters.add(rowIndex + 1, filters.remove(rowIndex));
249 updateFilterMatcher();
250 return true;
251 }
252
253 /**
254 * Moves up the filter in the given row
255 * @param rowIndex The filter row
256 * @return true if the filter has been moved up
257 */
258 public boolean moveUpFilter(int rowIndex) {
259 if (rowIndex == 0)
260 return false;
261 filters.add(rowIndex - 1, filters.remove(rowIndex));
262 updateFilterMatcher();
263 return true;
264 }
265
266 /**
267 * Removes the filter that is displayed in the given row
268 * @param rowIndex The index of the filter to remove
269 * @return the filter previously at the specified position
270 */
271 public Filter removeFilter(int rowIndex) {
272 Filter result = filters.remove(rowIndex);
273 updateFilterMatcher();
274 return result;
275 }
276
277 /**
278 * Sets/replaces the filter for a given row.
279 * @param rowIndex The row index
280 * @param filter The filter that should be placed in that row
281 * @return the filter previously at the specified position
282 */
283 public Filter setFilter(int rowIndex, Filter filter) {
284 Filter result = filters.set(rowIndex, filter);
285 updateFilterMatcher();
286 return result;
287 }
288
289 /**
290 * Gets the filter by row index
291 * @param rowIndex The row index
292 * @return The filter in that row
293 */
294 public Filter getFilter(int rowIndex) {
295 return filters.get(rowIndex);
296 }
297
298 /**
299 * Draws a text on the map display that indicates that filters are active.
300 * @param g The graphics to draw that text on.
301 * @param lblOSD On Screen Display label
302 * @param header The title to display at the beginning of OSD
303 * @param footer The message to display at the bottom of OSD. Must end by {@code </html>}
304 */
305 public void drawOSDText(Graphics2D g, OSDLabel lblOSD, String header, String footer) {
306 if (disabledCount == 0 && disabledAndHiddenCount == 0)
307 return;
308
309 String message = "<html>" + header;
310
311 if (disabledAndHiddenCount != 0) {
312 /* for correct i18n of plural forms - see #9110 */
313 message += trn("<p><b>{0}</b> object hidden", "<p><b>{0}</b> objects hidden", disabledAndHiddenCount, disabledAndHiddenCount);
314 }
315
316 if (disabledAndHiddenCount != 0 && disabledCount != 0) {
317 message += "<br>";
318 }
319
320 if (disabledCount != 0) {
321 /* for correct i18n of plural forms - see #9110 */
322 message += trn("<b>{0}</b> object disabled", "<b>{0}</b> objects disabled", disabledCount, disabledCount);
323 }
324
325 message += footer;
326
327 lblOSD.setText(message);
328 lblOSD.setSize(lblOSD.getPreferredSize());
329
330 int dx = Main.map.mapView.getWidth() - lblOSD.getPreferredSize().width - 15;
331 int dy = 15;
332 g.translate(dx, dy);
333 lblOSD.paintComponent(g);
334 g.translate(-dx, -dy);
335 }
336
337 /**
338 * Returns the list of filters.
339 * @return the list of filters
340 */
341 public List<Filter> getFilters() {
342 return new ArrayList<>(filters);
343 }
344
345 /**
346 * Returns the number of filters.
347 * @return the number of filters
348 */
349 public int getFiltersCount() {
350 return filters.size();
351 }
352
353 /**
354 * Returns the number of primitives that are disabled but not hidden.
355 * @return the number of primitives that are disabled but not hidden
356 */
357 public int getDisabledCount() {
358 return disabledCount;
359 }
360
361 /**
362 * Returns the number of primitives that are disabled and hidden.
363 * @return the number of primitives that are disabled and hidden
364 */
365 public int getDisabledAndHiddenCount() {
366 return disabledAndHiddenCount;
367 }
368
369 /**
370 * Determines if the filter state (normal / disabled / hidden) of any primitive has changed in the process.
371 * @return true, if the filter state (normal / disabled / hidden) of any primitive has changed in the process
372 */
373 public boolean isChanged() {
374 return changed;
375 }
376
377 /**
378 * Returns the list of primitives whose filtering can be affected by change in primitive
379 * @param primitives list of primitives to check
380 * @return List of primitives whose filtering can be affected by change in source primitives
381 */
382 public static Collection<OsmPrimitive> getAffectedPrimitives(Collection<? extends OsmPrimitive> primitives) {
383 // Filters can use nested parent/child expression so complete tree is necessary
384 Set<OsmPrimitive> result = new HashSet<>();
385 Stack<OsmPrimitive> stack = new Stack<>();
386 stack.addAll(primitives);
387
388 while (!stack.isEmpty()) {
389 OsmPrimitive p = stack.pop();
390
391 if (result.contains(p)) {
392 continue;
393 }
394
395 result.add(p);
396
397 if (p instanceof Way) {
398 for (OsmPrimitive n: ((Way) p).getNodes()) {
399 stack.push(n);
400 }
401 } else if (p instanceof Relation) {
402 for (RelationMember rm: ((Relation) p).getMembers()) {
403 stack.push(rm.getMember());
404 }
405 }
406
407 for (OsmPrimitive ref: p.getReferrers()) {
408 stack.push(ref);
409 }
410 }
411
412 return result;
413 }
414}
Note: See TracBrowser for help on using the repository browser.