source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java@ 5672

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

fix #3596 - Validator list conflicts with keyboard shortcuts

  • Property svn:eol-style set to native
File size: 12.7 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.gui.dialogs.validator;
3
4import java.awt.event.KeyListener;
5import java.awt.event.MouseEvent;
6import java.util.ArrayList;
7import java.util.Collections;
8import java.util.Enumeration;
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.LinkedHashSet;
12import java.util.List;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Set;
16
17import javax.swing.JTree;
18import javax.swing.ToolTipManager;
19import javax.swing.tree.DefaultMutableTreeNode;
20import javax.swing.tree.DefaultTreeModel;
21import javax.swing.tree.TreePath;
22import javax.swing.tree.TreeSelectionModel;
23
24import org.openstreetmap.josm.Main;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.validation.Severity;
27import org.openstreetmap.josm.data.validation.TestError;
28import org.openstreetmap.josm.data.validation.util.MultipleNameVisitor;
29import org.openstreetmap.josm.gui.preferences.ValidatorPreference;
30import org.openstreetmap.josm.tools.MultiMap;
31
32/**
33 * A panel that displays the error tree. The selection manager
34 * respects clicks into the selection list. Ctrl-click will remove entries from
35 * the list while single click will make the clicked entry the only selection.
36 *
37 * @author frsantos
38 */
39public class ValidatorTreePanel extends JTree {
40 /** Serializable ID */
41 private static final long serialVersionUID = 2952292777351992696L;
42
43 /**
44 * The validation data.
45 */
46 protected DefaultTreeModel valTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
47
48 /** The list of errors shown in the tree */
49 private List<TestError> errors = new ArrayList<TestError>();
50
51 /**
52 * If {@link #filter} is not <code>null</code> only errors are displayed
53 * that refer to one of the primitives in the filter.
54 */
55 private Set<OsmPrimitive> filter = null;
56
57 private int updateCount;
58
59 /**
60 * Constructor
61 * @param errors The list of errors
62 */
63 public ValidatorTreePanel(List<TestError> errors) {
64 ToolTipManager.sharedInstance().registerComponent(this);
65 this.setModel(valTreeModel);
66 this.setRootVisible(false);
67 this.setShowsRootHandles(true);
68 this.expandRow(0);
69 this.setVisibleRowCount(8);
70 this.setCellRenderer(new ValidatorTreeRenderer());
71 this.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
72 setErrorList(errors);
73 for (KeyListener keyListener : getKeyListeners()) {
74 // Fix #3596 - Remove default keyListener to avoid conflicts with JOSM commands
75 if (keyListener.getClass().getName().equals("javax.swing.plaf.basic.BasicTreeUI$Handler")) {
76 removeKeyListener(keyListener);
77 }
78 }
79 }
80
81 @Override
82 public String getToolTipText(MouseEvent e) {
83 String res = null;
84 TreePath path = getPathForLocation(e.getX(), e.getY());
85 if (path != null) {
86 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
87 Object nodeInfo = node.getUserObject();
88
89 if (nodeInfo instanceof TestError) {
90 TestError error = (TestError) nodeInfo;
91 MultipleNameVisitor v = new MultipleNameVisitor();
92 v.visit(error.getPrimitives());
93 res = "<html>" + v.getText() + "<br>" + error.getMessage();
94 String d = error.getDescription();
95 if (d != null)
96 res += "<br>" + d;
97 res += "</html>";
98 } else {
99 res = node.toString();
100 }
101 }
102 return res;
103 }
104
105 /** Constructor */
106 public ValidatorTreePanel() {
107 this(null);
108 }
109
110 @Override
111 public void setVisible(boolean v) {
112 if (v) {
113 buildTree();
114 } else {
115 valTreeModel.setRoot(new DefaultMutableTreeNode());
116 }
117 super.setVisible(v);
118 }
119
120 /**
121 * Builds the errors tree
122 */
123 public void buildTree() {
124 updateCount++;
125 DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
126
127 if (errors == null || errors.isEmpty()) {
128 valTreeModel.setRoot(rootNode);
129 return;
130 }
131
132 // Remember the currently expanded rows
133 Set<Object> oldSelectedRows = new HashSet<Object>();
134 Enumeration<TreePath> expanded = getExpandedDescendants(new TreePath(getRoot()));
135 if (expanded != null) {
136 while (expanded.hasMoreElements()) {
137 TreePath path = expanded.nextElement();
138 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
139 Object userObject = node.getUserObject();
140 if (userObject instanceof Severity) {
141 oldSelectedRows.add(userObject);
142 } else if (userObject instanceof String) {
143 String msg = (String) userObject;
144 msg = msg.substring(0, msg.lastIndexOf(" ("));
145 oldSelectedRows.add(msg);
146 }
147 }
148 }
149
150 Map<Severity, MultiMap<String, TestError>> errorTree = new HashMap<Severity, MultiMap<String, TestError>>();
151 Map<Severity, HashMap<String, MultiMap<String, TestError>>> errorTreeDeep = new HashMap<Severity, HashMap<String, MultiMap<String, TestError>>>();
152 for (Severity s : Severity.values()) {
153 errorTree.put(s, new MultiMap<String, TestError>(20));
154 errorTreeDeep.put(s, new HashMap<String, MultiMap<String, TestError>>());
155 }
156
157 boolean other = Main.pref.getBoolean(ValidatorPreference.PREF_OTHER, false);
158 for (TestError e : errors) {
159 if (e.getIgnored()) {
160 continue;
161 }
162 Severity s = e.getSeverity();
163 if(!other && s == Severity.OTHER) {
164 continue;
165 }
166 String d = e.getDescription();
167 String m = e.getMessage();
168 if (filter != null) {
169 boolean found = false;
170 for (OsmPrimitive p : e.getPrimitives()) {
171 if (filter.contains(p)) {
172 found = true;
173 break;
174 }
175 }
176 if (!found) {
177 continue;
178 }
179 }
180 if (d != null) {
181 MultiMap<String, TestError> b = errorTreeDeep.get(s).get(m);
182 if (b == null) {
183 b = new MultiMap<String, TestError>(20);
184 errorTreeDeep.get(s).put(m, b);
185 }
186 b.put(d, e);
187 } else {
188 errorTree.get(s).put(m, e);
189 }
190 }
191
192 List<TreePath> expandedPaths = new ArrayList<TreePath>();
193 for (Severity s : Severity.values()) {
194 MultiMap<String, TestError> severityErrors = errorTree.get(s);
195 Map<String, MultiMap<String, TestError>> severityErrorsDeep = errorTreeDeep.get(s);
196 if (severityErrors.isEmpty() && severityErrorsDeep.isEmpty()) {
197 continue;
198 }
199
200 // Severity node
201 DefaultMutableTreeNode severityNode = new DefaultMutableTreeNode(s);
202 rootNode.add(severityNode);
203
204 if (oldSelectedRows.contains(s)) {
205 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode }));
206 }
207
208 for (Entry<String, LinkedHashSet<TestError>> msgErrors : severityErrors.entrySet()) {
209 // Message node
210 Set<TestError> errs = msgErrors.getValue();
211 String msg = msgErrors.getKey() + " (" + errs.size() + ")";
212 DefaultMutableTreeNode messageNode = new DefaultMutableTreeNode(msg);
213 severityNode.add(messageNode);
214
215 if (oldSelectedRows.contains(msgErrors.getKey())) {
216 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, messageNode }));
217 }
218
219 for (TestError error : errs) {
220 // Error node
221 DefaultMutableTreeNode errorNode = new DefaultMutableTreeNode(error);
222 messageNode.add(errorNode);
223 }
224 }
225 for (Entry<String, MultiMap<String, TestError>> bag : severityErrorsDeep.entrySet()) {
226 // Group node
227 MultiMap<String, TestError> errorlist = bag.getValue();
228 DefaultMutableTreeNode groupNode = null;
229 if (errorlist.size() > 1) {
230 String nmsg = bag.getKey() + " (" + errorlist.size() + ")";
231 groupNode = new DefaultMutableTreeNode(nmsg);
232 severityNode.add(groupNode);
233 if (oldSelectedRows.contains(bag.getKey())) {
234 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, groupNode }));
235 }
236 }
237
238 for (Entry<String, LinkedHashSet<TestError>> msgErrors : errorlist.entrySet()) {
239 // Message node
240 Set<TestError> errs = msgErrors.getValue();
241 String msg;
242 if (groupNode != null) {
243 msg = msgErrors.getKey() + " (" + errs.size() + ")";
244 } else {
245 msg = msgErrors.getKey() + " - " + bag.getKey() + " (" + errs.size() + ")";
246 }
247 DefaultMutableTreeNode messageNode = new DefaultMutableTreeNode(msg);
248 if (groupNode != null) {
249 groupNode.add(messageNode);
250 } else {
251 severityNode.add(messageNode);
252 }
253
254 if (oldSelectedRows.contains(msgErrors.getKey())) {
255 if (groupNode != null) {
256 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, groupNode,
257 messageNode }));
258 } else {
259 expandedPaths.add(new TreePath(new Object[] { rootNode, severityNode, messageNode }));
260 }
261 }
262
263 for (TestError error : errs) {
264 // Error node
265 DefaultMutableTreeNode errorNode = new DefaultMutableTreeNode(error);
266 messageNode.add(errorNode);
267 }
268 }
269 }
270 }
271
272 valTreeModel.setRoot(rootNode);
273 for (TreePath path : expandedPaths) {
274 this.expandPath(path);
275 }
276 }
277
278 /**
279 * Sets the errors list used by a data layer
280 * @param errors The error list that is used by a data layer
281 */
282 public void setErrorList(List<TestError> errors) {
283 this.errors = errors;
284 if (isVisible()) {
285 buildTree();
286 }
287 }
288
289 /**
290 * Clears the current error list and adds these errors to it
291 * @param errors The validation errors
292 */
293 public void setErrors(List<TestError> newerrors) {
294 if (errors == null)
295 return;
296 errors.clear();
297 for (TestError error : newerrors) {
298 if (!error.getIgnored()) {
299 errors.add(error);
300 }
301 }
302 if (isVisible()) {
303 buildTree();
304 }
305 }
306
307 /**
308 * Returns the errors of the tree
309 * @return the errors of the tree
310 */
311 public List<TestError> getErrors() {
312 return errors != null ? errors : Collections.<TestError> emptyList();
313 }
314
315 public Set<OsmPrimitive> getFilter() {
316 return filter;
317 }
318
319 public void setFilter(Set<OsmPrimitive> filter) {
320 if (filter != null && filter.isEmpty()) {
321 this.filter = null;
322 } else {
323 this.filter = filter;
324 }
325 if (isVisible()) {
326 buildTree();
327 }
328 }
329
330 /**
331 * Updates the current errors list
332 * @param errors The validation errors
333 */
334 public void resetErrors() {
335 List<TestError> e = new ArrayList<TestError>(errors);
336 setErrors(e);
337 }
338
339 /**
340 * Expands all tree
341 */
342 @SuppressWarnings("unchecked")
343 public void expandAll() {
344 DefaultMutableTreeNode root = getRoot();
345
346 int row = 0;
347 Enumeration<DefaultMutableTreeNode> children = root.breadthFirstEnumeration();
348 while (children.hasMoreElements()) {
349 children.nextElement();
350 expandRow(row++);
351 }
352 }
353
354 /**
355 * Returns the root node model.
356 * @return The root node model
357 */
358 public DefaultMutableTreeNode getRoot() {
359 return (DefaultMutableTreeNode) valTreeModel.getRoot();
360 }
361
362 public int getUpdateCount() {
363 return updateCount;
364 }
365}
Note: See TracBrowser for help on using the repository browser.