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}