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