/*
* Copyright (C) 2014 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.window;
import com.bc.ceres.core.SubProgressMonitor;
import com.bc.ceres.swing.progress.ProgressMonitorSwingWorker;
import org.esa.snap.core.datamodel.RasterDataNode;
import org.esa.snap.core.util.Debug;
import org.esa.snap.netbeans.docwin.DocumentWindowManager;
import org.esa.snap.netbeans.docwin.WindowUtilities;
import org.esa.snap.rcp.SnapApp;
import org.esa.snap.rcp.windows.ProductSceneViewTopComponent;
import org.esa.snap.ui.UIUtils;
import org.esa.snap.ui.product.ProductSceneImage;
import org.esa.snap.ui.product.ProductSceneView;
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.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import java.awt.event.ActionEvent;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* This action opens an image view of the currently selected raster.
*
* @author Marco Peters
* @author Norman Fomferra
*/
@ActionID(category = "View", id = "OpenImageViewAction")
@ActionRegistration(
displayName = "#CTL_OpenImageViewActionName",
iconBase = "org/esa/snap/rcp/icons/RsBandAsSwath.gif"
)
@ActionReferences({
@ActionReference(path = "Menu/Window", position = 100),
@ActionReference(path = "Context/Product/RasterDataNode", position = 100),
})
@NbBundle.Messages("CTL_OpenImageViewActionName=Open Image Window")
public class OpenImageViewAction extends AbstractAction implements ContextAwareAction, LookupListener {
private RasterDataNode rasterDataNode;
private Lookup lookup;
public OpenImageViewAction() {
this(Utilities.actionsGlobalContext());
}
public OpenImageViewAction(Lookup lookup) {
putValue(Action.NAME, Bundle.CTL_OpenImageViewActionName());
this.lookup = lookup;
Lookup.Result<RasterDataNode> rasterDataNodeResult = lookup.lookupResult(RasterDataNode.class);
rasterDataNodeResult.addLookupListener(WeakListeners.create(LookupListener.class, this, rasterDataNodeResult));
setEnabledState();
setActionName();
}
public OpenImageViewAction(RasterDataNode rasterDataNode) {
putValue(Action.NAME, Bundle.CTL_OpenImageViewActionName());
this.rasterDataNode = rasterDataNode;
}
public static OpenImageViewAction create(RasterDataNode rasterDataNode) {
return new OpenImageViewAction(rasterDataNode);
}
public static void showImageView(RasterDataNode rasterDataNode) {
new OpenImageViewAction().openRasterDataNode(rasterDataNode);
}
public static void openImageView(RasterDataNode rasterDataNode) {
new OpenImageViewAction().openRasterDataNode(rasterDataNode);
}
@Override
public void actionPerformed(ActionEvent e) {
execute();
}
public void execute() {
Collection<? extends RasterDataNode> selectedRasterD = getSelectedRasterDataNodes();
if (Objects.nonNull(selectedRasterD)) {
for (RasterDataNode rasterDataNode : selectedRasterD) {
openRasterDataNode(rasterDataNode);
}
} else if (Objects.nonNull(rasterDataNode)) {
openRasterDataNode(rasterDataNode);
}
}
public static ProductSceneViewTopComponent getProductSceneViewTopComponent(RasterDataNode raster) {
return WindowUtilities.getOpened(ProductSceneViewTopComponent.class)
.filter(topComponent -> topComponent.getView().getNumRasters() == 1 && raster == topComponent.getView().getRaster())
.findFirst()
.orElse(null);
}
public static ProductSceneView getProductSceneView(RasterDataNode raster) {
ProductSceneViewTopComponent component = getProductSceneViewTopComponent(raster);
return component != null ? component.getView() : null;
}
public static void updateProductSceneViewImage(final ProductSceneView view) {
SwingUtilities.invokeLater(view::updateImage);
}
public static void updateProductSceneViewImages(final RasterDataNode[] rasters) {
updateProductSceneViewImages(rasters, ProductSceneViewImageUpdater.DEFAULT);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ProductSceneView helper methods
public static void updateProductSceneViewImages(final RasterDataNode[] rasters, ProductSceneViewImageUpdater updateMethod) {
List<ProductSceneView> views = WindowUtilities.getOpened(ProductSceneViewTopComponent.class).map(ProductSceneViewTopComponent::getView).collect(Collectors.toList());
for (ProductSceneView view : views) {
boolean updateView = false;
for (int j = 0; j < rasters.length && !updateView; j++) {
final RasterDataNode raster = rasters[j];
for (int k = 0; k < view.getNumRasters() && !updateView; k++) {
if (view.getRaster(k) == raster) {
updateView = true;
}
}
}
if (updateView) {
SwingUtilities.invokeLater(() -> updateMethod.updateView(view));
}
}
}
@Override
public Action createContextAwareInstance(Lookup actionContext) {
return new OpenImageViewAction(actionContext);
}
@Override
public void resultChanged(LookupEvent ev) {
setEnabledState();
setActionName();
}
private void openRasterDataNode(RasterDataNode rasterDataNode) {
ProductSceneViewTopComponent tc = getProductSceneViewTopComponent(rasterDataNode);
if (tc != null) {
tc.requestSelected();
} else {
openProductSceneView(rasterDataNode);
}
}
private Collection<? extends RasterDataNode> getSelectedRasterDataNodes() {
return lookup.lookupAll(RasterDataNode.class);
}
private void setActionName() {
Collection<? extends RasterDataNode> selectedRasterDataNode = getSelectedRasterDataNodes();
int size = selectedRasterDataNode.size();
if (size > 1) {
this.putValue(Action.NAME, String.format("Open %d Image Window", size));
} else {
this.putValue(Action.NAME, Bundle.CTL_OpenImageViewActionName());
}
}
private void setEnabledState() {
if (Objects.nonNull(lookup)) {
setEnabled(lookup.lookup(RasterDataNode.class) != null);
}
}
private void openProductSceneView(RasterDataNode rasterDataNode) {
SnapApp snapApp = SnapApp.getDefault();
snapApp.setStatusBarMessage("Opening image view...");
UIUtils.setRootFrameWaitCursor(snapApp.getMainFrame());
String progressMonitorTitle = MessageFormat.format("Creating image for ''{0}''", rasterDataNode.getName());
ProductSceneView existingView = getProductSceneView(rasterDataNode);
SwingWorker worker = new ProgressMonitorSwingWorker<ProductSceneImage, Object>(snapApp.getMainFrame(), progressMonitorTitle) {
@Override
public void done() {
UIUtils.setRootFrameDefaultCursor(snapApp.getMainFrame());
snapApp.setStatusBarMessage("");
try {
ProductSceneImage sceneImage = get();
UndoRedo.Manager undoManager = SnapApp.getDefault().getUndoManager(sceneImage.getProduct());
ProductSceneView view = new ProductSceneView(sceneImage, undoManager);
// get the preferences: SnapApp.getInstance().getPreferences()
// add the view (as listener) to it
openDocumentWindow(view);
} catch (Exception e) {
snapApp.handleError(MessageFormat.format("Failed to open image view.\n\n{0}", e.getMessage()), e);
}
}
@Override
protected ProductSceneImage doInBackground(com.bc.ceres.core.ProgressMonitor pm) throws Exception {
try {
return createProductSceneImage(rasterDataNode, existingView, pm);
} finally {
if (pm.isCanceled()) {
rasterDataNode.unloadRasterData();
}
}
}
};
worker.execute();
}
private ProductSceneViewTopComponent openDocumentWindow(final ProductSceneView view) {
UndoRedo.Manager undoManager = SnapApp.getDefault().getUndoManager(view.getProduct());
ProductSceneViewTopComponent productSceneViewWindow = new ProductSceneViewTopComponent(view, undoManager);
DocumentWindowManager.getDefault().openWindow(productSceneViewWindow);
productSceneViewWindow.requestSelected();
return productSceneViewWindow;
}
private ProductSceneImage createProductSceneImage(final RasterDataNode raster, ProductSceneView existingView, com.bc.ceres.core.ProgressMonitor pm) {
Debug.assertNotNull(raster);
Debug.assertNotNull(pm);
try {
pm.beginTask("Creating image...", 1);
ProductSceneImage sceneImage;
if (existingView != null) {
sceneImage = new ProductSceneImage(raster, existingView);
} else {
sceneImage = new ProductSceneImage(raster,
SnapApp.getDefault().getPreferencesPropertyMap(),
SubProgressMonitor.create(pm, 1));
}
sceneImage.initVectorDataCollectionLayer();
sceneImage.initMaskCollectionLayer();
return sceneImage;
} finally {
pm.done();
}
}
/**
* A method used to update a <code>ProductSceneView</code>.
*/
public interface ProductSceneViewImageUpdater {
ProductSceneViewImageUpdater DEFAULT = ProductSceneView::updateImage;
void updateView(ProductSceneView view);
}
}