001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.imagery; 003 004import java.awt.Component; 005import java.util.Collection; 006import java.util.LinkedList; 007import java.util.List; 008 009import javax.swing.JTree; 010import javax.swing.event.TreeSelectionEvent; 011import javax.swing.event.TreeSelectionListener; 012import javax.swing.tree.DefaultMutableTreeNode; 013import javax.swing.tree.DefaultTreeCellRenderer; 014import javax.swing.tree.DefaultTreeModel; 015import javax.swing.tree.MutableTreeNode; 016import javax.swing.tree.TreePath; 017 018import org.openstreetmap.josm.data.imagery.LayerDetails; 019import org.openstreetmap.josm.io.imagery.WMSImagery; 020 021/** 022 * The layer tree of a WMS server. 023 */ 024public class WMSLayerTree { 025 private final MutableTreeNode treeRootNode = new DefaultMutableTreeNode(); 026 private final DefaultTreeModel treeData = new DefaultTreeModel(treeRootNode); 027 private final JTree layerTree = new JTree(treeData); 028 private final List<LayerDetails> selectedLayers = new LinkedList<>(); 029 030 /** 031 * Returns the root node. 032 * @return The root node 033 */ 034 public MutableTreeNode getTreeRootNode() { 035 return treeRootNode; 036 } 037 038 /** 039 * Returns the {@code JTree}. 040 * @return The {@code JTree} 041 */ 042 public JTree getLayerTree() { 043 return layerTree; 044 } 045 046 /** 047 * Returns the list of selected layers. 048 * @return the list of selected layers 049 */ 050 public List<LayerDetails> getSelectedLayers() { 051 return selectedLayers; 052 } 053 054 /** 055 * Constructs a new {@code WMSLayerTree}. 056 */ 057 public WMSLayerTree() { 058 layerTree.setCellRenderer(new LayerTreeCellRenderer()); 059 layerTree.addTreeSelectionListener(new WMSTreeSelectionListener()); 060 } 061 062 void addLayersToTreeData(MutableTreeNode parent, Collection<LayerDetails> layers) { 063 for (LayerDetails layerDetails : layers.stream() 064 .sorted((l1, l2) -> -1 * l1.toString().compareTo(l2.toString())) 065 .toArray(LayerDetails[]::new) 066 ) { 067 DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(layerDetails); 068 addLayersToTreeData(treeNode, layerDetails.getChildren()); 069 treeData.insertNodeInto(treeNode, parent, 0); 070 } 071 } 072 073 /** 074 * Updates the whole tree with the given WMS imagery info. All previous content is removed 075 * @param wms The imagery info for a given WMS server 076 */ 077 public void updateTree(WMSImagery wms) { 078 while (treeRootNode.getChildCount() > 0) { 079 treeRootNode.remove(0); 080 } 081 treeRootNode.setUserObject(wms.buildRootUrl()); 082 updateTreeList(wms.getLayers()); 083 } 084 085 /** 086 * Updates the list of WMS layers. 087 * @param layers The list of layers to add to the root node 088 */ 089 public void updateTreeList(Collection<LayerDetails> layers) { 090 addLayersToTreeData(getTreeRootNode(), layers); 091 getLayerTree().expandRow(0); 092 getLayerTree().expandRow(1); 093 } 094 095 private static class LayerTreeCellRenderer extends DefaultTreeCellRenderer { 096 @Override 097 public Component getTreeCellRendererComponent(JTree tree, Object value, 098 boolean sel, boolean expanded, boolean leaf, int row, 099 boolean hasFocus) { 100 super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, 101 row, hasFocus); 102 DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value; 103 Object userObject = treeNode.getUserObject(); 104 if (userObject instanceof LayerDetails) { 105 LayerDetails ld = (LayerDetails) userObject; 106 setEnabled(ld.isSelectable()); 107 } 108 return this; 109 } 110 } 111 112 private class WMSTreeSelectionListener implements TreeSelectionListener { 113 114 @Override 115 public void valueChanged(TreeSelectionEvent e) { 116 TreePath[] selectionRows = layerTree.getSelectionPaths(); 117 if (selectionRows == null) { 118 return; 119 } 120 121 selectedLayers.clear(); 122 for (TreePath i : selectionRows) { 123 Object userObject = ((DefaultMutableTreeNode) i.getLastPathComponent()).getUserObject(); 124 if (userObject instanceof LayerDetails) { 125 LayerDetails detail = (LayerDetails) userObject; 126 if (detail.isSelectable()) { 127 selectedLayers.add(detail); 128 } 129 } 130 } 131 layerTree.firePropertyChange("selectedLayers", /*dummy values*/ false, true); 132 } 133 } 134}