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

Last change on this file since 4766 was 4766, checked in by akks, 12 years ago

Fixing shortcut editor problems #7177, #7213 (empty names, incorrect editing after sorting)

  • Property svn:eol-style set to native
File size: 14.4 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Component;
8import java.awt.Graphics2D;
9import java.awt.event.ActionEvent;
10import java.awt.event.ActionListener;
11import java.awt.event.KeyEvent;
12import java.awt.event.MouseEvent;
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.HashSet;
17import java.util.List;
18import java.util.Set;
19import java.util.Stack;
20
21import javax.swing.AbstractAction;
22import javax.swing.JCheckBox;
23import javax.swing.JTable;
24import javax.swing.KeyStroke;
25import javax.swing.ListSelectionModel;
26import javax.swing.SwingUtilities;
27import javax.swing.table.DefaultTableCellRenderer;
28import javax.swing.table.JTableHeader;
29import javax.swing.table.TableCellRenderer;
30
31import org.openstreetmap.josm.Main;
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.SideButton;
50import org.openstreetmap.josm.tools.MultikeyActionsHandler;
51import org.openstreetmap.josm.tools.MultikeyShortcutAction;
52import org.openstreetmap.josm.tools.Shortcut;
53
54/**
55 *
56 * @author Petr_Dlouhý
57 */
58public class FilterDialog extends ToggleDialog implements DataSetListener {
59
60 private JTable userTable;
61 private FilterTableModel filterModel = new FilterTableModel();
62 private SideButton addButton;
63 private SideButton editButton;
64 private SideButton deleteButton;
65 private SideButton upButton;
66 private SideButton downButton;
67
68
69 public FilterDialog(){
70 super(tr("Filter"), "filter", tr("Filter objects and hide/disable them."),
71 Shortcut.registerShortcut("subwindow:filter", tr("Toggle: {0}", tr("Filter")), KeyEvent.VK_F, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 162);
72 build();
73
74 MultikeyActionsHandler.getInstance().addAction(new EnableFilterAction());
75 MultikeyActionsHandler.getInstance().addAction(new HidingFilterAction());
76 }
77
78 @Override
79 public void showNotify() {
80 DatasetEventManager.getInstance().addDatasetListener(this, FireMode.IN_EDT_CONSOLIDATED);
81 filterModel.executeFilters();
82 }
83
84 @Override
85 public void hideNotify() {
86 DatasetEventManager.getInstance().removeDatasetListener(this);
87 filterModel.clearFilterFlags();
88 Main.map.mapView.repaint();
89 }
90
91 private static final KeyStroke ENABLE_FILTER_SHORTCUT
92 = Shortcut.registerShortcut("core_multikey:enableFilter", tr("Multikey: {0}", tr("Enable filter")), 'E', Shortcut.GROUP_DIRECT, KeyEvent.CTRL_DOWN_MASK + KeyEvent.ALT_DOWN_MASK).getKeyStroke();
93
94 private static final KeyStroke HIDING_FILTER_SHORTCUT
95 = Shortcut.registerShortcut("core_multikey:hidingFilter", tr("Multikey: {0}", tr("Hide filter")), 'H', Shortcut.GROUP_DIRECT, KeyEvent.CTRL_DOWN_MASK + KeyEvent.ALT_DOWN_MASK).getKeyStroke();
96
97
98 protected final String[] columnToolTips = {
99 tr("Enable filter ({0}+{1})", KeyEvent.getKeyModifiersText(ENABLE_FILTER_SHORTCUT.getModifiers()), KeyEvent.getKeyText(ENABLE_FILTER_SHORTCUT.getKeyCode())),
100 tr("Hiding filter ({0}+{1})", KeyEvent.getKeyModifiersText(HIDING_FILTER_SHORTCUT.getModifiers()), KeyEvent.getKeyText(HIDING_FILTER_SHORTCUT.getKeyCode())),
101 null,
102 tr("Inverse filter"),
103 tr("Filter mode")
104 };
105
106 protected void build() {
107 userTable = new JTable(filterModel){
108 @Override
109 protected JTableHeader createDefaultTableHeader() {
110 return new JTableHeader(columnModel) {
111 @Override
112 public String getToolTipText(MouseEvent e) {
113 java.awt.Point p = e.getPoint();
114 int index = columnModel.getColumnIndexAtX(p.x);
115 int realIndex = columnModel.getColumn(index).getModelIndex();
116 return columnToolTips[realIndex];
117 }
118 };
119 }
120 };
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 addButton = new SideButton(marktr("Add"), "add", "SelectionList", tr("Add filter."),
138 new ActionListener(){
139 public void actionPerformed(ActionEvent evt){
140 Filter filter = (Filter)SearchAction.showSearchDialog(new Filter());
141 if(filter != null){
142 filterModel.addFilter(filter);
143 }
144 }
145 });
146
147 editButton = new SideButton(marktr("Edit"), "edit", "SelectionList", tr("Edit filter."),
148 new ActionListener(){
149 public void actionPerformed(ActionEvent evt){
150 int index = userTable.getSelectionModel().getMinSelectionIndex();
151 if(index < 0) return;
152 Filter f = filterModel.getFilter(index);
153 Filter filter = (Filter)SearchAction.showSearchDialog(f);
154 if(filter != null){
155 filterModel.setFilter(index, filter);
156 }
157 }
158 });
159
160 deleteButton = new SideButton(marktr("Delete"), "delete", "SelectionList", tr("Delete filter."),
161 new ActionListener(){
162 public void actionPerformed(ActionEvent evt){
163 int index = userTable.getSelectionModel().getMinSelectionIndex();
164 if(index < 0) return;
165 filterModel.removeFilter(index);
166 }
167 });
168
169 upButton = new SideButton(marktr("Up"), "up", "SelectionList", tr("Move filter up."),
170 new ActionListener(){
171 public void actionPerformed(ActionEvent evt){
172 int index = userTable.getSelectionModel().getMinSelectionIndex();
173 if(index < 0) return;
174 filterModel.moveUpFilter(index);
175 userTable.getSelectionModel().setSelectionInterval(index-1, index-1);
176 }
177 });
178
179 downButton = new SideButton(marktr("Down"), "down", "SelectionList", tr("Move filter down."),
180 new ActionListener(){
181 public void actionPerformed(ActionEvent evt){
182 int index = userTable.getSelectionModel().getMinSelectionIndex();
183 if(index < 0) return;
184 filterModel.moveDownFilter(index);
185 userTable.getSelectionModel().setSelectionInterval(index+1, index+1);
186 }
187 });
188
189 createLayout(userTable, true, Arrays.asList(new SideButton[] {
190 addButton, editButton, deleteButton, upButton, downButton
191 }));
192 }
193
194 static class StringRenderer extends DefaultTableCellRenderer {
195 @Override
196 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row,int column) {
197 FilterTableModel model = (FilterTableModel)table.getModel();
198 Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
199 cell.setEnabled(model.isCellEnabled(row, column));
200 return cell;
201 }
202 }
203
204 static class BooleanRenderer extends JCheckBox implements TableCellRenderer {
205 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row,int column) {
206 FilterTableModel model = (FilterTableModel)table.getModel();
207 setSelected(value != null && (Boolean)value);
208 setEnabled(model.isCellEnabled(row, column));
209 setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
210 return this;
211 }
212 }
213
214 public void updateDialogHeader() {
215 SwingUtilities.invokeLater(new Runnable() {
216 public void run() {
217 setTitle(tr("Filter Hidden:{0} Disabled:{1}", filterModel.disabledAndHiddenCount, filterModel.disabledCount));
218 }
219 });
220 }
221
222 public void drawOSDText(Graphics2D g) {
223 filterModel.drawOSDText(g);
224 }
225
226 /**
227 *
228 * @param primitive
229 * @return List of primitives whose filtering can be affected by change in primitive
230 */
231 private Collection<OsmPrimitive> getAffectedPrimitives(Collection<? extends OsmPrimitive> primitives) {
232 // Filters can use nested parent/child expression so complete tree is necessary
233 Set<OsmPrimitive> result = new HashSet<OsmPrimitive>();
234 Stack<OsmPrimitive> stack = new Stack<OsmPrimitive>();
235 stack.addAll(primitives);
236
237 while (!stack.isEmpty()) {
238 OsmPrimitive p = stack.pop();
239
240 if (result.contains(p)) {
241 continue;
242 }
243
244 result.add(p);
245
246 if (p instanceof Way) {
247 for (OsmPrimitive n: ((Way)p).getNodes()) {
248 stack.push(n);
249 }
250 } else if (p instanceof Relation) {
251 for (RelationMember rm: ((Relation)p).getMembers()) {
252 stack.push(rm.getMember());
253 }
254 }
255
256 for (OsmPrimitive ref: p.getReferrers()) {
257 stack.push(ref);
258 }
259 }
260
261 return result;
262 }
263
264 public void dataChanged(DataChangedEvent event) {
265 filterModel.executeFilters();
266 }
267
268 public void nodeMoved(NodeMovedEvent event) {
269 // Do nothing
270 }
271
272 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
273 filterModel.executeFilters();
274 }
275
276 public void primitivesAdded(PrimitivesAddedEvent event) {
277 filterModel.executeFilters(event.getPrimitives());
278 }
279
280 public void primitivesRemoved(PrimitivesRemovedEvent event) {
281 filterModel.executeFilters();
282 }
283
284 public void relationMembersChanged(RelationMembersChangedEvent event) {
285 filterModel.executeFilters(getAffectedPrimitives(event.getPrimitives()));
286 }
287
288 public void tagsChanged(TagsChangedEvent event) {
289 filterModel.executeFilters(getAffectedPrimitives(event.getPrimitives()));
290 }
291
292 public void wayNodesChanged(WayNodesChangedEvent event) {
293 filterModel.executeFilters(getAffectedPrimitives(event.getPrimitives()));
294 }
295
296 abstract class AbstractFilterAction extends AbstractAction implements MultikeyShortcutAction {
297
298 protected Filter lastFilter;
299
300 @Override
301 public void actionPerformed(ActionEvent e) {
302 throw new UnsupportedOperationException();
303 }
304
305 @Override
306 public List<MultikeyInfo> getMultikeyCombinations() {
307 List<MultikeyInfo> result = new ArrayList<MultikeyShortcutAction.MultikeyInfo>();
308
309 for (int i=0; i<filterModel.getRowCount(); i++) {
310 Filter filter = filterModel.getFilter(i);
311 MultikeyInfo info = new MultikeyInfo(i, filter.text);
312 result.add(info);
313 }
314
315 return result;
316 }
317
318 protected boolean isLastFilterValid() {
319 return lastFilter != null && filterModel.getFilters().contains(lastFilter);
320 }
321
322 @Override
323 public MultikeyInfo getLastMultikeyAction() {
324 if (isLastFilterValid())
325 return new MultikeyInfo(-1, lastFilter.text);
326 else
327 return null;
328 }
329
330 }
331
332 private class EnableFilterAction extends AbstractFilterAction {
333
334 EnableFilterAction() {
335 putValue(SHORT_DESCRIPTION, tr("Enable filter"));
336 putValue(ACCELERATOR_KEY, ENABLE_FILTER_SHORTCUT);
337 }
338
339 @Override
340 public void executeMultikeyAction(int index, boolean repeatLastAction) {
341 if (index >= 0 && index < filterModel.getRowCount()) {
342 Filter filter = filterModel.getFilter(index);
343 filterModel.setValueAt(!filter.enable, index, FilterTableModel.COL_ENABLED);
344 lastFilter = filter;
345 } else if (repeatLastAction && isLastFilterValid()) {
346 filterModel.setValueAt(!lastFilter.enable, filterModel.getFilters().indexOf(lastFilter), FilterTableModel.COL_ENABLED);
347 }
348 }
349 }
350
351 private class HidingFilterAction extends AbstractFilterAction {
352
353 public HidingFilterAction() {
354 putValue(SHORT_DESCRIPTION, tr("Hiding filter"));
355 putValue(ACCELERATOR_KEY, HIDING_FILTER_SHORTCUT);
356 }
357
358 @Override
359 public void executeMultikeyAction(int index, boolean repeatLastAction) {
360 if (index >= 0 && index < filterModel.getRowCount()) {
361 Filter filter = filterModel.getFilter(index);
362 filterModel.setValueAt(!filter.hiding, index, FilterTableModel.COL_HIDING);
363 lastFilter = filter;
364 } else if (repeatLastAction && isLastFilterValid()) {
365 filterModel.setValueAt(!lastFilter.hiding, filterModel.getFilters().indexOf(lastFilter), FilterTableModel.COL_HIDING);
366 }
367 }
368
369 }
370}
Note: See TracBrowser for help on using the repository browser.