source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/FilterTableModel.java@ 12387

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

see #14929 - filter dialog: invalidate only edit layer instead of repainting the whole mapview

  • Property svn:eol-style set to native
File size: 14.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trc;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.Color;
9import java.awt.Font;
10import java.awt.Graphics;
11import java.awt.Graphics2D;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.HashSet;
15import java.util.LinkedList;
16import java.util.List;
17
18import javax.swing.BorderFactory;
19import javax.swing.JLabel;
20import javax.swing.JOptionPane;
21import javax.swing.table.AbstractTableModel;
22
23import org.openstreetmap.josm.Main;
24import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
25import org.openstreetmap.josm.data.osm.DataSet;
26import org.openstreetmap.josm.data.osm.Filter;
27import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry;
28import org.openstreetmap.josm.data.osm.FilterMatcher;
29import org.openstreetmap.josm.data.osm.FilterWorker;
30import org.openstreetmap.josm.data.osm.Node;
31import org.openstreetmap.josm.data.osm.OsmPrimitive;
32import org.openstreetmap.josm.gui.layer.OsmDataLayer;
33import org.openstreetmap.josm.tools.Utils;
34
35/**
36 * The model that is used for the table in the {@link FilterDialog}.
37 *
38 * @author Petr_Dlouhý
39 */
40public class FilterTableModel extends AbstractTableModel {
41
42 /**
43 * The filter enabled column
44 */
45 public static final int COL_ENABLED = 0;
46 /**
47 * The column indicating if the filter is hiding.
48 */
49 public static final int COL_HIDING = 1;
50 /**
51 * The column that displays the filter text
52 */
53 public static final int COL_TEXT = 2;
54 /**
55 * The column to invert the filter
56 */
57 public static final int COL_INVERTED = 3;
58
59 /**
60 * number of primitives that are disabled but not hidden
61 */
62 public int disabledCount;
63 /**
64 * number of primitives that are disabled and hidden
65 */
66 public int disabledAndHiddenCount;
67
68 /**
69 * A helper for {@link #drawOSDText(Graphics2D)}.
70 */
71 private final OSDLabel lblOSD = new OSDLabel("");
72
73 /**
74 * Constructs a new {@code FilterTableModel}.
75 */
76 public FilterTableModel() {
77 loadPrefs();
78 }
79
80 private final transient List<Filter> filters = new LinkedList<>();
81 private final transient FilterMatcher filterMatcher = new FilterMatcher();
82
83 private void updateFilters() {
84 filterMatcher.reset();
85 for (Filter filter : filters) {
86 try {
87 filterMatcher.add(filter);
88 } catch (ParseError e) {
89 Main.error(e);
90 JOptionPane.showMessageDialog(
91 Main.parent,
92 tr("<html>Error in filter <code>{0}</code>:<br>{1}",
93 Utils.escapeReservedCharactersHTML(Utils.shortenString(filter.text, 80)),
94 Utils.escapeReservedCharactersHTML(e.getMessage())),
95 tr("Error in filter"),
96 JOptionPane.ERROR_MESSAGE);
97 filter.enable = false;
98 savePrefs();
99 }
100 }
101 executeFilters();
102 }
103
104 /**
105 * Runs the filters on the current edit data set.
106 */
107 public void executeFilters() {
108 DataSet ds = Main.getLayerManager().getEditDataSet();
109 boolean changed = false;
110 if (ds == null) {
111 disabledAndHiddenCount = 0;
112 disabledCount = 0;
113 changed = true;
114 } else {
115 final Collection<OsmPrimitive> deselect = new HashSet<>();
116
117 ds.beginUpdate();
118 try {
119
120 final Collection<OsmPrimitive> all = ds.allNonDeletedCompletePrimitives();
121
122 changed = FilterWorker.executeFilters(all, filterMatcher);
123
124 disabledCount = 0;
125 disabledAndHiddenCount = 0;
126 // collect disabled and selected the primitives
127 for (OsmPrimitive osm : all) {
128 if (osm.isDisabled()) {
129 disabledCount++;
130 if (osm.isSelected()) {
131 deselect.add(osm);
132 }
133 if (osm.isDisabledAndHidden()) {
134 disabledAndHiddenCount++;
135 }
136 }
137 }
138 disabledCount -= disabledAndHiddenCount;
139 } finally {
140 ds.endUpdate();
141 }
142
143 if (!deselect.isEmpty()) {
144 ds.clearSelection(deselect);
145 }
146 }
147
148 if (changed && Main.isDisplayingMapView()) {
149 updateMap();
150 }
151 }
152
153 /**
154 * Runs the filter on a list of primitives that are part of the edit data set.
155 * @param primitives The primitives
156 */
157 public void executeFilters(Collection<? extends OsmPrimitive> primitives) {
158 DataSet ds = Main.getLayerManager().getEditDataSet();
159 if (ds == null)
160 return;
161
162 boolean changed = false;
163 List<OsmPrimitive> deselect = new ArrayList<>();
164
165 ds.beginUpdate();
166 try {
167 for (int i = 0; i < 2; i++) {
168 for (OsmPrimitive primitive: primitives) {
169
170 if (i == 0 && primitive instanceof Node) {
171 continue;
172 }
173
174 if (i == 1 && !(primitive instanceof Node)) {
175 continue;
176 }
177
178 if (primitive.isDisabled()) {
179 disabledCount--;
180 }
181 if (primitive.isDisabledAndHidden()) {
182 disabledAndHiddenCount--;
183 }
184 changed |= FilterWorker.executeFilters(primitive, filterMatcher);
185 if (primitive.isDisabled()) {
186 disabledCount++;
187 }
188 if (primitive.isDisabledAndHidden()) {
189 disabledAndHiddenCount++;
190 }
191
192 if (primitive.isSelected() && primitive.isDisabled()) {
193 deselect.add(primitive);
194 }
195
196 }
197 }
198 } finally {
199 ds.endUpdate();
200 }
201
202 if (changed) {
203 updateMap();
204 ds.clearSelection(deselect);
205 }
206 }
207
208 private static void updateMap() {
209 OsmDataLayer editLayer = Main.getLayerManager().getEditLayer();
210 if (editLayer != null) {
211 editLayer.invalidate();
212 }
213 Main.map.filterDialog.updateDialogHeader();
214 }
215
216 /**
217 * Clears all filtered flags from all primitives in the dataset
218 */
219 public void clearFilterFlags() {
220 DataSet ds = Main.getLayerManager().getEditDataSet();
221 if (ds != null) {
222 FilterWorker.clearFilterFlags(ds.allPrimitives());
223 }
224 disabledCount = 0;
225 disabledAndHiddenCount = 0;
226 }
227
228 private void loadPrefs() {
229 List<FilterPreferenceEntry> entries = Main.pref.getListOfStructs("filters.entries", null, FilterPreferenceEntry.class);
230 if (entries != null) {
231 for (FilterPreferenceEntry e : entries) {
232 filters.add(new Filter(e));
233 }
234 updateFilters();
235 }
236 }
237
238 private void savePrefs() {
239 Collection<FilterPreferenceEntry> entries = new ArrayList<>();
240 for (Filter flt : filters) {
241 entries.add(flt.getPreferenceEntry());
242 }
243 Main.pref.putListOfStructs("filters.entries", entries, FilterPreferenceEntry.class);
244 }
245
246 /**
247 * Adds a new filter to the filter list.
248 * @param filter The new filter
249 */
250 public void addFilter(Filter filter) {
251 filters.add(filter);
252 savePrefs();
253 updateFilters();
254 fireTableRowsInserted(filters.size() - 1, filters.size() - 1);
255 }
256
257 /**
258 * Moves down the filter in the given row.
259 * @param rowIndex The filter row
260 */
261 public void moveDownFilter(int rowIndex) {
262 if (rowIndex >= filters.size() - 1)
263 return;
264 filters.add(rowIndex + 1, filters.remove(rowIndex));
265 savePrefs();
266 updateFilters();
267 fireTableRowsUpdated(rowIndex, rowIndex + 1);
268 }
269
270 /**
271 * Moves up the filter in the given row
272 * @param rowIndex The filter row
273 */
274 public void moveUpFilter(int rowIndex) {
275 if (rowIndex == 0)
276 return;
277 filters.add(rowIndex - 1, filters.remove(rowIndex));
278 savePrefs();
279 updateFilters();
280 fireTableRowsUpdated(rowIndex - 1, rowIndex);
281 }
282
283 /**
284 * Removes the filter that is displayed in the given row
285 * @param rowIndex The index of the filter to remove
286 */
287 public void removeFilter(int rowIndex) {
288 filters.remove(rowIndex);
289 savePrefs();
290 updateFilters();
291 fireTableRowsDeleted(rowIndex, rowIndex);
292 }
293
294 /**
295 * Sets/replaces the filter for a given row.
296 * @param rowIndex The row index
297 * @param filter The filter that should be placed in that row
298 */
299 public void setFilter(int rowIndex, Filter filter) {
300 filters.set(rowIndex, filter);
301 savePrefs();
302 updateFilters();
303 fireTableRowsUpdated(rowIndex, rowIndex);
304 }
305
306 /**
307 * Gets the filter by row index
308 * @param rowIndex The row index
309 * @return The filter in that row
310 */
311 public Filter getFilter(int rowIndex) {
312 return filters.get(rowIndex);
313 }
314
315 @Override
316 public int getRowCount() {
317 return filters.size();
318 }
319
320 @Override
321 public int getColumnCount() {
322 return 5;
323 }
324
325 @Override
326 public String getColumnName(int column) {
327 String[] names = {/* translators notes must be in front */
328 /* column header: enable filter */trc("filter", "E"),
329 /* column header: hide filter */trc("filter", "H"),
330 /* column header: filter text */trc("filter", "Text"),
331 /* column header: inverted filter */trc("filter", "I"),
332 /* column header: filter mode */trc("filter", "M")};
333 return names[column];
334 }
335
336 @Override
337 public Class<?> getColumnClass(int column) {
338 Class<?>[] classes = {Boolean.class, Boolean.class, String.class, Boolean.class, String.class};
339 return classes[column];
340 }
341
342 /**
343 * Determines if a cell is enabled.
344 * @param row row index
345 * @param column column index
346 * @return {@code true} if the cell at (row, column) is enabled
347 */
348 public boolean isCellEnabled(int row, int column) {
349 return filters.get(row).enable || column == 0;
350 }
351
352 @Override
353 public boolean isCellEditable(int row, int column) {
354 return column < 4 && isCellEnabled(row, column);
355 }
356
357 @Override
358 public void setValueAt(Object aValue, int row, int column) {
359 if (row >= filters.size()) {
360 return;
361 }
362 Filter f = filters.get(row);
363 switch (column) {
364 case COL_ENABLED:
365 f.enable = (Boolean) aValue;
366 savePrefs();
367 updateFilters();
368 fireTableRowsUpdated(row, row);
369 break;
370 case COL_HIDING:
371 f.hiding = (Boolean) aValue;
372 savePrefs();
373 updateFilters();
374 break;
375 case COL_TEXT:
376 f.text = (String) aValue;
377 savePrefs();
378 break;
379 case COL_INVERTED:
380 f.inverted = (Boolean) aValue;
381 savePrefs();
382 updateFilters();
383 break;
384 default: // Do nothing
385 }
386 if (column != 0) {
387 fireTableCellUpdated(row, column);
388 }
389 }
390
391 @Override
392 public Object getValueAt(int row, int column) {
393 if (row >= filters.size()) {
394 return null;
395 }
396 Filter f = filters.get(row);
397 switch (column) {
398 case COL_ENABLED:
399 return f.enable;
400 case COL_HIDING:
401 return f.hiding;
402 case COL_TEXT:
403 return f.text;
404 case COL_INVERTED:
405 return f.inverted;
406 case 4:
407 switch (f.mode) { /* translators notes must be in front */
408 case replace: /* filter mode: replace */
409 return trc("filter", "R");
410 case add: /* filter mode: add */
411 return trc("filter", "A");
412 case remove: /* filter mode: remove */
413 return trc("filter", "D");
414 case in_selection: /* filter mode: in selection */
415 return trc("filter", "F");
416 default:
417 Main.warn("Unknown filter mode: " + f.mode);
418 }
419 break;
420 default: // Do nothing
421 }
422 return null;
423 }
424
425 /**
426 * On screen display label
427 */
428 private static class OSDLabel extends JLabel {
429 OSDLabel(String text) {
430 super(text);
431 setOpaque(true);
432 setForeground(Color.black);
433 setBackground(new Color(0, 0, 0, 0));
434 setFont(getFont().deriveFont(Font.PLAIN));
435 setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
436 }
437
438 @Override
439 public void paintComponent(Graphics g) {
440 g.setColor(new Color(255, 255, 255, 140));
441 g.fillRoundRect(getX(), getY(), getWidth(), getHeight(), 10, 10);
442 super.paintComponent(g);
443 }
444 }
445
446 /**
447 * Draws a text on the map display that indicates that filters are active.
448 * @param g The graphics to draw that text on.
449 */
450 public void drawOSDText(Graphics2D g) {
451 String message = "<html>" + tr("<h2>Filter active</h2>");
452
453 if (disabledCount == 0 && disabledAndHiddenCount == 0)
454 return;
455
456 if (disabledAndHiddenCount != 0) {
457 /* for correct i18n of plural forms - see #9110 */
458 message += trn("<p><b>{0}</b> object hidden", "<p><b>{0}</b> objects hidden", disabledAndHiddenCount, disabledAndHiddenCount);
459 }
460
461 if (disabledAndHiddenCount != 0 && disabledCount != 0) {
462 message += "<br>";
463 }
464
465 if (disabledCount != 0) {
466 /* for correct i18n of plural forms - see #9110 */
467 message += trn("<b>{0}</b> object disabled", "<b>{0}</b> objects disabled", disabledCount, disabledCount);
468 }
469
470 message += tr("</p><p>Close the filter dialog to see all objects.<p></html>");
471
472 lblOSD.setText(message);
473 lblOSD.setSize(lblOSD.getPreferredSize());
474
475 int dx = Main.map.mapView.getWidth() - lblOSD.getPreferredSize().width - 15;
476 int dy = 15;
477 g.translate(dx, dy);
478 lblOSD.paintComponent(g);
479 g.translate(-dx, -dy);
480 }
481
482 /**
483 * Returns the list of filters.
484 * @return the list of filters
485 */
486 public List<Filter> getFilters() {
487 return filters;
488 }
489}
Note: See TracBrowser for help on using the repository browser.