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