001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005import static org.openstreetmap.josm.tools.I18n.trc;
006
007import java.awt.Graphics2D;
008import java.util.Collection;
009import java.util.List;
010
011import javax.swing.ListSelectionModel;
012import javax.swing.table.AbstractTableModel;
013
014import org.openstreetmap.josm.data.osm.Filter;
015import org.openstreetmap.josm.data.osm.FilterModel;
016import org.openstreetmap.josm.data.osm.OsmPrimitive;
017import org.openstreetmap.josm.gui.MainApplication;
018import org.openstreetmap.josm.gui.MapFrame;
019import org.openstreetmap.josm.gui.autofilter.AutoFilterManager;
020import org.openstreetmap.josm.gui.util.SortableTableModel;
021import org.openstreetmap.josm.gui.widgets.OSDLabel;
022import org.openstreetmap.josm.tools.Logging;
023
024/**
025 * The model that is used for the table in the {@link FilterDialog}.
026 *
027 * @author Petr_DlouhĂ˝
028 * @since 2125
029 */
030public class FilterTableModel extends AbstractTableModel implements SortableTableModel<Filter> {
031
032    /**
033     * The filter enabled column
034     */
035    public static final int COL_ENABLED = 0;
036    /**
037     * The column indicating if the filter is hiding.
038     */
039    public static final int COL_HIDING = 1;
040    /**
041     * The column that displays the filter text
042     */
043    public static final int COL_TEXT = 2;
044    /**
045     * The column to invert the filter
046     */
047    public static final int COL_INVERTED = 3;
048
049    /**
050     * The filter model
051     */
052    final FilterModel model = new FilterModel();
053
054    /**
055     * The selection model
056     */
057    final ListSelectionModel selectionModel;
058
059    /**
060     * A helper for {@link #drawOSDText(Graphics2D)}
061     */
062    private final OSDLabel lblOSD = new OSDLabel("");
063
064    /**
065     * Constructs a new {@code FilterTableModel}.
066     * @param listSelectionModel selection model
067     */
068    public FilterTableModel(ListSelectionModel listSelectionModel) {
069        this.selectionModel = listSelectionModel;
070        loadPrefs();
071    }
072
073    private void updateFilters() {
074        AutoFilterManager.getInstance().setCurrentAutoFilter(null);
075        executeFilters(true);
076    }
077
078    /**
079     * Runs the filters on the current edit data set, if any. Does nothing if no filter is enabled.
080     */
081    public void executeFilters() {
082        executeFilters(false);
083    }
084
085    /**
086     * Runs the filter on a list of primitives that are part of the edit data set, if any. Does nothing if no filter is enabled.
087     * @param primitives The primitives
088     */
089    public void executeFilters(Collection<? extends OsmPrimitive> primitives) {
090        executeFilters(primitives, false);
091    }
092
093    /**
094     * Runs the filters on the current edit data set, if any.
095     * @param force force execution of filters even if no filter is enabled. Useful to reset state after change of filters
096     * @since 14206
097     */
098    public void executeFilters(boolean force) {
099        if (AutoFilterManager.getInstance().getCurrentAutoFilter() == null && (force || model.hasFilters())) {
100            model.executeFilters();
101            updateMap();
102        }
103    }
104
105    /**
106     * Runs the filter on a list of primitives that are part of the edit data set, if any.
107     * @param force force execution of filters even if no filter is enabled. Useful to reset state after change of filters
108     * @param primitives The primitives
109     * @since 14206
110     */
111    public void executeFilters(Collection<? extends OsmPrimitive> primitives, boolean force) {
112        if (AutoFilterManager.getInstance().getCurrentAutoFilter() == null && (force || model.hasFilters())) {
113            model.executeFilters(primitives);
114            updateMap();
115        }
116    }
117
118    private void updateMap() {
119        MapFrame map = MainApplication.getMap();
120        if (map != null && model.isChanged()) {
121            map.filterDialog.updateDialogHeader();
122        }
123    }
124
125    private void loadPrefs() {
126        model.loadPrefs("filters.entries");
127    }
128
129    private void savePrefs() {
130        model.savePrefs("filters.entries");
131    }
132
133    /**
134     * Adds a new filter to the filter list.
135     * @param filter The new filter
136     */
137    public void addFilter(Filter filter) {
138        if (model.addFilter(filter)) {
139            savePrefs();
140            updateFilters();
141            int size = model.getFiltersCount();
142            fireTableRowsInserted(size - 1, size - 1);
143        }
144    }
145
146    @Override
147    public boolean doMove(int delta, int... selectedRows) {
148        return model.moveFilters(delta, selectedRows);
149    }
150
151    @Override
152    public boolean move(int delta, int... selectedRows) {
153        if (!SortableTableModel.super.move(delta, selectedRows))
154            return false;
155        savePrefs();
156        updateFilters();
157        int rowIndex = selectedRows[0];
158        if (delta < 0)
159            fireTableRowsUpdated(rowIndex + delta, rowIndex);
160        else if (delta > 0)
161            fireTableRowsUpdated(rowIndex, rowIndex + delta);
162        return true;
163    }
164
165    /**
166     * Removes the filter that is displayed in the given row
167     * @param rowIndex The index of the filter to remove
168     */
169    public void removeFilter(int rowIndex) {
170        if (model.removeFilter(rowIndex) != null) {
171            savePrefs();
172            updateFilters();
173            fireTableRowsDeleted(rowIndex, rowIndex);
174        }
175    }
176
177    @Override
178    public Filter setValue(int rowIndex, Filter filter) {
179        Filter result = model.setValue(rowIndex, filter);
180        savePrefs();
181        updateFilters();
182        fireTableRowsUpdated(rowIndex, rowIndex);
183        return result;
184    }
185
186    @Override
187    public Filter getValue(int rowIndex) {
188        return model.getValue(rowIndex);
189    }
190
191    @Override
192    public ListSelectionModel getSelectionModel() {
193        return selectionModel;
194    }
195
196    @Override
197    public int getRowCount() {
198        return model.getFiltersCount();
199    }
200
201    @Override
202    public int getColumnCount() {
203        return 5;
204    }
205
206    @Override
207    public String getColumnName(int column) {
208        String[] names = {/* translators notes must be in front */
209                /* column header: enable filter */trc("filter", "E"),
210                /* column header: hide filter */trc("filter", "H"),
211                /* column header: filter text */trc("filter", "Text"),
212                /* column header: inverted filter */trc("filter", "I"),
213                /* column header: filter mode */trc("filter", "M")};
214        return names[column];
215    }
216
217    @Override
218    public Class<?> getColumnClass(int column) {
219        Class<?>[] classes = {Boolean.class, Boolean.class, String.class, Boolean.class, String.class};
220        return classes[column];
221    }
222
223    /**
224     * Determines if a cell is enabled.
225     * @param row row index
226     * @param column column index
227     * @return {@code true} if the cell at (row, column) is enabled
228     */
229    public boolean isCellEnabled(int row, int column) {
230        return model.getValue(row).enable || column == 0;
231    }
232
233    @Override
234    public boolean isCellEditable(int row, int column) {
235        return column < 4 && isCellEnabled(row, column);
236    }
237
238    @Override
239    public void setValueAt(Object aValue, int row, int column) {
240        if (row >= model.getFiltersCount()) {
241            return;
242        }
243        Filter f = model.getValue(row);
244        switch (column) {
245        case COL_ENABLED:
246            f.enable = (Boolean) aValue;
247            break;
248        case COL_HIDING:
249            f.hiding = (Boolean) aValue;
250            break;
251        case COL_TEXT:
252            f.text = (String) aValue;
253            break;
254        case COL_INVERTED:
255            f.inverted = (Boolean) aValue;
256            break;
257        default: // Do nothing
258        }
259        setValue(row, f);
260    }
261
262    @Override
263    public Object getValueAt(int row, int column) {
264        if (row >= model.getFiltersCount()) {
265            return null;
266        }
267        Filter f = model.getValue(row);
268        switch (column) {
269        case COL_ENABLED:
270            return f.enable;
271        case COL_HIDING:
272            return f.hiding;
273        case COL_TEXT:
274            return f.text;
275        case COL_INVERTED:
276            return f.inverted;
277        case 4:
278            switch (f.mode) { /* translators notes must be in front */
279            case replace: /* filter mode: replace */
280                return trc("filter", "R");
281            case add: /* filter mode: add */
282                return trc("filter", "A");
283            case remove: /* filter mode: remove */
284                return trc("filter", "D");
285            case in_selection: /* filter mode: in selection */
286                return trc("filter", "F");
287            default:
288                Logging.warn("Unknown filter mode: " + f.mode);
289            }
290            break;
291        default: // Do nothing
292        }
293        return null;
294    }
295
296    /**
297     * Draws a text on the map display that indicates that filters are active.
298     * @param g The graphics to draw that text on.
299     */
300    public void drawOSDText(Graphics2D g) {
301        model.drawOSDText(g, lblOSD,
302                tr("<h2>Filter active</h2>"),
303                tr("</p><p>Close the filter dialog to see all objects.<p></html>"));
304    }
305
306    /**
307     * Returns the list of filters.
308     * @return the list of filters
309     */
310    public List<Filter> getFilters() {
311        return model.getFilters();
312    }
313
314    @Override
315    public void sort() {
316        model.sort();
317        fireTableDataChanged();
318    }
319
320    @Override
321    public void reverse() {
322        model.reverse();
323        fireTableDataChanged();
324    }
325}