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}