FileDocCategorySizeDatePackage
BasicOptionPaneUI.javaAPI DocJava SE 5 API48604Fri Aug 26 14:58:04 BST 2005javax.swing.plaf.basic

BasicOptionPaneUI.java

/*
 * @(#)BasicOptionPaneUI.java	1.58 03/12/19
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

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 <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:<p>
 * <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.
 * 
 * @version 1.58 12/19/03
 * @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;

    /**
     * <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. */
    protected JComponent          inputComponent;

    /** 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. */
    protected boolean             hasCustomComponents;

    protected PropertyChangeListener propertyChangeListener;

    private Handler handler;


    static {
	newline = (String)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.
      */
    public static ComponentUI createUI(JComponent x) {
	return new BasicOptionPaneUI();
    }

    /**
      * Installs the receiver as the L&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.
      */
    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.
     */
    public Dimension getMinimumOptionPaneSize() {
        if (minimumSize == null) {
            return new Dimension(MinimumWidth, MinimumHeight);
        }
	return new Dimension(minimumSize.width,
			     minimumSize.height);
    }

    /**
     * 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 ((JOptionPane)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
     * <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 <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 (int i = 0; i < msgs.length; i++) {
		addMessageComponents(container, cons, msgs[i], 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 = -1;
	    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.
     */
    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
     * <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
     * <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.
     */
    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.
     */
    protected int getMaxCharactersPerLineCount() {
	return optionPane.getMaxCharactersPerLineCount();
    }

   /**
     * 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 <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
     * <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.addAncestorListener(new AncestorListener() {
                           public void ancestorAdded(AncestorEvent e) { 
                               JButton defaultButton = (JButton)e.getComponent();
                               JRootPane root = SwingUtilities.getRootPane(defaultButton);
                               if (root != null) {
                                   root.setDefaultButton(defaultButton);
                               }
                           }
                           public void ancestorRemoved(AncestorEvent event) {}
                           public void ancestorMoved(AncestorEvent event) {}
                        });
                    }
		}
	    }
	    ((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.
     */
    protected Object[] getButtons() {
	if (optionPane != null) {
	    Object[] suppliedOptions = optionPane.getOptions();

	    if (suppliedOptions == null) {
                Object[] defaultOptions;
		int type = optionPane.getOptionType();
                Locale l = optionPane.getLocale();
		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"));
                    defaultOptions[1] = new ButtonFactory(
                        UIManager.getString("OptionPane.noButtonText", l),
                        getMnemonic("OptionPane.noButtonMnemonic", l),
                        (Icon)DefaultLookup.get(optionPane, this,
                                          "OptionPane.noIcon"));
		} 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"));
                    defaultOptions[1] = new ButtonFactory(
                        UIManager.getString("OptionPane.noButtonText",l),
                        getMnemonic("OptionPane.noButtonMnemonic", l),
                        (Icon)DefaultLookup.get(optionPane, this,
                                          "OptionPane.noIcon"));
                    defaultOptions[2] = new ButtonFactory(
                        UIManager.getString("OptionPane.cancelButtonText",l),
                        getMnemonic("OptionPane.cancelButtonMnemonic", l),
                        (Icon)DefaultLookup.get(optionPane, this,
                                          "OptionPane.cancelIcon"));
		} 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"));
                    defaultOptions[1] = new ButtonFactory(
                        UIManager.getString("OptionPane.cancelButtonText",l),
                        getMnemonic("OptionPane.cancelButtonMnemonic", l),
                        (Icon)DefaultLookup.get(optionPane, this,
                                          "OptionPane.cancelIcon"));
		} 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"));
                }
                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.
     */
    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.
     */
    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.
     */
    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
     */
    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.
     */
    public boolean containsCustomComponents(JOptionPane op) {
	return hasCustomComponents;
    }


    /**
     * <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 inner class is marked "public" due to a compiler bug.
     * This class should be treated as a "protected" inner class.
     * Instantiate it only within subclasses of BasicOptionPaneUI.
     */  
    public static class ButtonAreaLayout implements LayoutManager {
	protected boolean           syncAllWidths;
	protected int               padding;
        /** 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 compatability
         * 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 inner class is marked "public" due to a compiler bug.
     * This class should be treated as a "protected" inner class.
     * Instantiate it only within subclasses of BasicOptionPaneUI.
     */  
    public class PropertyChangeHandler implements PropertyChangeListener {
        /**
         * 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.
     */
    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.
     */
    private void configureButton(JButton button) {
        Font buttonFont = (Font)DefaultLookup.get(optionPane, this,
                                            "OptionPane.buttonFont");
        if (buttonFont != null) {
            button.setFont(buttonFont);
        }
    }

    /**
     * This inner class is marked "public" due to a compiler bug.
     * This class should be treated as a "protected" inner class.
     * Instantiate it only within subclasses of 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(new Integer(2));
                    
		    } else {
		        optionPane.setValue(new Integer(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));
	    }
	}

        //
        // 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 != (ComponentOrientation)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.
     */
    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.
         */
        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 <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(new Integer(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.
     */
    private static class ButtonFactory {
        private String text;
        private int mnemonic;
        private Icon icon;

        ButtonFactory(String text, int mnemonic, Icon icon) {
            this.text = text;
            this.mnemonic = mnemonic;
            this.icon = icon;
        }

        JButton createButton() {
            JButton button = new JButton(text);
            if (icon != null) {
                button.setIcon(icon);
            }
            if (mnemonic != 0) {
                button.setMnemonic(mnemonic);
            }
            return button;
        }
    }
}