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