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