/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javax.swing.plaf.basic;

import sun.swing.DefaultLookup;
import sun.swing.UIAction;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.OptionPaneUI;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Locale;
import java.security.AccessController;

import sun.security.action.GetPropertyAction;


Provides the basic look and feel for a JOptionPane. BasicMessagePaneUI provides a means to place an icon, message and buttons into a Container. Generally, the layout will look like:
       ------------------
       | i | message    |
       | c | message    |
       | o | message    |
       | n | message    |
       ------------------
       |     buttons    |
       |________________|
icon is an instance of Icon that is wrapped inside a JLabel. The message is an opaque object and is tested for the following: if the message is a Component it is added to the Container, if it is an Icon it is wrapped inside a JLabel and added to the Container otherwise it is wrapped inside a JLabel.

The above layout is used when the option pane's ComponentOrientation property is horizontal, left-to-right. The layout will be adjusted appropriately for other orientations.

The Container, message, icon, and buttons are all determined from abstract methods.

Author:James Gosling, Scott Violet, Amy Fowler
/** * Provides the basic look and feel for a <code>JOptionPane</code>. * <code>BasicMessagePaneUI</code> provides a means to place an icon, * message and buttons into a <code>Container</code>. * Generally, the layout will look like: * <pre> * ------------------ * | i | message | * | c | message | * | o | message | * | n | message | * ------------------ * | buttons | * |________________| * </pre> * icon is an instance of <code>Icon</code> that is wrapped inside a * <code>JLabel</code>. The message is an opaque object and is tested * for the following: if the message is a <code>Component</code> it is * added to the <code>Container</code>, if it is an <code>Icon</code> * it is wrapped inside a <code>JLabel</code> and added to the * <code>Container</code> otherwise it is wrapped inside a <code>JLabel</code>. * <p> * The above layout is used when the option pane's * <code>ComponentOrientation</code> property is horizontal, left-to-right. * The layout will be adjusted appropriately for other orientations. * <p> * The <code>Container</code>, message, icon, and buttons are all * determined from abstract methods. * * @author James Gosling * @author Scott Violet * @author Amy Fowler */
public class BasicOptionPaneUI extends OptionPaneUI { public static final int MinimumWidth = 262; public static final int MinimumHeight = 90; private static String newline;
JOptionPane that the receiver is providing the look and feel for.
/** * <code>JOptionPane</code> that the receiver is providing the * look and feel for. */
protected JOptionPane optionPane; protected Dimension minimumSize;
JComponent provide for input if optionPane.getWantsInput() returns true.
/** JComponent provide for input if optionPane.getWantsInput() returns * true. */
protected JComponent inputComponent;
Component to receive focus when messaged with selectInitialValue.
/** Component to receive focus when messaged with selectInitialValue. */
protected Component initialFocusComponent;
This is set to true in validateComponent if a Component is contained in either the message or the buttons.
/** This is set to true in validateComponent if a Component is contained * in either the message or the buttons. */
protected boolean hasCustomComponents; protected PropertyChangeListener propertyChangeListener; private Handler handler; static { newline = java.security.AccessController.doPrivileged( new GetPropertyAction("line.separator")); if (newline == null) { newline = "\n"; } } static void loadActionMap(LazyActionMap map) { map.put(new Actions(Actions.CLOSE)); BasicLookAndFeel.installAudioActionMap(map); }
Creates a new BasicOptionPaneUI instance.
/** * Creates a new BasicOptionPaneUI instance. */
public static ComponentUI createUI(JComponent x) { return new BasicOptionPaneUI(); }
Installs the receiver as the L&F for the passed in JOptionPane.
/** * Installs the receiver as the L&amp;F for the passed in * <code>JOptionPane</code>. */
public void installUI(JComponent c) { optionPane = (JOptionPane)c; installDefaults(); optionPane.setLayout(createLayoutManager()); installComponents(); installListeners(); installKeyboardActions(); }
Removes the receiver from the L&F controller of the passed in split pane.
/** * Removes the receiver from the L&amp;F controller of the passed in split * pane. */
public void uninstallUI(JComponent c) { uninstallComponents(); optionPane.setLayout(null); uninstallKeyboardActions(); uninstallListeners(); uninstallDefaults(); optionPane = null; } protected void installDefaults() { LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background", "OptionPane.foreground", "OptionPane.font"); LookAndFeel.installBorder(optionPane, "OptionPane.border"); minimumSize = UIManager.getDimension("OptionPane.minimumSize"); LookAndFeel.installProperty(optionPane, "opaque", Boolean.TRUE); } protected void uninstallDefaults() { LookAndFeel.uninstallBorder(optionPane); } protected void installComponents() { optionPane.add(createMessageArea()); Container separator = createSeparator(); if (separator != null) { optionPane.add(separator); } optionPane.add(createButtonArea()); optionPane.applyComponentOrientation(optionPane.getComponentOrientation()); } protected void uninstallComponents() { hasCustomComponents = false; inputComponent = null; initialFocusComponent = null; optionPane.removeAll(); } protected LayoutManager createLayoutManager() { return new BoxLayout(optionPane, BoxLayout.Y_AXIS); } protected void installListeners() { if ((propertyChangeListener = createPropertyChangeListener()) != null) { optionPane.addPropertyChangeListener(propertyChangeListener); } } protected void uninstallListeners() { if (propertyChangeListener != null) { optionPane.removePropertyChangeListener(propertyChangeListener); propertyChangeListener = null; } handler = null; } protected PropertyChangeListener createPropertyChangeListener() { return getHandler(); } private Handler getHandler() { if (handler == null) { handler = new Handler(); } return handler; } protected void installKeyboardActions() { InputMap map = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); SwingUtilities.replaceUIInputMap(optionPane, JComponent. WHEN_IN_FOCUSED_WINDOW, map); LazyActionMap.installLazyActionMap(optionPane, BasicOptionPaneUI.class, "OptionPane.actionMap"); } protected void uninstallKeyboardActions() { SwingUtilities.replaceUIInputMap(optionPane, JComponent. WHEN_IN_FOCUSED_WINDOW, null); SwingUtilities.replaceUIActionMap(optionPane, null); } InputMap getInputMap(int condition) { if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) { Object[] bindings = (Object[])DefaultLookup.get( optionPane, this, "OptionPane.windowBindings"); if (bindings != null) { return LookAndFeel.makeComponentInputMap(optionPane, bindings); } } return null; }
Returns the minimum size the option pane should be. Primarily provided for subclassers wishing to offer a different minimum size.
/** * Returns the minimum size the option pane should be. Primarily * provided for subclassers wishing to offer a different minimum size. */
public Dimension getMinimumOptionPaneSize() { if (minimumSize == null) { return new Dimension(MinimumWidth, MinimumHeight); } return new Dimension(minimumSize.width, minimumSize.height); }
If c is the JOptionPane the receiver is contained in, the preferred size that is returned is the maximum of the preferred size of the LayoutManager for the JOptionPane, and getMinimumOptionPaneSize.
/** * If <code>c</code> is the <code>JOptionPane</code> the receiver * is contained in, the preferred * size that is returned is the maximum of the preferred size of * the <code>LayoutManager</code> for the <code>JOptionPane</code>, and * <code>getMinimumOptionPaneSize</code>. */
public Dimension getPreferredSize(JComponent c) { if (c == optionPane) { Dimension ourMin = getMinimumOptionPaneSize(); LayoutManager lm = c.getLayout(); if (lm != null) { Dimension lmSize = lm.preferredLayoutSize(c); if (ourMin != null) return new Dimension (Math.max(lmSize.width, ourMin.width), Math.max(lmSize.height, ourMin.height)); return lmSize; } return ourMin; } return null; }
Messaged from installComponents to create a Container containing the body of the message. The icon is the created by calling addIcon.
/** * Messaged from installComponents to create a Container containing the * body of the message. The icon is the created by calling * <code>addIcon</code>. */
protected Container createMessageArea() { JPanel top = new JPanel(); Border topBorder = (Border)DefaultLookup.get(optionPane, this, "OptionPane.messageAreaBorder"); if (topBorder != null) { top.setBorder(topBorder); } top.setLayout(new BorderLayout()); /* Fill the body. */ Container body = new JPanel(new GridBagLayout()); Container realBody = new JPanel(new BorderLayout()); body.setName("OptionPane.body"); realBody.setName("OptionPane.realBody"); if (getIcon() != null) { JPanel sep = new JPanel(); sep.setName("OptionPane.separator"); sep.setPreferredSize(new Dimension(15, 1)); realBody.add(sep, BorderLayout.BEFORE_LINE_BEGINS); } realBody.add(body, BorderLayout.CENTER); GridBagConstraints cons = new GridBagConstraints(); cons.gridx = cons.gridy = 0; cons.gridwidth = GridBagConstraints.REMAINDER; cons.gridheight = 1; cons.anchor = DefaultLookup.getInt(optionPane, this, "OptionPane.messageAnchor", GridBagConstraints.CENTER); cons.insets = new Insets(0,0,3,0); addMessageComponents(body, cons, getMessage(), getMaxCharactersPerLineCount(), false); top.add(realBody, BorderLayout.CENTER); addIcon(top); return top; }
Creates the appropriate object to represent msg and places it into container. If msg is an instance of Component, it is added directly, if it is an Icon, a JLabel is created to represent it, otherwise a JLabel is created for the string, if d is an Object[], this method will be recursively invoked for the children. internallyCreated is true if Objc is an instance of Component and was created internally by this method (this is used to correctly set hasCustomComponents only if !internallyCreated).
/** * Creates the appropriate object to represent <code>msg</code> and * places it into <code>container</code>. If <code>msg</code> is an * instance of Component, it is added directly, if it is an Icon, * a JLabel is created to represent it, otherwise a JLabel is * created for the string, if <code>d</code> is an Object[], this * method will be recursively invoked for the children. * <code>internallyCreated</code> is true if Objc is an instance * of Component and was created internally by this method (this is * used to correctly set hasCustomComponents only if !internallyCreated). */
protected void addMessageComponents(Container container, GridBagConstraints cons, Object msg, int maxll, boolean internallyCreated) { if (msg == null) { return; } if (msg instanceof Component) { // To workaround problem where Gridbad will set child // to its minimum size if its preferred size will not fit // within allocated cells if (msg instanceof JScrollPane || msg instanceof JPanel) { cons.fill = GridBagConstraints.BOTH; cons.weighty = 1; } else { cons.fill = GridBagConstraints.HORIZONTAL; } cons.weightx = 1; container.add((Component) msg, cons); cons.weightx = 0; cons.weighty = 0; cons.fill = GridBagConstraints.NONE; cons.gridy++; if (!internallyCreated) { hasCustomComponents = true; } } else if (msg instanceof Object[]) { Object [] msgs = (Object[]) msg; for (Object o : msgs) { addMessageComponents(container, cons, o, maxll, false); } } else if (msg instanceof Icon) { JLabel label = new JLabel( (Icon)msg, SwingConstants.CENTER ); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); } else { String s = msg.toString(); int len = s.length(); if (len <= 0) { return; } int nl; int nll = 0; if ((nl = s.indexOf(newline)) >= 0) { nll = newline.length(); } else if ((nl = s.indexOf("\r\n")) >= 0) { nll = 2; } else if ((nl = s.indexOf('\n')) >= 0) { nll = 1; } if (nl >= 0) { // break up newlines if (nl == 0) { JPanel breakPanel = new JPanel() { public Dimension getPreferredSize() { Font f = getFont(); if (f != null) { return new Dimension(1, f.getSize() + 2); } return new Dimension(0, 0); } }; breakPanel.setName("OptionPane.break"); addMessageComponents(container, cons, breakPanel, maxll, true); } else { addMessageComponents(container, cons, s.substring(0, nl), maxll, false); } addMessageComponents(container, cons, s.substring(nl + nll), maxll, false); } else if (len > maxll) { Container c = Box.createVerticalBox(); c.setName("OptionPane.verticalBox"); burstStringInto(c, s, maxll); addMessageComponents(container, cons, c, maxll, true ); } else { JLabel label; label = new JLabel( s, JLabel.LEADING ); label.setName("OptionPane.label"); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); } } }
Returns the message to display from the JOptionPane the receiver is providing the look and feel for.
/** * Returns the message to display from the JOptionPane the receiver is * providing the look and feel for. */
protected Object getMessage() { inputComponent = null; if (optionPane != null) { if (optionPane.getWantsInput()) { /* Create a user component to capture the input. If the selectionValues are non null the component and there are < 20 values it'll be a combobox, if non null and >= 20, it'll be a list, otherwise it'll be a textfield. */ Object message = optionPane.getMessage(); Object[] sValues = optionPane.getSelectionValues(); Object inputValue = optionPane .getInitialSelectionValue(); JComponent toAdd; if (sValues != null) { if (sValues.length < 20) { JComboBox cBox = new JComboBox(); cBox.setName("OptionPane.comboBox"); for(int counter = 0, maxCounter = sValues.length; counter < maxCounter; counter++) { cBox.addItem(sValues[counter]); } if (inputValue != null) { cBox.setSelectedItem(inputValue); } inputComponent = cBox; toAdd = cBox; } else { JList list = new JList(sValues); JScrollPane sp = new JScrollPane(list); sp.setName("OptionPane.scrollPane"); list.setName("OptionPane.list"); list.setVisibleRowCount(10); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); if(inputValue != null) list.setSelectedValue(inputValue, true); list.addMouseListener(getHandler()); toAdd = sp; inputComponent = list; } } else { MultiplexingTextField tf = new MultiplexingTextField(20); tf.setName("OptionPane.textField"); tf.setKeyStrokes(new KeyStroke[] { KeyStroke.getKeyStroke("ENTER") } ); if (inputValue != null) { String inputString = inputValue.toString(); tf.setText(inputString); tf.setSelectionStart(0); tf.setSelectionEnd(inputString.length()); } tf.addActionListener(getHandler()); toAdd = inputComponent = tf; } Object[] newMessage; if (message == null) { newMessage = new Object[1]; newMessage[0] = toAdd; } else { newMessage = new Object[2]; newMessage[0] = message; newMessage[1] = toAdd; } return newMessage; } return optionPane.getMessage(); } return null; }
Creates and adds a JLabel representing the icon returned from getIcon to top. This is messaged from createMessageArea
/** * Creates and adds a JLabel representing the icon returned from * <code>getIcon</code> to <code>top</code>. This is messaged from * <code>createMessageArea</code> */
protected void addIcon(Container top) { /* Create the icon. */ Icon sideIcon = getIcon(); if (sideIcon != null) { JLabel iconLabel = new JLabel(sideIcon); iconLabel.setName("OptionPane.iconLabel"); iconLabel.setVerticalAlignment(SwingConstants.TOP); top.add(iconLabel, BorderLayout.BEFORE_LINE_BEGINS); } }
Returns the icon from the JOptionPane the receiver is providing the look and feel for, or the default icon as returned from getDefaultIcon.
/** * Returns the icon from the JOptionPane the receiver is providing * the look and feel for, or the default icon as returned from * <code>getDefaultIcon</code>. */
protected Icon getIcon() { Icon mIcon = (optionPane == null ? null : optionPane.getIcon()); if(mIcon == null && optionPane != null) mIcon = getIconForType(optionPane.getMessageType()); return mIcon; }
Returns the icon to use for the passed in type.
/** * Returns the icon to use for the passed in type. */
protected Icon getIconForType(int messageType) { if(messageType < 0 || messageType > 3) return null; String propertyName = null; switch(messageType) { case 0: propertyName = "OptionPane.errorIcon"; break; case 1: propertyName = "OptionPane.informationIcon"; break; case 2: propertyName = "OptionPane.warningIcon"; break; case 3: propertyName = "OptionPane.questionIcon"; break; } if (propertyName != null) { return (Icon)DefaultLookup.get(optionPane, this, propertyName); } return null; }
Returns the maximum number of characters to place on a line.
/** * Returns the maximum number of characters to place on a line. */
protected int getMaxCharactersPerLineCount() { return optionPane.getMaxCharactersPerLineCount(); }
Recursively creates new JLabel instances to represent d. Each JLabel instance is added to c.
/** * Recursively creates new JLabel instances to represent <code>d</code>. * Each JLabel instance is added to <code>c</code>. */
protected void burstStringInto(Container c, String d, int maxll) { // Primitive line wrapping int len = d.length(); if (len <= 0) return; if (len > maxll) { int p = d.lastIndexOf(' ', maxll); if (p <= 0) p = d.indexOf(' ', maxll); if (p > 0 && p < len) { burstStringInto(c, d.substring(0, p), maxll); burstStringInto(c, d.substring(p + 1), maxll); return; } } JLabel label = new JLabel(d, JLabel.LEFT); label.setName("OptionPane.label"); configureMessageLabel(label); c.add(label); } protected Container createSeparator() { return null; }
Creates and returns a Container containing the buttons. The buttons are created by calling getButtons.
/** * Creates and returns a Container containing the buttons. The buttons * are created by calling <code>getButtons</code>. */
protected Container createButtonArea() { JPanel bottom = new JPanel(); Border border = (Border)DefaultLookup.get(optionPane, this, "OptionPane.buttonAreaBorder"); bottom.setName("OptionPane.buttonArea"); if (border != null) { bottom.setBorder(border); } bottom.setLayout(new ButtonAreaLayout( DefaultLookup.getBoolean(optionPane, this, "OptionPane.sameSizeButtons", true), DefaultLookup.getInt(optionPane, this, "OptionPane.buttonPadding", 6), DefaultLookup.getInt(optionPane, this, "OptionPane.buttonOrientation", SwingConstants.CENTER), DefaultLookup.getBoolean(optionPane, this, "OptionPane.isYesLast", false))); addButtonComponents(bottom, getButtons(), getInitialValueIndex()); return bottom; }
Creates the appropriate object to represent each of the objects in buttons and adds it to container. This differs from addMessageComponents in that it will recurse on buttons and that if button is not a Component it will create an instance of JButton.
/** * Creates the appropriate object to represent each of the objects in * <code>buttons</code> and adds it to <code>container</code>. This * differs from addMessageComponents in that it will recurse on * <code>buttons</code> and that if button is not a Component * it will create an instance of JButton. */
protected void addButtonComponents(Container container, Object[] buttons, int initialIndex) { if (buttons != null && buttons.length > 0) { boolean sizeButtonsToSame = getSizeButtonsToSameWidth(); boolean createdAll = true; int numButtons = buttons.length; JButton[] createdButtons = null; int maxWidth = 0; if (sizeButtonsToSame) { createdButtons = new JButton[numButtons]; } for(int counter = 0; counter < numButtons; counter++) { Object button = buttons[counter]; Component newComponent; if (button instanceof Component) { createdAll = false; newComponent = (Component)button; container.add(newComponent); hasCustomComponents = true; } else { JButton aButton; if (button instanceof ButtonFactory) { aButton = ((ButtonFactory)button).createButton(); } else if (button instanceof Icon) aButton = new JButton((Icon)button); else aButton = new JButton(button.toString()); aButton.setName("OptionPane.button"); aButton.setMultiClickThreshhold(DefaultLookup.getInt( optionPane, this, "OptionPane.buttonClickThreshhold", 0)); configureButton(aButton); container.add(aButton); ActionListener buttonListener = createButtonActionListener(counter); if (buttonListener != null) { aButton.addActionListener(buttonListener); } newComponent = aButton; } if (sizeButtonsToSame && createdAll && (newComponent instanceof JButton)) { createdButtons[counter] = (JButton)newComponent; maxWidth = Math.max(maxWidth, newComponent.getMinimumSize().width); } if (counter == initialIndex) { initialFocusComponent = newComponent; if (initialFocusComponent instanceof JButton) { JButton defaultB = (JButton)initialFocusComponent; defaultB.addHierarchyListener(new HierarchyListener() { public void hierarchyChanged(HierarchyEvent e) { if ((e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0) { JButton defaultButton = (JButton) e.getComponent(); JRootPane root = SwingUtilities.getRootPane(defaultButton); if (root != null) { root.setDefaultButton(defaultButton); } } } }); } } } ((ButtonAreaLayout)container.getLayout()). setSyncAllWidths((sizeButtonsToSame && createdAll)); /* Set the padding, windows seems to use 8 if <= 2 components, otherwise 4 is used. It may actually just be the size of the buttons is always the same, not sure. */ if (DefaultLookup.getBoolean(optionPane, this, "OptionPane.setButtonMargin", true) && sizeButtonsToSame && createdAll) { JButton aButton; int padSize; padSize = (numButtons <= 2? 8 : 4); for(int counter = 0; counter < numButtons; counter++) { aButton = createdButtons[counter]; aButton.setMargin(new Insets(2, padSize, 2, padSize)); } } } } protected ActionListener createButtonActionListener(int buttonIndex) { return new ButtonActionListener(buttonIndex); }
Returns the buttons to display from the JOptionPane the receiver is providing the look and feel for. If the JOptionPane has options set, they will be provided, otherwise if the optionType is YES_NO_OPTION, yesNoOptions is returned, if the type is YES_NO_CANCEL_OPTION yesNoCancelOptions is returned, otherwise defaultButtons are returned.
/** * Returns the buttons to display from the JOptionPane the receiver is * providing the look and feel for. If the JOptionPane has options * set, they will be provided, otherwise if the optionType is * YES_NO_OPTION, yesNoOptions is returned, if the type is * YES_NO_CANCEL_OPTION yesNoCancelOptions is returned, otherwise * defaultButtons are returned. */
protected Object[] getButtons() { if (optionPane != null) { Object[] suppliedOptions = optionPane.getOptions(); if (suppliedOptions == null) { Object[] defaultOptions; int type = optionPane.getOptionType(); Locale l = optionPane.getLocale(); int minimumWidth = DefaultLookup.getInt(optionPane, this, "OptionPane.buttonMinimumWidth",-1); if (type == JOptionPane.YES_NO_OPTION) { defaultOptions = new ButtonFactory[2]; defaultOptions[0] = new ButtonFactory( UIManager.getString("OptionPane.yesButtonText", l), getMnemonic("OptionPane.yesButtonMnemonic", l), (Icon)DefaultLookup.get(optionPane, this, "OptionPane.yesIcon"), minimumWidth); defaultOptions[1] = new ButtonFactory( UIManager.getString("OptionPane.noButtonText", l), getMnemonic("OptionPane.noButtonMnemonic", l), (Icon)DefaultLookup.get(optionPane, this, "OptionPane.noIcon"), minimumWidth); } else if (type == JOptionPane.YES_NO_CANCEL_OPTION) { defaultOptions = new ButtonFactory[3]; defaultOptions[0] = new ButtonFactory( UIManager.getString("OptionPane.yesButtonText", l), getMnemonic("OptionPane.yesButtonMnemonic", l), (Icon)DefaultLookup.get(optionPane, this, "OptionPane.yesIcon"), minimumWidth); defaultOptions[1] = new ButtonFactory( UIManager.getString("OptionPane.noButtonText",l), getMnemonic("OptionPane.noButtonMnemonic", l), (Icon)DefaultLookup.get(optionPane, this, "OptionPane.noIcon"), minimumWidth); defaultOptions[2] = new ButtonFactory( UIManager.getString("OptionPane.cancelButtonText",l), getMnemonic("OptionPane.cancelButtonMnemonic", l), (Icon)DefaultLookup.get(optionPane, this, "OptionPane.cancelIcon"), minimumWidth); } else if (type == JOptionPane.OK_CANCEL_OPTION) { defaultOptions = new ButtonFactory[2]; defaultOptions[0] = new ButtonFactory( UIManager.getString("OptionPane.okButtonText",l), getMnemonic("OptionPane.okButtonMnemonic", l), (Icon)DefaultLookup.get(optionPane, this, "OptionPane.okIcon"), minimumWidth); defaultOptions[1] = new ButtonFactory( UIManager.getString("OptionPane.cancelButtonText",l), getMnemonic("OptionPane.cancelButtonMnemonic", l), (Icon)DefaultLookup.get(optionPane, this, "OptionPane.cancelIcon"), minimumWidth); } else { defaultOptions = new ButtonFactory[1]; defaultOptions[0] = new ButtonFactory( UIManager.getString("OptionPane.okButtonText",l), getMnemonic("OptionPane.okButtonMnemonic", l), (Icon)DefaultLookup.get(optionPane, this, "OptionPane.okIcon"), minimumWidth); } return defaultOptions; } return suppliedOptions; } return null; } private int getMnemonic(String key, Locale l) { String value = (String)UIManager.get(key, l); if (value == null) { return 0; } try { return Integer.parseInt(value); } catch (NumberFormatException nfe) { } return 0; }
Returns true, basic L&F wants all the buttons to have the same width.
/** * Returns true, basic L&amp;F wants all the buttons to have the same * width. */
protected boolean getSizeButtonsToSameWidth() { return true; }
Returns the initial index into the buttons to select. The index is calculated from the initial value from the JOptionPane and options of the JOptionPane or 0.
/** * Returns the initial index into the buttons to select. The index * is calculated from the initial value from the JOptionPane and * options of the JOptionPane or 0. */
protected int getInitialValueIndex() { if (optionPane != null) { Object iv = optionPane.getInitialValue(); Object[] options = optionPane.getOptions(); if(options == null) { return 0; } else if(iv != null) { for(int counter = options.length - 1; counter >= 0; counter--){ if(options[counter].equals(iv)) return counter; } } } return -1; }
Sets the input value in the option pane the receiver is providing the look and feel for based on the value in the inputComponent.
/** * Sets the input value in the option pane the receiver is providing * the look and feel for based on the value in the inputComponent. */
protected void resetInputValue() { if(inputComponent != null && (inputComponent instanceof JTextField)) { optionPane.setInputValue(((JTextField)inputComponent).getText()); } else if(inputComponent != null && (inputComponent instanceof JComboBox)) { optionPane.setInputValue(((JComboBox)inputComponent) .getSelectedItem()); } else if(inputComponent != null) { optionPane.setInputValue(((JList)inputComponent) .getSelectedValue()); } }
If inputComponent is non-null, the focus is requested on that, otherwise request focus on the default value
/** * If inputComponent is non-null, the focus is requested on that, * otherwise request focus on the default value */
public void selectInitialValue(JOptionPane op) { if (inputComponent != null) inputComponent.requestFocus(); else { if (initialFocusComponent != null) initialFocusComponent.requestFocus(); if (initialFocusComponent instanceof JButton) { JRootPane root = SwingUtilities.getRootPane(initialFocusComponent); if (root != null) { root.setDefaultButton((JButton)initialFocusComponent); } } } }
Returns true if in the last call to validateComponent the message or buttons contained a subclass of Component.
/** * Returns true if in the last call to validateComponent the message * or buttons contained a subclass of Component. */
public boolean containsCustomComponents(JOptionPane op) { return hasCustomComponents; }
ButtonAreaLayout behaves in a similar manner to FlowLayout. It lays out all components from left to right. If syncAllWidths is true, the widths of each component will be set to the largest preferred size width. This class should be treated as a "protected" inner class. Instantiate it only within subclasses of BasicOptionPaneUI.
/** * <code>ButtonAreaLayout</code> behaves in a similar manner to * <code>FlowLayout</code>. It lays out all components from left to * right. If <code>syncAllWidths</code> is true, the widths of each * component will be set to the largest preferred size width. * * This class should be treated as a &quot;protected&quot; inner class. * Instantiate it only within subclasses of {@code BasicOptionPaneUI}. */
public static class ButtonAreaLayout implements LayoutManager { protected boolean syncAllWidths; protected int padding;
If true, children are lumped together in parent.
/** If true, children are lumped together in parent. */
protected boolean centersChildren; private int orientation; private boolean reverseButtons;
Indicates whether or not centersChildren should be used vs the orientation. This is done for backward compatibility for subclassers.
/** * Indicates whether or not centersChildren should be used vs * the orientation. This is done for backward compatibility * for subclassers. */
private boolean useOrientation; public ButtonAreaLayout(boolean syncAllWidths, int padding) { this.syncAllWidths = syncAllWidths; this.padding = padding; centersChildren = true; useOrientation = false; } ButtonAreaLayout(boolean syncAllSizes, int padding, int orientation, boolean reverseButtons) { this(syncAllSizes, padding); useOrientation = true; this.orientation = orientation; this.reverseButtons = reverseButtons; } public void setSyncAllWidths(boolean newValue) { syncAllWidths = newValue; } public boolean getSyncAllWidths() { return syncAllWidths; } public void setPadding(int newPadding) { this.padding = newPadding; } public int getPadding() { return padding; } public void setCentersChildren(boolean newValue) { centersChildren = newValue; useOrientation = false; } public boolean getCentersChildren() { return centersChildren; } private int getOrientation(Container container) { if (!useOrientation) { return SwingConstants.CENTER; } if (container.getComponentOrientation().isLeftToRight()) { return orientation; } switch (orientation) { case SwingConstants.LEFT: return SwingConstants.RIGHT; case SwingConstants.RIGHT: return SwingConstants.LEFT; case SwingConstants.CENTER: return SwingConstants.CENTER; } return SwingConstants.LEFT; } public void addLayoutComponent(String string, Component comp) { } public void layoutContainer(Container container) { Component[] children = container.getComponents(); if(children != null && children.length > 0) { int numChildren = children.length; Insets insets = container.getInsets(); int maxWidth = 0; int maxHeight = 0; int totalButtonWidth = 0; int x = 0; int xOffset = 0; boolean ltr = container.getComponentOrientation(). isLeftToRight(); boolean reverse = (ltr) ? reverseButtons : !reverseButtons; for(int counter = 0; counter < numChildren; counter++) { Dimension pref = children[counter].getPreferredSize(); maxWidth = Math.max(maxWidth, pref.width); maxHeight = Math.max(maxHeight, pref.height); totalButtonWidth += pref.width; } if (getSyncAllWidths()) { totalButtonWidth = maxWidth * numChildren; } totalButtonWidth += (numChildren - 1) * padding; switch (getOrientation(container)) { case SwingConstants.LEFT: x = insets.left; break; case SwingConstants.RIGHT: x = container.getWidth() - insets.right - totalButtonWidth; break; case SwingConstants.CENTER: if (getCentersChildren() || numChildren < 2) { x = (container.getWidth() - totalButtonWidth) / 2; } else { x = insets.left; if (getSyncAllWidths()) { xOffset = (container.getWidth() - insets.left - insets.right - totalButtonWidth) / (numChildren - 1) + maxWidth; } else { xOffset = (container.getWidth() - insets.left - insets.right - totalButtonWidth) / (numChildren - 1); } } break; } for (int counter = 0; counter < numChildren; counter++) { int index = (reverse) ? numChildren - counter - 1 : counter; Dimension pref = children[index].getPreferredSize(); if (getSyncAllWidths()) { children[index].setBounds(x, insets.top, maxWidth, maxHeight); } else { children[index].setBounds(x, insets.top, pref.width, pref.height); } if (xOffset != 0) { x += xOffset; } else { x += children[index].getWidth() + padding; } } } } public Dimension minimumLayoutSize(Container c) { if(c != null) { Component[] children = c.getComponents(); if(children != null && children.length > 0) { Dimension aSize; int numChildren = children.length; int height = 0; Insets cInsets = c.getInsets(); int extraHeight = cInsets.top + cInsets.bottom; int extraWidth = cInsets.left + cInsets.right; if (syncAllWidths) { int maxWidth = 0; for(int counter = 0; counter < numChildren; counter++){ aSize = children[counter].getPreferredSize(); height = Math.max(height, aSize.height); maxWidth = Math.max(maxWidth, aSize.width); } return new Dimension(extraWidth + (maxWidth * numChildren) + (numChildren - 1) * padding, extraHeight + height); } else { int totalWidth = 0; for(int counter = 0; counter < numChildren; counter++){ aSize = children[counter].getPreferredSize(); height = Math.max(height, aSize.height); totalWidth += aSize.width; } totalWidth += ((numChildren - 1) * padding); return new Dimension(extraWidth + totalWidth, extraHeight + height); } } } return new Dimension(0, 0); } public Dimension preferredLayoutSize(Container c) { return minimumLayoutSize(c); } public void removeLayoutComponent(Component c) { } }
This class should be treated as a "protected" inner class. Instantiate it only within subclasses of BasicOptionPaneUI.
/** * This class should be treated as a &quot;protected&quot; inner class. * Instantiate it only within subclasses of {@code BasicOptionPaneUI}. */
public class PropertyChangeHandler implements PropertyChangeListener {
If the source of the PropertyChangeEvent e equals the optionPane and is one of the ICON_PROPERTY, MESSAGE_PROPERTY, OPTIONS_PROPERTY or INITIAL_VALUE_PROPERTY, validateComponent is invoked.
/** * If the source of the PropertyChangeEvent <code>e</code> equals the * optionPane and is one of the ICON_PROPERTY, MESSAGE_PROPERTY, * OPTIONS_PROPERTY or INITIAL_VALUE_PROPERTY, * validateComponent is invoked. */
public void propertyChange(PropertyChangeEvent e) { getHandler().propertyChange(e); } }
Configures any necessary colors/fonts for the specified label used representing the message.
/** * Configures any necessary colors/fonts for the specified label * used representing the message. */
private void configureMessageLabel(JLabel label) { Color color = (Color)DefaultLookup.get(optionPane, this, "OptionPane.messageForeground"); if (color != null) { label.setForeground(color); } Font messageFont = (Font)DefaultLookup.get(optionPane, this, "OptionPane.messageFont"); if (messageFont != null) { label.setFont(messageFont); } }
Configures any necessary colors/fonts for the specified button used representing the button portion of the optionpane.
/** * Configures any necessary colors/fonts for the specified button * used representing the button portion of the optionpane. */
private void configureButton(JButton button) { Font buttonFont = (Font)DefaultLookup.get(optionPane, this, "OptionPane.buttonFont"); if (buttonFont != null) { button.setFont(buttonFont); } }
This class should be treated as a "protected" inner class. Instantiate it only within subclasses of BasicOptionPaneUI.
/** * This class should be treated as a &quot;protected&quot; inner class. * Instantiate it only within subclasses of {@code BasicOptionPaneUI}. */
public class ButtonActionListener implements ActionListener { protected int buttonIndex; public ButtonActionListener(int buttonIndex) { this.buttonIndex = buttonIndex; } public void actionPerformed(ActionEvent e) { if (optionPane != null) { int optionType = optionPane.getOptionType(); Object[] options = optionPane.getOptions(); /* If the option pane takes input, then store the input value * if custom options were specified, if the option type is * DEFAULT_OPTION, OR if option type is set to a predefined * one and the user chose the affirmative answer. */ if (inputComponent != null) { if (options != null || optionType == JOptionPane.DEFAULT_OPTION || ((optionType == JOptionPane.YES_NO_OPTION || optionType == JOptionPane.YES_NO_CANCEL_OPTION || optionType == JOptionPane.OK_CANCEL_OPTION) && buttonIndex == 0)) { resetInputValue(); } } if (options == null) { if (optionType == JOptionPane.OK_CANCEL_OPTION && buttonIndex == 1) { optionPane.setValue(Integer.valueOf(2)); } else { optionPane.setValue(Integer.valueOf(buttonIndex)); } } else { optionPane.setValue(options[buttonIndex]); } } } } private class Handler implements ActionListener, MouseListener, PropertyChangeListener { // // ActionListener // public void actionPerformed(ActionEvent e) { optionPane.setInputValue(((JTextField)e.getSource()).getText()); } // // MouseListener // public void mouseClicked(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { if (e.getClickCount() == 2) { JList list = (JList)e.getSource(); int index = list.locationToIndex(e.getPoint()); optionPane.setInputValue(list.getModel().getElementAt(index)); optionPane.setValue(JOptionPane.OK_OPTION); } } // // PropertyChangeListener // public void propertyChange(PropertyChangeEvent e) { if(e.getSource() == optionPane) { // Option Pane Auditory Cue Activation // only respond to "ancestor" changes // the idea being that a JOptionPane gets a JDialog when it is // set to appear and loses it's JDialog when it is dismissed. if ("ancestor" == e.getPropertyName()) { JOptionPane op = (JOptionPane)e.getSource(); boolean isComingUp; // if the old value is null, then the JOptionPane is being // created since it didn't previously have an ancestor. if (e.getOldValue() == null) { isComingUp = true; } else { isComingUp = false; } // figure out what to do based on the message type switch (op.getMessageType()) { case JOptionPane.PLAIN_MESSAGE: if (isComingUp) { BasicLookAndFeel.playSound(optionPane, "OptionPane.informationSound"); } break; case JOptionPane.QUESTION_MESSAGE: if (isComingUp) { BasicLookAndFeel.playSound(optionPane, "OptionPane.questionSound"); } break; case JOptionPane.INFORMATION_MESSAGE: if (isComingUp) { BasicLookAndFeel.playSound(optionPane, "OptionPane.informationSound"); } break; case JOptionPane.WARNING_MESSAGE: if (isComingUp) { BasicLookAndFeel.playSound(optionPane, "OptionPane.warningSound"); } break; case JOptionPane.ERROR_MESSAGE: if (isComingUp) { BasicLookAndFeel.playSound(optionPane, "OptionPane.errorSound"); } break; default: System.err.println("Undefined JOptionPane type: " + op.getMessageType()); break; } } // Visual activity String changeName = e.getPropertyName(); if(changeName == JOptionPane.OPTIONS_PROPERTY || changeName == JOptionPane.INITIAL_VALUE_PROPERTY || changeName == JOptionPane.ICON_PROPERTY || changeName == JOptionPane.MESSAGE_TYPE_PROPERTY || changeName == JOptionPane.OPTION_TYPE_PROPERTY || changeName == JOptionPane.MESSAGE_PROPERTY || changeName == JOptionPane.SELECTION_VALUES_PROPERTY || changeName == JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY || changeName == JOptionPane.WANTS_INPUT_PROPERTY) { uninstallComponents(); installComponents(); optionPane.validate(); } else if (changeName == "componentOrientation") { ComponentOrientation o = (ComponentOrientation)e.getNewValue(); JOptionPane op = (JOptionPane)e.getSource(); if (o != e.getOldValue()) { op.applyComponentOrientation(o); } } } } } // // Classes used when optionPane.getWantsInput returns true. //
A JTextField that allows you to specify an array of KeyStrokes that that will have their bindings processed regardless of whether or not they are registered on the JTextField. This is used as we really want the ActionListener to be notified so that we can push the change to the JOptionPane, but we also want additional bindings (those of the JRootPane) to be processed as well.
/** * A JTextField that allows you to specify an array of KeyStrokes that * that will have their bindings processed regardless of whether or * not they are registered on the JTextField. This is used as we really * want the ActionListener to be notified so that we can push the * change to the JOptionPane, but we also want additional bindings * (those of the JRootPane) to be processed as well. */
private static class MultiplexingTextField extends JTextField { private KeyStroke[] strokes; MultiplexingTextField(int cols) { super(cols); }
Sets the KeyStrokes that will be additional processed for ancestor bindings.
/** * Sets the KeyStrokes that will be additional processed for * ancestor bindings. */
void setKeyStrokes(KeyStroke[] strokes) { this.strokes = strokes; } protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { boolean processed = super.processKeyBinding(ks, e, condition, pressed); if (processed && condition != JComponent.WHEN_IN_FOCUSED_WINDOW) { for (int counter = strokes.length - 1; counter >= 0; counter--) { if (strokes[counter].equals(ks)) { // Returning false will allow further processing // of the bindings, eg our parent Containers will get a // crack at them. return false; } } } return processed; } }
Registered in the ActionMap. Sets the value of the option pane to JOptionPane.CLOSED_OPTION.
/** * Registered in the ActionMap. Sets the value of the option pane * to <code>JOptionPane.CLOSED_OPTION</code>. */
private static class Actions extends UIAction { private static final String CLOSE = "close"; Actions(String key) { super(key); } public void actionPerformed(ActionEvent e) { if (getName() == CLOSE) { JOptionPane optionPane = (JOptionPane)e.getSource(); optionPane.setValue(Integer.valueOf(JOptionPane.CLOSED_OPTION)); } } }
This class is used to create the default buttons. This indirection is used so that addButtonComponents can tell which Buttons were created by us vs subclassers or from the JOptionPane itself.
/** * This class is used to create the default buttons. This indirection is * used so that addButtonComponents can tell which Buttons were created * by us vs subclassers or from the JOptionPane itself. */
private static class ButtonFactory { private String text; private int mnemonic; private Icon icon; private int minimumWidth = -1; ButtonFactory(String text, int mnemonic, Icon icon, int minimumWidth) { this.text = text; this.mnemonic = mnemonic; this.icon = icon; this.minimumWidth = minimumWidth; } JButton createButton() { JButton button; if (minimumWidth > 0) { button = new ConstrainedButton(text, minimumWidth); } else { button = new JButton(text); } if (icon != null) { button.setIcon(icon); } if (mnemonic != 0) { button.setMnemonic(mnemonic); } return button; } private static class ConstrainedButton extends JButton { int minimumWidth; ConstrainedButton(String text, int minimumWidth) { super(text); this.minimumWidth = minimumWidth; } public Dimension getMinimumSize() { Dimension min = super.getMinimumSize(); min.width = Math.max(min.width, minimumWidth); return min; } public Dimension getPreferredSize() { Dimension pref = super.getPreferredSize(); pref.width = Math.max(pref.width, minimumWidth); return pref; } } } }