/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.snap.rcp.layermanager; import com.bc.ceres.glayer.Layer; import com.bc.ceres.glayer.support.AbstractLayerListener; import com.bc.ceres.glayer.support.LayerUtils; import com.bc.ceres.swing.TreeCellExtender; import com.jidesoft.swing.CheckBoxTree; import com.jidesoft.swing.CheckBoxTreeSelectionModel; import org.esa.snap.core.layer.MaskLayerType; import org.esa.snap.rcp.layermanager.layersrc.SelectLayerSourceAssistantPage; import org.esa.snap.rcp.windows.ToolTopComponent; import org.esa.snap.ui.GridBagUtils; import org.esa.snap.ui.UIUtils; import org.esa.snap.ui.layer.LayerSourceAssistantPane; import org.esa.snap.ui.layer.LayerSourceDescriptor; import org.esa.snap.ui.product.ProductSceneView; import org.esa.snap.ui.product.VectorDataLayer; import org.esa.snap.ui.tool.ToolButtonFactory; import org.openide.util.HelpCtx; import javax.swing.AbstractButton; import javax.swing.DropMode; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.util.Hashtable; import java.util.List; import java.util.Map; class LayerManagerForm implements AbstractLayerForm { private final ProductSceneView view; private final ToolTopComponent parentComponent; private CheckBoxTree layerTree; private JSlider transparencySlider; private JSlider swipeSlider; private JPanel control; private boolean adjusting; private LayerTreeModel layerTreeModel; private JLabel transparencyLabel; private JLabel swipeLabel; private RemoveLayerAction removeLayerAction; private MoveLayerUpAction moveLayerUpAction; private MoveLayerDownAction moveLayerDownAction; private MoveLayerLeftAction moveLayerLeftAction; private MoveLayerRightAction moveLayerRightAction; private OpenLayerEditorAction openLayerEditorAction; private ZoomToLayerAction zoomToLayerAction; private LayerManagerForm.TransparencyChangeListener transparencyChangeListener; private LayerManagerForm.SwipeChangeListener swipeChangeListener; LayerManagerForm(ToolTopComponent parentComponent) { super(); this.parentComponent = parentComponent; this.view = parentComponent.getSelectedProductSceneView(); transparencyChangeListener = new TransparencyChangeListener(); swipeChangeListener = new SwipeChangeListener(); initUI(); } private void initUI() { layerTreeModel = new LayerTreeModel(view.getRootLayer()); layerTree = createCheckBoxTree(layerTreeModel); layerTree.setCellRenderer(new MyTreeCellRenderer()); TreeCellExtender.equip(layerTree); Hashtable<Integer, JLabel> transparencySliderLabelTable = new Hashtable<>(); transparencySliderLabelTable.put(0, createSliderLabel("0%")); transparencySliderLabelTable.put(127, createSliderLabel("50%")); transparencySliderLabelTable.put(255, createSliderLabel("100%")); transparencySlider = new JSlider(0, 255, 0); transparencySlider.setLabelTable(transparencySliderLabelTable); transparencySlider.setPaintLabels(true); transparencySlider.addChangeListener(new TransparencySliderListener()); transparencyLabel = new JLabel("Transparency:"); Hashtable<Integer, JLabel> swipeSliderLabelTable = new Hashtable<>(); swipeSliderLabelTable.put(0, createSliderLabel("0%")); swipeSliderLabelTable.put(50, createSliderLabel("50%")); swipeSliderLabelTable.put(100, createSliderLabel("100%")); swipeSlider = new JSlider(0, 100, 0); swipeSlider.setLabelTable(swipeSliderLabelTable); swipeSlider.setPaintLabels(true); swipeSlider.addChangeListener(new SwipeSliderListener()); swipeLabel = new JLabel("Swipe:"); final JPanel sliderPanel = GridBagUtils.createPanel(); final GridBagConstraints slidegbc = new GridBagConstraints(); slidegbc.anchor = GridBagConstraints.NORTHWEST; slidegbc.fill = GridBagConstraints.HORIZONTAL; slidegbc.gridy = 0; slidegbc.gridx = 0; sliderPanel.add(transparencyLabel, slidegbc); slidegbc.gridx = 1; slidegbc.weightx = 2; sliderPanel.add(transparencySlider, slidegbc); slidegbc.gridy++; slidegbc.gridx = 0; sliderPanel.add(swipeLabel, slidegbc); slidegbc.gridx = 1; slidegbc.weightx = 2; sliderPanel.add(swipeSlider, slidegbc); getRootLayer().addListener(new RootLayerListener()); AbstractButton addButton = createToolButton("icons/Plus24.gif"); addButton.addActionListener(new AddLayerActionListener()); removeLayerAction = new RemoveLayerAction(); AbstractButton removeButton = ToolButtonFactory.createButton(removeLayerAction, false); openLayerEditorAction = new OpenLayerEditorAction(); AbstractButton openButton = ToolButtonFactory.createButton(openLayerEditorAction, false); zoomToLayerAction = new ZoomToLayerAction(); AbstractButton zoomButton = ToolButtonFactory.createButton(zoomToLayerAction, false); moveLayerUpAction = new MoveLayerUpAction(); AbstractButton upButton = ToolButtonFactory.createButton(moveLayerUpAction, false); moveLayerDownAction = new MoveLayerDownAction(); AbstractButton downButton = ToolButtonFactory.createButton(moveLayerDownAction, false); moveLayerLeftAction = new MoveLayerLeftAction(); AbstractButton leftButton = ToolButtonFactory.createButton(moveLayerLeftAction, false); moveLayerRightAction = new MoveLayerRightAction(); AbstractButton rightButton = ToolButtonFactory.createButton(moveLayerRightAction, false); AbstractButton helpButton = createToolButton("icons/Help22.png"); helpButton.setToolTipText("Help."); /*I18N*/ helpButton.setName("helpButton"); helpButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { new HelpCtx(Bundle.CTL_LayerManagerTopComponent_HelpId()).display(); } }); final JPanel actionBar = GridBagUtils.createPanel(); final GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.NONE; gbc.insets.top = 2; gbc.gridy = 0; actionBar.add(addButton, gbc); gbc.gridy++; actionBar.add(removeButton, gbc); gbc.gridy++; actionBar.add(openButton, gbc); gbc.gridy++; actionBar.add(zoomButton, gbc); gbc.gridy++; actionBar.add(upButton, gbc); gbc.gridy++; actionBar.add(downButton, gbc); gbc.gridy++; actionBar.add(leftButton, gbc); gbc.gridy++; actionBar.add(rightButton, gbc); gbc.gridy++; gbc.insets.bottom = 0; gbc.fill = GridBagConstraints.VERTICAL; gbc.weighty = 1.0; gbc.gridwidth = 2; actionBar.add(new JLabel(" "), gbc); // filler gbc.fill = GridBagConstraints.NONE; gbc.weighty = 0.0; gbc.gridy = 10; gbc.anchor = GridBagConstraints.EAST; actionBar.add(helpButton, gbc); JPanel layerPanel = new JPanel(new BorderLayout(4, 4)); layerPanel.add(new JScrollPane(layerTree), BorderLayout.CENTER); layerPanel.add(sliderPanel, BorderLayout.SOUTH); control = new JPanel(new BorderLayout(4, 4)); control.add(layerPanel, BorderLayout.CENTER); control.add(actionBar, BorderLayout.EAST); initLayerTreeVisibility(view.getRootLayer()); updateFormControl(); } private static JLabel createSliderLabel(String text) { JLabel label = new JLabel(text); Font oldFont = label.getFont(); Font newFont = oldFont.deriveFont(oldFont.getSize2D() * 0.85f); label.setFont(newFont); return label; } public Layer getRootLayer() { return view.getRootLayer(); } @Override public JComponent getFormControl() { return control; } @Override public void updateFormControl() { Layer selectedLayer = getSelectedLayer(); updateLayerStyleUI(selectedLayer); updateLayerTreeSelection(selectedLayer); boolean isLayerSelected = selectedLayer != null; removeLayerAction.setEnabled(isLayerSelected && !isLayerProtected(selectedLayer)); openLayerEditorAction.setEnabled(isLayerSelected); moveLayerUpAction.setEnabled(isLayerSelected && moveLayerUpAction.canMove(selectedLayer)); moveLayerDownAction.setEnabled(isLayerSelected && moveLayerDownAction.canMove(selectedLayer)); moveLayerLeftAction.setEnabled(isLayerSelected && moveLayerLeftAction.canMove(selectedLayer)); moveLayerRightAction.setEnabled(isLayerSelected && moveLayerRightAction.canMove(selectedLayer)); zoomToLayerAction.setEnabled(isLayerSelected); } public static boolean isLayerProtected(Layer layer) { return isLayerProtectedImpl(layer) || isChildLayerProtected(layer); } private Layer getSelectedLayer() { return view.getSelectedLayer(); } private static boolean isLayerProtectedImpl(Layer layer) { return layer.getId().equals(ProductSceneView.BASE_IMAGE_LAYER_ID); } private static boolean isChildLayerProtected(Layer selectedLayer) { Layer[] children = selectedLayer.getChildren().toArray(new Layer[selectedLayer.getChildren().size()]); for (Layer childLayer : children) { if (isLayerProtectedImpl(childLayer) || isChildLayerProtected(childLayer)) { return true; } } return false; } private static boolean isLayerNameEditable(Layer layer) { if (layer instanceof VectorDataLayer || layer.getConfiguration().isPropertyDefined(MaskLayerType.PROPERTY_NAME_MASK)) { return false; } return true; } private static Layer getLayer(TreePath path) { if (path == null) { return null; } return (Layer) path.getLastPathComponent(); } private void initLayerTreeVisibility(final Layer layer) { updateLayerTreeVisibility(layer); for (Layer childLayer : layer.getChildren()) { initLayerTreeVisibility(childLayer); } } private void updateLayerTreeVisibility(Layer layer) { CheckBoxTreeSelectionModel checkBoxTreeSelectionModel = layerTree.getCheckBoxTreeSelectionModel(); Layer[] layerPath = LayerUtils.getLayerPath(layerTreeModel.getRootLayer(), layer); if (layerPath.length > 0) { if (layer.isVisible()) { checkBoxTreeSelectionModel.addSelectionPath(new TreePath(layerPath)); } else { checkBoxTreeSelectionModel.removeSelectionPath(new TreePath(layerPath)); } final List<Layer> children = layer.getChildren(); if (!children.isEmpty()) { for (Layer child : children) { updateLayerTreeVisibility(child); } } } } private void updateLayerTreeSelection(Layer selectedLayer) { if (selectedLayer != null) { Layer[] layerPath = LayerUtils.getLayerPath(layerTreeModel.getRootLayer(), selectedLayer); if (layerPath.length > 0) { layerTree.setSelectionPath(new TreePath(layerPath)); } else { layerTree.clearSelection(); } } else { layerTree.clearSelection(); } } private void updateLayerStyleUI(Layer layer) { transparencyLabel.setEnabled(layer != null); transparencySlider.setEnabled(layer != null); if (layer != null) { final double transparency = layer.getTransparency(); final int n = (int) Math.round(255.0 * transparency); transparencySlider.setValue(n); } swipeLabel.setEnabled(layer != null); swipeSlider.setEnabled(layer != null); if (layer != null) { final double swipe = layer.getSwipePercent(); final int n = (int) Math.round(100.0 * swipe); swipeSlider.setValue(n); } } private CheckBoxTree createCheckBoxTree(LayerTreeModel treeModel) { final CheckBoxTree checkBoxTree = new CheckBoxTree(treeModel) { @Override public boolean isPathEditable(TreePath path) { Layer layer = getLayer(path); if (layer != null) { return isLayerNameEditable(layer); } return false; } }; checkBoxTree.setRootVisible(false); checkBoxTree.setShowsRootHandles(true); checkBoxTree.setDigIn(false); checkBoxTree.setEditable(true); checkBoxTree.setDragEnabled(true); checkBoxTree.setDropMode(DropMode.ON_OR_INSERT); checkBoxTree.setTransferHandler(new LayerTreeTransferHandler(view, checkBoxTree)); checkBoxTree.getSelectionModel().addTreeSelectionListener(new LayerSelectionListener()); final CheckBoxTreeSelectionModel checkBoxSelectionModel = checkBoxTree.getCheckBoxTreeSelectionModel(); checkBoxSelectionModel.addTreeSelectionListener(new CheckBoxTreeSelectionListener()); final DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) checkBoxTree.getActualCellRenderer(); renderer.setLeafIcon(null); renderer.setClosedIcon(null); renderer.setOpenIcon(null); return checkBoxTree; } private void installTransparencyChangeListener(Layer selectedLayer) { selectedLayer.addListener(transparencyChangeListener); } private void removeTransparencyChangeListener(Layer selectedLayer) { selectedLayer.removeListener(transparencyChangeListener); } private void installSwipeChangeListener(Layer selectedLayer) { selectedLayer.addListener(swipeChangeListener); } private void removeSwipeChangeListener(Layer selectedLayer) { selectedLayer.removeListener(swipeChangeListener); } public static AbstractButton createToolButton(final String iconPath) { return ToolButtonFactory.createButton(UIUtils.loadImageIcon(iconPath), false); } private class RootLayerListener extends AbstractLayerListener { @Override public void handleLayerPropertyChanged(Layer layer, PropertyChangeEvent event) { if ("visible".equals(event.getPropertyName())) { updateLayerTreeVisibility(layer); } } @Override public void handleLayersAdded(Layer parentLayer, Layer[] childLayers) { for (Layer layer : childLayers) { updateLayerTreeVisibility(layer); updateLayerTreeSelection(layer); } } } private class TransparencySliderListener implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { TreePath path = layerTree.getSelectionPath(); if (path != null) { Layer layer = getLayer(path); adjusting = true; layer.setTransparency(transparencySlider.getValue() / 255.0); adjusting = false; } } } private class SwipeSliderListener implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { TreePath path = layerTree.getSelectionPath(); if (path != null) { Layer layer = getLayer(path); adjusting = true; layer.setSwipePercent(swipeSlider.getValue() / 100.0); adjusting = false; } } } private class AddLayerActionListener implements ActionListener { private Rectangle screenBounds; @Override public void actionPerformed(ActionEvent e) { LayerSourceAssistantPane pane = new LayerSourceAssistantPane(SwingUtilities.getWindowAncestor(control), "Add Layer"); final Map<String, LayerSourceDescriptor> layerSourceDescriptors1 = LayerManager.getDefault().getLayerSourceDescriptors(); final LayerSourceDescriptor[] layerSourceDescriptors2 = layerSourceDescriptors1.values().toArray(new LayerSourceDescriptor[layerSourceDescriptors1.size()]); pane.show(new SelectLayerSourceAssistantPage(layerSourceDescriptors2), screenBounds); screenBounds = pane.getWindow().getBounds(); } } private static class MyTreeCellRenderer extends DefaultTreeCellRenderer { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { JLabel label = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); Layer layer = (Layer) value; if (ProductSceneView.BASE_IMAGE_LAYER_ID.equals(layer.getId())) { label.setText(String.format("<html><b>%s</b></html>", layer.getName())); } return label; } } private class TransparencyChangeListener extends AbstractLayerListener { @Override public void handleLayerPropertyChanged(Layer layer, PropertyChangeEvent event) { if("transparency".equals(event.getPropertyName())) { updateLayerStyleUI(layer); } } } private class SwipeChangeListener extends AbstractLayerListener { @Override public void handleLayerPropertyChanged(Layer layer, PropertyChangeEvent event) { if("swipePercent".equals(event.getPropertyName())) { updateLayerStyleUI(layer); } } } private class LayerSelectionListener implements TreeSelectionListener { private Layer selectedLayer; @Override public void valueChanged(TreeSelectionEvent event) { if (selectedLayer != null) { removeTransparencyChangeListener(selectedLayer); removeSwipeChangeListener(selectedLayer); } selectedLayer = getLayer(event.getNewLeadSelectionPath()); if (selectedLayer != null) { installTransparencyChangeListener(selectedLayer); installSwipeChangeListener(selectedLayer); } if(parentComponent.getSelectedProductSceneView() != null) { parentComponent.getSelectedProductSceneView().setSelectedLayer(selectedLayer); } } } private class CheckBoxTreeSelectionListener implements TreeSelectionListener { @Override public void valueChanged(TreeSelectionEvent event) { if (!adjusting) { TreePath path = event.getPath(); Layer layer = getLayer(path); if (layer.getParent() != null) { boolean pathSelected = ((TreeSelectionModel) event.getSource()).isPathSelected(path); layer.setVisible(pathSelected); } } } } }