/* * Copyright (c) 2008, SQL Power Group Inc. * * This file is part of Power*Architect. * * Power*Architect 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. * * Power*Architect 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 ca.sqlpower.swingui; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.InvocationTargetException; import java.text.MessageFormat; import java.util.Arrays; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.SwingUtilities; import javax.swing.text.JTextComponent; import org.apache.log4j.Logger; import ca.sqlpower.util.UserPrompter; import ca.sqlpower.util.UserPrompterFactory; import com.jgoodies.forms.builder.ButtonBarBuilder; import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; public class DialogUserPrompter implements UserPrompter { private static Logger logger = Logger.getLogger(DialogUserPrompter.class); /** * The dialog that poses the question to the user. */ private JDialog confirmDialog; /** * The component that owns the modal dialog. Also used as the owner * for error dialogs. */ private JFrame owner; /** * The component that contains the question. The text will be replaced every * time {@link #promptUser(Object[])} gets called, based on the format * arguments provided. */ private final JTextComponent questionField; /** * The formatter responsible for doing formatting and parameter substitution * in the question text each time the dialog is displayed. */ private final MessageFormat questionFormat; /** * The user's most recent response. */ private UserPromptResponse response; /** * The check box that decides if the decision should be applied to all * future responses. */ private final JCheckBox applyToAll; /** * Keeps track of whether or not the dialog has already been displayed * at least once. */ private boolean firstPrompt = true; /** * The default response type which is different from the response type. */ private final UserPromptResponse defaultResponseType; /** * Creates a new user prompter that uses a modal dialog to prompt the user. * Normally this constructor should be called via a {@link UserPrompterFactory} * such as the current Session. */ public DialogUserPrompter(UserPromptOptions optionType, UserPromptResponse defaultResponseType, JFrame owner, String questionMessage, String ... buttonNames) { this(optionType, defaultResponseType, owner, questionMessage, true, buttonNames); } /** * Creates a new user prompter that uses a dialog to prompt the user. * Normally this constructor should be called via a {@link UserPrompterFactory} * such as the current ArchitectSession. */ public DialogUserPrompter(UserPromptOptions optionType, UserPromptResponse defaultResponseType, JFrame owner, String questionMessage, boolean modal, String ... buttonNames) { if(optionType.getButtonCount() != buttonNames.length) { throw new IllegalStateException("Expecting " + optionType.getButtonCount() + " arguments for the optionType " + optionType + "Recieved only " + buttonNames.length + "arguments\n" + Arrays.toString(buttonNames)); } this.defaultResponseType = defaultResponseType; this.owner = owner; applyToAll = new JCheckBox(Messages.getString("ModalDialogUserPrompter.applyToAllOption")); //$NON-NLS-1$ confirmDialog = new JDialog(owner); // FIXME the title needs to be configurable and/or set itself based on prompt type confirmDialog.setTitle(""); //$NON-NLS-1$ // this is just filled with the message pattern template to help with sizing //Using JEditorPane for html messages as JTextAreas do not respect html tags. //Using JTextArea for the rest of the messages as JEditorPane does not support //new line characters and to keep font consistent. if (questionMessage.startsWith("<html>")) { questionField = new JEditorPane(); questionField.setText(questionMessage); ((JEditorPane) questionField).setContentType("text/html"); questionField.setFont(new JTextArea().getFont()); } else { questionField = new JTextArea(questionMessage); } questionField.setEditable(false); questionField.setBackground(null); questionFormat = new MessageFormat(questionMessage); JPanel confirmPanel = new JPanel(); FormLayout formLayout = new FormLayout("10dlu, 2dlu, pref:grow, 2dlu, 10dlu" //$NON-NLS-1$ , ""); //$NON-NLS-1$ DefaultFormBuilder builder = new DefaultFormBuilder(formLayout, confirmPanel); builder.setDefaultDialogBorder(); builder.nextColumn(2); builder.append(questionField); builder.nextLine(); builder.appendParagraphGapRow(); builder.nextLine(); ButtonBarBuilder buttonBar = new ButtonBarBuilder(); buttonBar.addGlue(); JButton okButton = new JButton(); if(optionType == UserPromptOptions.OK_NEW_NOTOK_CANCEL || optionType == UserPromptOptions.OK_NOTOK_CANCEL || optionType == UserPromptOptions.OK_NEW_CANCEL || optionType == UserPromptOptions.OK_CANCEL || optionType == UserPromptOptions.OK) { okButton.setText(buttonNames[0]); buttonBar.addGridded(okButton); } JButton notOkButton = new JButton(); if(optionType == UserPromptOptions.OK_NEW_NOTOK_CANCEL || optionType == UserPromptOptions.OK_NOTOK_CANCEL) { buttonBar.addRelatedGap(); notOkButton.setText((optionType == UserPromptOptions.OK_NOTOK_CANCEL)? buttonNames[1]: buttonNames[2]); buttonBar.addGridded(notOkButton); } JButton cancelButton = new JButton(); if(optionType == UserPromptOptions.OK_NEW_NOTOK_CANCEL || optionType == UserPromptOptions.OK_NOTOK_CANCEL || optionType == UserPromptOptions.OK_NEW_CANCEL || optionType == UserPromptOptions.OK_CANCEL) { buttonBar.addRelatedGap(); cancelButton.setText(buttonNames[buttonNames.length-1]); buttonBar.addGridded(cancelButton); } buttonBar.addGlue(); okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { response = UserPromptResponse.OK; confirmDialog.dispose(); } }); notOkButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { response = UserPromptResponse.NOT_OK; confirmDialog.dispose(); } }); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { response = UserPromptResponse.CANCEL; confirmDialog.dispose(); } }); builder.append(""); //$NON-NLS-1$ builder.append(buttonBar.getPanel()); builder.nextLine(); builder.append(""); //$NON-NLS-1$ builder.append(applyToAll); switch (defaultResponseType) { case OK: okButton.requestFocusInWindow(); break; case NOT_OK: notOkButton.requestFocusInWindow(); break; case CANCEL: cancelButton.requestFocusInWindow(); break; default: throw new UnsupportedOperationException("Default response type : " + defaultResponseType + " is not known"); } confirmDialog.setModal(modal); confirmDialog.add(builder.getPanel()); } /** * Solicits a response from the user by presenting the modal dialog (unless * the user has previously selected "apply to all"). This method can be * called from any thread; if not called from the Swing EDT and the dialog * has to be shown, the current thread will be suspended until the dialog * has been shown and dismissed. */ public UserPromptResponse promptUser(final Object ... formatArgs) { if (logger.isDebugEnabled()) { logger.debug("Prompting user. Format Args: " + Arrays.asList(formatArgs)); //$NON-NLS-1$ } if (applyToAll.isSelected()) { return response; } // The default response, in case the user closes the dialog without // pressing one of the buttons response = defaultResponseType; Runnable promptUser = new Runnable() { public void run() { questionField.setText(questionFormat.format(formatArgs)); confirmDialog.pack(); if (firstPrompt) { confirmDialog.setLocationRelativeTo(owner); firstPrompt = false; } confirmDialog.setVisible(true); } }; if (SwingUtilities.isEventDispatchThread()) { promptUser.run(); } else { try { SwingUtilities.invokeAndWait(promptUser); } catch (InterruptedException e) { SPSUtils.showExceptionDialogNoReport(owner, Messages.getString("ModalDialogUserPrompter.showPromptDialogFailed"), e); //$NON-NLS-1$ } catch (InvocationTargetException e) { SPSUtils.showExceptionDialogNoReport(owner, Messages.getString("ModalDialogUserPrompter.showPromptDialogFailed"), e); //$NON-NLS-1$ } } return response; } public Object getUserSelectedResponse() { if (response == UserPromptResponse.OK) { return true; } else { return false; } } }