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

Last change on this file since 12373 was 12298, checked in by michael2402, 7 years ago

Rename one character variables in filter dialog, add javadoc.

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