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