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&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&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&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 "protected" 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 "protected" 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 "protected" 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 }