[3719] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[3356] | 2 | package org.openstreetmap.josm.gui.dialogs;
|
---|
[2125] | 3 |
|
---|
[3178] | 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
[2179] | 5 | import static org.openstreetmap.josm.tools.I18n.trc;
|
---|
[2125] | 6 |
|
---|
[3178] | 7 | import java.awt.Color;
|
---|
| 8 | import java.awt.Font;
|
---|
| 9 | import java.awt.Graphics;
|
---|
| 10 | import java.awt.Graphics2D;
|
---|
[3371] | 11 | import java.util.ArrayList;
|
---|
[2620] | 12 | import java.util.Collection;
|
---|
[3300] | 13 | import java.util.HashSet;
|
---|
[2125] | 14 | import java.util.LinkedList;
|
---|
| 15 | import java.util.List;
|
---|
| 16 |
|
---|
[3178] | 17 | import javax.swing.BorderFactory;
|
---|
| 18 | import javax.swing.JLabel;
|
---|
[3355] | 19 | import javax.swing.JOptionPane;
|
---|
[2620] | 20 | import javax.swing.table.AbstractTableModel;
|
---|
| 21 |
|
---|
[2125] | 22 | import org.openstreetmap.josm.Main;
|
---|
[3355] | 23 | import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
|
---|
[3356] | 24 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
| 25 | import org.openstreetmap.josm.data.osm.Filter;
|
---|
[3908] | 26 | import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry;
|
---|
[3356] | 27 | import org.openstreetmap.josm.data.osm.FilterMatcher;
|
---|
| 28 | import org.openstreetmap.josm.data.osm.FilterWorker;
|
---|
[3371] | 29 | import org.openstreetmap.josm.data.osm.Node;
|
---|
[3356] | 30 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
[2125] | 31 |
|
---|
| 32 | /**
|
---|
| 33 | *
|
---|
| 34 | * @author Petr_Dlouhý
|
---|
| 35 | */
|
---|
[3356] | 36 | public class FilterTableModel extends AbstractTableModel {
|
---|
[2125] | 37 |
|
---|
[4615] | 38 | public static final int COL_ENABLED = 0;
|
---|
| 39 | public static final int COL_HIDING = 1;
|
---|
| 40 | public static final int COL_TEXT = 2;
|
---|
| 41 | public static final int COL_INVERTED = 3;
|
---|
| 42 |
|
---|
[3300] | 43 | // number of primitives that are disabled but not hidden
|
---|
| 44 | public int disabledCount;
|
---|
| 45 | // number of primitives that are disabled and hidden
|
---|
| 46 | public int disabledAndHiddenCount;
|
---|
[2145] | 47 |
|
---|
[3356] | 48 | public FilterTableModel() {
|
---|
[2620] | 49 | loadPrefs();
|
---|
| 50 | }
|
---|
[2125] | 51 |
|
---|
[3355] | 52 | private final List<Filter> filters = new LinkedList<Filter>();
|
---|
| 53 | private final FilterMatcher filterMatcher = new FilterMatcher();
|
---|
[3178] | 54 |
|
---|
[3355] | 55 | private void updateFilters() {
|
---|
| 56 | try {
|
---|
| 57 | filterMatcher.update(filters);
|
---|
| 58 | executeFilters();
|
---|
| 59 | } catch (ParseError e) {
|
---|
| 60 | JOptionPane.showMessageDialog(
|
---|
| 61 | Main.parent,
|
---|
| 62 | e.getMessage(),
|
---|
| 63 | tr("Error in filter"),
|
---|
| 64 | JOptionPane.ERROR_MESSAGE);
|
---|
| 65 | }
|
---|
| 66 | }
|
---|
| 67 |
|
---|
[3346] | 68 | public void executeFilters() {
|
---|
[3199] | 69 | DataSet ds = Main.main.getCurrentDataSet();
|
---|
[3471] | 70 | boolean changed = false;
|
---|
[3393] | 71 | if (ds == null) {
|
---|
| 72 | disabledAndHiddenCount = 0;
|
---|
| 73 | disabledCount = 0;
|
---|
[3471] | 74 | changed = true;
|
---|
[3393] | 75 | } else {
|
---|
| 76 | final Collection<OsmPrimitive> deselect = new HashSet<OsmPrimitive>();
|
---|
[3300] | 77 |
|
---|
[3393] | 78 | ds.beginUpdate();
|
---|
| 79 | try {
|
---|
[3300] | 80 |
|
---|
[3393] | 81 | final Collection<OsmPrimitive> all = ds.allNonDeletedCompletePrimitives();
|
---|
[3300] | 82 |
|
---|
[3471] | 83 | changed = FilterWorker.executeFilters(all, filterMatcher);
|
---|
[3371] | 84 |
|
---|
[3393] | 85 | disabledCount = 0;
|
---|
| 86 | disabledAndHiddenCount = 0;
|
---|
| 87 | // collect disabled and selected the primitives
|
---|
| 88 | for (OsmPrimitive osm : all) {
|
---|
| 89 | if (osm.isDisabled()) {
|
---|
| 90 | disabledCount++;
|
---|
| 91 | if (osm.isSelected()) {
|
---|
| 92 | deselect.add(osm);
|
---|
| 93 | }
|
---|
| 94 | if (osm.isDisabledAndHidden()) {
|
---|
| 95 | disabledAndHiddenCount++;
|
---|
| 96 | }
|
---|
[3371] | 97 | }
|
---|
[3300] | 98 | }
|
---|
[3393] | 99 | disabledCount -= disabledAndHiddenCount;
|
---|
| 100 | } finally {
|
---|
| 101 | ds.endUpdate();
|
---|
[3300] | 102 | }
|
---|
[3393] | 103 |
|
---|
| 104 | if (!deselect.isEmpty()) {
|
---|
| 105 | ds.clearSelection(deselect);
|
---|
| 106 | }
|
---|
[3354] | 107 | }
|
---|
[3371] | 108 |
|
---|
[3471] | 109 | if (Main.isDisplayingMapView() && changed) {
|
---|
[3393] | 110 | Main.map.mapView.repaint();
|
---|
| 111 | Main.map.filterDialog.updateDialogHeader();
|
---|
[3354] | 112 | }
|
---|
[2620] | 113 | }
|
---|
[2125] | 114 |
|
---|
[3371] | 115 | public void executeFilters(Collection<? extends OsmPrimitive> primitives) {
|
---|
| 116 | DataSet ds = Main.main.getCurrentDataSet();
|
---|
| 117 | if (ds == null)
|
---|
| 118 | return;
|
---|
| 119 |
|
---|
| 120 | boolean changed = false;
|
---|
| 121 | List<OsmPrimitive> deselect = new ArrayList<OsmPrimitive>();
|
---|
| 122 |
|
---|
| 123 | ds.beginUpdate();
|
---|
| 124 | try {
|
---|
| 125 | for (int i=0; i<2; i++) {
|
---|
| 126 | for (OsmPrimitive primitive: primitives) {
|
---|
| 127 |
|
---|
| 128 | if (i == 0 && primitive instanceof Node) {
|
---|
| 129 | continue;
|
---|
| 130 | }
|
---|
| 131 |
|
---|
| 132 | if (i == 1 && !(primitive instanceof Node)) {
|
---|
| 133 | continue;
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | if (primitive.isDisabled()) {
|
---|
| 137 | disabledCount--;
|
---|
| 138 | }
|
---|
| 139 | if (primitive.isDisabledAndHidden()) {
|
---|
| 140 | disabledAndHiddenCount--;
|
---|
| 141 | }
|
---|
| 142 | changed = changed | FilterWorker.executeFilters(primitive, filterMatcher);
|
---|
| 143 | if (primitive.isDisabled()) {
|
---|
| 144 | disabledCount++;
|
---|
| 145 | }
|
---|
| 146 | if (primitive.isDisabledAndHidden()) {
|
---|
| 147 | disabledAndHiddenCount++;
|
---|
| 148 | }
|
---|
| 149 |
|
---|
| 150 | if (primitive.isSelected() && primitive.isDisabled()) {
|
---|
| 151 | deselect.add(primitive);
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | }
|
---|
| 155 | }
|
---|
| 156 | } finally {
|
---|
| 157 | ds.endUpdate();
|
---|
| 158 | }
|
---|
| 159 |
|
---|
| 160 | if (changed) {
|
---|
| 161 | Main.map.mapView.repaint();
|
---|
| 162 | Main.map.filterDialog.updateDialogHeader();
|
---|
| 163 | ds.clearSelection(deselect);
|
---|
| 164 | }
|
---|
| 165 |
|
---|
| 166 | }
|
---|
| 167 |
|
---|
[3178] | 168 | public void clearFilterFlags() {
|
---|
| 169 | DataSet ds = Main.main.getCurrentDataSet();
|
---|
| 170 | if (ds != null) {
|
---|
[3356] | 171 | FilterWorker.clearFilterFlags(ds.allPrimitives());
|
---|
[3178] | 172 | }
|
---|
| 173 | disabledCount = 0;
|
---|
[3300] | 174 | disabledAndHiddenCount = 0;
|
---|
[3178] | 175 | }
|
---|
| 176 |
|
---|
| 177 | private void loadPrefs() {
|
---|
[3908] | 178 | List<FilterPreferenceEntry> entries = Main.pref.getListOfStructs("filters.entries", null, FilterPreferenceEntry.class);
|
---|
[4896] | 179 | if (entries != null) {
|
---|
| 180 | for (FilterPreferenceEntry e : entries) {
|
---|
| 181 | filters.add(new Filter(e));
|
---|
| 182 | }
|
---|
| 183 | updateFilters();
|
---|
[3908] | 184 | }
|
---|
| 185 | }
|
---|
| 186 |
|
---|
[3355] | 187 | private void savePrefs() {
|
---|
[3908] | 188 | Collection<FilterPreferenceEntry> entries = new ArrayList<FilterPreferenceEntry>();
|
---|
[3355] | 189 | for (Filter flt : filters) {
|
---|
[3908] | 190 | entries.add(flt.getPreferenceEntry());
|
---|
[2620] | 191 | }
|
---|
[3908] | 192 | Main.pref.putListOfStructs("filters.entries", entries, FilterPreferenceEntry.class);
|
---|
[2620] | 193 | }
|
---|
[2125] | 194 |
|
---|
[3355] | 195 | public void addFilter(Filter f) {
|
---|
[2620] | 196 | filters.add(f);
|
---|
[3908] | 197 | savePrefs();
|
---|
[3355] | 198 | updateFilters();
|
---|
| 199 | fireTableRowsInserted(filters.size() - 1, filters.size() - 1);
|
---|
[2620] | 200 | }
|
---|
[2125] | 201 |
|
---|
[3355] | 202 | public void moveDownFilter(int i) {
|
---|
| 203 | if (i >= filters.size() - 1)
|
---|
| 204 | return;
|
---|
| 205 | filters.add(i + 1, filters.remove(i));
|
---|
[3908] | 206 | savePrefs();
|
---|
[3355] | 207 | updateFilters();
|
---|
| 208 | fireTableRowsUpdated(i, i + 1);
|
---|
[2620] | 209 | }
|
---|
[2125] | 210 |
|
---|
[3355] | 211 | public void moveUpFilter(int i) {
|
---|
| 212 | if (i == 0)
|
---|
| 213 | return;
|
---|
| 214 | filters.add(i - 1, filters.remove(i));
|
---|
[3908] | 215 | savePrefs();
|
---|
[3355] | 216 | updateFilters();
|
---|
| 217 | fireTableRowsUpdated(i - 1, i);
|
---|
[2620] | 218 | }
|
---|
[2125] | 219 |
|
---|
[3355] | 220 | public void removeFilter(int i) {
|
---|
[2620] | 221 | filters.remove(i);
|
---|
| 222 | savePrefs();
|
---|
[3355] | 223 | updateFilters();
|
---|
[2620] | 224 | fireTableRowsDeleted(i, i);
|
---|
| 225 | }
|
---|
[2125] | 226 |
|
---|
[3355] | 227 | public void setFilter(int i, Filter f) {
|
---|
[2620] | 228 | filters.set(i, f);
|
---|
[3908] | 229 | savePrefs();
|
---|
[3355] | 230 | updateFilters();
|
---|
[2620] | 231 | fireTableRowsUpdated(i, i);
|
---|
| 232 | }
|
---|
[2125] | 233 |
|
---|
[3355] | 234 | public Filter getFilter(int i) {
|
---|
[2620] | 235 | return filters.get(i);
|
---|
| 236 | }
|
---|
[2125] | 237 |
|
---|
[6084] | 238 | @Override
|
---|
[3355] | 239 | public int getRowCount() {
|
---|
[2620] | 240 | return filters.size();
|
---|
| 241 | }
|
---|
[2125] | 242 |
|
---|
[6084] | 243 | @Override
|
---|
[3355] | 244 | public int getColumnCount() {
|
---|
[3300] | 245 | return 5;
|
---|
[2620] | 246 | }
|
---|
[2125] | 247 |
|
---|
[2620] | 248 | @Override
|
---|
[3355] | 249 | public String getColumnName(int column) {
|
---|
[2620] | 250 | String[] names = { /* translators notes must be in front */
|
---|
[3355] | 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") };
|
---|
[2620] | 256 | return names[column];
|
---|
| 257 | }
|
---|
[2125] | 258 |
|
---|
[2620] | 259 | @Override
|
---|
[3355] | 260 | public Class<?> getColumnClass(int column) {
|
---|
[3300] | 261 | Class<?>[] classes = { Boolean.class, Boolean.class, String.class, Boolean.class, String.class };
|
---|
[2620] | 262 | return classes[column];
|
---|
| 263 | }
|
---|
[2125] | 264 |
|
---|
[3355] | 265 | public boolean isCellEnabled(int row, int column) {
|
---|
| 266 | if (!filters.get(row).enable && column != 0)
|
---|
| 267 | return false;
|
---|
[2620] | 268 | return true;
|
---|
| 269 | }
|
---|
[2145] | 270 |
|
---|
[2620] | 271 | @Override
|
---|
[3355] | 272 | public boolean isCellEditable(int row, int column) {
|
---|
| 273 | if (!filters.get(row).enable && column != 0)
|
---|
| 274 | return false;
|
---|
| 275 | if (column < 4)
|
---|
| 276 | return true;
|
---|
[2620] | 277 | return false;
|
---|
| 278 | }
|
---|
[2125] | 279 |
|
---|
[2620] | 280 | @Override
|
---|
[3355] | 281 | public void setValueAt(Object aValue, int row, int column) {
|
---|
[2620] | 282 | Filter f = filters.get(row);
|
---|
[3355] | 283 | switch (column) {
|
---|
[4615] | 284 | case COL_ENABLED:
|
---|
[3355] | 285 | f.enable = (Boolean) aValue;
|
---|
[3908] | 286 | savePrefs();
|
---|
[3355] | 287 | updateFilters();
|
---|
[3178] | 288 | fireTableRowsUpdated(row, row);
|
---|
| 289 | break;
|
---|
[4615] | 290 | case COL_HIDING:
|
---|
[3355] | 291 | f.hiding = (Boolean) aValue;
|
---|
[3908] | 292 | savePrefs();
|
---|
[3355] | 293 | updateFilters();
|
---|
[3178] | 294 | break;
|
---|
[4615] | 295 | case COL_TEXT:
|
---|
[3355] | 296 | f.text = (String) aValue;
|
---|
[3908] | 297 | savePrefs();
|
---|
[3178] | 298 | break;
|
---|
[4615] | 299 | case COL_INVERTED:
|
---|
[3355] | 300 | f.inverted = (Boolean) aValue;
|
---|
[3908] | 301 | savePrefs();
|
---|
[3355] | 302 | updateFilters();
|
---|
[3178] | 303 | break;
|
---|
[2620] | 304 | }
|
---|
[3355] | 305 | if (column != 0) {
|
---|
[2620] | 306 | fireTableCellUpdated(row, column);
|
---|
| 307 | }
|
---|
| 308 | }
|
---|
[2125] | 309 |
|
---|
[6084] | 310 | @Override
|
---|
[3355] | 311 | public Object getValueAt(int row, int column) {
|
---|
[2620] | 312 | Filter f = filters.get(row);
|
---|
[3355] | 313 | switch (column) {
|
---|
[4615] | 314 | case COL_ENABLED:
|
---|
[3355] | 315 | return f.enable;
|
---|
[4615] | 316 | case COL_HIDING:
|
---|
[3355] | 317 | return f.hiding;
|
---|
[4615] | 318 | case COL_TEXT:
|
---|
[3355] | 319 | return f.text;
|
---|
[4615] | 320 | case COL_INVERTED:
|
---|
[3355] | 321 | return f.inverted;
|
---|
[3300] | 322 | case 4:
|
---|
[3355] | 323 | switch (f.mode) { /* translators notes must be in front */
|
---|
| 324 | case replace: /* filter mode: replace */
|
---|
| 325 | return trc("filter", "R");
|
---|
| 326 | case add: /* filter mode: add */
|
---|
| 327 | return trc("filter", "A");
|
---|
| 328 | case remove: /* filter mode: remove */
|
---|
| 329 | return trc("filter", "D");
|
---|
| 330 | case in_selection: /* filter mode: in selection */
|
---|
| 331 | return trc("filter", "F");
|
---|
[2620] | 332 | }
|
---|
| 333 | }
|
---|
| 334 | return null;
|
---|
| 335 | }
|
---|
[3178] | 336 |
|
---|
[3300] | 337 | /**
|
---|
| 338 | * On screen display label
|
---|
| 339 | */
|
---|
[3178] | 340 | private static class OSDLabel extends JLabel {
|
---|
| 341 | public OSDLabel(String text) {
|
---|
| 342 | super(text);
|
---|
| 343 | setOpaque(true);
|
---|
| 344 | setForeground(Color.black);
|
---|
[3355] | 345 | setBackground(new Color(0, 0, 0, 0));
|
---|
[3178] | 346 | setFont(getFont().deriveFont(Font.PLAIN));
|
---|
| 347 | setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
---|
| 348 | }
|
---|
| 349 |
|
---|
| 350 | @Override
|
---|
| 351 | public void paintComponent(Graphics g) {
|
---|
| 352 | g.setColor(new Color(255, 255, 255, 140));
|
---|
| 353 | g.fillRoundRect(getX(), getY(), getWidth(), getHeight(), 10, 10);
|
---|
| 354 | super.paintComponent(g);
|
---|
| 355 | }
|
---|
| 356 | }
|
---|
| 357 |
|
---|
| 358 | private OSDLabel lblOSD = new OSDLabel("");
|
---|
| 359 |
|
---|
| 360 | public void drawOSDText(Graphics2D g) {
|
---|
[3355] | 361 | String message = "<html>" + tr("<h2>Filter active</h2>");
|
---|
[3178] | 362 |
|
---|
[3300] | 363 | if (disabledCount == 0 && disabledAndHiddenCount == 0)
|
---|
[3178] | 364 | return;
|
---|
| 365 |
|
---|
[3300] | 366 | if (disabledAndHiddenCount != 0) {
|
---|
| 367 | message += tr("<p><b>{0}</b> objects hidden", disabledAndHiddenCount);
|
---|
[3178] | 368 | }
|
---|
| 369 |
|
---|
[3300] | 370 | if (disabledAndHiddenCount != 0 && disabledCount != 0) {
|
---|
[3178] | 371 | message += "<br>";
|
---|
| 372 | }
|
---|
| 373 |
|
---|
| 374 | if (disabledCount != 0) {
|
---|
| 375 | message += tr("<b>{0}</b> objects disabled", disabledCount);
|
---|
| 376 | }
|
---|
| 377 |
|
---|
| 378 | message += tr("</p><p>Close the filter dialog to see all objects.<p></html>");
|
---|
| 379 |
|
---|
| 380 | lblOSD.setText(message);
|
---|
| 381 | lblOSD.setSize(lblOSD.getPreferredSize());
|
---|
| 382 |
|
---|
| 383 | int dx = Main.map.mapView.getWidth() - lblOSD.getPreferredSize().width - 15;
|
---|
| 384 | int dy = 15;
|
---|
| 385 | g.translate(dx, dy);
|
---|
| 386 | lblOSD.paintComponent(g);
|
---|
| 387 | g.translate(-dx, -dy);
|
---|
| 388 | }
|
---|
[4615] | 389 |
|
---|
| 390 | public List<Filter> getFilters() {
|
---|
| 391 | return filters;
|
---|
| 392 | }
|
---|
[2125] | 393 | }
|
---|