source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java@ 11366

Last change on this file since 11366 was 11366, checked in by Don-vip, 8 years ago

findbugs - SIC_INNER_SHOULD_BE_STATIC_ANON

  • Property svn:eol-style set to native
File size: 17.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.Graphics2D;
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
10import java.awt.event.MouseEvent;
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Set;
17import java.util.Stack;
18
19import javax.swing.AbstractAction;
20import javax.swing.JCheckBox;
21import javax.swing.JTable;
22import javax.swing.ListSelectionModel;
23import javax.swing.SwingUtilities;
24import javax.swing.table.DefaultTableCellRenderer;
25import javax.swing.table.JTableHeader;
26import javax.swing.table.TableCellRenderer;
27import javax.swing.table.TableColumnModel;
28import javax.swing.table.TableModel;
29
30import org.openstreetmap.josm.Main;
31import org.openstreetmap.josm.actions.search.SearchAction;
32import org.openstreetmap.josm.data.osm.Filter;
33import org.openstreetmap.josm.data.osm.OsmPrimitive;
34import org.openstreetmap.josm.data.osm.Relation;
35import org.openstreetmap.josm.data.osm.RelationMember;
36import org.openstreetmap.josm.data.osm.Way;
37import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
38import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
39import org.openstreetmap.josm.data.osm.event.DataSetListener;
40import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
41import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
42import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
43import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
44import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
45import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
46import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
47import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
48import org.openstreetmap.josm.gui.SideButton;
49import org.openstreetmap.josm.tools.ImageProvider;
50import org.openstreetmap.josm.tools.InputMapUtils;
51import org.openstreetmap.josm.tools.MultikeyActionsHandler;
52import org.openstreetmap.josm.tools.MultikeyShortcutAction;
53import org.openstreetmap.josm.tools.Shortcut;
54
55/**
56 *
57 * @author Petr_Dlouhý
58 */
59public class FilterDialog extends ToggleDialog implements DataSetListener {
60
61 private JTable userTable;
62 private final FilterTableModel filterModel = new FilterTableModel();
63
64 private final EnableFilterAction enableFilterAction;
65 private final HidingFilterAction hidingFilterAction;
66
67 /**
68 * Constructs a new {@code FilterDialog}
69 */
70 public FilterDialog() {
71 super(tr("Filter"), "filter", tr("Filter objects and hide/disable them."),
72 Shortcut.registerShortcut("subwindow:filter", tr("Toggle: {0}", tr("Filter")),
73 KeyEvent.VK_F, Shortcut.ALT_SHIFT), 162);
74 build();
75 enableFilterAction = new EnableFilterAction();
76 hidingFilterAction = new HidingFilterAction();
77 MultikeyActionsHandler.getInstance().addAction(enableFilterAction);
78 MultikeyActionsHandler.getInstance().addAction(hidingFilterAction);
79 }
80
81 @Override
82 public void showNotify() {
83 DatasetEventManager.getInstance().addDatasetListener(this, FireMode.IN_EDT_CONSOLIDATED);
84 filterModel.executeFilters();
85 }
86
87 @Override
88 public void hideNotify() {
89 DatasetEventManager.getInstance().removeDatasetListener(this);
90 filterModel.clearFilterFlags();
91 Main.map.mapView.repaint();
92 }
93
94 private static final Shortcut ENABLE_FILTER_SHORTCUT
95 = Shortcut.registerShortcut("core_multikey:enableFilter", tr("Multikey: {0}", tr("Enable filter")),
96 KeyEvent.VK_E, Shortcut.ALT_CTRL);
97
98 private static final Shortcut HIDING_FILTER_SHORTCUT
99 = Shortcut.registerShortcut("core_multikey:hidingFilter", tr("Multikey: {0}", tr("Hide filter")),
100 KeyEvent.VK_H, Shortcut.ALT_CTRL);
101
102 private static final String[] COLUMN_TOOLTIPS = {
103 Main.platform.makeTooltip(tr("Enable filter"), ENABLE_FILTER_SHORTCUT),
104 Main.platform.makeTooltip(tr("Hiding filter"), HIDING_FILTER_SHORTCUT),
105 null,
106 tr("Inverse filter"),
107 tr("Filter mode")
108 };
109
110 protected void build() {
111 userTable = new UserTable(filterModel);
112
113 userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
114
115 userTable.getColumnModel().getColumn(0).setMaxWidth(1);
116 userTable.getColumnModel().getColumn(1).setMaxWidth(1);
117 userTable.getColumnModel().getColumn(3).setMaxWidth(1);
118 userTable.getColumnModel().getColumn(4).setMaxWidth(1);
119
120 userTable.getColumnModel().getColumn(0).setResizable(false);
121 userTable.getColumnModel().getColumn(1).setResizable(false);
122 userTable.getColumnModel().getColumn(3).setResizable(false);
123 userTable.getColumnModel().getColumn(4).setResizable(false);
124
125 userTable.setDefaultRenderer(Boolean.class, new BooleanRenderer());
126 userTable.setDefaultRenderer(String.class, new StringRenderer());
127
128 SideButton addButton = new SideButton(new AbstractAction() {
129 {
130 putValue(NAME, tr("Add"));
131 putValue(SHORT_DESCRIPTION, tr("Add filter."));
132 new ImageProvider("dialogs", "add").getResource().attachImageIcon(this, true);
133 }
134
135 @Override
136 public void actionPerformed(ActionEvent e) {
137 Filter filter = (Filter) SearchAction.showSearchDialog(new Filter());
138 if (filter != null) {
139 filterModel.addFilter(filter);
140 }
141 }
142 });
143 SideButton editButton = new SideButton(new AbstractAction() {
144 {
145 putValue(NAME, tr("Edit"));
146 putValue(SHORT_DESCRIPTION, tr("Edit filter."));
147 new ImageProvider("dialogs", "edit").getResource().attachImageIcon(this, true);
148 }
149
150 @Override
151 public void actionPerformed(ActionEvent e) {
152 int index = userTable.getSelectionModel().getMinSelectionIndex();
153 if (index < 0) return;
154 Filter f = filterModel.getFilter(index);
155 Filter filter = (Filter) SearchAction.showSearchDialog(f);
156 if (filter != null) {
157 filterModel.setFilter(index, filter);
158 }
159 }
160 });
161 SideButton deleteButton = new SideButton(new AbstractAction() {
162 {
163 putValue(NAME, tr("Delete"));
164 putValue(SHORT_DESCRIPTION, tr("Delete filter."));
165 new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this, true);
166 }
167
168 @Override
169 public void actionPerformed(ActionEvent e) {
170 int index = userTable.getSelectionModel().getMinSelectionIndex();
171 if (index >= 0) {
172 filterModel.removeFilter(index);
173 }
174 }
175 });
176 SideButton upButton = new SideButton(new AbstractAction() {
177 {
178 putValue(NAME, tr("Up"));
179 putValue(SHORT_DESCRIPTION, tr("Move filter up."));
180 new ImageProvider("dialogs", "up").getResource().attachImageIcon(this, true);
181 }
182
183 @Override
184 public void actionPerformed(ActionEvent e) {
185 int index = userTable.getSelectionModel().getMinSelectionIndex();
186 if (index >= 0) {
187 filterModel.moveUpFilter(index);
188 userTable.getSelectionModel().setSelectionInterval(index-1, index-1);
189 }
190 }
191 });
192 SideButton downButton = new SideButton(new AbstractAction() {
193 {
194 putValue(NAME, tr("Down"));
195 putValue(SHORT_DESCRIPTION, tr("Move filter down."));
196 new ImageProvider("dialogs", "down").getResource().attachImageIcon(this, true);
197 }
198
199 @Override
200 public void actionPerformed(ActionEvent e) {
201 int index = userTable.getSelectionModel().getMinSelectionIndex();
202 if (index >= 0) {
203 filterModel.moveDownFilter(index);
204 userTable.getSelectionModel().setSelectionInterval(index+1, index+1);
205 }
206 }
207 });
208
209 // Toggle filter "enabled" on Enter
210 InputMapUtils.addEnterAction(userTable, new AbstractAction() {
211 @Override
212 public void actionPerformed(ActionEvent e) {
213 int index = userTable.getSelectedRow();
214 if (index >= 0) {
215 Filter filter = filterModel.getFilter(index);
216 filterModel.setValueAt(!filter.enable, index, FilterTableModel.COL_ENABLED);
217 }
218 }
219 });
220
221 // Toggle filter "hiding" on Spacebar
222 InputMapUtils.addSpacebarAction(userTable, new AbstractAction() {
223 @Override
224 public void actionPerformed(ActionEvent e) {
225 int index = userTable.getSelectedRow();
226 if (index >= 0) {
227 Filter filter = filterModel.getFilter(index);
228 filterModel.setValueAt(!filter.hiding, index, FilterTableModel.COL_HIDING);
229 }
230 }
231 });
232
233 createLayout(userTable, true, Arrays.asList(new SideButton[] {
234 addButton, editButton, deleteButton, upButton, downButton
235 }));
236 }
237
238 @Override
239 public void destroy() {
240 MultikeyActionsHandler.getInstance().removeAction(enableFilterAction);
241 MultikeyActionsHandler.getInstance().removeAction(hidingFilterAction);
242 super.destroy();
243 }
244
245 static final class UserTable extends JTable {
246 static final class UserTableHeader extends JTableHeader {
247 UserTableHeader(TableColumnModel cm) {
248 super(cm);
249 }
250
251 @Override
252 public String getToolTipText(MouseEvent e) {
253 int index = columnModel.getColumnIndexAtX(e.getPoint().x);
254 int realIndex = columnModel.getColumn(index).getModelIndex();
255 return COLUMN_TOOLTIPS[realIndex];
256 }
257 }
258
259 UserTable(TableModel dm) {
260 super(dm);
261 }
262
263 @Override
264 protected JTableHeader createDefaultTableHeader() {
265 return new UserTableHeader(columnModel);
266 }
267 }
268
269 static class StringRenderer extends DefaultTableCellRenderer {
270 @Override
271 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
272 FilterTableModel model = (FilterTableModel) table.getModel();
273 Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
274 cell.setEnabled(model.isCellEnabled(row, column));
275 return cell;
276 }
277 }
278
279 static class BooleanRenderer extends JCheckBox implements TableCellRenderer {
280 @Override
281 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
282 FilterTableModel model = (FilterTableModel) table.getModel();
283 setSelected(value != null && (Boolean) value);
284 setEnabled(model.isCellEnabled(row, column));
285 setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
286 return this;
287 }
288 }
289
290 public void updateDialogHeader() {
291 SwingUtilities.invokeLater(() -> setTitle(
292 tr("Filter Hidden:{0} Disabled:{1}", filterModel.disabledAndHiddenCount, filterModel.disabledCount)));
293 }
294
295 public void drawOSDText(Graphics2D g) {
296 filterModel.drawOSDText(g);
297 }
298
299 /**
300 * Returns the list of primitives whose filtering can be affected by change in primitive
301 * @param primitives list of primitives to check
302 * @return List of primitives whose filtering can be affected by change in source primitives
303 */
304 private static Collection<OsmPrimitive> getAffectedPrimitives(Collection<? extends OsmPrimitive> primitives) {
305 // Filters can use nested parent/child expression so complete tree is necessary
306 Set<OsmPrimitive> result = new HashSet<>();
307 Stack<OsmPrimitive> stack = new Stack<>();
308 stack.addAll(primitives);
309
310 while (!stack.isEmpty()) {
311 OsmPrimitive p = stack.pop();
312
313 if (result.contains(p)) {
314 continue;
315 }
316
317 result.add(p);
318
319 if (p instanceof Way) {
320 for (OsmPrimitive n: ((Way) p).getNodes()) {
321 stack.push(n);
322 }
323 } else if (p instanceof Relation) {
324 for (RelationMember rm: ((Relation) p).getMembers()) {
325 stack.push(rm.getMember());
326 }
327 }
328
329 for (OsmPrimitive ref: p.getReferrers()) {
330 stack.push(ref);
331 }
332 }
333
334 return result;
335 }
336
337 @Override
338 public void dataChanged(DataChangedEvent event) {
339 filterModel.executeFilters();
340 }
341
342 @Override
343 public void nodeMoved(NodeMovedEvent event) {
344 filterModel.executeFilters();
345 }
346
347 @Override
348 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
349 filterModel.executeFilters();
350 }
351
352 @Override
353 public void primitivesAdded(PrimitivesAddedEvent event) {
354 filterModel.executeFilters(event.getPrimitives());
355 }
356
357 @Override
358 public void primitivesRemoved(PrimitivesRemovedEvent event) {
359 filterModel.executeFilters();
360 }
361
362 @Override
363 public void relationMembersChanged(RelationMembersChangedEvent event) {
364 filterModel.executeFilters(getAffectedPrimitives(event.getPrimitives()));
365 }
366
367 @Override
368 public void tagsChanged(TagsChangedEvent event) {
369 filterModel.executeFilters(getAffectedPrimitives(event.getPrimitives()));
370 }
371
372 @Override
373 public void wayNodesChanged(WayNodesChangedEvent event) {
374 filterModel.executeFilters(getAffectedPrimitives(event.getPrimitives()));
375 }
376
377 /**
378 * This method is intendet for Plugins getting the filtermodel and using .addFilter() to
379 * add a new filter.
380 * @return the filtermodel
381 */
382 public FilterTableModel getFilterModel() {
383 return filterModel;
384 }
385
386 abstract class AbstractFilterAction extends AbstractAction implements MultikeyShortcutAction {
387
388 protected transient Filter lastFilter;
389
390 @Override
391 public void actionPerformed(ActionEvent e) {
392 throw new UnsupportedOperationException();
393 }
394
395 @Override
396 public List<MultikeyInfo> getMultikeyCombinations() {
397 List<MultikeyInfo> result = new ArrayList<>();
398
399 for (int i = 0; i < filterModel.getRowCount(); i++) {
400 Filter filter = filterModel.getFilter(i);
401 MultikeyInfo info = new MultikeyInfo(i, filter.text);
402 result.add(info);
403 }
404
405 return result;
406 }
407
408 protected final boolean isLastFilterValid() {
409 return lastFilter != null && filterModel.getFilters().contains(lastFilter);
410 }
411
412 @Override
413 public MultikeyInfo getLastMultikeyAction() {
414 if (isLastFilterValid())
415 return new MultikeyInfo(-1, lastFilter.text);
416 else
417 return null;
418 }
419 }
420
421 private class EnableFilterAction extends AbstractFilterAction {
422
423 EnableFilterAction() {
424 putValue(SHORT_DESCRIPTION, tr("Enable filter"));
425 ENABLE_FILTER_SHORTCUT.setAccelerator(this);
426 }
427
428 @Override
429 public Shortcut getMultikeyShortcut() {
430 return ENABLE_FILTER_SHORTCUT;
431 }
432
433 @Override
434 public void executeMultikeyAction(int index, boolean repeatLastAction) {
435 if (index >= 0 && index < filterModel.getRowCount()) {
436 Filter filter = filterModel.getFilter(index);
437 filterModel.setValueAt(!filter.enable, index, FilterTableModel.COL_ENABLED);
438 lastFilter = filter;
439 } else if (repeatLastAction && isLastFilterValid()) {
440 filterModel.setValueAt(!lastFilter.enable, filterModel.getFilters().indexOf(lastFilter), FilterTableModel.COL_ENABLED);
441 }
442 }
443 }
444
445 private class HidingFilterAction extends AbstractFilterAction {
446
447 HidingFilterAction() {
448 putValue(SHORT_DESCRIPTION, tr("Hiding filter"));
449 HIDING_FILTER_SHORTCUT.setAccelerator(this);
450 }
451
452 @Override
453 public Shortcut getMultikeyShortcut() {
454 return HIDING_FILTER_SHORTCUT;
455 }
456
457 @Override
458 public void executeMultikeyAction(int index, boolean repeatLastAction) {
459 if (index >= 0 && index < filterModel.getRowCount()) {
460 Filter filter = filterModel.getFilter(index);
461 filterModel.setValueAt(!filter.hiding, index, FilterTableModel.COL_HIDING);
462 lastFilter = filter;
463 } else if (repeatLastAction && isLastFilterValid()) {
464 filterModel.setValueAt(!lastFilter.hiding, filterModel.getFilters().indexOf(lastFilter), FilterTableModel.COL_HIDING);
465 }
466 }
467 }
468}
Note: See TracBrowser for help on using the repository browser.