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 }