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