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

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

findbugs - UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR

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