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

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

fix #14613 - Special HTML characters not escaped in GUI error messages

  • Property svn:eol-style set to native
File size: 12.8 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.tools.Utils;
33
34/**
35 *
36 * @author Petr_Dlouhý
37 */
38public class FilterTableModel extends AbstractTableModel {
39
40    public static final int COL_ENABLED = 0;
41    public static final int COL_HIDING = 1;
42    public static final int COL_TEXT = 2;
43    public static final int COL_INVERTED = 3;
44
45    // number of primitives that are disabled but not hidden
46    public int disabledCount;
47    // number of primitives that are disabled and hidden
48    public int disabledAndHiddenCount;
49
50    /**
51     * Constructs a new {@code FilterTableModel}.
52     */
53    public FilterTableModel() {
54        loadPrefs();
55    }
56
57    private final transient List<Filter> filters = new LinkedList<>();
58    private final transient FilterMatcher filterMatcher = new FilterMatcher();
59
60    private void updateFilters() {
61        filterMatcher.reset();
62        for (Filter filter : filters) {
63            try {
64                filterMatcher.add(filter);
65            } catch (ParseError e) {
66                Main.error(e);
67                JOptionPane.showMessageDialog(
68                        Main.parent,
69                        tr("<html>Error in filter <code>{0}</code>:<br>{1}",
70                                Utils.escapeReservedCharactersHTML(Utils.shortenString(filter.text, 80)),
71                                Utils.escapeReservedCharactersHTML(e.getMessage())),
72                        tr("Error in filter"),
73                        JOptionPane.ERROR_MESSAGE);
74                filter.enable = false;
75                savePrefs();
76            }
77        }
78        executeFilters();
79    }
80
81    public void executeFilters() {
82        DataSet ds = Main.getLayerManager().getEditDataSet();
83        boolean changed = false;
84        if (ds == null) {
85            disabledAndHiddenCount = 0;
86            disabledCount = 0;
87            changed = true;
88        } else {
89            final Collection<OsmPrimitive> deselect = new HashSet<>();
90
91            ds.beginUpdate();
92            try {
93
94                final Collection<OsmPrimitive> all = ds.allNonDeletedCompletePrimitives();
95
96                changed = FilterWorker.executeFilters(all, filterMatcher);
97
98                disabledCount = 0;
99                disabledAndHiddenCount = 0;
100                // collect disabled and selected the primitives
101                for (OsmPrimitive osm : all) {
102                    if (osm.isDisabled()) {
103                        disabledCount++;
104                        if (osm.isSelected()) {
105                            deselect.add(osm);
106                        }
107                        if (osm.isDisabledAndHidden()) {
108                            disabledAndHiddenCount++;
109                        }
110                    }
111                }
112                disabledCount -= disabledAndHiddenCount;
113            } finally {
114                ds.endUpdate();
115            }
116
117            if (!deselect.isEmpty()) {
118                ds.clearSelection(deselect);
119            }
120        }
121
122        if (changed && Main.isDisplayingMapView()) {
123            Main.map.mapView.repaint();
124            Main.map.filterDialog.updateDialogHeader();
125        }
126    }
127
128    public void executeFilters(Collection<? extends OsmPrimitive> primitives) {
129        DataSet ds = Main.getLayerManager().getEditDataSet();
130        if (ds == null)
131            return;
132
133        boolean changed = false;
134        List<OsmPrimitive> deselect = new ArrayList<>();
135
136        ds.beginUpdate();
137        try {
138            for (int i = 0; i < 2; i++) {
139                for (OsmPrimitive primitive: primitives) {
140
141                    if (i == 0 && primitive instanceof Node) {
142                        continue;
143                    }
144
145                    if (i == 1 && !(primitive instanceof Node)) {
146                        continue;
147                    }
148
149                    if (primitive.isDisabled()) {
150                        disabledCount--;
151                    }
152                    if (primitive.isDisabledAndHidden()) {
153                        disabledAndHiddenCount--;
154                    }
155                    changed = changed | FilterWorker.executeFilters(primitive, filterMatcher);
156                    if (primitive.isDisabled()) {
157                        disabledCount++;
158                    }
159                    if (primitive.isDisabledAndHidden()) {
160                        disabledAndHiddenCount++;
161                    }
162
163                    if (primitive.isSelected() && primitive.isDisabled()) {
164                        deselect.add(primitive);
165                    }
166
167                }
168            }
169        } finally {
170            ds.endUpdate();
171        }
172
173        if (changed) {
174            Main.map.mapView.repaint();
175            Main.map.filterDialog.updateDialogHeader();
176            ds.clearSelection(deselect);
177        }
178
179    }
180
181    public void clearFilterFlags() {
182        DataSet ds = Main.getLayerManager().getEditDataSet();
183        if (ds != null) {
184            FilterWorker.clearFilterFlags(ds.allPrimitives());
185        }
186        disabledCount = 0;
187        disabledAndHiddenCount = 0;
188    }
189
190    private void loadPrefs() {
191        List<FilterPreferenceEntry> entries = Main.pref.getListOfStructs("filters.entries", null, FilterPreferenceEntry.class);
192        if (entries != null) {
193            for (FilterPreferenceEntry e : entries) {
194                filters.add(new Filter(e));
195            }
196            updateFilters();
197        }
198    }
199
200    private void savePrefs() {
201        Collection<FilterPreferenceEntry> entries = new ArrayList<>();
202        for (Filter flt : filters) {
203            entries.add(flt.getPreferenceEntry());
204        }
205        Main.pref.putListOfStructs("filters.entries", entries, FilterPreferenceEntry.class);
206    }
207
208    public void addFilter(Filter f) {
209        filters.add(f);
210        savePrefs();
211        updateFilters();
212        fireTableRowsInserted(filters.size() - 1, filters.size() - 1);
213    }
214
215    public void moveDownFilter(int i) {
216        if (i >= filters.size() - 1)
217            return;
218        filters.add(i + 1, filters.remove(i));
219        savePrefs();
220        updateFilters();
221        fireTableRowsUpdated(i, i + 1);
222    }
223
224    public void moveUpFilter(int i) {
225        if (i == 0)
226            return;
227        filters.add(i - 1, filters.remove(i));
228        savePrefs();
229        updateFilters();
230        fireTableRowsUpdated(i - 1, i);
231    }
232
233    public void removeFilter(int i) {
234        filters.remove(i);
235        savePrefs();
236        updateFilters();
237        fireTableRowsDeleted(i, i);
238    }
239
240    public void setFilter(int i, Filter f) {
241        filters.set(i, f);
242        savePrefs();
243        updateFilters();
244        fireTableRowsUpdated(i, i);
245    }
246
247    public Filter getFilter(int i) {
248        return filters.get(i);
249    }
250
251    @Override
252    public int getRowCount() {
253        return filters.size();
254    }
255
256    @Override
257    public int getColumnCount() {
258        return 5;
259    }
260
261    @Override
262    public String getColumnName(int column) {
263        String[] names = {/* translators notes must be in front */
264                /* column header: enable filter */trc("filter", "E"),
265                /* column header: hide filter */trc("filter", "H"),
266                /* column header: filter text */trc("filter", "Text"),
267                /* column header: inverted filter */trc("filter", "I"),
268                /* column header: filter mode */trc("filter", "M")};
269        return names[column];
270    }
271
272    @Override
273    public Class<?> getColumnClass(int column) {
274        Class<?>[] classes = {Boolean.class, Boolean.class, String.class, Boolean.class, String.class};
275        return classes[column];
276    }
277
278    public boolean isCellEnabled(int row, int column) {
279        if (!filters.get(row).enable && column != 0)
280            return false;
281        return true;
282    }
283
284    @Override
285    public boolean isCellEditable(int row, int column) {
286        if (!filters.get(row).enable && column != 0)
287            return false;
288        if (column < 4)
289            return true;
290        return false;
291    }
292
293    @Override
294    public void setValueAt(Object aValue, int row, int column) {
295        if (row >= filters.size()) {
296            return;
297        }
298        Filter f = filters.get(row);
299        switch (column) {
300        case COL_ENABLED:
301            f.enable = (Boolean) aValue;
302            savePrefs();
303            updateFilters();
304            fireTableRowsUpdated(row, row);
305            break;
306        case COL_HIDING:
307            f.hiding = (Boolean) aValue;
308            savePrefs();
309            updateFilters();
310            break;
311        case COL_TEXT:
312            f.text = (String) aValue;
313            savePrefs();
314            break;
315        case COL_INVERTED:
316            f.inverted = (Boolean) aValue;
317            savePrefs();
318            updateFilters();
319            break;
320        default: // Do nothing
321        }
322        if (column != 0) {
323            fireTableCellUpdated(row, column);
324        }
325    }
326
327    @Override
328    public Object getValueAt(int row, int column) {
329        if (row >= filters.size()) {
330            return null;
331        }
332        Filter f = filters.get(row);
333        switch (column) {
334        case COL_ENABLED:
335            return f.enable;
336        case COL_HIDING:
337            return f.hiding;
338        case COL_TEXT:
339            return f.text;
340        case COL_INVERTED:
341            return f.inverted;
342        case 4:
343            switch (f.mode) { /* translators notes must be in front */
344            case replace: /* filter mode: replace */
345                return trc("filter", "R");
346            case add: /* filter mode: add */
347                return trc("filter", "A");
348            case remove: /* filter mode: remove */
349                return trc("filter", "D");
350            case in_selection: /* filter mode: in selection */
351                return trc("filter", "F");
352            default:
353                Main.warn("Unknown filter mode: " + f.mode);
354            }
355            break;
356        default: // Do nothing
357        }
358        return null;
359    }
360
361    /**
362     * On screen display label
363     */
364    private static class OSDLabel extends JLabel {
365        OSDLabel(String text) {
366            super(text);
367            setOpaque(true);
368            setForeground(Color.black);
369            setBackground(new Color(0, 0, 0, 0));
370            setFont(getFont().deriveFont(Font.PLAIN));
371            setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
372        }
373
374        @Override
375        public void paintComponent(Graphics g) {
376            g.setColor(new Color(255, 255, 255, 140));
377            g.fillRoundRect(getX(), getY(), getWidth(), getHeight(), 10, 10);
378            super.paintComponent(g);
379        }
380    }
381
382    private final OSDLabel lblOSD = new OSDLabel("");
383
384    public void drawOSDText(Graphics2D g) {
385        String message = "<html>" + tr("<h2>Filter active</h2>");
386
387        if (disabledCount == 0 && disabledAndHiddenCount == 0)
388            return;
389
390        if (disabledAndHiddenCount != 0) {
391            /* for correct i18n of plural forms - see #9110 */
392            message += trn("<p><b>{0}</b> object hidden", "<p><b>{0}</b> objects hidden", disabledAndHiddenCount, disabledAndHiddenCount);
393        }
394
395        if (disabledAndHiddenCount != 0 && disabledCount != 0) {
396            message += "<br>";
397        }
398
399        if (disabledCount != 0) {
400            /* for correct i18n of plural forms - see #9110 */
401            message += trn("<b>{0}</b> object disabled", "<b>{0}</b> objects disabled", disabledCount, disabledCount);
402        }
403
404        message += tr("</p><p>Close the filter dialog to see all objects.<p></html>");
405
406        lblOSD.setText(message);
407        lblOSD.setSize(lblOSD.getPreferredSize());
408
409        int dx = Main.map.mapView.getWidth() - lblOSD.getPreferredSize().width - 15;
410        int dy = 15;
411        g.translate(dx, dy);
412        lblOSD.paintComponent(g);
413        g.translate(-dx, -dy);
414    }
415
416    /**
417     * Returns the list of filters.
418     * @return the list of filters
419     */
420    public List<Filter> getFilters() {
421        return filters;
422    }
423}
Note: See TracBrowser for help on using the repository browser.