package org.esa.snap.rcp.util; import com.bc.ceres.core.Assert; import org.esa.snap.core.util.SystemUtils; import org.esa.snap.core.util.io.FileUtils; import org.esa.snap.rcp.SnapApp; import org.esa.snap.ui.SnapFileChooser; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.util.NbBundle; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.filechooser.FileFilter; import java.awt.BorderLayout; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.event.KeyEvent; import java.io.File; import java.text.MessageFormat; import java.util.prefs.Preferences; /** * Utility class which is used to display various commonly and frequently used message dialogs. * * @author Marco, Norman * @since 2.0 */ @NbBundle.Messages({ "LBL_Information=Information", "LBL_Question=Question", "LBL_Message=Message", "LBL_Warning=Warning", "LBL_Error=Error", "LBL_DoNotShowThisMessage=Don't show this message anymore.", "LBL_QuestionRemember=Remember my decision and don't ask again." }) public class Dialogs { public static final String PREF_KEY_SUFFIX_DONTSHOW = ".dontShow"; public enum Answer { YES, NO, CANCELLED } private static final String PREF_KEY_SUFFIX_DECISION = ".decision"; private static final String PREF_VALUE_YES = "yes"; private static final String PREF_VALUE_NO = "no"; private static final String PREF_VALUE_TRUE = "true"; private Dialogs() { } /** * Displays a modal dialog with the provided information message text. * * @param message The message text to be displayed. */ public static void showInformation(String message) { showInformation(message, null); } /** * Displays a modal dialog with the provided information message text. * * @param message The message text to be displayed. * @param preferencesKey If not {@code null}, a checkbox is displayed, and if checked the dialog will not be displayed again which lets users store the answer */ public static void showInformation(String message, String preferencesKey) { showInformation(Bundle.LBL_Information(), message, preferencesKey); } /** * Displays a modal dialog with the provided information message text. * * @param title The dialog title. May be {@code null}. * @param message The information message text to be displayed. * @param preferencesKey If not {@code null}, a checkbox is displayed, and if checked the dialog will not be displayed again which lets users store the answer */ public static void showInformation(String title, String message, String preferencesKey) { showMessage(title != null ? title : Bundle.LBL_Information(), message, JOptionPane.INFORMATION_MESSAGE, preferencesKey); } /** * Displays a modal dialog with the provided warning message text. * * @param message The information message text to be displayed. */ public static void showWarning(String message) { showWarning(null, message, null); } /** * Displays a modal dialog with the provided warning message text. * * @param title The dialog title. May be {@code null}. * @param message The warning message text to be displayed. * @param preferencesKey If not {@code null}, a checkbox is displayed, and if checked the dialog will not be displayed again which lets users store the answer */ public static void showWarning(String title, String message, String preferencesKey) { showMessage(title != null ? title : Bundle.LBL_Warning(), message, JOptionPane.WARNING_MESSAGE, preferencesKey); } /** * Displays a modal dialog with the provided error message text. * * @param message The error message text to be displayed. */ public static void showError(String message) { showError(null, message); } /** * Displays a modal dialog with the provided error message text. * * @param title The dialog title. May be {@code null}. * @param message The error message text to be displayed. */ public static void showError(String title, String message) { showMessage(title != null ? title : Bundle.LBL_Error(), message, JOptionPane.ERROR_MESSAGE, null); } /** * Displays a modal dialog indicating an 'Out of Memory'-error with the * provided error message text. It also displays a hint how to solve the problem. * * @param message The error message text to be displayed. */ public static void showOutOfMemoryError(String message) { showError("Out of Memory", String.format("%s\n\n" + "You can try to release memory by closing products or image views which\n" + "you currently not really need.", message)); } /** * Displays a modal dialog with the provided message text. * * @param title The dialog title. May be {@code null}. * @param message The message text to be displayed. * @param messageType The type of the message. * @param preferencesKey If not {@code null}, a checkbox is displayed, and if checked the dialog will not be displayed again which lets users store the answer */ public static void showMessage(String title, String message, int messageType, String preferencesKey) { KeyEventDispatcher keyEventDispatcher = e -> { if ((e.getKeyCode() == KeyEvent.VK_C) && e.isControlDown()) { SystemUtils.copyToClipboard(message); } e.consume(); return false; }; KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(keyEventDispatcher); title = getDialogTitle(title != null ? title : Bundle.LBL_Message()); if (preferencesKey != null) { String decision = getPreferences().get(preferencesKey + PREF_KEY_SUFFIX_DONTSHOW, ""); if (decision.equals(PREF_VALUE_TRUE)) { return; } JPanel panel = new JPanel(new BorderLayout(4, 4)); panel.add(new JLabel(message), BorderLayout.CENTER); JCheckBox dontShowCheckBox = new JCheckBox(Bundle.LBL_DoNotShowThisMessage(), false); panel.add(dontShowCheckBox, BorderLayout.SOUTH); NotifyDescriptor d = new NotifyDescriptor(panel, title, JOptionPane.DEFAULT_OPTION, messageType, null, null); DialogDisplayer.getDefault().notify(d); if (d.getValue() != NotifyDescriptor.CANCEL_OPTION) { boolean storeResult = dontShowCheckBox.isSelected(); if (storeResult) { getPreferences().put(preferencesKey + PREF_KEY_SUFFIX_DONTSHOW, PREF_VALUE_TRUE); } } } else { NotifyDescriptor d = new NotifyDescriptor(message, title, JOptionPane.DEFAULT_OPTION, messageType, null, null); DialogDisplayer.getDefault().notify(d); } KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(keyEventDispatcher); } /** * Displays a modal dialog which requests a decision from the user. * * @param title The dialog title. May be {@code null}. * @param message The question text to be displayed. * @param allowCancel If {@code true}, the dialog also offers a cancel button. * @param preferencesKey If not {@code null}, a checkbox is displayed, and if checked the dialog will not be displayed again which lets users store the answer * @return {@link Answer#YES}, {@link Answer#NO}, or {@link Answer#CANCELLED}. */ public static Answer requestDecision(String title, String message, boolean allowCancel, String preferencesKey) { Object result; boolean storeResult; int optionType = allowCancel ? NotifyDescriptor.YES_NO_CANCEL_OPTION : NotifyDescriptor.YES_NO_OPTION; title = getDialogTitle(title != null ? title : Bundle.LBL_Question()); if (preferencesKey != null) { String decision = getPreferences().get(preferencesKey + PREF_KEY_SUFFIX_DECISION, ""); if (decision.equals(PREF_VALUE_YES)) { return Answer.YES; } else if (decision.equals(PREF_VALUE_NO)) { return Answer.NO; } JCheckBox decisionCheckBox = new JCheckBox(Bundle.LBL_QuestionRemember(), false); NotifyDescriptor d = new NotifyDescriptor.Confirmation(new Object[]{message, decisionCheckBox}, title, optionType); result = DialogDisplayer.getDefault().notify(d); storeResult = decisionCheckBox.isSelected(); } else { NotifyDescriptor d = new NotifyDescriptor.Confirmation(message, title, optionType); result = DialogDisplayer.getDefault().notify(d); storeResult = false; } if (NotifyDescriptor.YES_OPTION.equals(result)) { if (storeResult) { getPreferences().put(preferencesKey + PREF_KEY_SUFFIX_DECISION, PREF_VALUE_YES); } return Answer.YES; } else if (NotifyDescriptor.NO_OPTION.equals(result)) { if (storeResult) { getPreferences().put(preferencesKey + PREF_KEY_SUFFIX_DECISION, PREF_VALUE_NO); } return Answer.NO; } else { return Answer.CANCELLED; } } /** * Opens question dialog asking the user whether or not to overwrite an existing file. If the given * file does not exists, the question dialog is not shown. * * @param file the file to check for existance * @return <code>True</code> if the user confirms the dialog with 'yes' or the given file does not exist.<br> * <code>False</code> if the user does not want to overwrite the existing file.<br> * <code>null</code> if the user canceled the operation.<br> */ public static Boolean requestOverwriteDecision(String title, File file) { if (!file.exists()) { return Boolean.TRUE; } Answer answer = requestDecision(getDialogTitle(title), MessageFormat.format( "The file ''{0}'' already exists.\nDo you wish to overwrite it?", file), true, null); return answer == Answer.YES ? Boolean.TRUE : answer == Answer.NO ? Boolean.FALSE : null; } /** * Opens a standard file-open dialog box. * * @param title a dialog-box title * @param dirsOnly whether or not to select only directories * @param fileFilter the file filter to be used, can be <code>null</code> * @param preferencesKey the key under which the last directory the user visited is stored * @return the file selected by the user or <code>null</code> if the user canceled file selection */ public static File requestFileForOpen(String title, boolean dirsOnly, FileFilter fileFilter, String preferencesKey) { Assert.notNull(preferencesKey, "preferencesKey"); String lastDir = getPreferences().get(preferencesKey, SystemUtils.getUserHomeDir().getPath()); File currentDir = new File(lastDir); SnapFileChooser fileChooser = new SnapFileChooser(); fileChooser.setCurrentDirectory(currentDir); if (fileFilter != null) { fileChooser.setFileFilter(fileFilter); } fileChooser.setDialogTitle(getDialogTitle(title)); fileChooser.setFileSelectionMode(dirsOnly ? JFileChooser.DIRECTORIES_ONLY : JFileChooser.FILES_ONLY); int result = fileChooser.showOpenDialog(SnapApp.getDefault().getMainFrame()); if (fileChooser.getCurrentDirectory() != null) { getPreferences().put(preferencesKey, fileChooser.getCurrentDirectory().getPath()); } if (result == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); if (file == null || file.getName().equals("")) { return null; } return file; } return null; } /** * Opens a standard save dialog box. * * @param title A dialog-box title. * @param dirsOnly Whether or not to select only directories. * @param fileFilter The file filter to be used, can be <code>null</code>. * @param defaultExtension The extension used as default. * @param fileName The initial filename. * @param accessory An accessory UI component to be shown in the {@link JFileChooser#setAccessory(JComponent) file chooser}, * can be <code>null</code>. * @param preferenceKey The key under which the last directory the user visited is stored. * @return The file selected by the user or <code>null</code> if the user cancelled the file selection. */ public static File requestFileForSave(String title, boolean dirsOnly, FileFilter fileFilter, String defaultExtension, String fileName, JComponent accessory, String preferenceKey) { // Loop while the user does not want to overwrite a selected, existing file // or if the user presses "Cancel" // File file; do { file = requestFileForSave2(title, dirsOnly, fileFilter, defaultExtension, fileName, accessory, preferenceKey); if (file == null) { return null; // Cancelled } else if (file.exists()) { Boolean overwrite = requestOverwriteDecision(title, file); if (overwrite == null) { return null; } else if (!overwrite) { file = null; // No, do not overwrite, let user select another file } } } while (file == null); return file; } private static File requestFileForSave2(String title, boolean dirsOnly, FileFilter fileFilter, String defaultExtension, final String fileName, JComponent accessory, final String preferenceKey) { Assert.notNull(preferenceKey, "preferenceKey"); String lastDir = getPreferences().get(preferenceKey, SystemUtils.getUserHomeDir().getPath()); File currentDir = new File(lastDir); SnapFileChooser fileChooser = new SnapFileChooser(); fileChooser.setCurrentDirectory(currentDir); if (fileFilter != null) { fileChooser.setFileFilter(fileFilter); } if (fileName != null) { fileChooser.setSelectedFile(new File(FileUtils.exchangeExtension(fileName, defaultExtension))); } fileChooser.setDialogTitle(getDialogTitle(title)); fileChooser.setFileSelectionMode(dirsOnly ? JFileChooser.DIRECTORIES_ONLY : JFileChooser.FILES_ONLY); if (accessory != null) { fileChooser.setAccessory(accessory); } int result = fileChooser.showSaveDialog(SnapApp.getDefault().getMainFrame()); if (fileChooser.getCurrentDirectory() != null) { getPreferences().put(preferenceKey, fileChooser.getCurrentDirectory().getPath()); } if (result == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); if (file == null || file.getName().equals("")) { return null; } String path = file.getPath(); if (defaultExtension != null) { if (!path.toLowerCase().endsWith(defaultExtension.toLowerCase())) { path = path.concat(defaultExtension); } } return new File(path); } return null; } public static String getDialogTitle(String titleText) { return MessageFormat.format("{0} - {1}", SnapApp.getDefault().getInstanceName(), titleText); } private static Preferences getPreferences() { return SnapApp.getDefault().getPreferences(); } }