001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.dialogs;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    import static org.openstreetmap.josm.tools.I18n.trc;
006    
007    import java.awt.Color;
008    import java.awt.Font;
009    import java.awt.Graphics;
010    import java.awt.Graphics2D;
011    import java.util.ArrayList;
012    import java.util.Collection;
013    import java.util.HashSet;
014    import java.util.LinkedList;
015    import java.util.List;
016    import java.util.Map;
017    
018    import javax.swing.BorderFactory;
019    import javax.swing.JLabel;
020    import javax.swing.JOptionPane;
021    import javax.swing.table.AbstractTableModel;
022    
023    import org.openstreetmap.josm.Main;
024    import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
025    import org.openstreetmap.josm.data.osm.DataSet;
026    import org.openstreetmap.josm.data.osm.Filter;
027    import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry;
028    import org.openstreetmap.josm.data.osm.FilterMatcher;
029    import org.openstreetmap.josm.data.osm.FilterWorker;
030    import org.openstreetmap.josm.data.osm.Node;
031    import org.openstreetmap.josm.data.osm.OsmPrimitive;
032    
033    /**
034     *
035     * @author Petr_Dlouh??
036     */
037    public class FilterTableModel extends AbstractTableModel {
038    
039        public static final int COL_ENABLED = 0;
040        public static final int COL_HIDING = 1;
041        public static final int COL_TEXT = 2;
042        public static final int COL_INVERTED = 3;
043    
044        // number of primitives that are disabled but not hidden
045        public int disabledCount;
046        // number of primitives that are disabled and hidden
047        public int disabledAndHiddenCount;
048    
049        public FilterTableModel() {
050            loadPrefs();
051        }
052    
053        private final List<Filter> filters = new LinkedList<Filter>();
054        private final FilterMatcher filterMatcher = new FilterMatcher();
055    
056        private void updateFilters() {
057            try {
058                filterMatcher.update(filters);
059                executeFilters();
060            } catch (ParseError e) {
061                JOptionPane.showMessageDialog(
062                        Main.parent,
063                        e.getMessage(),
064                        tr("Error in filter"),
065                        JOptionPane.ERROR_MESSAGE);
066            }
067        }
068    
069        public void executeFilters() {
070            DataSet ds = Main.main.getCurrentDataSet();
071            boolean changed = false;
072            if (ds == null) {
073                disabledAndHiddenCount = 0;
074                disabledCount = 0;
075                changed = true;
076            } else {
077                final Collection<OsmPrimitive> deselect = new HashSet<OsmPrimitive>();
078    
079                ds.beginUpdate();
080                try {
081    
082                    final Collection<OsmPrimitive> all = ds.allNonDeletedCompletePrimitives();
083    
084                    changed = FilterWorker.executeFilters(all, filterMatcher);
085    
086                    disabledCount = 0;
087                    disabledAndHiddenCount = 0;
088                    // collect disabled and selected the primitives
089                    for (OsmPrimitive osm : all) {
090                        if (osm.isDisabled()) {
091                            disabledCount++;
092                            if (osm.isSelected()) {
093                                deselect.add(osm);
094                            }
095                            if (osm.isDisabledAndHidden()) {
096                                disabledAndHiddenCount++;
097                            }
098                        }
099                    }
100                    disabledCount -= disabledAndHiddenCount;
101                } finally {
102                    ds.endUpdate();
103                }
104    
105                if (!deselect.isEmpty()) {
106                    ds.clearSelection(deselect);
107                }
108            }
109    
110            if (Main.isDisplayingMapView() && changed) {
111                Main.map.mapView.repaint();
112                Main.map.filterDialog.updateDialogHeader();
113            }
114        }
115    
116        public void executeFilters(Collection<? extends OsmPrimitive> primitives) {
117            DataSet ds = Main.main.getCurrentDataSet();
118            if (ds == null)
119                return;
120    
121            boolean changed = false;
122            List<OsmPrimitive> deselect = new ArrayList<OsmPrimitive>();
123    
124            ds.beginUpdate();
125            try {
126                for (int i=0; i<2; i++) {
127                    for (OsmPrimitive primitive: primitives) {
128    
129                        if (i == 0 && primitive instanceof Node) {
130                            continue;
131                        }
132    
133                        if (i == 1 && !(primitive instanceof Node)) {
134                            continue;
135                        }
136    
137                        if (primitive.isDisabled()) {
138                            disabledCount--;
139                        }
140                        if (primitive.isDisabledAndHidden()) {
141                            disabledAndHiddenCount--;
142                        }
143                        changed = changed | FilterWorker.executeFilters(primitive, filterMatcher);
144                        if (primitive.isDisabled()) {
145                            disabledCount++;
146                        }
147                        if (primitive.isDisabledAndHidden()) {
148                            disabledAndHiddenCount++;
149                        }
150    
151                        if (primitive.isSelected() && primitive.isDisabled()) {
152                            deselect.add(primitive);
153                        }
154    
155                    }
156                }
157            } finally {
158                ds.endUpdate();
159            }
160    
161            if (changed) {
162                Main.map.mapView.repaint();
163                Main.map.filterDialog.updateDialogHeader();
164                ds.clearSelection(deselect);
165            }
166    
167        }
168    
169        public void clearFilterFlags() {
170            DataSet ds = Main.main.getCurrentDataSet();
171            if (ds != null) {
172                FilterWorker.clearFilterFlags(ds.allPrimitives());
173            }
174            disabledCount = 0;
175            disabledAndHiddenCount = 0;
176        }
177    
178        private void loadPrefs() {
179            List<FilterPreferenceEntry> entries = Main.pref.getListOfStructs("filters.entries", null, FilterPreferenceEntry.class);
180            if (entries != null) {
181                for (FilterPreferenceEntry e : entries) {
182                    filters.add(new Filter(e));
183                }
184                updateFilters();
185            }
186        }
187    
188        private void savePrefs() {
189            Collection<FilterPreferenceEntry> entries = new ArrayList<FilterPreferenceEntry>();
190            for (Filter flt : filters) {
191                entries.add(flt.getPreferenceEntry());
192            }
193            Main.pref.putListOfStructs("filters.entries", entries, FilterPreferenceEntry.class);
194        }
195    
196        public void addFilter(Filter f) {
197            filters.add(f);
198            savePrefs();
199            updateFilters();
200            fireTableRowsInserted(filters.size() - 1, filters.size() - 1);
201        }
202    
203        public void moveDownFilter(int i) {
204            if (i >= filters.size() - 1)
205                return;
206            filters.add(i + 1, filters.remove(i));
207            savePrefs();
208            updateFilters();
209            fireTableRowsUpdated(i, i + 1);
210        }
211    
212        public void moveUpFilter(int i) {
213            if (i == 0)
214                return;
215            filters.add(i - 1, filters.remove(i));
216            savePrefs();
217            updateFilters();
218            fireTableRowsUpdated(i - 1, i);
219        }
220    
221        public void removeFilter(int i) {
222            filters.remove(i);
223            savePrefs();
224            updateFilters();
225            fireTableRowsDeleted(i, i);
226        }
227    
228        public void setFilter(int i, Filter f) {
229            filters.set(i, f);
230            savePrefs();
231            updateFilters();
232            fireTableRowsUpdated(i, i);
233        }
234    
235        public Filter getFilter(int i) {
236            return filters.get(i);
237        }
238    
239        public int getRowCount() {
240            return filters.size();
241        }
242    
243        public int getColumnCount() {
244            return 5;
245        }
246    
247        @Override
248        public String getColumnName(int column) {
249            String[] names = { /* translators notes must be in front */
250                    /* column header: enable filter */trc("filter", "E"),
251                    /* column header: hide filter */trc("filter", "H"),
252                    /* column header: filter text */trc("filter", "Text"),
253                    /* column header: inverted filter */trc("filter", "I"),
254                    /* column header: filter mode */trc("filter", "M") };
255            return names[column];
256        }
257    
258        @Override
259        public Class<?> getColumnClass(int column) {
260            Class<?>[] classes = { Boolean.class, Boolean.class, String.class, Boolean.class, String.class };
261            return classes[column];
262        }
263    
264        public boolean isCellEnabled(int row, int column) {
265            if (!filters.get(row).enable && column != 0)
266                return false;
267            return true;
268        }
269    
270        @Override
271        public boolean isCellEditable(int row, int column) {
272            if (!filters.get(row).enable && column != 0)
273                return false;
274            if (column < 4)
275                return true;
276            return false;
277        }
278    
279        @Override
280        public void setValueAt(Object aValue, int row, int column) {
281            Filter f = filters.get(row);
282            switch (column) {
283            case COL_ENABLED:
284                f.enable = (Boolean) aValue;
285                savePrefs();
286                updateFilters();
287                fireTableRowsUpdated(row, row);
288                break;
289            case COL_HIDING:
290                f.hiding = (Boolean) aValue;
291                savePrefs();
292                updateFilters();
293                break;
294            case COL_TEXT:
295                f.text = (String) aValue;
296                savePrefs();
297                break;
298            case COL_INVERTED:
299                f.inverted = (Boolean) aValue;
300                savePrefs();
301                updateFilters();
302                break;
303            }
304            if (column != 0) {
305                fireTableCellUpdated(row, column);
306            }
307        }
308    
309        public Object getValueAt(int row, int column) {
310            Filter f = filters.get(row);
311            switch (column) {
312            case COL_ENABLED:
313                return f.enable;
314            case COL_HIDING:
315                return f.hiding;
316            case COL_TEXT:
317                return f.text;
318            case COL_INVERTED:
319                return f.inverted;
320            case 4:
321                switch (f.mode) { /* translators notes must be in front */
322                case replace: /* filter mode: replace */
323                    return trc("filter", "R");
324                case add: /* filter mode: add */
325                    return trc("filter", "A");
326                case remove: /* filter mode: remove */
327                    return trc("filter", "D");
328                case in_selection: /* filter mode: in selection */
329                    return trc("filter", "F");
330                }
331            }
332            return null;
333        }
334    
335        /**
336         * On screen display label
337         */
338        private static class OSDLabel extends JLabel {
339            public OSDLabel(String text) {
340                super(text);
341                setOpaque(true);
342                setForeground(Color.black);
343                setBackground(new Color(0, 0, 0, 0));
344                setFont(getFont().deriveFont(Font.PLAIN));
345                setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
346            }
347    
348            @Override
349            public void paintComponent(Graphics g) {
350                g.setColor(new Color(255, 255, 255, 140));
351                g.fillRoundRect(getX(), getY(), getWidth(), getHeight(), 10, 10);
352                super.paintComponent(g);
353            }
354        }
355    
356        private OSDLabel lblOSD = new OSDLabel("");
357    
358        public void drawOSDText(Graphics2D g) {
359            String message = "<html>" + tr("<h2>Filter active</h2>");
360    
361            if (disabledCount == 0 && disabledAndHiddenCount == 0)
362                return;
363    
364            if (disabledAndHiddenCount != 0) {
365                message += tr("<p><b>{0}</b> objects hidden", disabledAndHiddenCount);
366            }
367    
368            if (disabledAndHiddenCount != 0 && disabledCount != 0) {
369                message += "<br>";
370            }
371    
372            if (disabledCount != 0) {
373                message += tr("<b>{0}</b> objects disabled", disabledCount);
374            }
375    
376            message += tr("</p><p>Close the filter dialog to see all objects.<p></html>");
377    
378            lblOSD.setText(message);
379            lblOSD.setSize(lblOSD.getPreferredSize());
380    
381            int dx = Main.map.mapView.getWidth() - lblOSD.getPreferredSize().width - 15;
382            int dy = 15;
383            g.translate(dx, dy);
384            lblOSD.paintComponent(g);
385            g.translate(-dx, -dy);
386        }
387    
388        public List<Filter> getFilters() {
389            return filters;
390        }
391    }