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();
066    }
067
068    /**
069     * Runs the filters on the current edit data set.
070     */
071    public void executeFilters() {
072        if (AutoFilterManager.getInstance().getCurrentAutoFilter() == null) {
073            model.executeFilters();
074            updateMap();
075        }
076    }
077
078    /**
079     * Runs the filter on a list of primitives that are part of the edit data set.
080     * @param primitives The primitives
081     */
082    public void executeFilters(Collection<? extends OsmPrimitive> primitives) {
083        if (AutoFilterManager.getInstance().getCurrentAutoFilter() == null) {
084            model.executeFilters(primitives);
085            updateMap();
086        }
087    }
088
089    private void updateMap() {
090        MapFrame map = MainApplication.getMap();
091        if (map != null && model.isChanged()) {
092            map.filterDialog.updateDialogHeader();
093        }
094    }
095
096    private void loadPrefs() {
097        model.loadPrefs("filters.entries");
098    }
099
100    private void savePrefs() {
101        model.savePrefs("filters.entries");
102    }
103
104    /**
105     * Adds a new filter to the filter list.
106     * @param filter The new filter
107     */
108    public void addFilter(Filter filter) {
109        if (model.addFilter(filter)) {
110            savePrefs();
111            updateFilters();
112            int size = model.getFiltersCount();
113            fireTableRowsInserted(size - 1, size - 1);
114        }
115    }
116
117    /**
118     * Moves down the filter in the given row.
119     * @param rowIndex The filter row
120     */
121    public void moveDownFilter(int rowIndex) {
122        if (model.moveDownFilter(rowIndex)) {
123            savePrefs();
124            updateFilters();
125            fireTableRowsUpdated(rowIndex, rowIndex + 1);
126        }
127    }
128
129    /**
130     * Moves up the filter in the given row
131     * @param rowIndex The filter row
132     */
133    public void moveUpFilter(int rowIndex) {
134        if (model.moveUpFilter(rowIndex)) {
135            savePrefs();
136            updateFilters();
137            fireTableRowsUpdated(rowIndex - 1, rowIndex);
138        }
139    }
140
141    /**
142     * Removes the filter that is displayed in the given row
143     * @param rowIndex The index of the filter to remove
144     */
145    public void removeFilter(int rowIndex) {
146        if (model.removeFilter(rowIndex) != null) {
147            savePrefs();
148            updateFilters();
149            fireTableRowsDeleted(rowIndex, rowIndex);
150        }
151    }
152
153    /**
154     * Sets/replaces the filter for a given row.
155     * @param rowIndex The row index
156     * @param filter The filter that should be placed in that row
157     */
158    public void setFilter(int rowIndex, Filter filter) {
159        model.setFilter(rowIndex, filter);
160        savePrefs();
161        updateFilters();
162        fireTableRowsUpdated(rowIndex, rowIndex);
163    }
164
165    /**
166     * Gets the filter by row index
167     * @param rowIndex The row index
168     * @return The filter in that row
169     */
170    public Filter getFilter(int rowIndex) {
171        return model.getFilter(rowIndex);
172    }
173
174    @Override
175    public int getRowCount() {
176        return model.getFiltersCount();
177    }
178
179    @Override
180    public int getColumnCount() {
181        return 5;
182    }
183
184    @Override
185    public String getColumnName(int column) {
186        String[] names = {/* translators notes must be in front */
187                /* column header: enable filter */trc("filter", "E"),
188                /* column header: hide filter */trc("filter", "H"),
189                /* column header: filter text */trc("filter", "Text"),
190                /* column header: inverted filter */trc("filter", "I"),
191                /* column header: filter mode */trc("filter", "M")};
192        return names[column];
193    }
194
195    @Override
196    public Class<?> getColumnClass(int column) {
197        Class<?>[] classes = {Boolean.class, Boolean.class, String.class, Boolean.class, String.class};
198        return classes[column];
199    }
200
201    /**
202     * Determines if a cell is enabled.
203     * @param row row index
204     * @param column column index
205     * @return {@code true} if the cell at (row, column) is enabled
206     */
207    public boolean isCellEnabled(int row, int column) {
208        return model.getFilter(row).enable || column == 0;
209    }
210
211    @Override
212    public boolean isCellEditable(int row, int column) {
213        return column < 4 && isCellEnabled(row, column);
214    }
215
216    @Override
217    public void setValueAt(Object aValue, int row, int column) {
218        if (row >= model.getFiltersCount()) {
219            return;
220        }
221        Filter f = model.getFilter(row);
222        switch (column) {
223        case COL_ENABLED:
224            f.enable = (Boolean) aValue;
225            setFilter(row, f);
226            break;
227        case COL_HIDING:
228            f.hiding = (Boolean) aValue;
229            setFilter(row, f);
230            break;
231        case COL_TEXT:
232            f.text = (String) aValue;
233            savePrefs();
234            break;
235        case COL_INVERTED:
236            f.inverted = (Boolean) aValue;
237            setFilter(row, f);
238            break;
239        default: // Do nothing
240        }
241        if (column != 0) {
242            fireTableCellUpdated(row, column);
243        }
244    }
245
246    @Override
247    public Object getValueAt(int row, int column) {
248        if (row >= model.getFiltersCount()) {
249            return null;
250        }
251        Filter f = model.getFilter(row);
252        switch (column) {
253        case COL_ENABLED:
254            return f.enable;
255        case COL_HIDING:
256            return f.hiding;
257        case COL_TEXT:
258            return f.text;
259        case COL_INVERTED:
260            return f.inverted;
261        case 4:
262            switch (f.mode) { /* translators notes must be in front */
263            case replace: /* filter mode: replace */
264                return trc("filter", "R");
265            case add: /* filter mode: add */
266                return trc("filter", "A");
267            case remove: /* filter mode: remove */
268                return trc("filter", "D");
269            case in_selection: /* filter mode: in selection */
270                return trc("filter", "F");
271            default:
272                Logging.warn("Unknown filter mode: " + f.mode);
273            }
274            break;
275        default: // Do nothing
276        }
277        return null;
278    }
279
280    /**
281     * Draws a text on the map display that indicates that filters are active.
282     * @param g The graphics to draw that text on.
283     */
284    public void drawOSDText(Graphics2D g) {
285        model.drawOSDText(g, lblOSD,
286                tr("<h2>Filter active</h2>"),
287                tr("</p><p>Close the filter dialog to see all objects.<p></html>"));
288    }
289
290    /**
291     * Returns the list of filters.
292     * @return the list of filters
293     */
294    public List<Filter> getFilters() {
295        return model.getFilters();
296    }
297}