1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.plaf.basic;
  27 
  28 import sun.swing.DefaultLookup;
  29 import sun.swing.UIAction;
  30 import javax.swing.border.Border;
  31 import javax.swing.border.EmptyBorder;
  32 import javax.swing.*;
  33 import javax.swing.event.*;
  34 import javax.swing.plaf.ActionMapUIResource;
  35 import javax.swing.plaf.ComponentUI;
  36 import javax.swing.plaf.OptionPaneUI;
  37 import java.awt.*;
  38 import java.awt.event.*;
  39 import java.beans.PropertyChangeEvent;
  40 import java.beans.PropertyChangeListener;
  41 import java.util.Locale;
  42 import java.security.AccessController;
  43 
  44 import sun.security.action.GetPropertyAction;
  45 
  46 
  47 /**
  48  * Provides the basic look and feel for a <code>JOptionPane</code>.
  49  * <code>BasicMessagePaneUI</code> provides a means to place an icon,
  50  * message and buttons into a <code>Container</code>.
  51  * Generally, the layout will look like:
  52  * <pre>
  53  *        ------------------
  54  *        | i | message    |
  55  *        | c | message    |
  56  *        | o | message    |
  57  *        | n | message    |
  58  *        ------------------
  59  *        |     buttons    |
  60  *        |________________|
  61  * </pre>
  62  * icon is an instance of <code>Icon</code> that is wrapped inside a
  63  * <code>JLabel</code>.  The message is an opaque object and is tested
  64  * for the following: if the message is a <code>Component</code> it is
  65  * added to the <code>Container</code>, if it is an <code>Icon</code>
  66  * it is wrapped inside a <code>JLabel</code> and added to the
  67  * <code>Container</code> otherwise it is wrapped inside a <code>JLabel</code>.
  68  * <p>
  69  * The above layout is used when the option pane's
  70  * <code>ComponentOrientation</code> property is horizontal, left-to-right.
  71  * The layout will be adjusted appropriately for other orientations.
  72  * <p>
  73  * The <code>Container</code>, message, icon, and buttons are all
  74  * determined from abstract methods.
  75  *
  76  * @author James Gosling
  77  * @author Scott Violet
  78  * @author Amy Fowler
  79  */
  80 public class BasicOptionPaneUI extends OptionPaneUI {
  81 
  82     public static final int MinimumWidth = 262;
  83     public static final int MinimumHeight = 90;
  84 
  85     private static String newline;
  86 
  87     /**
  88      * <code>JOptionPane</code> that the receiver is providing the
  89      * look and feel for.
  90      */
  91     protected JOptionPane         optionPane;
  92 
  93     protected Dimension minimumSize;
  94 
  95     /** JComponent provide for input if optionPane.getWantsInput() returns
  96      * true. */
  97     protected JComponent          inputComponent;
  98 
  99     /** Component to receive focus when messaged with selectInitialValue. */
 100     protected Component           initialFocusComponent;
 101 
 102     /** This is set to true in validateComponent if a Component is contained
 103      * in either the message or the buttons. */
 104     protected boolean             hasCustomComponents;
 105 
 106     protected PropertyChangeListener propertyChangeListener;
 107 
 108     private Handler handler;
 109 
 110 
 111     static {
 112         newline = System.lineSeparator();
 113         if (newline == null) {
 114             newline = "\n";
 115         }
 116     }
 117 
 118     static void loadActionMap(LazyActionMap map) {
 119         map.put(new Actions(Actions.CLOSE));
 120         BasicLookAndFeel.installAudioActionMap(map);
 121     }
 122 
 123 
 124 
 125     /**
 126       * Creates a new BasicOptionPaneUI instance.
 127       */
 128     public static ComponentUI createUI(JComponent x) {
 129         return new BasicOptionPaneUI();
 130     }
 131 
 132     /**
 133       * Installs the receiver as the L&amp;F for the passed in
 134       * <code>JOptionPane</code>.
 135       */
 136     public void installUI(JComponent c) {
 137         optionPane = (JOptionPane)c;
 138         installDefaults();
 139         optionPane.setLayout(createLayoutManager());
 140         installComponents();
 141         installListeners();
 142         installKeyboardActions();
 143     }
 144 
 145     /**
 146       * Removes the receiver from the L&amp;F controller of the passed in split
 147       * pane.
 148       */
 149     public void uninstallUI(JComponent c) {
 150         uninstallComponents();
 151         optionPane.setLayout(null);
 152         uninstallKeyboardActions();
 153         uninstallListeners();
 154         uninstallDefaults();
 155         optionPane = null;
 156     }
 157 
 158     protected void installDefaults() {
 159         LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background",
 160                                          "OptionPane.foreground", "OptionPane.font");
 161         LookAndFeel.installBorder(optionPane, "OptionPane.border");
 162         minimumSize = UIManager.getDimension("OptionPane.minimumSize");
 163         LookAndFeel.installProperty(optionPane, "opaque", Boolean.TRUE);
 164     }
 165 
 166     protected void uninstallDefaults() {
 167         LookAndFeel.uninstallBorder(optionPane);
 168     }
 169 
 170     protected void installComponents() {
 171         optionPane.add(createMessageArea());
 172 
 173         Container separator = createSeparator();
 174         if (separator != null) {
 175             optionPane.add(separator);
 176         }
 177         optionPane.add(createButtonArea());
 178         optionPane.applyComponentOrientation(optionPane.getComponentOrientation());
 179     }
 180 
 181     protected void uninstallComponents() {
 182         hasCustomComponents = false;
 183         inputComponent = null;
 184         initialFocusComponent = null;
 185         optionPane.removeAll();
 186     }
 187 
 188     protected LayoutManager createLayoutManager() {
 189         return new BoxLayout(optionPane, BoxLayout.Y_AXIS);
 190     }
 191 
 192     protected void installListeners() {
 193         if ((propertyChangeListener = createPropertyChangeListener()) != null) {
 194             optionPane.addPropertyChangeListener(propertyChangeListener);
 195         }
 196     }
 197 
 198     protected void uninstallListeners() {
 199         if (propertyChangeListener != null) {
 200             optionPane.removePropertyChangeListener(propertyChangeListener);
 201             propertyChangeListener = null;
 202         }
 203         handler = null;
 204     }
 205 
 206     protected PropertyChangeListener createPropertyChangeListener() {
 207         return getHandler();
 208     }
 209 
 210     private Handler getHandler() {
 211         if (handler == null) {
 212             handler = new Handler();
 213         }
 214         return handler;
 215     }
 216 
 217     protected void installKeyboardActions() {
 218         InputMap map = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
 219 
 220         SwingUtilities.replaceUIInputMap(optionPane, JComponent.
 221                                        WHEN_IN_FOCUSED_WINDOW, map);
 222 
 223         LazyActionMap.installLazyActionMap(optionPane, BasicOptionPaneUI.class,
 224                                            "OptionPane.actionMap");
 225     }
 226 
 227     protected void uninstallKeyboardActions() {
 228         SwingUtilities.replaceUIInputMap(optionPane, JComponent.
 229                                        WHEN_IN_FOCUSED_WINDOW, null);
 230         SwingUtilities.replaceUIActionMap(optionPane, null);
 231     }
 232 
 233     InputMap getInputMap(int condition) {
 234         if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) {
 235             Object[] bindings = (Object[])DefaultLookup.get(
 236                              optionPane, this, "OptionPane.windowBindings");
 237             if (bindings != null) {
 238                 return LookAndFeel.makeComponentInputMap(optionPane, bindings);
 239             }
 240         }
 241         return null;
 242     }
 243 
 244     /**
 245      * Returns the minimum size the option pane should be. Primarily
 246      * provided for subclassers wishing to offer a different minimum size.
 247      */
 248     public Dimension getMinimumOptionPaneSize() {
 249         if (minimumSize == null) {
 250             return new Dimension(MinimumWidth, MinimumHeight);
 251         }
 252         return new Dimension(minimumSize.width,
 253                              minimumSize.height);
 254     }
 255 
 256     /**
 257      * If <code>c</code> is the <code>JOptionPane</code> the receiver
 258      * is contained in, the preferred
 259      * size that is returned is the maximum of the preferred size of
 260      * the <code>LayoutManager</code> for the <code>JOptionPane</code>, and
 261      * <code>getMinimumOptionPaneSize</code>.
 262      */
 263     public Dimension getPreferredSize(JComponent c) {
 264         if (c == optionPane) {
 265             Dimension            ourMin = getMinimumOptionPaneSize();
 266             LayoutManager        lm = c.getLayout();
 267 
 268             if (lm != null) {
 269                 Dimension         lmSize = lm.preferredLayoutSize(c);
 270 
 271                 if (ourMin != null)
 272                     return new Dimension
 273                         (Math.max(lmSize.width, ourMin.width),
 274                          Math.max(lmSize.height, ourMin.height));
 275                 return lmSize;
 276             }
 277             return ourMin;
 278         }
 279         return null;
 280     }
 281 
 282     /**
 283      * Messaged from installComponents to create a Container containing the
 284      * body of the message. The icon is the created by calling
 285      * <code>addIcon</code>.
 286      */
 287     protected Container createMessageArea() {
 288         JPanel top = new JPanel();
 289         Border topBorder = (Border)DefaultLookup.get(optionPane, this,
 290                                              "OptionPane.messageAreaBorder");
 291         if (topBorder != null) {
 292             top.setBorder(topBorder);
 293         }
 294         top.setLayout(new BorderLayout());
 295 
 296         /* Fill the body. */
 297         Container          body = new JPanel(new GridBagLayout());
 298         Container          realBody = new JPanel(new BorderLayout());
 299 
 300         body.setName("OptionPane.body");
 301         realBody.setName("OptionPane.realBody");
 302 
 303         if (getIcon() != null) {
 304             JPanel sep = new JPanel();
 305             sep.setName("OptionPane.separator");
 306             sep.setPreferredSize(new Dimension(15, 1));
 307             realBody.add(sep, BorderLayout.BEFORE_LINE_BEGINS);
 308         }
 309         realBody.add(body, BorderLayout.CENTER);
 310 
 311         GridBagConstraints cons = new GridBagConstraints();
 312         cons.gridx = cons.gridy = 0;
 313         cons.gridwidth = GridBagConstraints.REMAINDER;
 314         cons.gridheight = 1;
 315         cons.anchor = DefaultLookup.getInt(optionPane, this,
 316                       "OptionPane.messageAnchor", GridBagConstraints.CENTER);
 317         cons.insets = new Insets(0,0,3,0);
 318 
 319         addMessageComponents(body, cons, getMessage(),
 320                           getMaxCharactersPerLineCount(), false);
 321         top.add(realBody, BorderLayout.CENTER);
 322 
 323         addIcon(top);
 324         return top;
 325     }
 326 
 327     /**
 328      * Creates the appropriate object to represent <code>msg</code> and
 329      * places it into <code>container</code>. If <code>msg</code> is an
 330      * instance of Component, it is added directly, if it is an Icon,
 331      * a JLabel is created to represent it, otherwise a JLabel is
 332      * created for the string, if <code>d</code> is an Object[], this
 333      * method will be recursively invoked for the children.
 334      * <code>internallyCreated</code> is true if Objc is an instance
 335      * of Component and was created internally by this method (this is
 336      * used to correctly set hasCustomComponents only if !internallyCreated).
 337      */
 338     protected void addMessageComponents(Container container,
 339                                      GridBagConstraints cons,
 340                                      Object msg, int maxll,
 341                                      boolean internallyCreated) {
 342         if (msg == null) {
 343             return;
 344         }
 345         if (msg instanceof Component) {
 346             // To workaround problem where Gridbad will set child
 347             // to its minimum size if its preferred size will not fit
 348             // within allocated cells
 349             if (msg instanceof JScrollPane || msg instanceof JPanel) {
 350                 cons.fill = GridBagConstraints.BOTH;
 351                 cons.weighty = 1;
 352             } else {
 353                 cons.fill = GridBagConstraints.HORIZONTAL;
 354             }
 355             cons.weightx = 1;
 356 
 357             container.add((Component) msg, cons);
 358             cons.weightx = 0;
 359             cons.weighty = 0;
 360             cons.fill = GridBagConstraints.NONE;
 361             cons.gridy++;
 362             if (!internallyCreated) {
 363                 hasCustomComponents = true;
 364             }
 365 
 366         } else if (msg instanceof Object[]) {
 367             Object [] msgs = (Object[]) msg;
 368             for (Object o : msgs) {
 369                 addMessageComponents(container, cons, o, maxll, false);
 370             }
 371 
 372         } else if (msg instanceof Icon) {
 373             JLabel label = new JLabel( (Icon)msg, SwingConstants.CENTER );
 374             configureMessageLabel(label);
 375             addMessageComponents(container, cons, label, maxll, true);
 376 
 377         } else {
 378             String s = msg.toString();
 379             int len = s.length();
 380             if (len <= 0) {
 381                 return;
 382             }
 383             int nl;
 384             int nll = 0;
 385 
 386             if ((nl = s.indexOf(newline)) >= 0) {
 387                 nll = newline.length();
 388             } else if ((nl = s.indexOf("\r\n")) >= 0) {
 389                 nll = 2;
 390             } else if ((nl = s.indexOf('\n')) >= 0) {
 391                 nll = 1;
 392             }
 393             if (nl >= 0) {
 394                 // break up newlines
 395                 if (nl == 0) {
 396                     @SuppressWarnings("serial") // anonymous class
 397                     JPanel breakPanel = new JPanel() {
 398                         public Dimension getPreferredSize() {
 399                             Font       f = getFont();
 400 
 401                             if (f != null) {
 402                                 return new Dimension(1, f.getSize() + 2);
 403                             }
 404                             return new Dimension(0, 0);
 405                         }
 406                     };
 407                     breakPanel.setName("OptionPane.break");
 408                     addMessageComponents(container, cons, breakPanel, maxll,
 409                                          true);
 410                 } else {
 411                     addMessageComponents(container, cons, s.substring(0, nl),
 412                                       maxll, false);
 413                 }
 414                 addMessageComponents(container, cons, s.substring(nl + nll), maxll,
 415                                   false);
 416 
 417             } else if (len > maxll) {
 418                 Container c = Box.createVerticalBox();
 419                 c.setName("OptionPane.verticalBox");
 420                 burstStringInto(c, s, maxll);
 421                 addMessageComponents(container, cons, c, maxll, true );
 422 
 423             } else {
 424                 JLabel label;
 425                 label = new JLabel( s, JLabel.LEADING );
 426                 label.setName("OptionPane.label");
 427                 configureMessageLabel(label);
 428                 addMessageComponents(container, cons, label, maxll, true);
 429             }
 430         }
 431     }
 432 
 433     /**
 434      * Returns the message to display from the JOptionPane the receiver is
 435      * providing the look and feel for.
 436      */
 437     protected Object getMessage() {
 438         inputComponent = null;
 439         if (optionPane != null) {
 440             if (optionPane.getWantsInput()) {
 441                 /* Create a user component to capture the input. If the
 442                    selectionValues are non null the component and there
 443                    are < 20 values it'll be a combobox, if non null and
 444                    >= 20, it'll be a list, otherwise it'll be a textfield. */
 445                 Object             message = optionPane.getMessage();
 446                 Object[]           sValues = optionPane.getSelectionValues();
 447                 Object             inputValue = optionPane
 448                                            .getInitialSelectionValue();
 449                 JComponent         toAdd;
 450 
 451                 if (sValues != null) {
 452                     if (sValues.length < 20) {
 453                         JComboBox            cBox = new JComboBox();
 454 
 455                         cBox.setName("OptionPane.comboBox");
 456                         for(int counter = 0, maxCounter = sValues.length;
 457                             counter < maxCounter; counter++) {
 458                             cBox.addItem(sValues[counter]);
 459                         }
 460                         if (inputValue != null) {
 461                             cBox.setSelectedItem(inputValue);
 462                         }
 463                         inputComponent = cBox;
 464                         toAdd = cBox;
 465 
 466                     } else {
 467                         JList                list = new JList(sValues);
 468                         JScrollPane          sp = new JScrollPane(list);
 469 
 470                         sp.setName("OptionPane.scrollPane");
 471                         list.setName("OptionPane.list");
 472                         list.setVisibleRowCount(10);
 473                         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 474                         if(inputValue != null)
 475                             list.setSelectedValue(inputValue, true);
 476                         list.addMouseListener(getHandler());
 477                         toAdd = sp;
 478                         inputComponent = list;
 479                     }
 480 
 481                 } else {
 482                     MultiplexingTextField   tf = new MultiplexingTextField(20);
 483 
 484                     tf.setName("OptionPane.textField");
 485                     tf.setKeyStrokes(new KeyStroke[] {
 486                                      KeyStroke.getKeyStroke("ENTER") } );
 487                     if (inputValue != null) {
 488                         String inputString = inputValue.toString();
 489                         tf.setText(inputString);
 490                         tf.setSelectionStart(0);
 491                         tf.setSelectionEnd(inputString.length());
 492                     }
 493                     tf.addActionListener(getHandler());
 494                     toAdd = inputComponent = tf;
 495                 }
 496 
 497                 Object[]           newMessage;
 498 
 499                 if (message == null) {
 500                     newMessage = new Object[1];
 501                     newMessage[0] = toAdd;
 502 
 503                 } else {
 504                     newMessage = new Object[2];
 505                     newMessage[0] = message;
 506                     newMessage[1] = toAdd;
 507                 }
 508                 return newMessage;
 509             }
 510             return optionPane.getMessage();
 511         }
 512         return null;
 513     }
 514 
 515     /**
 516      * Creates and adds a JLabel representing the icon returned from
 517      * <code>getIcon</code> to <code>top</code>. This is messaged from
 518      * <code>createMessageArea</code>
 519      */
 520     protected void addIcon(Container top) {
 521         /* Create the icon. */
 522         Icon                  sideIcon = getIcon();
 523 
 524         if (sideIcon != null) {
 525             JLabel            iconLabel = new JLabel(sideIcon);
 526 
 527             iconLabel.setName("OptionPane.iconLabel");
 528             iconLabel.setVerticalAlignment(SwingConstants.TOP);
 529             top.add(iconLabel, BorderLayout.BEFORE_LINE_BEGINS);
 530         }
 531     }
 532 
 533     /**
 534      * Returns the icon from the JOptionPane the receiver is providing
 535      * the look and feel for, or the default icon as returned from
 536      * <code>getDefaultIcon</code>.
 537      */
 538     protected Icon getIcon() {
 539         Icon      mIcon = (optionPane == null ? null : optionPane.getIcon());
 540 
 541         if(mIcon == null && optionPane != null)
 542             mIcon = getIconForType(optionPane.getMessageType());
 543         return mIcon;
 544     }
 545 
 546     /**
 547      * Returns the icon to use for the passed in type.
 548      */
 549     protected Icon getIconForType(int messageType) {
 550         if(messageType < 0 || messageType > 3)
 551             return null;
 552         String propertyName = null;
 553         switch(messageType) {
 554         case 0:
 555             propertyName = "OptionPane.errorIcon";
 556             break;
 557         case 1:
 558             propertyName = "OptionPane.informationIcon";
 559             break;
 560         case 2:
 561             propertyName = "OptionPane.warningIcon";
 562             break;
 563         case 3:
 564             propertyName = "OptionPane.questionIcon";
 565             break;
 566         }
 567         if (propertyName != null) {
 568             return (Icon)DefaultLookup.get(optionPane, this, propertyName);
 569         }
 570         return null;
 571     }
 572 
 573     /**
 574      * Returns the maximum number of characters to place on a line.
 575      */
 576     protected int getMaxCharactersPerLineCount() {
 577         return optionPane.getMaxCharactersPerLineCount();
 578     }
 579 
 580    /**
 581      * Recursively creates new JLabel instances to represent <code>d</code>.
 582      * Each JLabel instance is added to <code>c</code>.
 583      */
 584     protected void burstStringInto(Container c, String d, int maxll) {
 585         // Primitive line wrapping
 586         int len = d.length();
 587         if (len <= 0)
 588             return;
 589         if (len > maxll) {
 590             int p = d.lastIndexOf(' ', maxll);
 591             if (p <= 0)
 592                 p = d.indexOf(' ', maxll);
 593             if (p > 0 && p < len) {
 594                 burstStringInto(c, d.substring(0, p), maxll);
 595                 burstStringInto(c, d.substring(p + 1), maxll);
 596                 return;
 597             }
 598         }
 599         JLabel label = new JLabel(d, JLabel.LEFT);
 600         label.setName("OptionPane.label");
 601         configureMessageLabel(label);
 602         c.add(label);
 603     }
 604 
 605     protected Container createSeparator() {
 606         return null;
 607     }
 608 
 609     /**
 610      * Creates and returns a Container containing the buttons. The buttons
 611      * are created by calling <code>getButtons</code>.
 612      */
 613     protected Container createButtonArea() {
 614         JPanel bottom = new JPanel();
 615         Border border = (Border)DefaultLookup.get(optionPane, this,
 616                                           "OptionPane.buttonAreaBorder");
 617         bottom.setName("OptionPane.buttonArea");
 618         if (border != null) {
 619             bottom.setBorder(border);
 620         }
 621         bottom.setLayout(new ButtonAreaLayout(
 622            DefaultLookup.getBoolean(optionPane, this,
 623                                     "OptionPane.sameSizeButtons", true),
 624            DefaultLookup.getInt(optionPane, this, "OptionPane.buttonPadding",
 625                                 6),
 626            DefaultLookup.getInt(optionPane, this,
 627                         "OptionPane.buttonOrientation", SwingConstants.CENTER),
 628            DefaultLookup.getBoolean(optionPane, this, "OptionPane.isYesLast",
 629                                     false)));
 630         addButtonComponents(bottom, getButtons(), getInitialValueIndex());
 631         return bottom;
 632     }
 633 
 634     /**
 635      * Creates the appropriate object to represent each of the objects in
 636      * <code>buttons</code> and adds it to <code>container</code>. This
 637      * differs from addMessageComponents in that it will recurse on
 638      * <code>buttons</code> and that if button is not a Component
 639      * it will create an instance of JButton.
 640      */
 641     protected void addButtonComponents(Container container, Object[] buttons,
 642                                  int initialIndex) {
 643         if (buttons != null && buttons.length > 0) {
 644             boolean            sizeButtonsToSame = getSizeButtonsToSameWidth();
 645             boolean            createdAll = true;
 646             int                numButtons = buttons.length;
 647             JButton[]          createdButtons = null;
 648             int                maxWidth = 0;
 649 
 650             if (sizeButtonsToSame) {
 651                 createdButtons = new JButton[numButtons];
 652             }
 653 
 654             for(int counter = 0; counter < numButtons; counter++) {
 655                 Object       button = buttons[counter];
 656                 Component    newComponent;
 657 
 658                 if (button instanceof Component) {
 659                     createdAll = false;
 660                     newComponent = (Component)button;
 661                     container.add(newComponent);
 662                     hasCustomComponents = true;
 663 
 664                 } else {
 665                     JButton      aButton;
 666 
 667                     if (button instanceof ButtonFactory) {
 668                         aButton = ((ButtonFactory)button).createButton();
 669                     }
 670                     else if (button instanceof Icon)
 671                         aButton = new JButton((Icon)button);
 672                     else
 673                         aButton = new JButton(button.toString());
 674 
 675                     aButton.setName("OptionPane.button");
 676                     aButton.setMultiClickThreshhold(DefaultLookup.getInt(
 677                           optionPane, this, "OptionPane.buttonClickThreshhold",
 678                           0));
 679                     configureButton(aButton);
 680 
 681                     container.add(aButton);
 682 
 683                     ActionListener buttonListener = createButtonActionListener(counter);
 684                     if (buttonListener != null) {
 685                         aButton.addActionListener(buttonListener);
 686                     }
 687                     newComponent = aButton;
 688                 }
 689                 if (sizeButtonsToSame && createdAll &&
 690                    (newComponent instanceof JButton)) {
 691                     createdButtons[counter] = (JButton)newComponent;
 692                     maxWidth = Math.max(maxWidth,
 693                                         newComponent.getMinimumSize().width);
 694                 }
 695                 if (counter == initialIndex) {
 696                     initialFocusComponent = newComponent;
 697                     if (initialFocusComponent instanceof JButton) {
 698                         JButton defaultB = (JButton)initialFocusComponent;
 699                         defaultB.addHierarchyListener(new HierarchyListener() {
 700                             public void hierarchyChanged(HierarchyEvent e) {
 701                                 if ((e.getChangeFlags() &
 702                                         HierarchyEvent.PARENT_CHANGED) != 0) {
 703                                     JButton defaultButton = (JButton) e.getComponent();
 704                                     JRootPane root =
 705                                             SwingUtilities.getRootPane(defaultButton);
 706                                     if (root != null) {
 707                                         root.setDefaultButton(defaultButton);
 708                                     }
 709                                 }
 710                             }
 711                         });
 712                     }
 713                 }
 714             }
 715             ((ButtonAreaLayout)container.getLayout()).
 716                               setSyncAllWidths((sizeButtonsToSame && createdAll));
 717             /* Set the padding, windows seems to use 8 if <= 2 components,
 718                otherwise 4 is used. It may actually just be the size of the
 719                buttons is always the same, not sure. */
 720             if (DefaultLookup.getBoolean(optionPane, this,
 721                    "OptionPane.setButtonMargin", true) && sizeButtonsToSame &&
 722                    createdAll) {
 723                 JButton               aButton;
 724                 int                   padSize;
 725 
 726                 padSize = (numButtons <= 2? 8 : 4);
 727 
 728                 for(int counter = 0; counter < numButtons; counter++) {
 729                     aButton = createdButtons[counter];
 730                     aButton.setMargin(new Insets(2, padSize, 2, padSize));
 731                 }
 732             }
 733         }
 734     }
 735 
 736     protected ActionListener createButtonActionListener(int buttonIndex) {
 737         return new ButtonActionListener(buttonIndex);
 738     }
 739 
 740     /**
 741      * Returns the buttons to display from the JOptionPane the receiver is
 742      * providing the look and feel for. If the JOptionPane has options
 743      * set, they will be provided, otherwise if the optionType is
 744      * YES_NO_OPTION, yesNoOptions is returned, if the type is
 745      * YES_NO_CANCEL_OPTION yesNoCancelOptions is returned, otherwise
 746      * defaultButtons are returned.
 747      */
 748     protected Object[] getButtons() {
 749         if (optionPane != null) {
 750             Object[] suppliedOptions = optionPane.getOptions();
 751 
 752             if (suppliedOptions == null) {
 753                 Object[] defaultOptions;
 754                 int type = optionPane.getOptionType();
 755                 Locale l = optionPane.getLocale();
 756                 int minimumWidth =
 757                     DefaultLookup.getInt(optionPane, this,
 758                                         "OptionPane.buttonMinimumWidth",-1);
 759                 if (type == JOptionPane.YES_NO_OPTION) {
 760                     defaultOptions = new ButtonFactory[2];
 761                     defaultOptions[0] = new ButtonFactory(
 762                         UIManager.getString("OptionPane.yesButtonText", l),
 763                         getMnemonic("OptionPane.yesButtonMnemonic", l),
 764                         (Icon)DefaultLookup.get(optionPane, this,
 765                                           "OptionPane.yesIcon"), minimumWidth);
 766                     defaultOptions[1] = new ButtonFactory(
 767                         UIManager.getString("OptionPane.noButtonText", l),
 768                         getMnemonic("OptionPane.noButtonMnemonic", l),
 769                         (Icon)DefaultLookup.get(optionPane, this,
 770                                           "OptionPane.noIcon"), minimumWidth);
 771                 } else if (type == JOptionPane.YES_NO_CANCEL_OPTION) {
 772                     defaultOptions = new ButtonFactory[3];
 773                     defaultOptions[0] = new ButtonFactory(
 774                         UIManager.getString("OptionPane.yesButtonText", l),
 775                         getMnemonic("OptionPane.yesButtonMnemonic", l),
 776                         (Icon)DefaultLookup.get(optionPane, this,
 777                                           "OptionPane.yesIcon"), minimumWidth);
 778                     defaultOptions[1] = new ButtonFactory(
 779                         UIManager.getString("OptionPane.noButtonText",l),
 780                         getMnemonic("OptionPane.noButtonMnemonic", l),
 781                         (Icon)DefaultLookup.get(optionPane, this,
 782                                           "OptionPane.noIcon"), minimumWidth);
 783                     defaultOptions[2] = new ButtonFactory(
 784                         UIManager.getString("OptionPane.cancelButtonText",l),
 785                         getMnemonic("OptionPane.cancelButtonMnemonic", l),
 786                         (Icon)DefaultLookup.get(optionPane, this,
 787                                           "OptionPane.cancelIcon"), minimumWidth);
 788                 } else if (type == JOptionPane.OK_CANCEL_OPTION) {
 789                     defaultOptions = new ButtonFactory[2];
 790                     defaultOptions[0] = new ButtonFactory(
 791                         UIManager.getString("OptionPane.okButtonText",l),
 792                         getMnemonic("OptionPane.okButtonMnemonic", l),
 793                         (Icon)DefaultLookup.get(optionPane, this,
 794                                           "OptionPane.okIcon"), minimumWidth);
 795                     defaultOptions[1] = new ButtonFactory(
 796                         UIManager.getString("OptionPane.cancelButtonText",l),
 797                         getMnemonic("OptionPane.cancelButtonMnemonic", l),
 798                         (Icon)DefaultLookup.get(optionPane, this,
 799                                           "OptionPane.cancelIcon"), minimumWidth);
 800                 } else {
 801                     defaultOptions = new ButtonFactory[1];
 802                     defaultOptions[0] = new ButtonFactory(
 803                         UIManager.getString("OptionPane.okButtonText",l),
 804                         getMnemonic("OptionPane.okButtonMnemonic", l),
 805                         (Icon)DefaultLookup.get(optionPane, this,
 806                                           "OptionPane.okIcon"), minimumWidth);
 807                 }
 808                 return defaultOptions;
 809 
 810             }
 811             return suppliedOptions;
 812         }
 813         return null;
 814     }
 815 
 816     private int getMnemonic(String key, Locale l) {
 817         String value = (String)UIManager.get(key, l);
 818 
 819         if (value == null) {
 820             return 0;
 821         }
 822         try {
 823             return Integer.parseInt(value);
 824         }
 825         catch (NumberFormatException nfe) { }
 826         return 0;
 827     }
 828 
 829     /**
 830      * Returns true, basic L&amp;F wants all the buttons to have the same
 831      * width.
 832      */
 833     protected boolean getSizeButtonsToSameWidth() {
 834         return true;
 835     }
 836 
 837     /**
 838      * Returns the initial index into the buttons to select. The index
 839      * is calculated from the initial value from the JOptionPane and
 840      * options of the JOptionPane or 0.
 841      */
 842     protected int getInitialValueIndex() {
 843         if (optionPane != null) {
 844             Object             iv = optionPane.getInitialValue();
 845             Object[]           options = optionPane.getOptions();
 846 
 847             if(options == null) {
 848                 return 0;
 849             }
 850             else if(iv != null) {
 851                 for(int counter = options.length - 1; counter >= 0; counter--){
 852                     if(options[counter].equals(iv))
 853                         return counter;
 854                 }
 855             }
 856         }
 857         return -1;
 858     }
 859 
 860     /**
 861      * Sets the input value in the option pane the receiver is providing
 862      * the look and feel for based on the value in the inputComponent.
 863      */
 864     protected void resetInputValue() {
 865         if(inputComponent != null && (inputComponent instanceof JTextField)) {
 866             optionPane.setInputValue(((JTextField)inputComponent).getText());
 867 
 868         } else if(inputComponent != null &&
 869                   (inputComponent instanceof JComboBox)) {
 870             optionPane.setInputValue(((JComboBox)inputComponent)
 871                                      .getSelectedItem());
 872         } else if(inputComponent != null) {
 873             optionPane.setInputValue(((JList)inputComponent)
 874                                      .getSelectedValue());
 875         }
 876     }
 877 
 878 
 879     /**
 880      * If inputComponent is non-null, the focus is requested on that,
 881      * otherwise request focus on the default value
 882      */
 883     public void selectInitialValue(JOptionPane op) {
 884         if (inputComponent != null)
 885             inputComponent.requestFocus();
 886         else {
 887             if (initialFocusComponent != null)
 888                 initialFocusComponent.requestFocus();
 889 
 890             if (initialFocusComponent instanceof JButton) {
 891                 JRootPane root = SwingUtilities.getRootPane(initialFocusComponent);
 892                 if (root != null) {
 893                     root.setDefaultButton((JButton)initialFocusComponent);
 894                 }
 895             }
 896         }
 897     }
 898 
 899     /**
 900      * Returns true if in the last call to validateComponent the message
 901      * or buttons contained a subclass of Component.
 902      */
 903     public boolean containsCustomComponents(JOptionPane op) {
 904         return hasCustomComponents;
 905     }
 906 
 907 
 908     /**
 909      * <code>ButtonAreaLayout</code> behaves in a similar manner to
 910      * <code>FlowLayout</code>. It lays out all components from left to
 911      * right. If <code>syncAllWidths</code> is true, the widths of each
 912      * component will be set to the largest preferred size width.
 913      *
 914      * This class should be treated as a &quot;protected&quot; inner class.
 915      * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
 916      */
 917     public static class ButtonAreaLayout implements LayoutManager {
 918         protected boolean           syncAllWidths;
 919         protected int               padding;
 920         /** If true, children are lumped together in parent. */
 921         protected boolean           centersChildren;
 922         private int orientation;
 923         private boolean reverseButtons;
 924         /**
 925          * Indicates whether or not centersChildren should be used vs
 926          * the orientation. This is done for backward compatibility
 927          * for subclassers.
 928          */
 929         private boolean useOrientation;
 930 
 931         public ButtonAreaLayout(boolean syncAllWidths, int padding) {
 932             this.syncAllWidths = syncAllWidths;
 933             this.padding = padding;
 934             centersChildren = true;
 935             useOrientation = false;
 936         }
 937 
 938         ButtonAreaLayout(boolean syncAllSizes, int padding, int orientation,
 939                          boolean reverseButtons) {
 940             this(syncAllSizes, padding);
 941             useOrientation = true;
 942             this.orientation = orientation;
 943             this.reverseButtons = reverseButtons;
 944         }
 945 
 946         public void setSyncAllWidths(boolean newValue) {
 947             syncAllWidths = newValue;
 948         }
 949 
 950         public boolean getSyncAllWidths() {
 951             return syncAllWidths;
 952         }
 953 
 954         public void setPadding(int newPadding) {
 955             this.padding = newPadding;
 956         }
 957 
 958         public int getPadding() {
 959             return padding;
 960         }
 961 
 962         public void setCentersChildren(boolean newValue) {
 963             centersChildren = newValue;
 964             useOrientation = false;
 965         }
 966 
 967         public boolean getCentersChildren() {
 968             return centersChildren;
 969         }
 970 
 971         private int getOrientation(Container container) {
 972             if (!useOrientation) {
 973                 return SwingConstants.CENTER;
 974             }
 975             if (container.getComponentOrientation().isLeftToRight()) {
 976                 return orientation;
 977             }
 978             switch (orientation) {
 979             case SwingConstants.LEFT:
 980                 return SwingConstants.RIGHT;
 981             case SwingConstants.RIGHT:
 982                 return SwingConstants.LEFT;
 983             case SwingConstants.CENTER:
 984                 return SwingConstants.CENTER;
 985             }
 986             return SwingConstants.LEFT;
 987         }
 988 
 989         public void addLayoutComponent(String string, Component comp) {
 990         }
 991 
 992         public void layoutContainer(Container container) {
 993             Component[]      children = container.getComponents();
 994 
 995             if(children != null && children.length > 0) {
 996                 int               numChildren = children.length;
 997                 Insets            insets = container.getInsets();
 998                 int maxWidth = 0;
 999                 int maxHeight = 0;
1000                 int totalButtonWidth = 0;
1001                 int x = 0;
1002                 int xOffset = 0;
1003                 boolean ltr = container.getComponentOrientation().
1004                                         isLeftToRight();
1005                 boolean reverse = (ltr) ? reverseButtons : !reverseButtons;
1006 
1007                 for(int counter = 0; counter < numChildren; counter++) {
1008                     Dimension pref = children[counter].getPreferredSize();
1009                     maxWidth = Math.max(maxWidth, pref.width);
1010                     maxHeight = Math.max(maxHeight, pref.height);
1011                     totalButtonWidth += pref.width;
1012                 }
1013                 if (getSyncAllWidths()) {
1014                     totalButtonWidth = maxWidth * numChildren;
1015                 }
1016                 totalButtonWidth += (numChildren - 1) * padding;
1017 
1018                 switch (getOrientation(container)) {
1019                 case SwingConstants.LEFT:
1020                     x = insets.left;
1021                     break;
1022                 case SwingConstants.RIGHT:
1023                     x = container.getWidth() - insets.right - totalButtonWidth;
1024                     break;
1025                 case SwingConstants.CENTER:
1026                     if (getCentersChildren() || numChildren < 2) {
1027                         x = (container.getWidth() - totalButtonWidth) / 2;
1028                     }
1029                     else {
1030                         x = insets.left;
1031                         if (getSyncAllWidths()) {
1032                             xOffset = (container.getWidth() - insets.left -
1033                                        insets.right - totalButtonWidth) /
1034                                 (numChildren - 1) + maxWidth;
1035                         }
1036                         else {
1037                             xOffset = (container.getWidth() - insets.left -
1038                                        insets.right - totalButtonWidth) /
1039                                       (numChildren - 1);
1040                         }
1041                     }
1042                     break;
1043                 }
1044 
1045                 for (int counter = 0; counter < numChildren; counter++) {
1046                     int index = (reverse) ? numChildren - counter - 1 :
1047                                 counter;
1048                     Dimension pref = children[index].getPreferredSize();
1049 
1050                     if (getSyncAllWidths()) {
1051                         children[index].setBounds(x, insets.top,
1052                                                   maxWidth, maxHeight);
1053                     }
1054                     else {
1055                         children[index].setBounds(x, insets.top, pref.width,
1056                                                   pref.height);
1057                     }
1058                     if (xOffset != 0) {
1059                         x += xOffset;
1060                     }
1061                     else {
1062                         x += children[index].getWidth() + padding;
1063                     }
1064                 }
1065             }
1066         }
1067 
1068         public Dimension minimumLayoutSize(Container c) {
1069             if(c != null) {
1070                 Component[]       children = c.getComponents();
1071 
1072                 if(children != null && children.length > 0) {
1073                     Dimension     aSize;
1074                     int           numChildren = children.length;
1075                     int           height = 0;
1076                     Insets        cInsets = c.getInsets();
1077                     int           extraHeight = cInsets.top + cInsets.bottom;
1078                     int           extraWidth = cInsets.left + cInsets.right;
1079 
1080                     if (syncAllWidths) {
1081                         int              maxWidth = 0;
1082 
1083                         for(int counter = 0; counter < numChildren; counter++){
1084                             aSize = children[counter].getPreferredSize();
1085                             height = Math.max(height, aSize.height);
1086                             maxWidth = Math.max(maxWidth, aSize.width);
1087                         }
1088                         return new Dimension(extraWidth + (maxWidth * numChildren) +
1089                                              (numChildren - 1) * padding,
1090                                              extraHeight + height);
1091                     }
1092                     else {
1093                         int        totalWidth = 0;
1094 
1095                         for(int counter = 0; counter < numChildren; counter++){
1096                             aSize = children[counter].getPreferredSize();
1097                             height = Math.max(height, aSize.height);
1098                             totalWidth += aSize.width;
1099                         }
1100                         totalWidth += ((numChildren - 1) * padding);
1101                         return new Dimension(extraWidth + totalWidth, extraHeight + height);
1102                     }
1103                 }
1104             }
1105             return new Dimension(0, 0);
1106         }
1107 
1108         public Dimension preferredLayoutSize(Container c) {
1109             return minimumLayoutSize(c);
1110         }
1111 
1112         public void removeLayoutComponent(Component c) { }
1113     }
1114 
1115 
1116     /**
1117      * This class should be treated as a &quot;protected&quot; inner class.
1118      * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
1119      */
1120     public class PropertyChangeHandler implements PropertyChangeListener {
1121         /**
1122          * If the source of the PropertyChangeEvent <code>e</code> equals the
1123          * optionPane and is one of the ICON_PROPERTY, MESSAGE_PROPERTY,
1124          * OPTIONS_PROPERTY or INITIAL_VALUE_PROPERTY,
1125          * validateComponent is invoked.
1126          */
1127         public void propertyChange(PropertyChangeEvent e) {
1128             getHandler().propertyChange(e);
1129         }
1130     }
1131 
1132     /**
1133      * Configures any necessary colors/fonts for the specified label
1134      * used representing the message.
1135      */
1136     private void configureMessageLabel(JLabel label) {
1137         Color color = (Color)DefaultLookup.get(optionPane, this,
1138                                                "OptionPane.messageForeground");
1139         if (color != null) {
1140             label.setForeground(color);
1141         }
1142         Font messageFont = (Font)DefaultLookup.get(optionPane, this,
1143                                                    "OptionPane.messageFont");
1144         if (messageFont != null) {
1145             label.setFont(messageFont);
1146         }
1147     }
1148 
1149     /**
1150      * Configures any necessary colors/fonts for the specified button
1151      * used representing the button portion of the optionpane.
1152      */
1153     private void configureButton(JButton button) {
1154         Font buttonFont = (Font)DefaultLookup.get(optionPane, this,
1155                                             "OptionPane.buttonFont");
1156         if (buttonFont != null) {
1157             button.setFont(buttonFont);
1158         }
1159     }
1160 
1161     /**
1162      * This class should be treated as a &quot;protected&quot; inner class.
1163      * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
1164      */
1165     public class ButtonActionListener implements ActionListener {
1166         protected int buttonIndex;
1167 
1168         public ButtonActionListener(int buttonIndex) {
1169             this.buttonIndex = buttonIndex;
1170         }
1171 
1172         public void actionPerformed(ActionEvent e) {
1173             if (optionPane != null) {
1174                 int optionType = optionPane.getOptionType();
1175                 Object[] options = optionPane.getOptions();
1176 
1177                 /* If the option pane takes input, then store the input value
1178                  * if custom options were specified, if the option type is
1179                  * DEFAULT_OPTION, OR if option type is set to a predefined
1180                  * one and the user chose the affirmative answer.
1181                  */
1182                 if (inputComponent != null) {
1183                     if (options != null ||
1184                         optionType == JOptionPane.DEFAULT_OPTION ||
1185                         ((optionType == JOptionPane.YES_NO_OPTION ||
1186                          optionType == JOptionPane.YES_NO_CANCEL_OPTION ||
1187                          optionType == JOptionPane.OK_CANCEL_OPTION) &&
1188                          buttonIndex == 0)) {
1189                         resetInputValue();
1190                     }
1191                 }
1192                 if (options == null) {
1193                     if (optionType == JOptionPane.OK_CANCEL_OPTION &&
1194                         buttonIndex == 1) {
1195                         optionPane.setValue(Integer.valueOf(2));
1196 
1197                     } else {
1198                         optionPane.setValue(Integer.valueOf(buttonIndex));
1199                     }
1200                 } else {
1201                     optionPane.setValue(options[buttonIndex]);
1202                 }
1203             }
1204         }
1205     }
1206 
1207 
1208     private class Handler implements ActionListener, MouseListener,
1209                                      PropertyChangeListener {
1210         //
1211         // ActionListener
1212         //
1213         public void actionPerformed(ActionEvent e) {
1214             optionPane.setInputValue(((JTextField)e.getSource()).getText());
1215         }
1216 
1217 
1218         //
1219         // MouseListener
1220         //
1221         public void mouseClicked(MouseEvent e) {
1222         }
1223 
1224         public void mouseReleased(MouseEvent e) {
1225         }
1226 
1227         public void mouseEntered(MouseEvent e) {
1228         }
1229 
1230         public void mouseExited(MouseEvent e) {
1231         }
1232 
1233         public void mousePressed(MouseEvent e) {
1234             if (e.getClickCount() == 2) {
1235                 JList     list = (JList)e.getSource();
1236                 int       index = list.locationToIndex(e.getPoint());
1237 
1238                 optionPane.setInputValue(list.getModel().getElementAt(index));
1239                 optionPane.setValue(JOptionPane.OK_OPTION);
1240             }
1241         }
1242 
1243         //
1244         // PropertyChangeListener
1245         //
1246         public void propertyChange(PropertyChangeEvent e) {
1247             if(e.getSource() == optionPane) {
1248                 // Option Pane Auditory Cue Activation
1249                 // only respond to "ancestor" changes
1250                 // the idea being that a JOptionPane gets a JDialog when it is
1251                 // set to appear and loses it's JDialog when it is dismissed.
1252                 if ("ancestor" == e.getPropertyName()) {
1253                     JOptionPane op = (JOptionPane)e.getSource();
1254                     boolean isComingUp;
1255 
1256                     // if the old value is null, then the JOptionPane is being
1257                     // created since it didn't previously have an ancestor.
1258                     if (e.getOldValue() == null) {
1259                         isComingUp = true;
1260                     } else {
1261                         isComingUp = false;
1262                     }
1263 
1264                     // figure out what to do based on the message type
1265                     switch (op.getMessageType()) {
1266                     case JOptionPane.PLAIN_MESSAGE:
1267                         if (isComingUp) {
1268                             BasicLookAndFeel.playSound(optionPane,
1269                                                "OptionPane.informationSound");
1270                         }
1271                         break;
1272                     case JOptionPane.QUESTION_MESSAGE:
1273                         if (isComingUp) {
1274                             BasicLookAndFeel.playSound(optionPane,
1275                                              "OptionPane.questionSound");
1276                         }
1277                         break;
1278                     case JOptionPane.INFORMATION_MESSAGE:
1279                         if (isComingUp) {
1280                             BasicLookAndFeel.playSound(optionPane,
1281                                              "OptionPane.informationSound");
1282                         }
1283                         break;
1284                     case JOptionPane.WARNING_MESSAGE:
1285                         if (isComingUp) {
1286                             BasicLookAndFeel.playSound(optionPane,
1287                                              "OptionPane.warningSound");
1288                         }
1289                         break;
1290                     case JOptionPane.ERROR_MESSAGE:
1291                         if (isComingUp) {
1292                             BasicLookAndFeel.playSound(optionPane,
1293                                              "OptionPane.errorSound");
1294                         }
1295                         break;
1296                     default:
1297                         System.err.println("Undefined JOptionPane type: " +
1298                                            op.getMessageType());
1299                         break;
1300                     }
1301                 }
1302                 // Visual activity
1303                 String         changeName = e.getPropertyName();
1304 
1305                 if(changeName == JOptionPane.OPTIONS_PROPERTY ||
1306                    changeName == JOptionPane.INITIAL_VALUE_PROPERTY ||
1307                    changeName == JOptionPane.ICON_PROPERTY ||
1308                    changeName == JOptionPane.MESSAGE_TYPE_PROPERTY ||
1309                    changeName == JOptionPane.OPTION_TYPE_PROPERTY ||
1310                    changeName == JOptionPane.MESSAGE_PROPERTY ||
1311                    changeName == JOptionPane.SELECTION_VALUES_PROPERTY ||
1312                    changeName == JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY ||
1313                    changeName == JOptionPane.WANTS_INPUT_PROPERTY) {
1314                    uninstallComponents();
1315                    installComponents();
1316                    optionPane.validate();
1317                 }
1318                 else if (changeName == "componentOrientation") {
1319                     ComponentOrientation o = (ComponentOrientation)e.getNewValue();
1320                     JOptionPane op = (JOptionPane)e.getSource();
1321                     if (o != e.getOldValue()) {
1322                         op.applyComponentOrientation(o);
1323                     }
1324                 }
1325             }
1326         }
1327     }
1328 
1329 
1330     //
1331     // Classes used when optionPane.getWantsInput returns true.
1332     //
1333 
1334     /**
1335      * A JTextField that allows you to specify an array of KeyStrokes that
1336      * that will have their bindings processed regardless of whether or
1337      * not they are registered on the JTextField. This is used as we really
1338      * want the ActionListener to be notified so that we can push the
1339      * change to the JOptionPane, but we also want additional bindings
1340      * (those of the JRootPane) to be processed as well.
1341      */
1342     @SuppressWarnings("serial") // Superclass is not serializable across versions
1343     private static class MultiplexingTextField extends JTextField {
1344         private KeyStroke[] strokes;
1345 
1346         MultiplexingTextField(int cols) {
1347             super(cols);
1348         }
1349 
1350         /**
1351          * Sets the KeyStrokes that will be additional processed for
1352          * ancestor bindings.
1353          */
1354         void setKeyStrokes(KeyStroke[] strokes) {
1355             this.strokes = strokes;
1356         }
1357 
1358         protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
1359                                             int condition, boolean pressed) {
1360             boolean processed = super.processKeyBinding(ks, e, condition,
1361                                                         pressed);
1362 
1363             if (processed && condition != JComponent.WHEN_IN_FOCUSED_WINDOW) {
1364                 for (int counter = strokes.length - 1; counter >= 0;
1365                          counter--) {
1366                     if (strokes[counter].equals(ks)) {
1367                         // Returning false will allow further processing
1368                         // of the bindings, eg our parent Containers will get a
1369                         // crack at them.
1370                         return false;
1371                     }
1372                 }
1373             }
1374             return processed;
1375         }
1376     }
1377 
1378 
1379 
1380     /**
1381      * Registered in the ActionMap. Sets the value of the option pane
1382      * to <code>JOptionPane.CLOSED_OPTION</code>.
1383      */
1384     private static class Actions extends UIAction {
1385         private static final String CLOSE = "close";
1386 
1387         Actions(String key) {
1388             super(key);
1389         }
1390 
1391         public void actionPerformed(ActionEvent e) {
1392             if (getName() == CLOSE) {
1393                 JOptionPane optionPane = (JOptionPane)e.getSource();
1394 
1395                 optionPane.setValue(Integer.valueOf(JOptionPane.CLOSED_OPTION));
1396             }
1397         }
1398     }
1399 
1400 
1401     /**
1402      * This class is used to create the default buttons. This indirection is
1403      * used so that addButtonComponents can tell which Buttons were created
1404      * by us vs subclassers or from the JOptionPane itself.
1405      */
1406     private static class ButtonFactory {
1407         private String text;
1408         private int mnemonic;
1409         private Icon icon;
1410         private int minimumWidth = -1;
1411 
1412         ButtonFactory(String text, int mnemonic, Icon icon, int minimumWidth) {
1413             this.text = text;
1414             this.mnemonic = mnemonic;
1415             this.icon = icon;
1416             this.minimumWidth = minimumWidth;
1417         }
1418 
1419         JButton createButton() {
1420             JButton button;
1421 
1422             if (minimumWidth > 0) {
1423                 button = new ConstrainedButton(text, minimumWidth);
1424             } else {
1425                 button = new JButton(text);
1426             }
1427             if (icon != null) {
1428                 button.setIcon(icon);
1429             }
1430             if (mnemonic != 0) {
1431                 button.setMnemonic(mnemonic);
1432             }
1433             return button;
1434         }
1435 
1436         @SuppressWarnings("serial") // Superclass is not serializable across versions
1437         private static class ConstrainedButton extends JButton {
1438             int minimumWidth;
1439 
1440             ConstrainedButton(String text, int minimumWidth) {
1441                 super(text);
1442                 this.minimumWidth = minimumWidth;
1443             }
1444 
1445             public Dimension getMinimumSize() {
1446                 Dimension min = super.getMinimumSize();
1447                 min.width = Math.max(min.width, minimumWidth);
1448                 return min;
1449             }
1450 
1451             public Dimension getPreferredSize() {
1452                 Dimension pref = super.getPreferredSize();
1453                 pref.width = Math.max(pref.width, minimumWidth);
1454                 return pref;
1455             }
1456         }
1457     }
1458 }