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 = 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                     @SuppressWarnings("serial") // anonymous class
 398                     JPanel breakPanel = new JPanel() {
 399                         public Dimension getPreferredSize() {
 400                             Font       f = getFont();
 401 
 402                             if (f != null) {
 403                                 return new Dimension(1, f.getSize() + 2);
 404                             }
 405                             return new Dimension(0, 0);
 406                         }
 407                     };
 408                     breakPanel.setName("OptionPane.break");
 409                     addMessageComponents(container, cons, breakPanel, maxll,
 410                                          true);
 411                 } else {
 412                     addMessageComponents(container, cons, s.substring(0, nl),
 413                                       maxll, false);
 414                 }
 415                 addMessageComponents(container, cons, s.substring(nl + nll), maxll,
 416                                   false);
 417 
 418             } else if (len > maxll) {
 419                 Container c = Box.createVerticalBox();
 420                 c.setName("OptionPane.verticalBox");
 421                 burstStringInto(c, s, maxll);
 422                 addMessageComponents(container, cons, c, maxll, true );
 423 
 424             } else {
 425                 JLabel label;
 426                 label = new JLabel( s, JLabel.LEADING );
 427                 label.setName("OptionPane.label");
 428                 configureMessageLabel(label);
 429                 addMessageComponents(container, cons, label, maxll, true);
 430             }
 431         }
 432     }
 433 
 434     /**
 435      * Returns the message to display from the JOptionPane the receiver is
 436      * providing the look and feel for.
 437      */
 438     protected Object getMessage() {
 439         inputComponent = null;
 440         if (optionPane != null) {
 441             if (optionPane.getWantsInput()) {
 442                 /* Create a user component to capture the input. If the
 443                    selectionValues are non null the component and there
 444                    are < 20 values it'll be a combobox, if non null and
 445                    >= 20, it'll be a list, otherwise it'll be a textfield. */
 446                 Object             message = optionPane.getMessage();
 447                 Object[]           sValues = optionPane.getSelectionValues();
 448                 Object             inputValue = optionPane
 449                                            .getInitialSelectionValue();
 450                 JComponent         toAdd;
 451 
 452                 if (sValues != null) {
 453                     if (sValues.length < 20) {
 454                         JComboBox            cBox = new JComboBox();
 455 
 456                         cBox.setName("OptionPane.comboBox");
 457                         for(int counter = 0, maxCounter = sValues.length;
 458                             counter < maxCounter; counter++) {
 459                             cBox.addItem(sValues[counter]);
 460                         }
 461                         if (inputValue != null) {
 462                             cBox.setSelectedItem(inputValue);
 463                         }
 464                         inputComponent = cBox;
 465                         toAdd = cBox;
 466 
 467                     } else {
 468                         JList                list = new JList(sValues);
 469                         JScrollPane          sp = new JScrollPane(list);
 470 
 471                         sp.setName("OptionPane.scrollPane");
 472                         list.setName("OptionPane.list");
 473                         list.setVisibleRowCount(10);
 474                         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 475                         if(inputValue != null)
 476                             list.setSelectedValue(inputValue, true);
 477                         list.addMouseListener(getHandler());
 478                         toAdd = sp;
 479                         inputComponent = list;
 480                     }
 481 
 482                 } else {
 483                     MultiplexingTextField   tf = new MultiplexingTextField(20);
 484 
 485                     tf.setName("OptionPane.textField");
 486                     tf.setKeyStrokes(new KeyStroke[] {
 487                                      KeyStroke.getKeyStroke("ENTER") } );
 488                     if (inputValue != null) {
 489                         String inputString = inputValue.toString();
 490                         tf.setText(inputString);
 491                         tf.setSelectionStart(0);
 492                         tf.setSelectionEnd(inputString.length());
 493                     }
 494                     tf.addActionListener(getHandler());
 495                     toAdd = inputComponent = tf;
 496                 }
 497 
 498                 Object[]           newMessage;
 499 
 500                 if (message == null) {
 501                     newMessage = new Object[1];
 502                     newMessage[0] = toAdd;
 503 
 504                 } else {
 505                     newMessage = new Object[2];
 506                     newMessage[0] = message;
 507                     newMessage[1] = toAdd;
 508                 }
 509                 return newMessage;
 510             }
 511             return optionPane.getMessage();
 512         }
 513         return null;
 514     }
 515 
 516     /**
 517      * Creates and adds a JLabel representing the icon returned from
 518      * <code>getIcon</code> to <code>top</code>. This is messaged from
 519      * <code>createMessageArea</code>
 520      */
 521     protected void addIcon(Container top) {
 522         /* Create the icon. */
 523         Icon                  sideIcon = getIcon();
 524 
 525         if (sideIcon != null) {
 526             JLabel            iconLabel = new JLabel(sideIcon);
 527 
 528             iconLabel.setName("OptionPane.iconLabel");
 529             iconLabel.setVerticalAlignment(SwingConstants.TOP);
 530             top.add(iconLabel, BorderLayout.BEFORE_LINE_BEGINS);
 531         }
 532     }
 533 
 534     /**
 535      * Returns the icon from the JOptionPane the receiver is providing
 536      * the look and feel for, or the default icon as returned from
 537      * <code>getDefaultIcon</code>.
 538      */
 539     protected Icon getIcon() {
 540         Icon      mIcon = (optionPane == null ? null : optionPane.getIcon());
 541 
 542         if(mIcon == null && optionPane != null)
 543             mIcon = getIconForType(optionPane.getMessageType());
 544         return mIcon;
 545     }
 546 
 547     /**
 548      * Returns the icon to use for the passed in type.
 549      */
 550     protected Icon getIconForType(int messageType) {
 551         if(messageType < 0 || messageType > 3)
 552             return null;
 553         String propertyName = null;
 554         switch(messageType) {
 555         case 0:
 556             propertyName = "OptionPane.errorIcon";
 557             break;
 558         case 1:
 559             propertyName = "OptionPane.informationIcon";
 560             break;
 561         case 2:
 562             propertyName = "OptionPane.warningIcon";
 563             break;
 564         case 3:
 565             propertyName = "OptionPane.questionIcon";
 566             break;
 567         }
 568         if (propertyName != null) {
 569             return (Icon)DefaultLookup.get(optionPane, this, propertyName);
 570         }
 571         return null;
 572     }
 573 
 574     /**
 575      * Returns the maximum number of characters to place on a line.
 576      */
 577     protected int getMaxCharactersPerLineCount() {
 578         return optionPane.getMaxCharactersPerLineCount();
 579     }
 580 
 581    /**
 582      * Recursively creates new JLabel instances to represent <code>d</code>.
 583      * Each JLabel instance is added to <code>c</code>.
 584      */
 585     protected void burstStringInto(Container c, String d, int maxll) {
 586         // Primitive line wrapping
 587         int len = d.length();
 588         if (len <= 0)
 589             return;
 590         if (len > maxll) {
 591             int p = d.lastIndexOf(' ', maxll);
 592             if (p <= 0)
 593                 p = d.indexOf(' ', maxll);
 594             if (p > 0 && p < len) {
 595                 burstStringInto(c, d.substring(0, p), maxll);
 596                 burstStringInto(c, d.substring(p + 1), maxll);
 597                 return;
 598             }
 599         }
 600         JLabel label = new JLabel(d, JLabel.LEFT);
 601         label.setName("OptionPane.label");
 602         configureMessageLabel(label);
 603         c.add(label);
 604     }
 605 
 606     protected Container createSeparator() {
 607         return null;
 608     }
 609 
 610     /**
 611      * Creates and returns a Container containing the buttons. The buttons
 612      * are created by calling <code>getButtons</code>.
 613      */
 614     protected Container createButtonArea() {
 615         JPanel bottom = new JPanel();
 616         Border border = (Border)DefaultLookup.get(optionPane, this,
 617                                           "OptionPane.buttonAreaBorder");
 618         bottom.setName("OptionPane.buttonArea");
 619         if (border != null) {
 620             bottom.setBorder(border);
 621         }
 622         bottom.setLayout(new ButtonAreaLayout(
 623            DefaultLookup.getBoolean(optionPane, this,
 624                                     "OptionPane.sameSizeButtons", true),
 625            DefaultLookup.getInt(optionPane, this, "OptionPane.buttonPadding",
 626                                 6),
 627            DefaultLookup.getInt(optionPane, this,
 628                         "OptionPane.buttonOrientation", SwingConstants.CENTER),
 629            DefaultLookup.getBoolean(optionPane, this, "OptionPane.isYesLast",
 630                                     false)));
 631         addButtonComponents(bottom, getButtons(), getInitialValueIndex());
 632         return bottom;
 633     }
 634 
 635     /**
 636      * Creates the appropriate object to represent each of the objects in
 637      * <code>buttons</code> and adds it to <code>container</code>. This
 638      * differs from addMessageComponents in that it will recurse on
 639      * <code>buttons</code> and that if button is not a Component
 640      * it will create an instance of JButton.
 641      */
 642     protected void addButtonComponents(Container container, Object[] buttons,
 643                                  int initialIndex) {
 644         if (buttons != null && buttons.length > 0) {
 645             boolean            sizeButtonsToSame = getSizeButtonsToSameWidth();
 646             boolean            createdAll = true;
 647             int                numButtons = buttons.length;
 648             JButton[]          createdButtons = null;
 649             int                maxWidth = 0;
 650 
 651             if (sizeButtonsToSame) {
 652                 createdButtons = new JButton[numButtons];
 653             }
 654 
 655             for(int counter = 0; counter < numButtons; counter++) {
 656                 Object       button = buttons[counter];
 657                 Component    newComponent;
 658 
 659                 if (button instanceof Component) {
 660                     createdAll = false;
 661                     newComponent = (Component)button;
 662                     container.add(newComponent);
 663                     hasCustomComponents = true;
 664 
 665                 } else {
 666                     JButton      aButton;
 667 
 668                     if (button instanceof ButtonFactory) {
 669                         aButton = ((ButtonFactory)button).createButton();
 670                     }
 671                     else if (button instanceof Icon)
 672                         aButton = new JButton((Icon)button);
 673                     else
 674                         aButton = new JButton(button.toString());
 675 
 676                     aButton.setName("OptionPane.button");
 677                     aButton.setMultiClickThreshhold(DefaultLookup.getInt(
 678                           optionPane, this, "OptionPane.buttonClickThreshhold",
 679                           0));
 680                     configureButton(aButton);
 681 
 682                     container.add(aButton);
 683 
 684                     ActionListener buttonListener = createButtonActionListener(counter);
 685                     if (buttonListener != null) {
 686                         aButton.addActionListener(buttonListener);
 687                     }
 688                     newComponent = aButton;
 689                 }
 690                 if (sizeButtonsToSame && createdAll &&
 691                    (newComponent instanceof JButton)) {
 692                     createdButtons[counter] = (JButton)newComponent;
 693                     maxWidth = Math.max(maxWidth,
 694                                         newComponent.getMinimumSize().width);
 695                 }
 696                 if (counter == initialIndex) {
 697                     initialFocusComponent = newComponent;
 698                     if (initialFocusComponent instanceof JButton) {
 699                         JButton defaultB = (JButton)initialFocusComponent;
 700                         defaultB.addHierarchyListener(new HierarchyListener() {
 701                             public void hierarchyChanged(HierarchyEvent e) {
 702                                 if ((e.getChangeFlags() &
 703                                         HierarchyEvent.PARENT_CHANGED) != 0) {
 704                                     JButton defaultButton = (JButton) e.getComponent();
 705                                     JRootPane root =
 706                                             SwingUtilities.getRootPane(defaultButton);
 707                                     if (root != null) {
 708                                         root.setDefaultButton(defaultButton);
 709                                     }
 710                                 }
 711                             }
 712                         });
 713                     }
 714                 }
 715             }
 716             ((ButtonAreaLayout)container.getLayout()).
 717                               setSyncAllWidths((sizeButtonsToSame && createdAll));
 718             /* Set the padding, windows seems to use 8 if <= 2 components,
 719                otherwise 4 is used. It may actually just be the size of the
 720                buttons is always the same, not sure. */
 721             if (DefaultLookup.getBoolean(optionPane, this,
 722                    "OptionPane.setButtonMargin", true) && sizeButtonsToSame &&
 723                    createdAll) {
 724                 JButton               aButton;
 725                 int                   padSize;
 726 
 727                 padSize = (numButtons <= 2? 8 : 4);
 728 
 729                 for(int counter = 0; counter < numButtons; counter++) {
 730                     aButton = createdButtons[counter];
 731                     aButton.setMargin(new Insets(2, padSize, 2, padSize));
 732                 }
 733             }
 734         }
 735     }
 736 
 737     protected ActionListener createButtonActionListener(int buttonIndex) {
 738         return new ButtonActionListener(buttonIndex);
 739     }
 740 
 741     /**
 742      * Returns the buttons to display from the JOptionPane the receiver is
 743      * providing the look and feel for. If the JOptionPane has options
 744      * set, they will be provided, otherwise if the optionType is
 745      * YES_NO_OPTION, yesNoOptions is returned, if the type is
 746      * YES_NO_CANCEL_OPTION yesNoCancelOptions is returned, otherwise
 747      * defaultButtons are returned.
 748      */
 749     protected Object[] getButtons() {
 750         if (optionPane != null) {
 751             Object[] suppliedOptions = optionPane.getOptions();
 752 
 753             if (suppliedOptions == null) {
 754                 Object[] defaultOptions;
 755                 int type = optionPane.getOptionType();
 756                 Locale l = optionPane.getLocale();
 757                 int minimumWidth =
 758                     DefaultLookup.getInt(optionPane, this,
 759                                         "OptionPane.buttonMinimumWidth",-1);
 760                 if (type == JOptionPane.YES_NO_OPTION) {
 761                     defaultOptions = new ButtonFactory[2];
 762                     defaultOptions[0] = new ButtonFactory(
 763                         UIManager.getString("OptionPane.yesButtonText", l),
 764                         getMnemonic("OptionPane.yesButtonMnemonic", l),
 765                         (Icon)DefaultLookup.get(optionPane, this,
 766                                           "OptionPane.yesIcon"), minimumWidth);
 767                     defaultOptions[1] = new ButtonFactory(
 768                         UIManager.getString("OptionPane.noButtonText", l),
 769                         getMnemonic("OptionPane.noButtonMnemonic", l),
 770                         (Icon)DefaultLookup.get(optionPane, this,
 771                                           "OptionPane.noIcon"), minimumWidth);
 772                 } else if (type == JOptionPane.YES_NO_CANCEL_OPTION) {
 773                     defaultOptions = new ButtonFactory[3];
 774                     defaultOptions[0] = new ButtonFactory(
 775                         UIManager.getString("OptionPane.yesButtonText", l),
 776                         getMnemonic("OptionPane.yesButtonMnemonic", l),
 777                         (Icon)DefaultLookup.get(optionPane, this,
 778                                           "OptionPane.yesIcon"), minimumWidth);
 779                     defaultOptions[1] = new ButtonFactory(
 780                         UIManager.getString("OptionPane.noButtonText",l),
 781                         getMnemonic("OptionPane.noButtonMnemonic", l),
 782                         (Icon)DefaultLookup.get(optionPane, this,
 783                                           "OptionPane.noIcon"), minimumWidth);
 784                     defaultOptions[2] = new ButtonFactory(
 785                         UIManager.getString("OptionPane.cancelButtonText",l),
 786                         getMnemonic("OptionPane.cancelButtonMnemonic", l),
 787                         (Icon)DefaultLookup.get(optionPane, this,
 788                                           "OptionPane.cancelIcon"), minimumWidth);
 789                 } else if (type == JOptionPane.OK_CANCEL_OPTION) {
 790                     defaultOptions = new ButtonFactory[2];
 791                     defaultOptions[0] = new ButtonFactory(
 792                         UIManager.getString("OptionPane.okButtonText",l),
 793                         getMnemonic("OptionPane.okButtonMnemonic", l),
 794                         (Icon)DefaultLookup.get(optionPane, this,
 795                                           "OptionPane.okIcon"), minimumWidth);
 796                     defaultOptions[1] = new ButtonFactory(
 797                         UIManager.getString("OptionPane.cancelButtonText",l),
 798                         getMnemonic("OptionPane.cancelButtonMnemonic", l),
 799                         (Icon)DefaultLookup.get(optionPane, this,
 800                                           "OptionPane.cancelIcon"), minimumWidth);
 801                 } else {
 802                     defaultOptions = new ButtonFactory[1];
 803                     defaultOptions[0] = new ButtonFactory(
 804                         UIManager.getString("OptionPane.okButtonText",l),
 805                         getMnemonic("OptionPane.okButtonMnemonic", l),
 806                         (Icon)DefaultLookup.get(optionPane, this,
 807                                           "OptionPane.okIcon"), minimumWidth);
 808                 }
 809                 return defaultOptions;
 810 
 811             }
 812             return suppliedOptions;
 813         }
 814         return null;
 815     }
 816 
 817     private int getMnemonic(String key, Locale l) {
 818         String value = (String)UIManager.get(key, l);
 819 
 820         if (value == null) {
 821             return 0;
 822         }
 823         try {
 824             return Integer.parseInt(value);
 825         }
 826         catch (NumberFormatException nfe) { }
 827         return 0;
 828     }
 829 
 830     /**
 831      * Returns true, basic L&amp;F wants all the buttons to have the same
 832      * width.
 833      */
 834     protected boolean getSizeButtonsToSameWidth() {
 835         return true;
 836     }
 837 
 838     /**
 839      * Returns the initial index into the buttons to select. The index
 840      * is calculated from the initial value from the JOptionPane and
 841      * options of the JOptionPane or 0.
 842      */
 843     protected int getInitialValueIndex() {
 844         if (optionPane != null) {
 845             Object             iv = optionPane.getInitialValue();
 846             Object[]           options = optionPane.getOptions();
 847 
 848             if(options == null) {
 849                 return 0;
 850             }
 851             else if(iv != null) {
 852                 for(int counter = options.length - 1; counter >= 0; counter--){
 853                     if(options[counter].equals(iv))
 854                         return counter;
 855                 }
 856             }
 857         }
 858         return -1;
 859     }
 860 
 861     /**
 862      * Sets the input value in the option pane the receiver is providing
 863      * the look and feel for based on the value in the inputComponent.
 864      */
 865     protected void resetInputValue() {
 866         if(inputComponent != null && (inputComponent instanceof JTextField)) {
 867             optionPane.setInputValue(((JTextField)inputComponent).getText());
 868 
 869         } else if(inputComponent != null &&
 870                   (inputComponent instanceof JComboBox)) {
 871             optionPane.setInputValue(((JComboBox)inputComponent)
 872                                      .getSelectedItem());
 873         } else if(inputComponent != null) {
 874             optionPane.setInputValue(((JList)inputComponent)
 875                                      .getSelectedValue());
 876         }
 877     }
 878 
 879 
 880     /**
 881      * If inputComponent is non-null, the focus is requested on that,
 882      * otherwise request focus on the default value
 883      */
 884     public void selectInitialValue(JOptionPane op) {
 885         if (inputComponent != null)
 886             inputComponent.requestFocus();
 887         else {
 888             if (initialFocusComponent != null)
 889                 initialFocusComponent.requestFocus();
 890 
 891             if (initialFocusComponent instanceof JButton) {
 892                 JRootPane root = SwingUtilities.getRootPane(initialFocusComponent);
 893                 if (root != null) {
 894                     root.setDefaultButton((JButton)initialFocusComponent);
 895                 }
 896             }
 897         }
 898     }
 899 
 900     /**
 901      * Returns true if in the last call to validateComponent the message
 902      * or buttons contained a subclass of Component.
 903      */
 904     public boolean containsCustomComponents(JOptionPane op) {
 905         return hasCustomComponents;
 906     }
 907 
 908 
 909     /**
 910      * <code>ButtonAreaLayout</code> behaves in a similar manner to
 911      * <code>FlowLayout</code>. It lays out all components from left to
 912      * right. If <code>syncAllWidths</code> is true, the widths of each
 913      * component will be set to the largest preferred size width.
 914      *
 915      * This class should be treated as a &quot;protected&quot; inner class.
 916      * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
 917      */
 918     public static class ButtonAreaLayout implements LayoutManager {
 919         protected boolean           syncAllWidths;
 920         protected int               padding;
 921         /** If true, children are lumped together in parent. */
 922         protected boolean           centersChildren;
 923         private int orientation;
 924         private boolean reverseButtons;
 925         /**
 926          * Indicates whether or not centersChildren should be used vs
 927          * the orientation. This is done for backward compatibility
 928          * for subclassers.
 929          */
 930         private boolean useOrientation;
 931 
 932         public ButtonAreaLayout(boolean syncAllWidths, int padding) {
 933             this.syncAllWidths = syncAllWidths;
 934             this.padding = padding;
 935             centersChildren = true;
 936             useOrientation = false;
 937         }
 938 
 939         ButtonAreaLayout(boolean syncAllSizes, int padding, int orientation,
 940                          boolean reverseButtons) {
 941             this(syncAllSizes, padding);
 942             useOrientation = true;
 943             this.orientation = orientation;
 944             this.reverseButtons = reverseButtons;
 945         }
 946 
 947         public void setSyncAllWidths(boolean newValue) {
 948             syncAllWidths = newValue;
 949         }
 950 
 951         public boolean getSyncAllWidths() {
 952             return syncAllWidths;
 953         }
 954 
 955         public void setPadding(int newPadding) {
 956             this.padding = newPadding;
 957         }
 958 
 959         public int getPadding() {
 960             return padding;
 961         }
 962 
 963         public void setCentersChildren(boolean newValue) {
 964             centersChildren = newValue;
 965             useOrientation = false;
 966         }
 967 
 968         public boolean getCentersChildren() {
 969             return centersChildren;
 970         }
 971 
 972         private int getOrientation(Container container) {
 973             if (!useOrientation) {
 974                 return SwingConstants.CENTER;
 975             }
 976             if (container.getComponentOrientation().isLeftToRight()) {
 977                 return orientation;
 978             }
 979             switch (orientation) {
 980             case SwingConstants.LEFT:
 981                 return SwingConstants.RIGHT;
 982             case SwingConstants.RIGHT:
 983                 return SwingConstants.LEFT;
 984             case SwingConstants.CENTER:
 985                 return SwingConstants.CENTER;
 986             }
 987             return SwingConstants.LEFT;
 988         }
 989 
 990         public void addLayoutComponent(String string, Component comp) {
 991         }
 992 
 993         public void layoutContainer(Container container) {
 994             Component[]      children = container.getComponents();
 995 
 996             if(children != null && children.length > 0) {
 997                 int               numChildren = children.length;
 998                 Insets            insets = container.getInsets();
 999                 int maxWidth = 0;
1000                 int maxHeight = 0;
1001                 int totalButtonWidth = 0;
1002                 int x = 0;
1003                 int xOffset = 0;
1004                 boolean ltr = container.getComponentOrientation().
1005                                         isLeftToRight();
1006                 boolean reverse = (ltr) ? reverseButtons : !reverseButtons;
1007 
1008                 for(int counter = 0; counter < numChildren; counter++) {
1009                     Dimension pref = children[counter].getPreferredSize();
1010                     maxWidth = Math.max(maxWidth, pref.width);
1011                     maxHeight = Math.max(maxHeight, pref.height);
1012                     totalButtonWidth += pref.width;
1013                 }
1014                 if (getSyncAllWidths()) {
1015                     totalButtonWidth = maxWidth * numChildren;
1016                 }
1017                 totalButtonWidth += (numChildren - 1) * padding;
1018 
1019                 switch (getOrientation(container)) {
1020                 case SwingConstants.LEFT:
1021                     x = insets.left;
1022                     break;
1023                 case SwingConstants.RIGHT:
1024                     x = container.getWidth() - insets.right - totalButtonWidth;
1025                     break;
1026                 case SwingConstants.CENTER:
1027                     if (getCentersChildren() || numChildren < 2) {
1028                         x = (container.getWidth() - totalButtonWidth) / 2;
1029                     }
1030                     else {
1031                         x = insets.left;
1032                         if (getSyncAllWidths()) {
1033                             xOffset = (container.getWidth() - insets.left -
1034                                        insets.right - totalButtonWidth) /
1035                                 (numChildren - 1) + maxWidth;
1036                         }
1037                         else {
1038                             xOffset = (container.getWidth() - insets.left -
1039                                        insets.right - totalButtonWidth) /
1040                                       (numChildren - 1);
1041                         }
1042                     }
1043                     break;
1044                 }
1045 
1046                 for (int counter = 0; counter < numChildren; counter++) {
1047                     int index = (reverse) ? numChildren - counter - 1 :
1048                                 counter;
1049                     Dimension pref = children[index].getPreferredSize();
1050 
1051                     if (getSyncAllWidths()) {
1052                         children[index].setBounds(x, insets.top,
1053                                                   maxWidth, maxHeight);
1054                     }
1055                     else {
1056                         children[index].setBounds(x, insets.top, pref.width,
1057                                                   pref.height);
1058                     }
1059                     if (xOffset != 0) {
1060                         x += xOffset;
1061                     }
1062                     else {
1063                         x += children[index].getWidth() + padding;
1064                     }
1065                 }
1066             }
1067         }
1068 
1069         public Dimension minimumLayoutSize(Container c) {
1070             if(c != null) {
1071                 Component[]       children = c.getComponents();
1072 
1073                 if(children != null && children.length > 0) {
1074                     Dimension     aSize;
1075                     int           numChildren = children.length;
1076                     int           height = 0;
1077                     Insets        cInsets = c.getInsets();
1078                     int           extraHeight = cInsets.top + cInsets.bottom;
1079                     int           extraWidth = cInsets.left + cInsets.right;
1080 
1081                     if (syncAllWidths) {
1082                         int              maxWidth = 0;
1083 
1084                         for(int counter = 0; counter < numChildren; counter++){
1085                             aSize = children[counter].getPreferredSize();
1086                             height = Math.max(height, aSize.height);
1087                             maxWidth = Math.max(maxWidth, aSize.width);
1088                         }
1089                         return new Dimension(extraWidth + (maxWidth * numChildren) +
1090                                              (numChildren - 1) * padding,
1091                                              extraHeight + height);
1092                     }
1093                     else {
1094                         int        totalWidth = 0;
1095 
1096                         for(int counter = 0; counter < numChildren; counter++){
1097                             aSize = children[counter].getPreferredSize();
1098                             height = Math.max(height, aSize.height);
1099                             totalWidth += aSize.width;
1100                         }
1101                         totalWidth += ((numChildren - 1) * padding);
1102                         return new Dimension(extraWidth + totalWidth, extraHeight + height);
1103                     }
1104                 }
1105             }
1106             return new Dimension(0, 0);
1107         }
1108 
1109         public Dimension preferredLayoutSize(Container c) {
1110             return minimumLayoutSize(c);
1111         }
1112 
1113         public void removeLayoutComponent(Component c) { }
1114     }
1115 
1116 
1117     /**
1118      * This class should be treated as a &quot;protected&quot; inner class.
1119      * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
1120      */
1121     public class PropertyChangeHandler implements PropertyChangeListener {
1122         /**
1123          * If the source of the PropertyChangeEvent <code>e</code> equals the
1124          * optionPane and is one of the ICON_PROPERTY, MESSAGE_PROPERTY,
1125          * OPTIONS_PROPERTY or INITIAL_VALUE_PROPERTY,
1126          * validateComponent is invoked.
1127          */
1128         public void propertyChange(PropertyChangeEvent e) {
1129             getHandler().propertyChange(e);
1130         }
1131     }
1132 
1133     /**
1134      * Configures any necessary colors/fonts for the specified label
1135      * used representing the message.
1136      */
1137     private void configureMessageLabel(JLabel label) {
1138         Color color = (Color)DefaultLookup.get(optionPane, this,
1139                                                "OptionPane.messageForeground");
1140         if (color != null) {
1141             label.setForeground(color);
1142         }
1143         Font messageFont = (Font)DefaultLookup.get(optionPane, this,
1144                                                    "OptionPane.messageFont");
1145         if (messageFont != null) {
1146             label.setFont(messageFont);
1147         }
1148     }
1149 
1150     /**
1151      * Configures any necessary colors/fonts for the specified button
1152      * used representing the button portion of the optionpane.
1153      */
1154     private void configureButton(JButton button) {
1155         Font buttonFont = (Font)DefaultLookup.get(optionPane, this,
1156                                             "OptionPane.buttonFont");
1157         if (buttonFont != null) {
1158             button.setFont(buttonFont);
1159         }
1160     }
1161 
1162     /**
1163      * This class should be treated as a &quot;protected&quot; inner class.
1164      * Instantiate it only within subclasses of {@code BasicOptionPaneUI}.
1165      */
1166     public class ButtonActionListener implements ActionListener {
1167         protected int buttonIndex;
1168 
1169         public ButtonActionListener(int buttonIndex) {
1170             this.buttonIndex = buttonIndex;
1171         }
1172 
1173         public void actionPerformed(ActionEvent e) {
1174             if (optionPane != null) {
1175                 int optionType = optionPane.getOptionType();
1176                 Object[] options = optionPane.getOptions();
1177 
1178                 /* If the option pane takes input, then store the input value
1179                  * if custom options were specified, if the option type is
1180                  * DEFAULT_OPTION, OR if option type is set to a predefined
1181                  * one and the user chose the affirmative answer.
1182                  */
1183                 if (inputComponent != null) {
1184                     if (options != null ||
1185                         optionType == JOptionPane.DEFAULT_OPTION ||
1186                         ((optionType == JOptionPane.YES_NO_OPTION ||
1187                          optionType == JOptionPane.YES_NO_CANCEL_OPTION ||
1188                          optionType == JOptionPane.OK_CANCEL_OPTION) &&
1189                          buttonIndex == 0)) {
1190                         resetInputValue();
1191                     }
1192                 }
1193                 if (options == null) {
1194                     if (optionType == JOptionPane.OK_CANCEL_OPTION &&
1195                         buttonIndex == 1) {
1196                         optionPane.setValue(Integer.valueOf(2));
1197 
1198                     } else {
1199                         optionPane.setValue(Integer.valueOf(buttonIndex));
1200                     }
1201                 } else {
1202                     optionPane.setValue(options[buttonIndex]);
1203                 }
1204             }
1205         }
1206     }
1207 
1208 
1209     private class Handler implements ActionListener, MouseListener,
1210                                      PropertyChangeListener {
1211         //
1212         // ActionListener
1213         //
1214         public void actionPerformed(ActionEvent e) {
1215             optionPane.setInputValue(((JTextField)e.getSource()).getText());
1216         }
1217 
1218 
1219         //
1220         // MouseListener
1221         //
1222         public void mouseClicked(MouseEvent e) {
1223         }
1224 
1225         public void mouseReleased(MouseEvent e) {
1226         }
1227 
1228         public void mouseEntered(MouseEvent e) {
1229         }
1230 
1231         public void mouseExited(MouseEvent e) {
1232         }
1233 
1234         public void mousePressed(MouseEvent e) {
1235             if (e.getClickCount() == 2) {
1236                 JList     list = (JList)e.getSource();
1237                 int       index = list.locationToIndex(e.getPoint());
1238 
1239                 optionPane.setInputValue(list.getModel().getElementAt(index));
1240                 optionPane.setValue(JOptionPane.OK_OPTION);
1241             }
1242         }
1243 
1244         //
1245         // PropertyChangeListener
1246         //
1247         public void propertyChange(PropertyChangeEvent e) {
1248             if(e.getSource() == optionPane) {
1249                 // Option Pane Auditory Cue Activation
1250                 // only respond to "ancestor" changes
1251                 // the idea being that a JOptionPane gets a JDialog when it is
1252                 // set to appear and loses it's JDialog when it is dismissed.
1253                 if ("ancestor" == e.getPropertyName()) {
1254                     JOptionPane op = (JOptionPane)e.getSource();
1255                     boolean isComingUp;
1256 
1257                     // if the old value is null, then the JOptionPane is being
1258                     // created since it didn't previously have an ancestor.
1259                     if (e.getOldValue() == null) {
1260                         isComingUp = true;
1261                     } else {
1262                         isComingUp = false;
1263                     }
1264 
1265                     // figure out what to do based on the message type
1266                     switch (op.getMessageType()) {
1267                     case JOptionPane.PLAIN_MESSAGE:
1268                         if (isComingUp) {
1269                             BasicLookAndFeel.playSound(optionPane,
1270                                                "OptionPane.informationSound");
1271                         }
1272                         break;
1273                     case JOptionPane.QUESTION_MESSAGE:
1274                         if (isComingUp) {
1275                             BasicLookAndFeel.playSound(optionPane,
1276                                              "OptionPane.questionSound");
1277                         }
1278                         break;
1279                     case JOptionPane.INFORMATION_MESSAGE:
1280                         if (isComingUp) {
1281                             BasicLookAndFeel.playSound(optionPane,
1282                                              "OptionPane.informationSound");
1283                         }
1284                         break;
1285                     case JOptionPane.WARNING_MESSAGE:
1286                         if (isComingUp) {
1287                             BasicLookAndFeel.playSound(optionPane,
1288                                              "OptionPane.warningSound");
1289                         }
1290                         break;
1291                     case JOptionPane.ERROR_MESSAGE:
1292                         if (isComingUp) {
1293                             BasicLookAndFeel.playSound(optionPane,
1294                                              "OptionPane.errorSound");
1295                         }
1296                         break;
1297                     default:
1298                         System.err.println("Undefined JOptionPane type: " +
1299                                            op.getMessageType());
1300                         break;
1301                     }
1302                 }
1303                 // Visual activity
1304                 String         changeName = e.getPropertyName();
1305 
1306                 if(changeName == JOptionPane.OPTIONS_PROPERTY ||
1307                    changeName == JOptionPane.INITIAL_VALUE_PROPERTY ||
1308                    changeName == JOptionPane.ICON_PROPERTY ||
1309                    changeName == JOptionPane.MESSAGE_TYPE_PROPERTY ||
1310                    changeName == JOptionPane.OPTION_TYPE_PROPERTY ||
1311                    changeName == JOptionPane.MESSAGE_PROPERTY ||
1312                    changeName == JOptionPane.SELECTION_VALUES_PROPERTY ||
1313                    changeName == JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY ||
1314                    changeName == JOptionPane.WANTS_INPUT_PROPERTY) {
1315                    uninstallComponents();
1316                    installComponents();
1317                    optionPane.validate();
1318                 }
1319                 else if (changeName == "componentOrientation") {
1320                     ComponentOrientation o = (ComponentOrientation)e.getNewValue();
1321                     JOptionPane op = (JOptionPane)e.getSource();
1322                     if (o != e.getOldValue()) {
1323                         op.applyComponentOrientation(o);
1324                     }
1325                 }
1326             }
1327         }
1328     }
1329 
1330 
1331     //
1332     // Classes used when optionPane.getWantsInput returns true.
1333     //
1334 
1335     /**
1336      * A JTextField that allows you to specify an array of KeyStrokes that
1337      * that will have their bindings processed regardless of whether or
1338      * not they are registered on the JTextField. This is used as we really
1339      * want the ActionListener to be notified so that we can push the
1340      * change to the JOptionPane, but we also want additional bindings
1341      * (those of the JRootPane) to be processed as well.
1342      */
1343     @SuppressWarnings("serial") // Superclass is not serializable across versions
1344     private static class MultiplexingTextField extends JTextField {
1345         private KeyStroke[] strokes;
1346 
1347         MultiplexingTextField(int cols) {
1348             super(cols);
1349         }
1350 
1351         /**
1352          * Sets the KeyStrokes that will be additional processed for
1353          * ancestor bindings.
1354          */
1355         void setKeyStrokes(KeyStroke[] strokes) {
1356             this.strokes = strokes;
1357         }
1358 
1359         protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
1360                                             int condition, boolean pressed) {
1361             boolean processed = super.processKeyBinding(ks, e, condition,
1362                                                         pressed);
1363 
1364             if (processed && condition != JComponent.WHEN_IN_FOCUSED_WINDOW) {
1365                 for (int counter = strokes.length - 1; counter >= 0;
1366                          counter--) {
1367                     if (strokes[counter].equals(ks)) {
1368                         // Returning false will allow further processing
1369                         // of the bindings, eg our parent Containers will get a
1370                         // crack at them.
1371                         return false;
1372                     }
1373                 }
1374             }
1375             return processed;
1376         }
1377     }
1378 
1379 
1380 
1381     /**
1382      * Registered in the ActionMap. Sets the value of the option pane
1383      * to <code>JOptionPane.CLOSED_OPTION</code>.
1384      */
1385     private static class Actions extends UIAction {
1386         private static final String CLOSE = "close";
1387 
1388         Actions(String key) {
1389             super(key);
1390         }
1391 
1392         public void actionPerformed(ActionEvent e) {
1393             if (getName() == CLOSE) {
1394                 JOptionPane optionPane = (JOptionPane)e.getSource();
1395 
1396                 optionPane.setValue(Integer.valueOf(JOptionPane.CLOSED_OPTION));
1397             }
1398         }
1399     }
1400 
1401 
1402     /**
1403      * This class is used to create the default buttons. This indirection is
1404      * used so that addButtonComponents can tell which Buttons were created
1405      * by us vs subclassers or from the JOptionPane itself.
1406      */
1407     private static class ButtonFactory {
1408         private String text;
1409         private int mnemonic;
1410         private Icon icon;
1411         private int minimumWidth = -1;
1412 
1413         ButtonFactory(String text, int mnemonic, Icon icon, int minimumWidth) {
1414             this.text = text;
1415             this.mnemonic = mnemonic;
1416             this.icon = icon;
1417             this.minimumWidth = minimumWidth;
1418         }
1419 
1420         JButton createButton() {
1421             JButton button;
1422 
1423             if (minimumWidth > 0) {
1424                 button = new ConstrainedButton(text, minimumWidth);
1425             } else {
1426                 button = new JButton(text);
1427             }
1428             if (icon != null) {
1429                 button.setIcon(icon);
1430             }
1431             if (mnemonic != 0) {
1432                 button.setMnemonic(mnemonic);
1433             }
1434             return button;
1435         }
1436 
1437         @SuppressWarnings("serial") // Superclass is not serializable across versions
1438         private static class ConstrainedButton extends JButton {
1439             int minimumWidth;
1440 
1441             ConstrainedButton(String text, int minimumWidth) {
1442                 super(text);
1443                 this.minimumWidth = minimumWidth;
1444             }
1445 
1446             public Dimension getMinimumSize() {
1447                 Dimension min = super.getMinimumSize();
1448                 min.width = Math.max(min.width, minimumWidth);
1449                 return min;
1450             }
1451 
1452             public Dimension getPreferredSize() {
1453                 Dimension pref = super.getPreferredSize();
1454                 pref.width = Math.max(pref.width, minimumWidth);
1455                 return pref;
1456             }
1457         }
1458     }
1459 }