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

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

see #15182 - deprecate Main.getLayerManager(). Replacement: gui.MainApplication.getLayerManager()

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