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.Color; 008import java.awt.Font; 009import java.awt.Graphics; 010import java.awt.Graphics2D; 011import java.util.ArrayList; 012import java.util.Collection; 013import java.util.HashSet; 014import java.util.LinkedList; 015import java.util.List; 016 017import javax.swing.BorderFactory; 018import javax.swing.JLabel; 019import javax.swing.JOptionPane; 020import javax.swing.table.AbstractTableModel; 021 022import org.openstreetmap.josm.Main; 023import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError; 024import org.openstreetmap.josm.data.osm.DataSet; 025import org.openstreetmap.josm.data.osm.Filter; 026import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry; 027import org.openstreetmap.josm.data.osm.FilterMatcher; 028import org.openstreetmap.josm.data.osm.FilterWorker; 029import org.openstreetmap.josm.data.osm.Node; 030import org.openstreetmap.josm.data.osm.OsmPrimitive; 031 032/** 033 * 034 * @author Petr_DlouhĂ˝ 035 */ 036public class FilterTableModel extends AbstractTableModel { 037 038 public static final int COL_ENABLED = 0; 039 public static final int COL_HIDING = 1; 040 public static final int COL_TEXT = 2; 041 public static final int COL_INVERTED = 3; 042 043 // number of primitives that are disabled but not hidden 044 public int disabledCount; 045 // number of primitives that are disabled and hidden 046 public int disabledAndHiddenCount; 047 048 /** 049 * Constructs a new {@code FilterTableModel}. 050 */ 051 public FilterTableModel() { 052 loadPrefs(); 053 } 054 055 private final List<Filter> filters = new LinkedList<Filter>(); 056 private final FilterMatcher filterMatcher = new FilterMatcher(); 057 058 private void updateFilters() { 059 try { 060 filterMatcher.update(filters); 061 executeFilters(); 062 } catch (ParseError e) { 063 JOptionPane.showMessageDialog( 064 Main.parent, 065 e.getMessage(), 066 tr("Error in filter"), 067 JOptionPane.ERROR_MESSAGE); 068 } 069 } 070 071 public void executeFilters() { 072 DataSet ds = Main.main.getCurrentDataSet(); 073 boolean changed = false; 074 if (ds == null) { 075 disabledAndHiddenCount = 0; 076 disabledCount = 0; 077 changed = true; 078 } else { 079 final Collection<OsmPrimitive> deselect = new HashSet<OsmPrimitive>(); 080 081 ds.beginUpdate(); 082 try { 083 084 final Collection<OsmPrimitive> all = ds.allNonDeletedCompletePrimitives(); 085 086 changed = FilterWorker.executeFilters(all, filterMatcher); 087 088 disabledCount = 0; 089 disabledAndHiddenCount = 0; 090 // collect disabled and selected the primitives 091 for (OsmPrimitive osm : all) { 092 if (osm.isDisabled()) { 093 disabledCount++; 094 if (osm.isSelected()) { 095 deselect.add(osm); 096 } 097 if (osm.isDisabledAndHidden()) { 098 disabledAndHiddenCount++; 099 } 100 } 101 } 102 disabledCount -= disabledAndHiddenCount; 103 } finally { 104 ds.endUpdate(); 105 } 106 107 if (!deselect.isEmpty()) { 108 ds.clearSelection(deselect); 109 } 110 } 111 112 if (Main.isDisplayingMapView() && changed) { 113 Main.map.mapView.repaint(); 114 Main.map.filterDialog.updateDialogHeader(); 115 } 116 } 117 118 public void executeFilters(Collection<? extends OsmPrimitive> primitives) { 119 DataSet ds = Main.main.getCurrentDataSet(); 120 if (ds == null) 121 return; 122 123 boolean changed = false; 124 List<OsmPrimitive> deselect = new ArrayList<OsmPrimitive>(); 125 126 ds.beginUpdate(); 127 try { 128 for (int i=0; i<2; i++) { 129 for (OsmPrimitive primitive: primitives) { 130 131 if (i == 0 && primitive instanceof Node) { 132 continue; 133 } 134 135 if (i == 1 && !(primitive instanceof Node)) { 136 continue; 137 } 138 139 if (primitive.isDisabled()) { 140 disabledCount--; 141 } 142 if (primitive.isDisabledAndHidden()) { 143 disabledAndHiddenCount--; 144 } 145 changed = changed | FilterWorker.executeFilters(primitive, filterMatcher); 146 if (primitive.isDisabled()) { 147 disabledCount++; 148 } 149 if (primitive.isDisabledAndHidden()) { 150 disabledAndHiddenCount++; 151 } 152 153 if (primitive.isSelected() && primitive.isDisabled()) { 154 deselect.add(primitive); 155 } 156 157 } 158 } 159 } finally { 160 ds.endUpdate(); 161 } 162 163 if (changed) { 164 Main.map.mapView.repaint(); 165 Main.map.filterDialog.updateDialogHeader(); 166 ds.clearSelection(deselect); 167 } 168 169 } 170 171 public void clearFilterFlags() { 172 DataSet ds = Main.main.getCurrentDataSet(); 173 if (ds != null) { 174 FilterWorker.clearFilterFlags(ds.allPrimitives()); 175 } 176 disabledCount = 0; 177 disabledAndHiddenCount = 0; 178 } 179 180 private void loadPrefs() { 181 List<FilterPreferenceEntry> entries = Main.pref.getListOfStructs("filters.entries", null, FilterPreferenceEntry.class); 182 if (entries != null) { 183 for (FilterPreferenceEntry e : entries) { 184 filters.add(new Filter(e)); 185 } 186 updateFilters(); 187 } 188 } 189 190 private void savePrefs() { 191 Collection<FilterPreferenceEntry> entries = new ArrayList<FilterPreferenceEntry>(); 192 for (Filter flt : filters) { 193 entries.add(flt.getPreferenceEntry()); 194 } 195 Main.pref.putListOfStructs("filters.entries", entries, FilterPreferenceEntry.class); 196 } 197 198 public void addFilter(Filter f) { 199 filters.add(f); 200 savePrefs(); 201 updateFilters(); 202 fireTableRowsInserted(filters.size() - 1, filters.size() - 1); 203 } 204 205 public void moveDownFilter(int i) { 206 if (i >= filters.size() - 1) 207 return; 208 filters.add(i + 1, filters.remove(i)); 209 savePrefs(); 210 updateFilters(); 211 fireTableRowsUpdated(i, i + 1); 212 } 213 214 public void moveUpFilter(int i) { 215 if (i == 0) 216 return; 217 filters.add(i - 1, filters.remove(i)); 218 savePrefs(); 219 updateFilters(); 220 fireTableRowsUpdated(i - 1, i); 221 } 222 223 public void removeFilter(int i) { 224 filters.remove(i); 225 savePrefs(); 226 updateFilters(); 227 fireTableRowsDeleted(i, i); 228 } 229 230 public void setFilter(int i, Filter f) { 231 filters.set(i, f); 232 savePrefs(); 233 updateFilters(); 234 fireTableRowsUpdated(i, i); 235 } 236 237 public Filter getFilter(int i) { 238 return filters.get(i); 239 } 240 241 @Override 242 public int getRowCount() { 243 return filters.size(); 244 } 245 246 @Override 247 public int getColumnCount() { 248 return 5; 249 } 250 251 @Override 252 public String getColumnName(int column) { 253 String[] names = { /* translators notes must be in front */ 254 /* column header: enable filter */trc("filter", "E"), 255 /* column header: hide filter */trc("filter", "H"), 256 /* column header: filter text */trc("filter", "Text"), 257 /* column header: inverted filter */trc("filter", "I"), 258 /* column header: filter mode */trc("filter", "M") }; 259 return names[column]; 260 } 261 262 @Override 263 public Class<?> getColumnClass(int column) { 264 Class<?>[] classes = { Boolean.class, Boolean.class, String.class, Boolean.class, String.class }; 265 return classes[column]; 266 } 267 268 public boolean isCellEnabled(int row, int column) { 269 if (!filters.get(row).enable && column != 0) 270 return false; 271 return true; 272 } 273 274 @Override 275 public boolean isCellEditable(int row, int column) { 276 if (!filters.get(row).enable && column != 0) 277 return false; 278 if (column < 4) 279 return true; 280 return false; 281 } 282 283 @Override 284 public void setValueAt(Object aValue, int row, int column) { 285 if (row >= filters.size()) { 286 return; 287 } 288 Filter f = filters.get(row); 289 switch (column) { 290 case COL_ENABLED: 291 f.enable = (Boolean) aValue; 292 savePrefs(); 293 updateFilters(); 294 fireTableRowsUpdated(row, row); 295 break; 296 case COL_HIDING: 297 f.hiding = (Boolean) aValue; 298 savePrefs(); 299 updateFilters(); 300 break; 301 case COL_TEXT: 302 f.text = (String) aValue; 303 savePrefs(); 304 break; 305 case COL_INVERTED: 306 f.inverted = (Boolean) aValue; 307 savePrefs(); 308 updateFilters(); 309 break; 310 } 311 if (column != 0) { 312 fireTableCellUpdated(row, column); 313 } 314 } 315 316 @Override 317 public Object getValueAt(int row, int column) { 318 if (row >= filters.size()) { 319 return null; 320 } 321 Filter f = filters.get(row); 322 switch (column) { 323 case COL_ENABLED: 324 return f.enable; 325 case COL_HIDING: 326 return f.hiding; 327 case COL_TEXT: 328 return f.text; 329 case COL_INVERTED: 330 return f.inverted; 331 case 4: 332 switch (f.mode) { /* translators notes must be in front */ 333 case replace: /* filter mode: replace */ 334 return trc("filter", "R"); 335 case add: /* filter mode: add */ 336 return trc("filter", "A"); 337 case remove: /* filter mode: remove */ 338 return trc("filter", "D"); 339 case in_selection: /* filter mode: in selection */ 340 return trc("filter", "F"); 341 } 342 } 343 return null; 344 } 345 346 /** 347 * On screen display label 348 */ 349 private static class OSDLabel extends JLabel { 350 public OSDLabel(String text) { 351 super(text); 352 setOpaque(true); 353 setForeground(Color.black); 354 setBackground(new Color(0, 0, 0, 0)); 355 setFont(getFont().deriveFont(Font.PLAIN)); 356 setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); 357 } 358 359 @Override 360 public void paintComponent(Graphics g) { 361 g.setColor(new Color(255, 255, 255, 140)); 362 g.fillRoundRect(getX(), getY(), getWidth(), getHeight(), 10, 10); 363 super.paintComponent(g); 364 } 365 } 366 367 private OSDLabel lblOSD = new OSDLabel(""); 368 369 public void drawOSDText(Graphics2D g) { 370 String message = "<html>" + tr("<h2>Filter active</h2>"); 371 372 if (disabledCount == 0 && disabledAndHiddenCount == 0) 373 return; 374 375 if (disabledAndHiddenCount != 0) { 376 message += tr("<p><b>{0}</b> objects hidden", disabledAndHiddenCount); 377 } 378 379 if (disabledAndHiddenCount != 0 && disabledCount != 0) { 380 message += "<br>"; 381 } 382 383 if (disabledCount != 0) { 384 message += tr("<b>{0}</b> objects disabled", disabledCount); 385 } 386 387 message += tr("</p><p>Close the filter dialog to see all objects.<p></html>"); 388 389 lblOSD.setText(message); 390 lblOSD.setSize(lblOSD.getPreferredSize()); 391 392 int dx = Main.map.mapView.getWidth() - lblOSD.getPreferredSize().width - 15; 393 int dy = 15; 394 g.translate(dx, dy); 395 lblOSD.paintComponent(g); 396 g.translate(-dx, -dy); 397 } 398 399 public List<Filter> getFilters() { 400 return filters; 401 } 402}