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

Last change on this file since 13206 was 13150, checked in by Don-vip, 6 years ago

fix #14778 - stroke not reset after drawing of large areas produced graphics artifacts when painting world bounds and selection rectangles, bug seen when filtering ways only, as way rendering reset the stroke

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