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

Last change on this file since 12973 was 12851, checked in by bastiK, 7 years ago

see #15229 - extract "struct" handling from Preference to StructUtils

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