/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.tools.dialogs; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dialog; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.GridLayout; import java.awt.Insets; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import javax.swing.AbstractButton; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import com.rapidminer.RapidMiner; import com.rapidminer.gui.ApplicationFrame; import com.rapidminer.gui.RapidMinerGUI; import com.rapidminer.gui.tools.ResourceAction; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.gui.tools.components.FixedWidthEditorPane; import com.rapidminer.tools.I18N; import com.rapidminer.tools.usagestats.ActionStatisticsCollector; /** * Dialog that provides some helper methods to create buttons. Automatically registers accelerators * and action listeners. Override {@link #ok()}, {@link #cancel()} and {@link #close()} to customize * the behaviour. * * The user can query if the ok button was pressed ({@link #wasConfirmed}). * * @author Simon Fischer, Marco Boeck */ public class ButtonDialog extends JDialog { /** * A builder for {@link ButtonDialog}s. After calling all relevant setters, call * {@link #build()} to create the actual dialog instance. * <p> * <strong>Attention:</strong> Without setting an explicit parent via {@link #setOwner(Window)}, * modal dialogs will display inconsistent behavior and sometimes appear behind other dialogs. * This can be a big problem as the user will not find the dialog, yet he is blocked from * interacting with the rest of the program! * </p> * * * @author Marco Boeck * @since 6.5.0 * */ public static class ButtonDialogBuilder { /** * Default buttons which can automatically added to the button dialog. * */ public enum DefaultButtons { /** labelled Ok, sets the status to confirmed */ OK_BUTTON, /** labelled Cancel, sets the status to not confirmed */ CANCEL_BUTTON, /** labelled Close, sets the status to not confirmed */ CLOSE_BUTTON; } /** the i18n key */ private String key; /** the optional i18n arguments */ private Object[] i18nArgs; /** the modality type which should be used for the dialog */ private ModalityType modalityType = ModalityType.MODELESS; /** the owner window for the dialog or {@code null} */ private Window owner; /** the graphics config to use for the dialog or {@code null} */ private GraphicsConfiguration graphicsConfig; /** if {@code true}, the layout is done by the builder as well */ private boolean doLayout = false; /** the main component if layout should be done */ private JComponent mainComponent; /** the size if layout should be done */ private int size; /** the buttons if layout should be done */ private AbstractButton[] buttons; /** default buttons if layout should be done */ private DefaultButtons[] defaultButtons; /** * Create a new builder for a {@link ButtonDialog}. * * @param key * the i18n key used for the properties gui.dialog.-key-.title and * gui.dialog.-key-.icon */ public ButtonDialogBuilder(final String key) { if (key == null || key.trim().isEmpty()) { throw new IllegalArgumentException("key must not be null or empty!"); } this.key = key; } /** * Sets the parent window for the dialog. Can be a {@link Dialog}, can be a {@link Frame}, * can be a {@link Window}. This is used to determine modality order. By default, no parent * is set. * * @param owner * the parent of the dialog * @return the builder itself, never {@code null} */ public ButtonDialogBuilder setOwner(final Window owner) { this.owner = owner; return this; } /** * Sets optional i18n arguments which are used to replace placeholders. By default, no * arguments are passed. * * @param args * arguments which will replace the placeholders in the I18n-Properties message. * The first argument will replace <code>{0}</code>, the second <code>{1}</code> * and so on. * @return the builder itself, never {@code null} */ public ButtonDialogBuilder setI18nArguments(final Object... args) { this.i18nArgs = args; return this; } /** * Sets the modality type which should be used for the dialog. By default, a dialog is * {@link ModalityType#MODELESS}. * <p> * <strong>Attention:</strong> Without setting an explicit parent via * {@link #setOwner(Window)} , modal dialogs will display inconsistent behavior and * sometimes appear behind other dialogs. This can be a big problem as the user will not * find the dialog, yet he is blocked from interacting with the rest of the program! * </p> * * @param modalityType * the modality type * @return the builder itself, never {@code null} */ public ButtonDialogBuilder setModalityType(final ModalityType modalityType) { if (modalityType == null) { throw new IllegalArgumentException("modalityType must not be null!"); } this.modalityType = modalityType; return this; } /** * Sets the graphics configuration which should be used for the dialog. Determines on which * screen the dialog opens in a multi-monitor setup. By default, the default configuration * is used. * * @param graphicsConfig * the graphics config to use * @return the builder itself, never {@code null} */ public ButtonDialogBuilder setGraphicsConfiguration(final GraphicsConfiguration graphicsConfig) { if (graphicsConfig == null) { throw new IllegalArgumentException("graphicsConfig must not be null!"); } this.graphicsConfig = graphicsConfig; return this; } /** * Optionally specify the contents of the button dialog including layout. To specify * buttons, call {@link #setButtons(AbstractButton...)}. * * @param mainComponent * the component in the center of the dialog * @param size * the size of the dialog, see constants in {@link ButtonDialog} * @return the builder itself, never {@code null} */ public ButtonDialogBuilder setContent(final JComponent mainComponent, final int size) { if (mainComponent == null) { throw new IllegalArgumentException("mainComponent must not be null!"); } this.doLayout = true; this.mainComponent = mainComponent; this.size = size; return this; } /** * Optionally specify custom buttons for the button dialog. Custom buttons always take * precedence over default buttons. Has no effect unless * {@link #setContent(JComponent, int)} has also been called. * * @param buttons * the custom buttons which should be added to the dialog. If neither custom nor * default buttons are specified, a default ok button is added * @return the the builder itself, never {@code null} */ public ButtonDialogBuilder setButtons(final AbstractButton... buttons) { this.buttons = buttons; return this; } /** * Optionally specify default buttons for the button dialog. Custom buttons always take * precedence over default buttons. Has no effect unless * {@link #setContent(JComponent, int)} has also been called. * * @param defaultButtons * the default buttons which should be added to the dialog. If neither custom nor * default buttons are specified, a default ok button is added * @return the the builder itself, never {@code null} */ public ButtonDialogBuilder setButtons(final DefaultButtons... defaultButtons) { this.defaultButtons = defaultButtons; return this; } /** * Creates the actual {@link ButtonDialog} instance according to the specified settings. * * @return the dialog instance, never {@code null} */ public ButtonDialog build() { ButtonDialog dialog = new ButtonDialog(owner, key, modalityType, graphicsConfig, i18nArgs); // see if we also should prepare the layout if (doLayout) { // prepare desired buttons boolean useCustomButtons = buttons != null && buttons.length > 0; boolean useDefaultButtons = defaultButtons != null && defaultButtons.length > 0; AbstractButton[] buttonArray; if (useCustomButtons) { // use user supplied buttons buttonArray = buttons; } else if (useDefaultButtons) { // create specified default buttons ArrayList<AbstractButton> list = new ArrayList<>(defaultButtons.length); for (DefaultButtons defB : defaultButtons) { switch (defB) { case OK_BUTTON: list.add(dialog.makeOkButton()); break; case CANCEL_BUTTON: list.add(dialog.makeCancelButton()); break; case CLOSE_BUTTON: list.add(dialog.makeCloseButton()); break; } } buttonArray = list.toArray(new AbstractButton[list.size()]); } else { // create single default button buttonArray = new AbstractButton[] { dialog.makeOkButton() }; } // do the layout dialog.layoutDefault(mainComponent, size, buttonArray); } return dialog; } } private static final long serialVersionUID = 1L; /** the maximum height ({@value #MAX_HEIGHT}) before size {@link #HUGE} will be reduced */ private static final int MAX_HEIGHT = 800; /** 720x540 */ public static final int NORMAL = 1; /** 720x300 */ public static final int BROAD = 12; /** 360x540 */ public static final int NARROW = 2; /** 800x600 */ public static final int LARGE = 3; /** 1020x700 */ public static final int WIDE = 11; /** 1000x760, automatically reduced to {@link #LARGE} for small resolutions */ public static final int HUGE = 9; /** 1000x760 */ public static final int HUGE_FORCED = 10; /** 600x200 */ public static final int MESSAGE = 4; /** 500x250 */ public static final int MESSAGE_BIT_EXTENDED = 15; /** 600x400 */ public static final int MESSAGE_EXTENDED = 5; /** 420x300 */ public static final int DEFAULT_SIZE = 8; /** 570x170 */ public static final int EXTENSIVE = 14; /** 520x770 */ public static final int TALL = 16; /** 720x700 */ public static final int NORMAL_EXTENDED = 13; private static final Dimension DIMENSION_MESSAGE = new Dimension(600, 200); private static final Dimension DIMENSION_MESSAGE_BIT_EXTENDED = new Dimension(500, 250); private static final Dimension DIMENSION_MESSAGE_EXTENDED = new Dimension(600, 400); private static final Dimension DIMENSION_DEFAULT = new Dimension(420, 300); private static final Dimension DIMENSION_NORMAL = new Dimension(720, 540); private static final Dimension DIMENSION_NORMAL_EXTENDED = new Dimension(720, 700); private static final Dimension DIMENSION_BROAD = new Dimension(720, 300); private static final Dimension DIMENSION_NARROW = new Dimension(360, 540); private static final Dimension DIMENSION_LARGE = new Dimension(800, 600); private static final Dimension DIMENSION_WIDE = new Dimension(1020, 700); private static final Dimension DIMENSION_HUGE = new Dimension(1000, 760); private static final Dimension DIMENSION_EXTENSIVE = new Dimension(570, 170); private static final Dimension DIMENSION_TALL = new Dimension(520, 770); public static final int GAP = 6; protected static final Insets INSETS = new Insets(GAP, GAP, GAP, GAP); protected FixedWidthEditorPane infoTextLabel; /** * Arguments which will replace the place holder in the I18n-Properties message. The first * argument will replace <code>{0}</code>, the second <code>{1}</code> and so on. */ protected final Object[] arguments; private Component centerComponent; private String key = null; protected boolean wasConfirmed = false; private final LinkedList<ChangeListener> listeners = new LinkedList<>(); /** * @deprecated Use {@link ButtonDialogBuilder} instead */ @Deprecated public ButtonDialog(String key, Object... arguments) { super(ApplicationFrame.getApplicationFrame(), I18N.getMessage(I18N.getGUIBundle(), "gui.dialog." + key + ".title", arguments), false); this.arguments = arguments; configure(key); pack(); ActionStatisticsCollector.getInstance().log(ActionStatisticsCollector.TYPE_DIALOG, key, "open"); checkForEDT(); } /** * @deprecated Use {@link ButtonDialogBuilder} instead */ @Deprecated public ButtonDialog(String key, boolean modal, Object... arguments) { super(ApplicationFrame.getApplicationFrame(), I18N.getMessage(I18N.getGUIBundle(), "gui.dialog." + key + ".title", arguments), modal); this.arguments = arguments; configure(key); pack(); ActionStatisticsCollector.getInstance().log(ActionStatisticsCollector.TYPE_DIALOG, key, "open"); checkForEDT(); } /** * @deprecated Use {@link ButtonDialogBuilder} instead */ @Deprecated public ButtonDialog(String key, ModalityType type, Object... arguments) { super(ApplicationFrame.getApplicationFrame(), I18N.getMessage(I18N.getGUIBundle(), "gui.dialog." + key + ".title", arguments), type); this.arguments = arguments; configure(key); pack(); ActionStatisticsCollector.getInstance().log(ActionStatisticsCollector.TYPE_DIALOG, key, "open"); checkForEDT(); } /** * @deprecated Use {@link ButtonDialogBuilder} instead */ @Deprecated public ButtonDialog(Dialog owner, String key, boolean modal, Object... arguments) { super(owner, I18N.getMessage(I18N.getGUIBundle(), "gui.dialog." + key + ".title", arguments), modal); this.arguments = arguments; configure(key); pack(); ActionStatisticsCollector.getInstance().log(ActionStatisticsCollector.TYPE_DIALOG, key, "open"); checkForEDT(); } /** * @deprecated Use {@link ButtonDialogBuilder} instead */ @Deprecated public ButtonDialog(Dialog owner, String key, Object... arguments) { super(owner, I18N.getMessage(I18N.getGUIBundle(), "gui.dialog." + key + ".title", arguments), false); this.arguments = arguments; configure(key); pack(); ActionStatisticsCollector.getInstance().log(ActionStatisticsCollector.TYPE_DIALOG, key, "open"); checkForEDT(); } /** * @deprecated Use {@link ButtonDialogBuilder} instead */ @Deprecated public ButtonDialog(Frame owner, String key, boolean modal, Object... arguments) { this(owner, key, modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS, arguments); } /** * @deprecated Use {@link ButtonDialogBuilder} instead */ @Deprecated public ButtonDialog(Frame owner, String key, Object... arguments) { this(owner, key, ModalityType.APPLICATION_MODAL, arguments); } /** * Constructor used by the {@link ButtonDialogBuilder} and can also be used when subclassing. * * @param owner * the owner or {@code null}. Note that an owner should be set if the dialog will be * modal, otherwise the order ends up being undefined and causing all sorts of * trouble * @param key * the i18n key * @param modalityType * the modality type * @param arguments * the optional i18n arguments * @since 6.5.0 */ protected ButtonDialog(Window owner, String key, ModalityType modalityType, Object... arguments) { this(owner, key, modalityType, owner != null ? owner.getGraphicsConfiguration() : null, arguments); } /** * Constructor used by the {@link ButtonDialogBuilder} and can also be used when subclassing. * * @param owner * the owner or {@code null}. Note that an owner should be set if the dialog will be * modal, otherwise the order ends up being undefined and causing all sorts of * trouble * @param key * the i18n key * @param modalityType * the modality type * @param graphicsConfig * the graphics config to use or {@code null} * @param arguments * the optional i18n arguments * @since 6.5.0 */ protected ButtonDialog(Window owner, String key, ModalityType modalityType, GraphicsConfiguration graphicsConfig, Object... arguments) { super(owner, I18N.getMessage(I18N.getGUIBundle(), "gui.dialog." + key + ".title", arguments), modalityType, graphicsConfig); this.arguments = arguments; configure(key); pack(); ActionStatisticsCollector.getInstance().log(ActionStatisticsCollector.TYPE_DIALOG, key, "open"); checkForEDT(); } private void configure(String key) { this.key = key; setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } protected final String getKey() { return "gui.dialog." + key; } /** * Returns the internationalized info text for this dialog. Argument formation is already * applied. */ protected String getInfoText() { return I18N.getMessage(I18N.getGUIBundle(), getKey() + ".message", this.arguments); } protected Icon getInfoIcon() { return SwingTools.createIcon("48/" + I18N.getMessage(I18N.getGUIBundle(), getKey() + ".icon")); } /** * Returns the internationalized title for this dialog. Argument formation is already applied. */ protected String getDialogTitle() { return I18N.getMessage(I18N.getGUIBundle(), getKey() + ".title", this.arguments); } private JPanel makeInfoPanel() { return makeInfoPanel(getInfoText(), getInfoIcon()); } private JPanel makeInfoPanel(String message, Icon icon) { JLabel infoIcon = new JLabel(icon); infoIcon.setVerticalAlignment(SwingConstants.TOP); JPanel infoPanel = new JPanel(new BorderLayout(20, 0)); infoPanel.setBorder(BorderFactory.createEmptyBorder(12, 16, 16, 4)); infoPanel.add(infoIcon, BorderLayout.WEST); int width; if (centerComponent != null) { width = (int) centerComponent.getPreferredSize().getWidth() - 88; // icon plus padding if (width < 420) { width = 420; } } else { width = 420; } infoTextLabel = new FixedWidthEditorPane(width, message); // set the background as for infoPanel such that infoTextLabel looks like a JLabel infoTextLabel.setBackground(infoPanel.getBackground()); infoPanel.add(infoTextLabel, BorderLayout.CENTER); return infoPanel; } protected void layoutDefault(JComponent centerComponent, int size, Collection<AbstractButton> buttons) { layoutDefault(centerComponent, size, buttons.toArray(new AbstractButton[buttons.size()])); } protected void layoutDefault(JComponent centerComponent, Collection<AbstractButton> buttons) { layoutDefault(centerComponent, DEFAULT_SIZE, buttons.toArray(new AbstractButton[buttons.size()])); } protected void layoutDefault(JComponent centerComponent, AbstractButton... buttons) { layoutDefault(centerComponent, DEFAULT_SIZE, buttons); } protected void layoutDefault(JComponent centerComponent, int size, AbstractButton... buttons) { layoutDefault(centerComponent, makeButtonPanel(buttons), size); } protected void layoutDefault(final JComponent centerComponent, JPanel buttonPanel) { layoutDefault(centerComponent, buttonPanel, DEFAULT_SIZE); } protected void layoutDefault(final JComponent centerComponent, JPanel buttonPanel, int size) { this.centerComponent = centerComponent; setTitle(getDialogTitle()); setLayout(new BorderLayout()); add(makeInfoPanel(), BorderLayout.NORTH); if (centerComponent != null) { JPanel centerPanel = new JPanel(new BorderLayout()); centerPanel.setBorder(BorderFactory.createEmptyBorder(0, GAP, 0, GAP)); centerPanel.add(centerComponent, BorderLayout.CENTER); add(centerPanel, BorderLayout.CENTER); } add(buttonPanel, BorderLayout.SOUTH); this.addComponentListener(new ComponentListener() { @Override public void componentHidden(ComponentEvent e) {} @Override public void componentMoved(ComponentEvent e) {} @Override public void componentResized(ComponentEvent e) { if (infoTextLabel != null && centerComponent != null) { int prefHeightBefore = infoTextLabel.getPreferredSize().height; infoTextLabel.setWidth(centerComponent.getWidth() - 88); int prefHeightAfter = infoTextLabel.getPreferredSize().height; int heightDiff = prefHeightAfter - prefHeightBefore; if (heightDiff > 0) { // re-pack this dialog if the infoTextLabel has changed its prefHeight after // the resize // fixes center component being overlapped/cut off ButtonDialog.this.pack(); } } } @Override public void componentShown(ComponentEvent e) {} }); switch (size) { case DEFAULT_SIZE: break; default: setPreferredSize(getDefaultSize(size)); break; } revalidate(); pack(); setDefaultLocation(); } protected void setDefaultLocation() { setLocationRelativeTo(getOwner() != null ? getOwner() : ApplicationFrame.getApplicationFrame()); } protected void setDefaultSize() { setDefaultSize(NORMAL); } protected Dimension getDefaultSize(int size) { switch (size) { case NARROW: return DIMENSION_NARROW; case NORMAL: return DIMENSION_NORMAL; case BROAD: return DIMENSION_BROAD; case LARGE: return DIMENSION_LARGE; case HUGE: // this dimension is too large for HD-ready displays and also for presentation // resolutions // return the next smaller dimension instead to avoid components being too large for // display if (RapidMinerGUI.getMainFrame() != null && RapidMinerGUI.getMainFrame().getGraphicsConfiguration() != null) { if (RapidMinerGUI.getMainFrame().getGraphicsConfiguration().getBounds().getHeight() <= MAX_HEIGHT) { return getDefaultSize(LARGE); } else { return DIMENSION_HUGE; } } else { if (Toolkit.getDefaultToolkit().getScreenSize().getHeight() <= MAX_HEIGHT) { return getDefaultSize(LARGE); } else { return DIMENSION_HUGE; } } case HUGE_FORCED: return DIMENSION_HUGE; case WIDE: return DIMENSION_WIDE; case MESSAGE: return DIMENSION_MESSAGE; case MESSAGE_BIT_EXTENDED: return DIMENSION_MESSAGE_BIT_EXTENDED; case NORMAL_EXTENDED: return DIMENSION_NORMAL_EXTENDED; case MESSAGE_EXTENDED: return DIMENSION_MESSAGE_EXTENDED; case EXTENSIVE: return DIMENSION_EXTENSIVE; case TALL: return DIMENSION_TALL; default: return DIMENSION_DEFAULT; } } protected void setDefaultSize(int size) { if (size != DEFAULT_SIZE) { setPreferredSize(getDefaultSize(size)); } pack(); } protected JPanel makeButtonPanel(Collection<AbstractButton> buttons) { return makeButtonPanel(buttons.toArray(new AbstractButton[buttons.size()])); } protected JPanel makeButtonPanel(AbstractButton... buttons) { JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, GAP, GAP)); for (final AbstractButton button : buttons) { if (button != null) { buttonPanel.add(button); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { ActionStatisticsCollector.getInstance().log(ActionStatisticsCollector.TYPE_DIALOG, key, button.getActionCommand()); } }); } } return buttonPanel; } /** Will be default button. */ protected JButton makeOkButton() { return makeOkButton("ok"); } protected JButton makeOkButton(String i18nKey) { Action okAction = new ResourceAction(i18nKey) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { wasConfirmed = true; ok(); } }; JButton button = new JButton(okAction); getRootPane().setDefaultButton(button); return button; } /** Will listen to ESCAPE. */ protected JButton makeCancelButton() { return makeCancelButton("cancel"); } protected JButton makeCancelButton(String i18nKey) { Action cancelAction = new ResourceAction(i18nKey) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { wasConfirmed = false; cancel(); } }; getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false), "CANCEL"); getRootPane().getActionMap().put("CANCEL", cancelAction); return new JButton(cancelAction); } /** Will be default button and listen to ESCAPE. */ protected JButton makeCloseButton() { Action action = new ResourceAction("close") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { wasConfirmed = false; close(); } }; getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false), "CLOSE"); getRootPane().getActionMap().put("CLOSE", action); JButton button = new JButton(action); getRootPane().setDefaultButton(button); return button; } protected void cancel() { dispose(); } protected void ok() { dispose(); } /** * * Calls {@link #setConfirmed(boolean)} and {@link #ok()} or {@link #cancel()} depending on the * provided input. * <p> * Necessary for extensions that cannot call the protected {@link #ok()}, {@link #cancel()} and * {@link #setConfirmed(boolean)} methods. * * @param accept * defines whether the user has accepted the dialog. If {@code true} {@link #ok()} is * called and {@link #setConfirmed(boolean)} is set to {@code true} as well. If * {@code false} {@link #cancel()} is called and {@link #setConfirmed(boolean)} is * set to {@code false}. * @since 6.5.0 */ public void accept(boolean accept) { if (accept) { setConfirmed(true); ok(); } else { setConfirmed(false); cancel(); } } protected void close() { dispose(); } /** Returns true iff the user pressed the generated ok button. */ public boolean wasConfirmed() { return wasConfirmed; } protected void setConfirmed(boolean b) { this.wasConfirmed = b; } public static TitledBorder createTitledBorder(String title) { TitledBorder border = new TitledBorder(createBorder(), title) { private static final long serialVersionUID = 3113821577644055057L; @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { super.paintBorder(c, g, x - EDGE_SPACING, y, width + 2 * EDGE_SPACING, height); } }; return border; } public static Border createBorder() { return BorderFactory.createMatteBorder(1, 1, 1, 1, Color.LIGHT_GRAY); } public static GridLayout createGridLayout(int rows, int columns) { return new GridLayout(rows, columns, GAP, GAP); } public void addChangeListener(ChangeListener l) { listeners.add(l); } public void removeChangeListener(ChangeListener l) { listeners.remove(l); } protected void fireStateChanged() { ChangeEvent e = new ChangeEvent(this); for (ChangeListener l : listeners) { l.stateChanged(e); } } private void checkForEDT() { if (RapidMiner.getVersion().isDevelopmentBuild() && !SwingUtilities.isEventDispatchThread()) { System.err.println("Button dialog constructor is not in EDT!"); new Exception().printStackTrace(); } } }