/* * 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.file.export; import org.esa.snap.core.datamodel.ImageInfo; import org.esa.snap.core.datamodel.ImageLegend; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.core.param.ParamGroup; import org.esa.snap.core.param.Parameter; import org.esa.snap.core.util.SystemUtils; import org.esa.snap.core.util.io.SnapFileFilter; import org.esa.snap.rcp.SnapApp; import org.esa.snap.ui.GridBagUtils; import org.esa.snap.ui.ModalDialog; import org.esa.snap.ui.SnapFileChooser; 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.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.Action; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.border.EmptyBorder; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; @ActionID( category = "File", id = "org.esa.snap.rcp.actions.file.export.ExportLegendImageAction" ) @ActionRegistration( displayName = "#CTL_ExportLegendImageAction_MenuText", popupText = "#CTL_ExportLegendImageAction_PopupText", lazy = false ) @ActionReferences({ @ActionReference(path = "Menu/File/Export/Other", position = 10), @ActionReference(path = "Context/ProductSceneView", position = 90) }) @NbBundle.Messages({ "CTL_ExportLegendImageAction_MenuText=Colour Legend as Image", "CTL_ExportLegendImageAction_PopupText=Export Colour Legend as Image", "CTL_ExportLegendImageAction_ShortDescription=Export the colour legend of the current view as an image." }) public class ExportLegendImageAction extends AbstractExportImageAction { private final static String[][] IMAGE_FORMAT_DESCRIPTIONS = { BMP_FORMAT_DESCRIPTION, PNG_FORMAT_DESCRIPTION, JPEG_FORMAT_DESCRIPTION, TIFF_FORMAT_DESCRIPTION, }; private static final String HELP_ID = "exportLegendImageFile"; private static final String HORIZONTAL_STR = "Horizontal"; private static final String VERTICAL_STR = "Vertical"; private SnapFileFilter[] imageFileFilters; private ParamGroup legendParamGroup; private ImageLegend imageLegend; @SuppressWarnings("FieldCanBeLocal") private Lookup.Result<ProductSceneView> result; public ExportLegendImageAction() { this(Utilities.actionsGlobalContext()); } public ExportLegendImageAction(Lookup lookup) { super(Bundle.CTL_ExportLegendImageAction_MenuText(), HELP_ID); putValue("popupText",Bundle.CTL_ExportLegendImageAction_PopupText()); imageFileFilters = new SnapFileFilter[IMAGE_FORMAT_DESCRIPTIONS.length]; for (int i = 0; i < IMAGE_FORMAT_DESCRIPTIONS.length; i++) { imageFileFilters[i] = createFileFilter(IMAGE_FORMAT_DESCRIPTIONS[i]); } result = lookup.lookupResult(ProductSceneView.class); result.addLookupListener(WeakListeners.create(LookupListener.class, this, result)); setEnabled(false); } @Override public Action createContextAwareInstance(Lookup lookup) { return new ExportLegendImageAction(lookup); } @Override public void actionPerformed(ActionEvent e) { exportImage(imageFileFilters); } @Override public void resultChanged(LookupEvent lookupEvent) { ProductSceneView view = SnapApp.getDefault().getSelectedProductSceneView(); boolean enabled = view != null && !view.isRGB(); setEnabled(enabled); } @Override protected void configureFileChooser(SnapFileChooser fileChooser, ProductSceneView view, String imageBaseName) { legendParamGroup = createLegendParamGroup(); legendParamGroup.setParameterValues(SnapApp.getDefault().getPreferencesPropertyMap(), null); modifyHeaderText(legendParamGroup, view.getRaster()); fileChooser.setDialogTitle(SnapApp.getDefault().getInstanceName() + " - export Colour Legend Image"); /*I18N*/ fileChooser.setCurrentFilename(imageBaseName + "_legend"); final RasterDataNode raster = view.getRaster(); imageLegend = new ImageLegend(raster.getImageInfo(), raster); fileChooser.setAccessory(createImageLegendAccessory( fileChooser, legendParamGroup, imageLegend, getHelpCtx().getHelpID())); } @Override protected RenderedImage createImage(String imageFormat, ProductSceneView view) { transferParamsToImageLegend(legendParamGroup, imageLegend); imageLegend.setBackgroundTransparencyEnabled(isTransparencySupportedByFormat(imageFormat)); return imageLegend.createImage(); } @Override protected boolean isEntireImageSelected() { return true; } private static ParamGroup createLegendParamGroup() { ParamGroup paramGroup = new ParamGroup(); Parameter param = new Parameter("legend.usingHeader", Boolean.TRUE); param.getProperties().setLabel("Show header text"); paramGroup.addParameter(param); param = new Parameter("legend.headerText", ""); param.getProperties().setLabel("Header text"); param.getProperties().setNumCols(24); param.getProperties().setNullValueAllowed(true); paramGroup.addParameter(param); param = new Parameter("legend.orientation", HORIZONTAL_STR); param.getProperties().setLabel("Orientation"); param.getProperties().setValueSet(new String[]{HORIZONTAL_STR, VERTICAL_STR}); param.getProperties().setValueSetBound(true); paramGroup.addParameter(param); param = new Parameter("legend.fontSize", 14); param.getProperties().setLabel("Font size"); param.getProperties().setMinValue(4); param.getProperties().setMaxValue(100); paramGroup.addParameter(param); param = new Parameter("legend.foregroundColor", Color.black); param.getProperties().setLabel("Foreground colour"); paramGroup.addParameter(param); param = new Parameter("legend.backgroundColor", Color.white); param.getProperties().setLabel("Background colour"); paramGroup.addParameter(param); param = new Parameter("legend.backgroundTransparency", 0.0f); param.getProperties().setLabel("Background transparency"); param.getProperties().setMinValue(0.0f); param.getProperties().setMaxValue(1.0f); paramGroup.addParameter(param); param = new Parameter("legend.antialiasing", Boolean.TRUE); param.getProperties().setLabel("Perform anti-aliasing"); paramGroup.addParameter(param); return paramGroup; } private static void modifyHeaderText(ParamGroup legendParamGroup, RasterDataNode raster) { String name = raster.getName(); String unit = raster.getUnit() != null ? raster.getUnit() : "-"; unit = unit.replace('*', ' '); String headerText = name + " [" + unit + "]"; legendParamGroup.getParameter("legend.headerText").setValue(headerText, null); } private static JComponent createImageLegendAccessory(final JFileChooser fileChooser, final ParamGroup legendParamGroup, final ImageLegend imageLegend, String helpId) { final JButton button = new JButton("Properties..."); button.setMnemonic('P'); button.addActionListener(e -> { final SnapFileFilter fileFilter = (SnapFileFilter) fileChooser.getFileFilter(); final ImageLegendDialog dialog = new ImageLegendDialog( legendParamGroup, imageLegend, isTransparencySupportedByFormat(fileFilter.getFormatName()), helpId); dialog.show(); }); final JPanel accessory = new JPanel(new BorderLayout()); accessory.setBorder(new EmptyBorder(3, 3, 3, 3)); accessory.add(button, BorderLayout.NORTH); return accessory; } private static void transferParamsToImageLegend(ParamGroup legendParamGroup, ImageLegend imageLegend) { Object value; value = legendParamGroup.getParameter("legend.usingHeader").getValue(); imageLegend.setUsingHeader((Boolean) value); value = legendParamGroup.getParameter("legend.headerText").getValue(); imageLegend.setHeaderText((String) value); value = legendParamGroup.getParameter("legend.orientation").getValue(); imageLegend.setOrientation(HORIZONTAL_STR.equals(value) ? ImageLegend.HORIZONTAL : ImageLegend.VERTICAL); value = legendParamGroup.getParameter("legend.fontSize").getValue(); imageLegend.setFont(imageLegend.getFont().deriveFont(((Number) value).floatValue())); value = legendParamGroup.getParameter("legend.backgroundColor").getValue(); imageLegend.setBackgroundColor((Color) value); value = legendParamGroup.getParameter("legend.foregroundColor").getValue(); imageLegend.setForegroundColor((Color) value); value = legendParamGroup.getParameter("legend.backgroundTransparency").getValue(); imageLegend.setBackgroundTransparency(((Number) value).floatValue()); value = legendParamGroup.getParameter("legend.antialiasing").getValue(); imageLegend.setAntialiasing((Boolean) value); } public static class ImageLegendDialog extends ModalDialog { private ImageInfo imageInfo; private RasterDataNode raster; private boolean transparencyEnabled; private ParamGroup paramGroup; private Parameter usingHeaderParam; private Parameter headerTextParam; private Parameter orientationParam; private Parameter fontSizeParam; private Parameter backgroundColorParam; private Parameter foregroundColorParam; private Parameter antialiasingParam; private Parameter backgroundTransparencyParam; public ImageLegendDialog(ParamGroup paramGroup, ImageLegend imageLegend, boolean transparencyEnabled, String helpId) { super(SnapApp.getDefault().getMainFrame(), SnapApp.getDefault().getInstanceName() + " - Colour Legend Properties", ID_OK_CANCEL, helpId); this.imageInfo = imageLegend.getImageInfo(); this.raster = imageLegend.getRaster(); this.transparencyEnabled = transparencyEnabled; this.paramGroup = paramGroup; initParams(); initUI(); updateUIState(); this.paramGroup.addParamChangeListener(event -> updateUIState()); } private void updateUIState() { boolean headerTextEnabled = (Boolean) usingHeaderParam.getValue(); headerTextParam.setUIEnabled(headerTextEnabled); backgroundTransparencyParam.setUIEnabled(transparencyEnabled); } public ParamGroup getParamGroup() { return paramGroup; } public void getImageLegend(ImageLegend imageLegend) { transferParamsToImageLegend(getParamGroup(), imageLegend); } public ImageInfo getImageInfo() { return imageInfo; } @Override protected void onOK() { getParamGroup().getParameterValues(SnapApp.getDefault().getPreferencesPropertyMap()); super.onOK(); } private void initUI() { final JButton previewButton = new JButton("Preview..."); previewButton.setMnemonic('v'); previewButton.addActionListener(e -> showPreview()); final GridBagConstraints gbc = new GridBagConstraints(); final JPanel p = GridBagUtils.createPanel(); gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.NONE; gbc.insets.top = 0; gbc.gridy = 0; gbc.gridwidth = 2; p.add(usingHeaderParam.getEditor().getEditorComponent(), gbc); gbc.gridy++; gbc.gridwidth = 1; p.add(headerTextParam.getEditor().getLabelComponent(), gbc); p.add(headerTextParam.getEditor().getEditorComponent(), gbc); gbc.gridy++; gbc.insets.top = 10; p.add(orientationParam.getEditor().getLabelComponent(), gbc); p.add(orientationParam.getEditor().getEditorComponent(), gbc); gbc.gridy++; gbc.insets.top = 3; p.add(fontSizeParam.getEditor().getLabelComponent(), gbc); p.add(fontSizeParam.getEditor().getEditorComponent(), gbc); gbc.gridy++; gbc.insets.top = 10; p.add(foregroundColorParam.getEditor().getLabelComponent(), gbc); p.add(foregroundColorParam.getEditor().getEditorComponent(), gbc); gbc.gridy++; gbc.insets.top = 3; p.add(backgroundColorParam.getEditor().getLabelComponent(), gbc); p.add(backgroundColorParam.getEditor().getEditorComponent(), gbc); gbc.gridy++; gbc.insets.top = 3; p.add(backgroundTransparencyParam.getEditor().getLabelComponent(), gbc); p.add(backgroundTransparencyParam.getEditor().getEditorComponent(), gbc); gbc.gridy++; gbc.insets.top = 10; gbc.gridx = 0; gbc.anchor = GridBagConstraints.NORTHWEST; p.add(antialiasingParam.getEditor().getEditorComponent(), gbc); gbc.insets.top = 10; gbc.gridx = 1; gbc.anchor = GridBagConstraints.NORTHEAST; p.add(previewButton, gbc); p.setBorder(new EmptyBorder(7, 7, 7, 7)); setContent(p); } private void initParams() { usingHeaderParam = paramGroup.getParameter("legend.usingHeader"); headerTextParam = paramGroup.getParameter("legend.headerText"); orientationParam = paramGroup.getParameter("legend.orientation"); fontSizeParam = paramGroup.getParameter("legend.fontSize"); foregroundColorParam = paramGroup.getParameter("legend.foregroundColor"); backgroundColorParam = paramGroup.getParameter("legend.backgroundColor"); backgroundTransparencyParam = paramGroup.getParameter("legend.backgroundTransparency"); antialiasingParam = paramGroup.getParameter("legend.antialiasing"); } private void showPreview() { final ImageLegend imageLegend = new ImageLegend(getImageInfo(), raster); getImageLegend(imageLegend); final BufferedImage image = imageLegend.createImage(); final JLabel imageDisplay = new JLabel(new ImageIcon(image)); imageDisplay.setOpaque(true); imageDisplay.addMouseListener(new MouseAdapter() { // Both events (releases & pressed) must be checked, otherwise it won't work on all // platforms /** * Invoked when a mouse button has been released on a component. */ @Override public void mouseReleased(MouseEvent e) { // On Windows showPopup(e, image, imageDisplay); } /** * Invoked when a mouse button has been pressed on a component. */ @Override public void mousePressed(MouseEvent e) { // On Linux // todo - clipboard does not work on linux. // todo - better not to show popup until it works correctly // showPopup(e, image, imageDisplay); } }); final ModalDialog dialog = new ModalDialog(getParent(), SnapApp.getDefault().getInstanceName() + " - Colour Legend Preview", imageDisplay, ID_OK, null); dialog.getJDialog().setResizable(false); dialog.show(); } private static void showPopup(final MouseEvent e, final BufferedImage image, final JComponent imageDisplay) { if (e.isPopupTrigger()) { final JPopupMenu popupMenu = new JPopupMenu(); final JMenuItem menuItem = new JMenuItem("Copy image to clipboard"); menuItem.addActionListener(e1 -> SystemUtils.copyToClipboard(image)); popupMenu.add(menuItem); popupMenu.show(imageDisplay, e.getX(), e.getY()); } } } }