1 /*
   2  * Copyright (c) 1997, 2008, 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 = java.security.AccessController.doPrivileged(
 113                                 new GetPropertyAction("line.separator"));
 114         if (newline == null) {
 115             newline = "\n";
 116         }
 117     }
 118 
 119     static void loadActionMap(LazyActionMap map) {
 120         map.put(new Actions(Actions.CLOSE));
 121         BasicLookAndFeel.installAudioActionMap(map);
 122     }
 123 
 124 
 125 
 126     /**
 127       * Creates a new BasicOptionPaneUI instance.
 128       */
 129     public static ComponentUI createUI(JComponent x) {
 130         return new BasicOptionPaneUI();
 131     }
 132 
 133     /**
 134       * Installs the receiver as the L&amp;F for the passed in
 135       * <code>JOptionPane</code>.
 136       */
 137     public void installUI(JComponent c) {
 138         optionPane = (JOptionPane)c;
 139         installDefaults();
 140         optionPane.setLayout(createLayoutManager());
 141         installComponents();
 142         installListeners();
 143         installKeyboardActions();
 144     }
 145 
 146     /**
 147       * Removes the receiver from the L&amp;F controller of the passed in split
 148       * pane.
 149       */
 150     public void uninstallUI(JComponent c) {
 151         uninstallComponents();
 152         optionPane.setLayout(null);
 153         uninstallKeyboardActions();
 154         uninstallListeners();
 155         uninstallDefaults();
 156         optionPane = null;
 157     }
 158 
 159     protected void installDefaults() {
 160         LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background",
 161                                          "OptionPane.foreground", "OptionPane.font");
 162         LookAndFeel.installBorder(optionPane, "OptionPane.border");
 163         minimumSize = UIManager.getDimension("OptionPane.minimumSize");
 164         LookAndFeel.installProperty(optionPane, "opaque", Boolean.TRUE);
 165     }
 166 
 167     protected void uninstallDefaults() {
 168         LookAndFeel.uninstallBorder(optionPane);
 169     }
 170 
 171     protected void installComponents() {
 172         optionPane.add(createMessageArea());
 173 
 174         Container separator = createSeparator();
 175         if (separator != null) {
 176             optionPane.add(separator);
 177         }
 178         optionPane.add(createButtonArea());
 179         optionPane.applyComponentOrientation(optionPane.getComponentOrientation());
 180     }
 181 
 182     protected void uninstallComponents() {
 183         hasCustomComponents = false;
 184         inputComponent = null;
 185         initialFocusComponent = null;
 186         optionPane.removeAll();
 187     }
 188 
 189     protected LayoutManager createLayoutManager() {
 190         return new BoxLayout(optionPane, BoxLayout.Y_AXIS);
 191     }
 192 
 193     protected void installListeners() {
 194         if ((propertyChangeListener = createPropertyChangeListener()) != null) {
 195             optionPane.addPropertyChangeListener(propertyChangeListener);
 196         }
 197     }
 198 
 199     protected void uninstallListeners() {
 200         if (propertyChangeListener != null) {
 201             optionPane.removePropertyChangeListener(propertyChangeListener);
 202             propertyChangeListener = null;
 203         }
 204         handler = null;
 205     }
 206 
 207     protected PropertyChangeListener createPropertyChangeListener() {
 208         return getHandler();
 209     }
 210 
 211     private Handler getHandler() {
 212         if (handler == null) {
 213             handler = new Handler();
 214         }
 215         return handler;
 216     }
 217 
 218     protected void installKeyboardActions() {
 219         InputMap map = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
 220 
 221         SwingUtilities.replaceUIInputMap(optionPane, JComponent.
 222                                        WHEN_IN_FOCUSED_WINDOW, map);
 223 
 224         LazyActionMap.installLazyActionMap(optionPane, BasicOptionPaneUI.class,
 225                                            "OptionPane.actionMap");
 226     }
 227 
 228     protected void uninstallKeyboardActions() {
 229         SwingUtilities.replaceUIInputMap(optionPane, JComponent.
 230                                        WHEN_IN_FOCUSED_WINDOW, null);
 231         SwingUtilities.replaceUIActionMap(optionPane, null);
 232     }
 233 
 234     InputMap getInputMap(int condition) {
 235         if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) {
 236             Object[] bindings = (Object[])DefaultLookup.get(
 237                              optionPane, this, "OptionPane.windowBindings");
 238             if (bindings != null) {
 239                 return LookAndFeel.makeComponentInputMap(optionPane, bindings);
 240             }
 241         }
 242         return null;
 243     }
 244 
 245     /**
 246      * Returns the minimum size the option pane should be. Primarily
 247      * provided for subclassers wishing to offer a different minimum size.
 248      */
 249     public Dimension getMinimumOptionPaneSize() {
 250         if (minimumSize == null) {
 251             return new Dimension(MinimumWidth, MinimumHeight);
 252         }
 253         return new Dimension(minimumSize.width,
 254                              minimumSize.height);
 255     }
 256 
 257     /**
 258      * If <code>c</code> is the <code>JOptionPane</code> the receiver
 259      * is contained in, the preferred
 260      * size that is returned is the maximum of the preferred size of
 261      * the <code>LayoutManager</code> for the <code>JOptionPane</code>, and
 262      * <code>getMinimumOptionPaneSize</code>.
 263      */
 264     public Dimension getPreferredSize(JComponent c) {
 265         if (c == optionPane) {
 266             Dimension            ourMin = getMinimumOptionPaneSize();
 267             LayoutManager        lm = c.getLayout();
 268 
 269             if (lm != null) {
 270                 Dimension         lmSize = lm.preferredLayoutSize(c);
 271 
 272                 if (ourMin != null)
 273                     return new Dimension
 274                         (Math.max(lmSize.width, ourMin.width),
 275                          Math.max(lmSize.height, ourMin.height));
 276                 return lmSize;
 277             }
 278             return ourMin;
 279         }
 280         return null;
 281     }
 282 
 283     /**
 284      * Messaged from installComponents to create a Container containing the
 285      * body of the message. The icon is the created by calling
 286      * <code>addIcon</code>.
 287      */
 288     protected Container createMessageArea() {
 289         JPanel top = new JPanel();
 290         Border topBorder = (Border)DefaultLookup.get(optionPane, this,
 291                                              "OptionPane.messageAreaBorder");
 292         if (topBorder != null) {
 293             top.setBorder(topBorder);
 294         }
 295         top.setLayout(new BorderLayout());
 296 
 297         /* Fill the body. */
 298         Container          body = new JPanel(new GridBagLayout());
 299         Container          realBody = new JPanel(new BorderLayout());
 300 
 301         body.setName("OptionPane.body");
 302         realBody.setName("OptionPane.realBody");
 303 
 304         if (getIcon() != null) {
 305             JPanel sep = new JPanel();
 306             sep.setName("OptionPane.separator");
 307             sep.setPreferredSize(new Dimension(15, 1));
 308             realBody.add(sep, BorderLayout.BEFORE_LINE_BEGINS);
 309         }
 310         realBody.add(body, BorderLayout.CENTER);
 311 
 312         GridBagConstraints cons = new GridBagConstraints();
 313         cons.gridx = cons.gridy = 0;
 314         cons.gridwidth = GridBagConstraints.REMAINDER;
 315         cons.gridheight = 1;
 316         cons.anchor = DefaultLookup.getInt(optionPane, this,
 317                       "OptionPane.messageAnchor", GridBagConstraints.CENTER);
 318         cons.insets = new Insets(0,0,3,0);
 319 
 320         addMessageComponents(body, cons, getMessage(),
 321                           getMaxCharactersPerLineCount(), false);
 322         top.add(realBody, BorderLayout.CENTER);
 323 
 324         addIcon(top);
 325         return top;
 326     }
 327 
 328     /**
 329      * Creates the appropriate object to represent <code>msg</code> and
 330      * places it into <code>container</code>. If <code>msg</code> is an
 331      * instance of Component, it is added directly, if it is an Icon,
 332      * a JLabel is created to represent it, otherwise a JLabel is
 333      * created for the string, if <code>d</code> is an Object[], this
 334      * method will be recursively invoked for the children.
 335      * <code>internallyCreated</code> is true if Objc is an instance
 336      * of Component and was created internally by this method (this is
 337      * used to correctly set hasCustomComponents only if !internallyCreated).
 338      */
 339     protected void addMessageComponents(Container container,
 340                                      GridBagConstraints cons,
 341                                      Object msg, int maxll,
 342                                      boolean internallyCreated) {
 343         if (msg == null) {
 344             return;
 345         }
 346         if (msg instanceof Component) {
 347             // To workaround problem where Gridbad will set child
 348             // to its minimum size if its preferred size will not fit
 349             // within allocated cells
 350             if (msg instanceof JScrollPane || msg instanceof JPanel) {
 351                 cons.fill = GridBagConstraints.BOTH;
 352                 cons.weighty = 1;
 353             } else {
 354                 cons.fill = GridBagConstraints.HORIZONTAL;
 355             }
 356             cons.weightx = 1;
 357 
 358             container.add((Component) msg, cons);
 359             cons.weightx = 0;
 360             cons.weighty = 0;
 361             cons.fill = GridBagConstraints.NONE;
 362             cons.gridy++;
 363             if (!internallyCreated) {
 364                 hasCustomComponents = true;
 365             }
 366 
 367         } else if (msg instanceof Object[]) {
 368             Object [] msgs = (Object[]) msg;
 369             for (Object o : msgs) {
 370                 addMessageComponents(container, cons, o, maxll, false);
 371             }
 372 
 373         } else if (msg instanceof Icon) {
 374             JLabel label = new JLabel( (Icon)msg, SwingConstants.CENTER );
 375             configureMessageLabel(label);
 376             addMessageComponents(container, cons, label, maxll, true);
 377 
 378         } else {
 379             String s = msg.toString();
 380             int len = s.length();
 381             if (len <= 0) {
 382                 return;
 383             }
 384             int nl;
 385             int nll = 0;
 386 
 387             if ((nl = s.indexOf(newline)) >= 0) {
 388                 nll = newline.length();
 389             } else if ((nl = s.indexOf("\r\n")) >= 0) {
 390                 nll = 2;
 391             } else if ((nl = s.indexOf('\n')) >= 0) {
 392                 nll = 1;
 393             }
 394             if (nl >= 0) {
 395                 // break up newlines
 396                 if (nl == 0) {
 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     private static class MultiplexingTextField extends JTextField {
1343         private KeyStroke[] strokes;
1344 
1345         MultiplexingTextField(int cols) {
1346             super(cols);
1347         }
1348 
1349         /**
1350          * Sets the KeyStrokes that will be additional processed for
1351          * ancestor bindings.
1352          */
1353         void setKeyStrokes(KeyStroke[] strokes) {
1354             this.strokes = strokes;
1355         }
1356 
1357         protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
1358                                             int condition, boolean pressed) {
1359             boolean processed = super.processKeyBinding(ks, e, condition,
1360                                                         pressed);
1361 
1362             if (processed && condition != JComponent.WHEN_IN_FOCUSED_WINDOW) {
1363                 for (int counter = strokes.length - 1; counter >= 0;
1364                          counter--) {
1365                     if (strokes[counter].equals(ks)) {
1366                         // Returning false will allow further processing
1367                         // of the bindings, eg our parent Containers will get a
1368                         // crack at them.
1369                         return false;
1370                     }
1371                 }
1372             }
1373             return processed;
1374         }
1375     }
1376 
1377 
1378 
1379     /**
1380      * Registered in the ActionMap. Sets the value of the option pane
1381      * to <code>JOptionPane.CLOSED_OPTION</code>.
1382      */
1383     private static class Actions extends UIAction {
1384         private static final String CLOSE = "close";
1385 
1386         Actions(String key) {
1387             super(key);
1388         }
1389 
1390         public void actionPerformed(ActionEvent e) {
1391             if (getName() == CLOSE) {
1392                 JOptionPane optionPane = (JOptionPane)e.getSource();
1393 
1394                 optionPane.setValue(Integer.valueOf(JOptionPane.CLOSED_OPTION));
1395             }
1396         }
1397     }
1398 
1399 
1400     /**
1401      * This class is used to create the default buttons. This indirection is
1402      * used so that addButtonComponents can tell which Buttons were created
1403      * by us vs subclassers or from the JOptionPane itself.
1404      */
1405     private static class ButtonFactory {
1406         private String text;
1407         private int mnemonic;
1408         private Icon icon;
1409         private int minimumWidth = -1;
1410 
1411         ButtonFactory(String text, int mnemonic, Icon icon, int minimumWidth) {
1412             this.text = text;
1413             this.mnemonic = mnemonic;
1414             this.icon = icon;
1415             this.minimumWidth = minimumWidth;
1416         }
1417 
1418         JButton createButton() {
1419             JButton button;
1420 
1421             if (minimumWidth > 0) {
1422                 button = new ConstrainedButton(text, minimumWidth);
1423             } else {
1424                 button = new JButton(text);
1425             }
1426             if (icon != null) {
1427                 button.setIcon(icon);
1428             }
1429             if (mnemonic != 0) {
1430                 button.setMnemonic(mnemonic);
1431             }
1432             return button;
1433         }
1434 
1435         private static class ConstrainedButton extends JButton {
1436             int minimumWidth;
1437 
1438             ConstrainedButton(String text, int minimumWidth) {
1439                 super(text);
1440                 this.minimumWidth = minimumWidth;
1441             }
1442 
1443             public Dimension getMinimumSize() {
1444                 Dimension min = super.getMinimumSize();
1445                 min.width = Math.max(min.width, minimumWidth);
1446                 return min;
1447             }
1448 
1449             public Dimension getPreferredSize() {
1450                 Dimension pref = super.getPreferredSize();
1451                 pref.width = Math.max(pref.width, minimumWidth);
1452                 return pref;
1453             }
1454         }
1455     }
1456 }