/* * 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.actions.vector; import com.bc.ceres.binding.Property; import com.bc.ceres.binding.PropertyContainer; import com.bc.ceres.binding.PropertySet; import com.bc.ceres.binding.ValidationException; import com.bc.ceres.binding.Validator; import com.bc.ceres.glayer.Layer; import com.bc.ceres.glayer.LayerFilter; import com.bc.ceres.glayer.support.LayerUtils; import com.bc.ceres.swing.binding.PropertyPane; import org.esa.snap.core.datamodel.PlainFeatureFactory; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductNode; import org.esa.snap.core.datamodel.ProductNodeGroup; import org.esa.snap.core.datamodel.VectorDataNode; import org.esa.snap.rcp.SnapApp; import org.esa.snap.rcp.nodes.UndoableProductNodeInsertion; import org.esa.snap.ui.ModalDialog; import org.esa.snap.ui.product.ProductSceneView; import org.esa.snap.ui.product.VectorDataLayerFilterFactory; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.awt.UndoRedo; import org.openide.util.ContextAwareAction; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; import org.openide.util.NbBundle.Messages; import org.openide.util.Utilities; import org.openide.util.WeakListeners; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JPanel; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.text.MessageFormat; @ActionID(category = "Tools", id = "CreateVectorDataNodeAction" ) @ActionRegistration( displayName = "#CTL_CreateVectorDataNodeActionText", popupText = "#CTL_CreateVectorDataNodeActionPopupText", lazy = false ) @ActionReferences({ @ActionReference(path = "Menu/Vector", position = 0), @ActionReference(path = "Toolbars/Tools", position = 191) }) @Messages({ "CTL_CreateVectorDataNodeActionText=New Vector Data Container", "CTL_CreateVectorDataNodeActionPopupText=New Vector Data Container" }) public class CreateVectorDataNodeAction extends AbstractAction implements ContextAwareAction, LookupListener { private static final String HELP_ID = "vectorDataManagement"; private static int numItems = 1; private Lookup lkp; private Lookup.Result<ProductNode> result; public CreateVectorDataNodeAction() { this(Utilities.actionsGlobalContext()); } public CreateVectorDataNodeAction(Lookup lkp) { super(Bundle.CTL_CreateVectorDataNodeActionText()); this.lkp = lkp; result = this.lkp.lookupResult(ProductNode.class); result.addLookupListener(WeakListeners.create(LookupListener.class, this, result)); putValue(Action.LARGE_ICON_KEY, ImageUtilities.loadImageIcon("org/esa/snap/rcp/icons/NewVectorDataNode16.gif", false)); putValue(Action.SMALL_ICON, ImageUtilities.loadImageIcon("org/esa/snap/rcp/icons/NewVectorDataNode24.gif", false)); setEnabled(false); } public static VectorDataNode createDefaultVectorDataNode(Product product) { return createDefaultVectorDataNode(product, getDefaultVectorDataNodeName(), "Default vector data container for geometries (automatically created)"); } public static VectorDataNode createDefaultVectorDataNode(Product product, String name, String description) { CoordinateReferenceSystem modelCrs = product.getSceneCRS(); SimpleFeatureType type = PlainFeatureFactory.createDefaultFeatureType(modelCrs); VectorDataNode vectorDataNode = new VectorDataNode(name, type); vectorDataNode.setDescription(description); product.getVectorDataGroup().add(vectorDataNode); vectorDataNode.getPlacemarkGroup(); String oldLayerId = selectVectorDataLayer(vectorDataNode); UndoRedo.Manager undoManager = SnapApp.getDefault().getUndoManager(product); if (undoManager != null) { undoManager.addEdit(new UndoableVectorDataNodeInsertion(product, vectorDataNode, oldLayerId)); } return vectorDataNode; } private static String selectVectorDataLayer(VectorDataNode vectorDataNode) { Layer oldLayer = null; ProductSceneView sceneView = SnapApp.getDefault().getSelectedProductSceneView(); if (sceneView != null) { oldLayer = sceneView.getSelectedLayer(); // todo find new solution //SnapApp.getDefault().getProductTree().expand(vectorDataNode); sceneView.selectVectorDataLayer(vectorDataNode); LayerFilter nodeFilter = VectorDataLayerFilterFactory.createNodeFilter(vectorDataNode); Layer newSelectedLayer = LayerUtils.getChildLayer(sceneView.getRootLayer(), LayerUtils.SEARCH_DEEP, nodeFilter); if (newSelectedLayer != null) { newSelectedLayer.setVisible(true); } } return oldLayer != null ? oldLayer.getId() : null; } public static String getDefaultVectorDataNodeName() { return "geometry"; } @Override public Action createContextAwareInstance(Lookup actionContext) { return new CreateVectorDataNodeAction(actionContext); } @Override public void resultChanged(LookupEvent ev) { ProductNode productNode = SnapApp.getDefault().getSelectedProductNode(SnapApp.SelectionSourceHint.VIEW); setEnabled(productNode != null); } @Override public void actionPerformed(ActionEvent e) { ProductNode productNode = SnapApp.getDefault().getSelectedProductNode(SnapApp.SelectionSourceHint.VIEW); if (productNode == null) { return; } Product product = productNode.getProduct(); if (product != null) { DialogData dialogData = new DialogData(product.getVectorDataGroup()); PropertySet propertySet = PropertyContainer.createObjectBacked(dialogData); propertySet.getDescriptor("name").setNotNull(true); propertySet.getDescriptor("name").setNotEmpty(true); propertySet.getDescriptor("name").setValidator(new NameValidator(product)); propertySet.getDescriptor("description").setNotNull(true); final PropertyPane propertyPane = new PropertyPane(propertySet); JPanel panel = propertyPane.createPanel(); panel.setPreferredSize(new Dimension(400, 100)); ModalDialog dialog = new MyModalDialog(propertyPane); dialog.setContent(panel); int i = dialog.show(); if (i == ModalDialog.ID_OK) { createDefaultVectorDataNode(product, dialogData.name, dialogData.description); } } } private static class NameValidator implements Validator { private final Product product; private NameValidator(Product product) { this.product = product; } @Override public void validateValue(Property property, Object value) throws ValidationException { String name = (String) value; if (product.getVectorDataGroup().contains(name)) { final String pattern = "A vector data container with name ''{0}'' already exists.\n" + "Please choose another one."; throw new ValidationException(MessageFormat.format(pattern, name)); } } } private static class MyModalDialog extends ModalDialog { private final PropertyPane propertyPane; private MyModalDialog(PropertyPane propertyPane) { super(SnapApp.getDefault().getMainFrame(), Bundle.CTL_CreateVectorDataNodeActionText(), ModalDialog.ID_OK_CANCEL_HELP, HELP_ID); this.propertyPane = propertyPane; } /** * Called in order to perform input validation. * * @return {@code true} if and only if the validation was successful. */ @Override protected boolean verifyUserInput() { return !propertyPane.getBindingContext().hasProblems(); } } private static class DialogData { private String name; private String description; private DialogData(ProductNodeGroup<VectorDataNode> vectorGroup) { String defaultPrefix = getDefaultVectorDataNodeName() + "_"; name = defaultPrefix + (numItems++); while (vectorGroup.contains(name)) { name = defaultPrefix + (numItems++); } description = ""; } } private static class UndoableVectorDataNodeInsertion extends UndoableProductNodeInsertion<VectorDataNode> { private String oldLayerId; public UndoableVectorDataNodeInsertion(Product product, VectorDataNode vectorDataNode, String oldLayerId) { super(product.getVectorDataGroup(), vectorDataNode); this.oldLayerId = oldLayerId; } private static String getSelectedLayerId() { ProductSceneView sceneView = SnapApp.getDefault().getSelectedProductSceneView(); if (sceneView != null) { Layer selectedLayer = sceneView.getSelectedLayer(); if (selectedLayer != null) { return selectedLayer.getId(); } } return null; } @Override public void undo() { super.undo(); // removes VDN setSelectedLayer(oldLayerId); } @Override public void redo() { oldLayerId = getSelectedLayerId(); super.redo(); // inserts VDN selectVectorDataLayer(getProductNode()); } private void setSelectedLayer(String layerId) { if (layerId != null) { ProductSceneView sceneView = SnapApp.getDefault().getSelectedProductSceneView(); if (sceneView != null) { Layer layer = LayerUtils.getChildLayerById(sceneView.getRootLayer(), layerId); if (layer != null) { sceneView.setSelectedLayer(layer); } } } } } }