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 * Moves down the filter in the given row. 167 * @param rowIndex The filter row 168 * @deprecated Use {@link #moveDown(int...)} 169 */ 170 @Deprecated 171 public void moveDownFilter(int rowIndex) { 172 moveDown(rowIndex); 173 } 174 175 /** 176 * Moves up the filter in the given row 177 * @param rowIndex The filter row 178 * @deprecated Use {@link #moveUp(int...)} 179 */ 180 @Deprecated 181 public void moveUpFilter(int rowIndex) { 182 moveUp(rowIndex); 183 } 184 185 /** 186 * Removes the filter that is displayed in the given row 187 * @param rowIndex The index of the filter to remove 188 */ 189 public void removeFilter(int rowIndex) { 190 if (model.removeFilter(rowIndex) != null) { 191 savePrefs(); 192 updateFilters(); 193 fireTableRowsDeleted(rowIndex, rowIndex); 194 } 195 } 196 197 /** 198 * Sets/replaces the filter for a given row. 199 * @param rowIndex The row index 200 * @param filter The filter that should be placed in that row 201 * @deprecated Use {@link #setValue} 202 */ 203 @Deprecated 204 public void setFilter(int rowIndex, Filter filter) { 205 setValue(rowIndex, filter); 206 } 207 208 @Override 209 public Filter setValue(int rowIndex, Filter filter) { 210 Filter result = model.setValue(rowIndex, filter); 211 savePrefs(); 212 updateFilters(); 213 fireTableRowsUpdated(rowIndex, rowIndex); 214 return result; 215 } 216 217 /** 218 * Gets the filter by row index 219 * @param rowIndex The row index 220 * @return The filter in that row 221 * @deprecated Use {@link #getValue} 222 */ 223 @Deprecated 224 public Filter getFilter(int rowIndex) { 225 return getValue(rowIndex); 226 } 227 228 @Override 229 public Filter getValue(int rowIndex) { 230 return model.getValue(rowIndex); 231 } 232 233 @Override 234 public ListSelectionModel getSelectionModel() { 235 return selectionModel; 236 } 237 238 @Override 239 public int getRowCount() { 240 return model.getFiltersCount(); 241 } 242 243 @Override 244 public int getColumnCount() { 245 return 5; 246 } 247 248 @Override 249 public String getColumnName(int column) { 250 String[] names = {/* translators notes must be in front */ 251 /* column header: enable filter */trc("filter", "E"), 252 /* column header: hide filter */trc("filter", "H"), 253 /* column header: filter text */trc("filter", "Text"), 254 /* column header: inverted filter */trc("filter", "I"), 255 /* column header: filter mode */trc("filter", "M")}; 256 return names[column]; 257 } 258 259 @Override 260 public Class<?> getColumnClass(int column) { 261 Class<?>[] classes = {Boolean.class, Boolean.class, String.class, Boolean.class, String.class}; 262 return classes[column]; 263 } 264 265 /** 266 * Determines if a cell is enabled. 267 * @param row row index 268 * @param column column index 269 * @return {@code true} if the cell at (row, column) is enabled 270 */ 271 public boolean isCellEnabled(int row, int column) { 272 return model.getValue(row).enable || column == 0; 273 } 274 275 @Override 276 public boolean isCellEditable(int row, int column) { 277 return column < 4 && isCellEnabled(row, column); 278 } 279 280 @Override 281 public void setValueAt(Object aValue, int row, int column) { 282 if (row >= model.getFiltersCount()) { 283 return; 284 } 285 Filter f = model.getValue(row); 286 switch (column) { 287 case COL_ENABLED: 288 f.enable = (Boolean) aValue; 289 break; 290 case COL_HIDING: 291 f.hiding = (Boolean) aValue; 292 break; 293 case COL_TEXT: 294 f.text = (String) aValue; 295 break; 296 case COL_INVERTED: 297 f.inverted = (Boolean) aValue; 298 break; 299 default: // Do nothing 300 } 301 setValue(row, f); 302 } 303 304 @Override 305 public Object getValueAt(int row, int column) { 306 if (row >= model.getFiltersCount()) { 307 return null; 308 } 309 Filter f = model.getValue(row); 310 switch (column) { 311 case COL_ENABLED: 312 return f.enable; 313 case COL_HIDING: 314 return f.hiding; 315 case COL_TEXT: 316 return f.text; 317 case COL_INVERTED: 318 return f.inverted; 319 case 4: 320 switch (f.mode) { /* translators notes must be in front */ 321 case replace: /* filter mode: replace */ 322 return trc("filter", "R"); 323 case add: /* filter mode: add */ 324 return trc("filter", "A"); 325 case remove: /* filter mode: remove */ 326 return trc("filter", "D"); 327 case in_selection: /* filter mode: in selection */ 328 return trc("filter", "F"); 329 default: 330 Logging.warn("Unknown filter mode: " + f.mode); 331 } 332 break; 333 default: // Do nothing 334 } 335 return null; 336 } 337 338 /** 339 * Draws a text on the map display that indicates that filters are active. 340 * @param g The graphics to draw that text on. 341 */ 342 public void drawOSDText(Graphics2D g) { 343 model.drawOSDText(g, lblOSD, 344 tr("<h2>Filter active</h2>"), 345 tr("</p><p>Close the filter dialog to see all objects.<p></html>")); 346 } 347 348 /** 349 * Returns the list of filters. 350 * @return the list of filters 351 */ 352 public List<Filter> getFilters() { 353 return model.getFilters(); 354 } 355 356 @Override 357 public void sort() { 358 model.sort(); 359 fireTableDataChanged(); 360 } 361 362 @Override 363 public void reverse() { 364 model.reverse(); 365 fireTableDataChanged(); 366 } 367}