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

Last change on this file since 12846 was 12846, checked in by bastiK, 7 years ago

see #15229 - use Config.getPref() wherever possible

  • Property svn:eol-style set to native
File size: 16.6 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.List;
14
15import javax.swing.AbstractAction;
16import javax.swing.JCheckBox;
17import javax.swing.JTable;
18import javax.swing.ListSelectionModel;
19import javax.swing.SwingUtilities;
20import javax.swing.table.DefaultTableCellRenderer;
21import javax.swing.table.JTableHeader;
22import javax.swing.table.TableCellRenderer;
23import javax.swing.table.TableColumnModel;
24import javax.swing.table.TableModel;
25
26import org.openstreetmap.josm.Main;
27import org.openstreetmap.josm.actions.mapmode.MapMode;
28import org.openstreetmap.josm.actions.search.SearchAction;
29import org.openstreetmap.josm.data.osm.Filter;
30import org.openstreetmap.josm.data.osm.FilterModel;
31import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
32import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
33import org.openstreetmap.josm.data.osm.event.DataSetListener;
34import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
35import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
36import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
37import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
38import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
39import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
40import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
41import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
42import org.openstreetmap.josm.gui.MainApplication;
43import org.openstreetmap.josm.gui.MapFrame;
44import org.openstreetmap.josm.gui.MapFrame.MapModeChangeListener;
45import org.openstreetmap.josm.gui.SideButton;
46import org.openstreetmap.josm.gui.util.MultikeyActionsHandler;
47import org.openstreetmap.josm.gui.util.MultikeyShortcutAction;
48import org.openstreetmap.josm.tools.ImageProvider;
49import org.openstreetmap.josm.tools.InputMapUtils;
50import org.openstreetmap.josm.tools.Shortcut;
51
52/**
53 * The filter dialog displays a list of filters that are active on the current edit layer.
54 *
55 * @author Petr_Dlouhý
56 */
57public class FilterDialog extends ToggleDialog implements DataSetListener, MapModeChangeListener {
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 MapFrame.addMapModeChangeListener(this);
83 filterModel.executeFilters();
84 }
85
86 @Override
87 public void hideNotify() {
88 DatasetEventManager.getInstance().removeDatasetListener(this);
89 MapFrame.removeMapModeChangeListener(this);
90 filterModel.model.clearFilterFlags();
91 MainApplication.getMap().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 /**
111 * Builds the GUI.
112 */
113 protected void build() {
114 userTable = new UserTable(filterModel);
115
116 userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
117
118 userTable.getColumnModel().getColumn(0).setMaxWidth(1);
119 userTable.getColumnModel().getColumn(1).setMaxWidth(1);
120 userTable.getColumnModel().getColumn(3).setMaxWidth(1);
121 userTable.getColumnModel().getColumn(4).setMaxWidth(1);
122
123 userTable.getColumnModel().getColumn(0).setResizable(false);
124 userTable.getColumnModel().getColumn(1).setResizable(false);
125 userTable.getColumnModel().getColumn(3).setResizable(false);
126 userTable.getColumnModel().getColumn(4).setResizable(false);
127
128 userTable.setDefaultRenderer(Boolean.class, new BooleanRenderer());
129 userTable.setDefaultRenderer(String.class, new StringRenderer());
130
131 SideButton addButton = new SideButton(new AbstractAction() {
132 {
133 putValue(NAME, tr("Add"));
134 putValue(SHORT_DESCRIPTION, tr("Add filter."));
135 new ImageProvider("dialogs", "add").getResource().attachImageIcon(this, true);
136 }
137
138 @Override
139 public void actionPerformed(ActionEvent e) {
140 Filter filter = (Filter) SearchAction.showSearchDialog(new Filter());
141 if (filter != null) {
142 filterModel.addFilter(filter);
143 }
144 }
145 });
146 SideButton editButton = new SideButton(new AbstractAction() {
147 {
148 putValue(NAME, tr("Edit"));
149 putValue(SHORT_DESCRIPTION, tr("Edit filter."));
150 new ImageProvider("dialogs", "edit").getResource().attachImageIcon(this, true);
151 }
152
153 @Override
154 public void actionPerformed(ActionEvent e) {
155 int index = userTable.getSelectionModel().getMinSelectionIndex();
156 if (index < 0) return;
157 Filter f = filterModel.getFilter(index);
158 Filter filter = (Filter) SearchAction.showSearchDialog(f);
159 if (filter != null) {
160 filterModel.setFilter(index, filter);
161 }
162 }
163 });
164 SideButton deleteButton = new SideButton(new AbstractAction() {
165 {
166 putValue(NAME, tr("Delete"));
167 putValue(SHORT_DESCRIPTION, tr("Delete filter."));
168 new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this, true);
169 }
170
171 @Override
172 public void actionPerformed(ActionEvent e) {
173 int index = userTable.getSelectionModel().getMinSelectionIndex();
174 if (index >= 0) {
175 filterModel.removeFilter(index);
176 }
177 }
178 });
179 SideButton upButton = new SideButton(new AbstractAction() {
180 {
181 putValue(NAME, tr("Up"));
182 putValue(SHORT_DESCRIPTION, tr("Move filter up."));
183 new ImageProvider("dialogs", "up").getResource().attachImageIcon(this, true);
184 }
185
186 @Override
187 public void actionPerformed(ActionEvent e) {
188 int index = userTable.getSelectionModel().getMinSelectionIndex();
189 if (index >= 0) {
190 filterModel.moveUpFilter(index);
191 userTable.getSelectionModel().setSelectionInterval(index-1, index-1);
192 }
193 }
194 });
195 SideButton downButton = new SideButton(new AbstractAction() {
196 {
197 putValue(NAME, tr("Down"));
198 putValue(SHORT_DESCRIPTION, tr("Move filter down."));
199 new ImageProvider("dialogs", "down").getResource().attachImageIcon(this, true);
200 }
201
202 @Override
203 public void actionPerformed(ActionEvent e) {
204 int index = userTable.getSelectionModel().getMinSelectionIndex();
205 if (index >= 0) {
206 filterModel.moveDownFilter(index);
207 userTable.getSelectionModel().setSelectionInterval(index+1, index+1);
208 }
209 }
210 });
211
212 // Toggle filter "enabled" on Enter
213 InputMapUtils.addEnterAction(userTable, new AbstractAction() {
214 @Override
215 public void actionPerformed(ActionEvent e) {
216 int index = userTable.getSelectedRow();
217 if (index >= 0) {
218 Filter filter = filterModel.getFilter(index);
219 filterModel.setValueAt(!filter.enable, index, FilterTableModel.COL_ENABLED);
220 }
221 }
222 });
223
224 // Toggle filter "hiding" on Spacebar
225 InputMapUtils.addSpacebarAction(userTable, new AbstractAction() {
226 @Override
227 public void actionPerformed(ActionEvent e) {
228 int index = userTable.getSelectedRow();
229 if (index >= 0) {
230 Filter filter = filterModel.getFilter(index);
231 filterModel.setValueAt(!filter.hiding, index, FilterTableModel.COL_HIDING);
232 }
233 }
234 });
235
236 createLayout(userTable, true, Arrays.asList(
237 addButton, editButton, deleteButton, upButton, downButton
238 ));
239 }
240
241 @Override
242 public void destroy() {
243 MultikeyActionsHandler.getInstance().removeAction(enableFilterAction);
244 MultikeyActionsHandler.getInstance().removeAction(hidingFilterAction);
245 super.destroy();
246 }
247
248 static final class UserTable extends JTable {
249 static final class UserTableHeader extends JTableHeader {
250 UserTableHeader(TableColumnModel cm) {
251 super(cm);
252 }
253
254 @Override
255 public String getToolTipText(MouseEvent e) {
256 int index = columnModel.getColumnIndexAtX(e.getPoint().x);
257 int realIndex = columnModel.getColumn(index).getModelIndex();
258 return COLUMN_TOOLTIPS[realIndex];
259 }
260 }
261
262 UserTable(TableModel dm) {
263 super(dm);
264 }
265
266 @Override
267 protected JTableHeader createDefaultTableHeader() {
268 return new UserTableHeader(columnModel);
269 }
270 }
271
272 static class StringRenderer extends DefaultTableCellRenderer {
273 @Override
274 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
275 FilterTableModel model = (FilterTableModel) table.getModel();
276 Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
277 cell.setEnabled(model.isCellEnabled(row, column));
278 return cell;
279 }
280 }
281
282 static class BooleanRenderer extends JCheckBox implements TableCellRenderer {
283 @Override
284 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
285 FilterTableModel model = (FilterTableModel) table.getModel();
286 setSelected(value != null && (Boolean) value);
287 setEnabled(model.isCellEnabled(row, column));
288 setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
289 return this;
290 }
291 }
292
293 /**
294 * Updates the headline of this dialog to display the number of active filters.
295 */
296 public void updateDialogHeader() {
297 SwingUtilities.invokeLater(() -> setTitle(
298 tr("Filter Hidden:{0} Disabled:{1}",
299 filterModel.model.getDisabledAndHiddenCount(), filterModel.model.getDisabledCount())));
300 }
301
302 /**
303 * Draws a text on the map display that indicates that filters are active.
304 * @param g The graphics to draw that text on.
305 */
306 public void drawOSDText(Graphics2D g) {
307 filterModel.drawOSDText(g);
308 }
309
310 @Override
311 public void dataChanged(DataChangedEvent event) {
312 filterModel.executeFilters();
313 }
314
315 @Override
316 public void nodeMoved(NodeMovedEvent event) {
317 filterModel.executeFilters();
318 }
319
320 @Override
321 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
322 filterModel.executeFilters();
323 }
324
325 @Override
326 public void primitivesAdded(PrimitivesAddedEvent event) {
327 filterModel.executeFilters(event.getPrimitives());
328 }
329
330 @Override
331 public void primitivesRemoved(PrimitivesRemovedEvent event) {
332 filterModel.executeFilters();
333 }
334
335 @Override
336 public void relationMembersChanged(RelationMembersChangedEvent event) {
337 filterModel.executeFilters(FilterModel.getAffectedPrimitives(event.getPrimitives()));
338 }
339
340 @Override
341 public void tagsChanged(TagsChangedEvent event) {
342 filterModel.executeFilters(FilterModel.getAffectedPrimitives(event.getPrimitives()));
343 }
344
345 @Override
346 public void wayNodesChanged(WayNodesChangedEvent event) {
347 filterModel.executeFilters(FilterModel.getAffectedPrimitives(event.getPrimitives()));
348 }
349
350 @Override
351 public void mapModeChange(MapMode oldMapMode, MapMode newMapMode) {
352 filterModel.executeFilters();
353 }
354
355 /**
356 * This method is intended for Plugins getting the filtermodel and using .addFilter() to
357 * add a new filter.
358 * @return the filtermodel
359 */
360 public FilterTableModel getFilterModel() {
361 return filterModel;
362 }
363
364 abstract class AbstractFilterAction extends AbstractAction implements MultikeyShortcutAction {
365
366 protected transient Filter lastFilter;
367
368 @Override
369 public void actionPerformed(ActionEvent e) {
370 throw new UnsupportedOperationException();
371 }
372
373 @Override
374 public List<MultikeyInfo> getMultikeyCombinations() {
375 List<MultikeyInfo> result = new ArrayList<>();
376
377 for (int i = 0; i < filterModel.getRowCount(); i++) {
378 Filter filter = filterModel.getFilter(i);
379 MultikeyInfo info = new MultikeyInfo(i, filter.text);
380 result.add(info);
381 }
382
383 return result;
384 }
385
386 protected final boolean isLastFilterValid() {
387 return lastFilter != null && filterModel.getFilters().contains(lastFilter);
388 }
389
390 @Override
391 public MultikeyInfo getLastMultikeyAction() {
392 if (isLastFilterValid())
393 return new MultikeyInfo(-1, lastFilter.text);
394 else
395 return null;
396 }
397 }
398
399 private class EnableFilterAction extends AbstractFilterAction {
400
401 EnableFilterAction() {
402 putValue(SHORT_DESCRIPTION, tr("Enable filter"));
403 ENABLE_FILTER_SHORTCUT.setAccelerator(this);
404 }
405
406 @Override
407 public Shortcut getMultikeyShortcut() {
408 return ENABLE_FILTER_SHORTCUT;
409 }
410
411 @Override
412 public void executeMultikeyAction(int index, boolean repeatLastAction) {
413 if (index >= 0 && index < filterModel.getRowCount()) {
414 Filter filter = filterModel.getFilter(index);
415 filterModel.setValueAt(!filter.enable, index, FilterTableModel.COL_ENABLED);
416 lastFilter = filter;
417 } else if (repeatLastAction && isLastFilterValid()) {
418 filterModel.setValueAt(!lastFilter.enable, filterModel.getFilters().indexOf(lastFilter), FilterTableModel.COL_ENABLED);
419 }
420 }
421 }
422
423 private class HidingFilterAction extends AbstractFilterAction {
424
425 HidingFilterAction() {
426 putValue(SHORT_DESCRIPTION, tr("Hiding filter"));
427 HIDING_FILTER_SHORTCUT.setAccelerator(this);
428 }
429
430 @Override
431 public Shortcut getMultikeyShortcut() {
432 return HIDING_FILTER_SHORTCUT;
433 }
434
435 @Override
436 public void executeMultikeyAction(int index, boolean repeatLastAction) {
437 if (index >= 0 && index < filterModel.getRowCount()) {
438 Filter filter = filterModel.getFilter(index);
439 filterModel.setValueAt(!filter.hiding, index, FilterTableModel.COL_HIDING);
440 lastFilter = filter;
441 } else if (repeatLastAction && isLastFilterValid()) {
442 filterModel.setValueAt(!lastFilter.hiding, filterModel.getFilters().indexOf(lastFilter), FilterTableModel.COL_HIDING);
443 }
444 }
445 }
446}
Note: See TracBrowser for help on using the repository browser.