source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/AddWMSLayerPanel.java@ 4468

Last change on this file since 4468 was 4468, checked in by simon04, 13 years ago

fix #6888 - WMS: avoid NullPointerException (patch by Don-vip)

  • Property svn:eol-style set to native
File size: 24.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trc;
6
7import java.awt.Component;
8import java.awt.Cursor;
9import java.awt.GridBagConstraints;
10import java.awt.GridBagLayout;
11import java.awt.HeadlessException;
12import java.awt.event.ActionEvent;
13import java.awt.event.ActionListener;
14import java.awt.event.KeyAdapter;
15import java.awt.event.KeyEvent;
16import java.io.BufferedReader;
17import java.io.IOException;
18import java.io.InputStream;
19import java.io.InputStreamReader;
20import java.io.StringReader;
21import java.net.MalformedURLException;
22import java.net.URL;
23import java.net.URLConnection;
24import java.util.HashSet;
25import java.util.Iterator;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.Set;
29
30import java.util.regex.Pattern;
31import javax.swing.JButton;
32import javax.swing.JFrame;
33import javax.swing.JLabel;
34import javax.swing.JOptionPane;
35import javax.swing.JPanel;
36import javax.swing.JScrollPane;
37import javax.swing.JTabbedPane;
38import javax.swing.JTextArea;
39import javax.swing.JTextField;
40import javax.swing.JTree;
41import javax.swing.event.ChangeEvent;
42import javax.swing.event.ChangeListener;
43import javax.swing.event.TreeSelectionEvent;
44import javax.swing.event.TreeSelectionListener;
45import javax.swing.tree.DefaultMutableTreeNode;
46import javax.swing.tree.DefaultTreeCellRenderer;
47import javax.swing.tree.DefaultTreeModel;
48import javax.swing.tree.MutableTreeNode;
49import javax.swing.tree.TreePath;
50import javax.xml.parsers.DocumentBuilder;
51import javax.xml.parsers.DocumentBuilderFactory;
52import javax.xml.parsers.ParserConfigurationException;
53
54import org.openstreetmap.josm.data.Bounds;
55import org.openstreetmap.josm.data.projection.Projection;
56import org.openstreetmap.josm.data.projection.ProjectionSubPrefs;
57import org.openstreetmap.josm.data.projection.Projections;
58import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser;
59import org.openstreetmap.josm.tools.GBC;
60import org.w3c.dom.Document;
61import org.w3c.dom.Element;
62import org.w3c.dom.Node;
63import org.w3c.dom.NodeList;
64import org.xml.sax.EntityResolver;
65import org.xml.sax.InputSource;
66import org.xml.sax.SAXException;
67
68
69public class AddWMSLayerPanel extends JPanel {
70 private List<LayerDetails> selectedLayers;
71 private URL serviceUrl;
72 private LayerDetails selectedLayer;
73
74 private JTextField menuName;
75 private JTextArea resultingLayerField;
76 private MutableTreeNode treeRootNode;
77 private DefaultTreeModel treeData;
78 private JTree layerTree;
79 private JButton showBoundsButton;
80
81 private boolean previouslyShownUnsupportedCrsError = false;
82 private JTextArea tmsURL;
83 private JTextField tmsZoom;
84
85 public AddWMSLayerPanel() {
86 JPanel imageryAddPanel = new JPanel(new GridBagLayout());
87 imageryAddPanel.add(new JLabel(tr("Menu Name")), GBC.std().insets(0,0,5,0));
88 menuName = new JTextField(40);
89 menuName.setText(tr("Unnamed Imagery Layer"));
90 imageryAddPanel.add(menuName, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
91
92 final JTabbedPane tabbedPane = new JTabbedPane();
93
94 final JPanel wmsFetchPanel = new JPanel(new GridBagLayout());
95 tabbedPane.addTab(tr("WMS"), wmsFetchPanel);
96 imageryAddPanel.add(tabbedPane, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
97
98 final JTextArea serviceUrlText = new JTextArea(3, 40);
99 serviceUrlText.setLineWrap(true);
100 serviceUrlText.setText("http://sample.com/wms?");
101 wmsFetchPanel.add(new JLabel(tr("Service URL")), GBC.std().insets(0,0,5,0));
102 JScrollPane scrollPane = new JScrollPane(serviceUrlText,
103 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
104 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
105 wmsFetchPanel.add(scrollPane, GBC.eop().insets(5,0,0,0));
106 JButton getLayersButton = new JButton(tr("Get Layers"));
107 getLayersButton.addActionListener(new ActionListener() {
108 @Override
109 public void actionPerformed(ActionEvent e) {
110 Cursor beforeCursor = getCursor();
111 try {
112 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
113 attemptGetCapabilities(sanitize(serviceUrlText.getText()));
114 } finally {
115 setCursor(beforeCursor);
116 }
117 }
118 });
119 wmsFetchPanel.add(getLayersButton, GBC.eop().anchor(GridBagConstraints.EAST));
120
121 treeRootNode = new DefaultMutableTreeNode();
122 treeData = new DefaultTreeModel(treeRootNode);
123 layerTree = new JTree(treeData);
124 layerTree.setCellRenderer(new LayerTreeCellRenderer());
125 layerTree.addTreeSelectionListener(new TreeSelectionListener() {
126
127 @Override
128 public void valueChanged(TreeSelectionEvent e) {
129 TreePath[] selectionRows = layerTree.getSelectionPaths();
130 if(selectionRows == null) {
131 showBoundsButton.setEnabled(false);
132 selectedLayer = null;
133 return;
134 }
135
136 selectedLayers = new LinkedList<LayerDetails>();
137 for (TreePath i : selectionRows) {
138 Object userObject = ((DefaultMutableTreeNode) i.getLastPathComponent()).getUserObject();
139 if(userObject instanceof LayerDetails) {
140 LayerDetails detail = (LayerDetails) userObject;
141 if(!detail.isSupported()) {
142 layerTree.removeSelectionPath(i);
143 if(!previouslyShownUnsupportedCrsError) {
144 JOptionPane.showMessageDialog(null, tr("That layer does not support any of JOSM''s projections,\n" +
145 "so you can not use it. This message will not show again."),
146 tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
147 previouslyShownUnsupportedCrsError = true;
148 }
149 } else if(detail.ident != null) {
150 selectedLayers.add(detail);
151 }
152 }
153 }
154
155 if (!selectedLayers.isEmpty()) {
156 resultingLayerField.setText(buildGetMapUrl());
157
158 if(selectedLayers.size() == 1) {
159 showBoundsButton.setEnabled(true);
160 selectedLayer = selectedLayers.get(0);
161 }
162 } else {
163 showBoundsButton.setEnabled(false);
164 selectedLayer = null;
165 }
166 }
167 });
168 wmsFetchPanel.add(new JScrollPane(layerTree), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
169
170 JPanel layerManipulationButtons = new JPanel();
171 showBoundsButton = new JButton(tr("Show Bounds"));
172 showBoundsButton.setEnabled(false);
173 showBoundsButton.addActionListener(new ActionListener() {
174 @Override
175 public void actionPerformed(ActionEvent e) {
176 if(selectedLayer.bounds != null) {
177 SlippyMapBBoxChooser mapPanel = new SlippyMapBBoxChooser();
178 mapPanel.setBoundingBox(selectedLayer.bounds);
179 JOptionPane.showMessageDialog(null, mapPanel, tr("Show Bounds"), JOptionPane.PLAIN_MESSAGE);
180 } else {
181 JOptionPane.showMessageDialog(null, tr("No bounding box was found for this layer."),
182 tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
183 }
184 }
185 });
186 layerManipulationButtons.add(showBoundsButton);
187
188 wmsFetchPanel.add(layerManipulationButtons, GBC.eol().insets(0,0,5,0));
189
190 final JPanel tmsView = new JPanel(new GridBagLayout());
191 tmsView.add(new JLabel(tr("TMS URL")), GBC.std().insets(0,0,5,0));
192 tmsURL = new JTextArea(3, 40);
193 tmsURL.setLineWrap(true);
194 tmsURL.setText("http://sample.com/tms/{zoom}/{x}/{y}.jpg");
195 tmsURL.addKeyListener(new KeyAdapter() {
196 @Override
197 public void keyReleased(KeyEvent e) {
198 resultingLayerField.setText(buildTMSUrl());
199 }
200 });
201 JScrollPane tmsUrlScrollPane = new JScrollPane(tmsURL,
202 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
203 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
204 tmsView.add(tmsUrlScrollPane, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
205 tmsView.add(new JLabel(trc("layer", "Zoom")), GBC.std().insets(0,0,5,0));
206 tmsZoom = new JTextField(3);
207 tmsZoom.addKeyListener(new KeyAdapter() {
208 @Override
209 public void keyReleased(KeyEvent e) {
210 resultingLayerField.setText(buildTMSUrl());
211 }
212 });
213 tmsView.add(tmsZoom, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
214 tabbedPane.addTab(tr("TMS"), tmsView);
215
216 imageryAddPanel.add(new JLabel(tr("Imagery URL")), GBC.std().insets(0,0,5,0));
217 resultingLayerField = new JTextArea(3, 40);
218 resultingLayerField.setLineWrap(true);
219 imageryAddPanel.add(new JScrollPane(resultingLayerField, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
220
221 tabbedPane.addChangeListener(new ChangeListener() {
222 @Override
223 public void stateChanged(ChangeEvent e) {
224 Component sel = tabbedPane.getSelectedComponent();
225 if(tmsView == sel) {
226 resultingLayerField.setText(buildTMSUrl());
227 } else if(wmsFetchPanel == sel) {
228 if(serviceUrl != null) {
229 resultingLayerField.setText(buildGetMapUrl());
230 }
231 }
232 }
233 });
234
235 add(imageryAddPanel);
236 }
237
238 private String sanitize(String s) {
239 return s.replaceAll("[\r\n]+","").trim();
240 }
241
242 private String buildTMSUrl() {
243 StringBuilder a = new StringBuilder("tms");
244 String z = sanitize(tmsZoom.getText());
245 if(!z.isEmpty())
246 a.append("["+z+"]");
247 a.append(":");
248 a.append(sanitize(tmsURL.getText()));
249 return a.toString();
250 }
251
252 private String buildRootUrl() {
253 StringBuilder a = new StringBuilder(serviceUrl.getProtocol());
254 a.append("://");
255 a.append(serviceUrl.getHost());
256 if(serviceUrl.getPort() != -1) {
257 a.append(":");
258 a.append(serviceUrl.getPort());
259 }
260 a.append(serviceUrl.getPath());
261 a.append("?");
262 if(serviceUrl.getQuery() != null) {
263 a.append(serviceUrl.getQuery());
264 if (!serviceUrl.getQuery().isEmpty() && !serviceUrl.getQuery().endsWith("&")) {
265 a.append("&");
266 }
267 }
268 return a.toString();
269 }
270
271 private String buildGetMapUrl() {
272 StringBuilder a = new StringBuilder();
273 a.append(buildRootUrl());
274 a.append("FORMAT=image/jpeg&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&Layers=");
275 a.append(commaSepLayerList());
276 a.append("&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}");
277
278 return a.toString();
279 }
280
281 private String commaSepLayerList() {
282 StringBuilder b = new StringBuilder();
283
284 if (selectedLayers != null) {
285 Iterator<LayerDetails> iterator = selectedLayers.iterator();
286 while (iterator.hasNext()) {
287 LayerDetails layerDetails = iterator.next();
288 b.append(layerDetails.ident);
289 if(iterator.hasNext()) {
290 b.append(",");
291 }
292 }
293 }
294
295 return b.toString();
296 }
297
298 private void showError(String incomingData, Exception e) {
299 JOptionPane.showMessageDialog(this, tr("Could not parse WMS layer list."),
300 tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
301 System.err.println("Could not parse WMS layer list. Incoming data:");
302 System.err.println(incomingData);
303 e.printStackTrace();
304 }
305
306 private void attemptGetCapabilities(String serviceUrlStr) {
307 URL getCapabilitiesUrl = null;
308 try {
309 if (!Pattern.compile(".*GetCapabilities.*", Pattern.CASE_INSENSITIVE).matcher(serviceUrlStr).matches()) {
310 // If the url doesn't already have GetCapabilities, add it in
311 getCapabilitiesUrl = new URL(serviceUrlStr);
312 final String getCapabilitiesQuery = "VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities";
313 if (getCapabilitiesUrl.getQuery() == null) {
314 getCapabilitiesUrl = new URL(serviceUrlStr + "?" + getCapabilitiesQuery);
315 } else if (!getCapabilitiesUrl.getQuery().isEmpty() && !getCapabilitiesUrl.getQuery().endsWith("&")) {
316 getCapabilitiesUrl = new URL(serviceUrlStr + "&" + getCapabilitiesQuery);
317 } else {
318 getCapabilitiesUrl = new URL(serviceUrlStr + getCapabilitiesQuery);
319 }
320 } else {
321 // Otherwise assume it's a good URL and let the subsequent error
322 // handling systems deal with problems
323 getCapabilitiesUrl = new URL(serviceUrlStr);
324 }
325 serviceUrl = new URL(serviceUrlStr);
326 } catch (HeadlessException e) {
327 return;
328 } catch (MalformedURLException e) {
329 JOptionPane.showMessageDialog(this, tr("Invalid service URL."),
330 tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
331 return;
332 }
333
334 String incomingData;
335 try {
336 System.out.println("GET "+getCapabilitiesUrl.toString());
337 URLConnection openConnection = getCapabilitiesUrl.openConnection();
338 InputStream inputStream = openConnection.getInputStream();
339 BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
340 String line;
341 StringBuilder ba = new StringBuilder();
342 while((line = br.readLine()) != null) {
343 ba.append(line);
344 ba.append("\n");
345 }
346 incomingData = ba.toString();
347 } catch (IOException e) {
348 JOptionPane.showMessageDialog(this, tr("Could not retrieve WMS layer list."),
349 tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
350 return;
351 }
352
353 Document document;
354 try {
355 //System.out.println("WMS capabilities:\n"+incomingData+"\n");
356 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
357 builderFactory.setValidating(false);
358 builderFactory.setNamespaceAware(true);
359 DocumentBuilder builder = builderFactory.newDocumentBuilder();
360 builder.setEntityResolver(new EntityResolver() {
361 @Override
362 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
363 System.out.println("Ignoring DTD " + publicId + ", " + systemId);
364 return new InputSource(new StringReader(""));
365 }
366 });
367 document = builder.parse(new InputSource(new StringReader(incomingData)));
368 } catch (ParserConfigurationException e) {
369 showError(incomingData, e);
370 return;
371 } catch (SAXException e) {
372 showError(incomingData, e);
373 return;
374 } catch (IOException e) {
375 showError(incomingData, e);
376 return;
377 }
378
379 // Some WMS service URLs specify a different base URL for their GetMap service
380 Element child = getChild(document.getDocumentElement(), "Capability");
381 child = getChild(child, "Request");
382 child = getChild(child, "GetMap");
383 child = getChild(child, "DCPType");
384 child = getChild(child, "HTTP");
385 child = getChild(child, "Get");
386 child = getChild(child, "OnlineResource");
387 if (child != null) {
388 String baseURL = child.getAttribute("xlink:href");
389 if(baseURL != null) {
390 try {
391 System.out.println("GetCapabilities specifies a different service URL: " + baseURL);
392 serviceUrl = new URL(baseURL);
393 } catch (MalformedURLException e1) {
394 }
395 }
396 }
397
398 try {
399 treeRootNode.setUserObject(getCapabilitiesUrl.getHost());
400 Element capabilityElem = getChild(document.getDocumentElement(), "Capability");
401 List<Element> children = getChildren(capabilityElem, "Layer");
402 List<LayerDetails> layers = parseLayers(children, new HashSet<String>());
403 updateTreeList(layers);
404 } catch(Exception e) {
405 showError(incomingData, e);
406 return;
407 }
408 }
409
410 private void updateTreeList(List<LayerDetails> layers) {
411 addLayersToTreeData(treeRootNode, layers);
412 layerTree.expandRow(0);
413 }
414
415 private void addLayersToTreeData(MutableTreeNode parent, List<LayerDetails> layers) {
416 for (LayerDetails layerDetails : layers) {
417 DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(layerDetails);
418 addLayersToTreeData(treeNode, layerDetails.children);
419 treeData.insertNodeInto(treeNode, parent, 0);
420 }
421 }
422
423 private List<LayerDetails> parseLayers(List<Element> children, Set<String> parentCrs) {
424 List<LayerDetails> details = new LinkedList<LayerDetails>();
425 for (Element element : children) {
426 details.add(parseLayer(element, parentCrs));
427 }
428 return details;
429 }
430
431 private LayerDetails parseLayer(Element element, Set<String> parentCrs) {
432 String name = getChildContent(element, "Title", null, null);
433 String ident = getChildContent(element, "Name", null, null);
434
435 // The set of supported CRS/SRS for this layer
436 Set<String> crsList = new HashSet<String>();
437 // ...including this layer's already-parsed parent projections
438 crsList.addAll(parentCrs);
439
440 // Parse the CRS/SRS pulled out of this layer's XML element
441 // I think CRS and SRS are the same at this point
442 List<Element> crsChildren = getChildren(element, "CRS");
443 crsChildren.addAll(getChildren(element, "SRS"));
444 for (Element child : crsChildren) {
445 String crs = (String) getContent(child);
446 if(crs != null) {
447 String upperCase = crs.trim().toUpperCase();
448 crsList.add(upperCase);
449 }
450 }
451
452 // Check to see if any of the specified projections are supported by JOSM
453 boolean josmSupportsThisLayer = false;
454 for (String crs : crsList) {
455 josmSupportsThisLayer |= isProjSupported(crs);
456 }
457
458 Bounds bounds = null;
459 Element bboxElem = getChild(element, "EX_GeographicBoundingBox");
460 if(bboxElem != null) {
461 // Attempt to use EX_GeographicBoundingBox for bounding box
462 double left = Double.parseDouble(getChildContent(bboxElem, "westBoundLongitude", null, null));
463 double top = Double.parseDouble(getChildContent(bboxElem, "northBoundLatitude", null, null));
464 double right = Double.parseDouble(getChildContent(bboxElem, "eastBoundLongitude", null, null));
465 double bot = Double.parseDouble(getChildContent(bboxElem, "southBoundLatitude", null, null));
466 bounds = new Bounds(bot, left, top, right);
467 } else {
468 // If that's not available, try LatLonBoundingBox
469 bboxElem = getChild(element, "LatLonBoundingBox");
470 if(bboxElem != null) {
471 double left = Double.parseDouble(bboxElem.getAttribute("minx"));
472 double top = Double.parseDouble(bboxElem.getAttribute("maxy"));
473 double right = Double.parseDouble(bboxElem.getAttribute("maxx"));
474 double bot = Double.parseDouble(bboxElem.getAttribute("miny"));
475 bounds = new Bounds(bot, left, top, right);
476 }
477 }
478
479 List<Element> layerChildren = getChildren(element, "Layer");
480 List<LayerDetails> childLayers = parseLayers(layerChildren, crsList);
481
482 return new LayerDetails(name, ident, crsList, josmSupportsThisLayer, bounds, childLayers);
483 }
484
485 private boolean isProjSupported(String crs) {
486 for (Projection proj : Projections.getProjections()) {
487 if (proj instanceof ProjectionSubPrefs) {
488 if (((ProjectionSubPrefs) proj).getPreferencesFromCode(crs) == null) {
489 return true;
490 }
491 } else {
492 if (proj.toCode().equals(crs)) {
493 return true;
494 }
495 }
496 }
497 return false;
498 }
499
500 public String getUrlName() {
501 return menuName.getText();
502 }
503
504 public String getUrl() {
505 return resultingLayerField.getText();
506 }
507
508 public static void main(String[] args) {
509 JFrame f = new JFrame("Test");
510 f.setContentPane(new AddWMSLayerPanel());
511 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
512 f.pack();
513 f.setVisible(true);
514 }
515
516 private static String getChildContent(Element parent, String name, String missing, String empty) {
517 Element child = getChild(parent, name);
518 if (child == null) {
519 return missing;
520 } else {
521 String content = (String) getContent(child);
522 return (content != null) ? content : empty;
523 }
524 }
525
526 private static Object getContent(Element element) {
527 NodeList nl = element.getChildNodes();
528 StringBuffer content = new StringBuffer();
529 for (int i = 0; i < nl.getLength(); i++) {
530 Node node = nl.item(i);
531 switch (node.getNodeType()) {
532 case Node.ELEMENT_NODE:
533 return node;
534 case Node.CDATA_SECTION_NODE:
535 case Node.TEXT_NODE:
536 content.append(node.getNodeValue());
537 break;
538 }
539 }
540 return content.toString().trim();
541 }
542
543 private static List<Element> getChildren(Element parent, String name) {
544 List<Element> retVal = new LinkedList<Element>();
545 for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
546 if (child instanceof Element && name.equals(child.getNodeName())) {
547 retVal.add((Element) child);
548 }
549 }
550 return retVal;
551 }
552
553 private static Element getChild(Element parent, String name) {
554 if (parent == null) {
555 return null;
556 }
557 for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
558 if (child instanceof Element && name.equals(child.getNodeName())) {
559 return (Element) child;
560 }
561 }
562 return null;
563 }
564
565 class LayerDetails {
566
567 private String name;
568 private String ident;
569 private List<LayerDetails> children;
570 private Bounds bounds;
571 private boolean supported;
572
573 public LayerDetails(String name, String ident, Set<String> crsList,
574 boolean supportedLayer, Bounds bounds,
575 List<LayerDetails> childLayers) {
576 this.name = name;
577 this.ident = ident;
578 this.supported = supportedLayer;
579 this.children = childLayers;
580 this.bounds = bounds;
581 }
582
583 public boolean isSupported() {
584 return this.supported;
585 }
586
587 @Override
588 public String toString() {
589 if(this.name == null || this.name.isEmpty()) {
590 return this.ident;
591 } else {
592 return this.name;
593 }
594 }
595
596 }
597
598 class LayerTreeCellRenderer extends DefaultTreeCellRenderer {
599 @Override
600 public Component getTreeCellRendererComponent(JTree tree, Object value,
601 boolean sel, boolean expanded, boolean leaf, int row,
602 boolean hasFocus) {
603 super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
604 row, hasFocus);
605 DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
606 Object userObject = treeNode.getUserObject();
607 if (userObject instanceof LayerDetails) {
608 LayerDetails layer = (LayerDetails) userObject;
609 setEnabled(layer.isSupported());
610 }
611 return this;
612 }
613 }
614
615}
Note: See TracBrowser for help on using the repository browser.