1: /* BasicOptionPaneUI.java -- 2: Copyright (C) 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing.plaf.basic; 40: 41: import java.awt.BorderLayout; 42: import java.awt.Color; 43: import java.awt.Component; 44: import java.awt.Container; 45: import java.awt.Dimension; 46: import java.awt.Font; 47: import java.awt.Graphics; 48: import java.awt.GridBagConstraints; 49: import java.awt.GridBagLayout; 50: import java.awt.Insets; 51: import java.awt.LayoutManager; 52: import java.awt.Polygon; 53: import java.awt.Window; 54: import java.awt.event.ActionEvent; 55: import java.awt.event.ActionListener; 56: import java.beans.PropertyChangeEvent; 57: import java.beans.PropertyChangeListener; 58: import java.beans.PropertyVetoException; 59: 60: import javax.swing.AbstractAction; 61: import javax.swing.Action; 62: import javax.swing.ActionMap; 63: import javax.swing.BorderFactory; 64: import javax.swing.Box; 65: import javax.swing.BoxLayout; 66: import javax.swing.Icon; 67: import javax.swing.InputMap; 68: import javax.swing.JButton; 69: import javax.swing.JComboBox; 70: import javax.swing.JComponent; 71: import javax.swing.JDialog; 72: import javax.swing.JInternalFrame; 73: import javax.swing.JLabel; 74: import javax.swing.JList; 75: import javax.swing.JOptionPane; 76: import javax.swing.JPanel; 77: import javax.swing.JTextField; 78: import javax.swing.LookAndFeel; 79: import javax.swing.SwingUtilities; 80: import javax.swing.UIManager; 81: import javax.swing.border.Border; 82: import javax.swing.plaf.ActionMapUIResource; 83: import javax.swing.plaf.ComponentUI; 84: import javax.swing.plaf.OptionPaneUI; 85: 86: /** 87: * This class is the UI delegate for JOptionPane in the Basic Look and Feel. 88: */ 89: public class BasicOptionPaneUI extends OptionPaneUI 90: { 91: /** 92: * Implements the "close" keyboard action. 93: */ 94: static class OptionPaneCloseAction 95: extends AbstractAction 96: { 97: 98: public void actionPerformed(ActionEvent event) 99: { 100: JOptionPane op = (JOptionPane) event.getSource(); 101: op.setValue(new Integer(JOptionPane.CLOSED_OPTION)); 102: } 103: 104: } 105: 106: /** 107: * This is a helper class that listens to the buttons located at the bottom 108: * of the JOptionPane. 109: * 110: * @specnote Apparently this class was intended to be protected, 111: * but was made public by a compiler bug and is now 112: * public for compatibility. 113: */ 114: public class ButtonActionListener implements ActionListener 115: { 116: /** The index of the option this button represents. */ 117: protected int buttonIndex; 118: 119: /** 120: * Creates a new ButtonActionListener object with the given buttonIndex. 121: * 122: * @param buttonIndex The index of the option this button represents. 123: */ 124: public ButtonActionListener(int buttonIndex) 125: { 126: this.buttonIndex = buttonIndex; 127: } 128: 129: /** 130: * This method is called when one of the option buttons are pressed. 131: * 132: * @param e The ActionEvent. 133: */ 134: public void actionPerformed(ActionEvent e) 135: { 136: Object value = new Integer(JOptionPane.CLOSED_OPTION); 137: Object[] options = optionPane.getOptions(); 138: if (options != null) 139: value = new Integer(buttonIndex); 140: else 141: { 142: String text = ((JButton) e.getSource()).getText(); 143: if (text.equals(OK_STRING)) 144: value = new Integer(JOptionPane.OK_OPTION); 145: if (text.equals(CANCEL_STRING)) 146: value = new Integer(JOptionPane.CANCEL_OPTION); 147: if (text.equals(YES_STRING)) 148: value = new Integer(JOptionPane.YES_OPTION); 149: if (text.equals(NO_STRING)) 150: value = new Integer(JOptionPane.NO_OPTION); 151: } 152: optionPane.setValue(value); 153: resetInputValue(); 154: 155: Window owner = SwingUtilities.windowForComponent(optionPane); 156: 157: if (owner instanceof JDialog) 158: ((JDialog) owner).dispose(); 159: 160: //else we probably have some kind of internal frame. 161: JInternalFrame inf = (JInternalFrame) SwingUtilities.getAncestorOfClass( 162: JInternalFrame.class, optionPane); 163: if (inf != null) 164: { 165: try 166: { 167: inf.setClosed(true); 168: } 169: catch (PropertyVetoException pve) 170: { 171: // We do nothing if attempt has been vetoed. 172: } 173: } 174: } 175: } 176: 177: /** 178: * This helper layout manager is responsible for the layout of the button 179: * area. The button area is the panel that holds the buttons which 180: * represent the options. 181: * 182: * @specnote Apparently this class was intended to be protected, 183: * but was made public by a compiler bug and is now 184: * public for compatibility. 185: */ 186: public static class ButtonAreaLayout implements LayoutManager 187: { 188: /** Whether this layout will center the buttons. */ 189: protected boolean centersChildren = true; 190: 191: /** The space between the buttons. */ 192: protected int padding; 193: 194: /** Whether the buttons will share the same widths. */ 195: protected boolean syncAllWidths; 196: 197: /** The width of the widest button. */ 198: private transient int widthOfWidestButton; 199: 200: /** The height of the tallest button. */ 201: private transient int tallestButton; 202: 203: /** 204: * Creates a new ButtonAreaLayout object with the given sync widths 205: * property and padding. 206: * 207: * @param syncAllWidths Whether the buttons will share the same widths. 208: * @param padding The padding between the buttons. 209: */ 210: public ButtonAreaLayout(boolean syncAllWidths, int padding) 211: { 212: this.syncAllWidths = syncAllWidths; 213: this.padding = padding; 214: } 215: 216: /** 217: * This method is called when a component is added to the container. 218: * 219: * @param string The constraints string. 220: * @param comp The component added. 221: */ 222: public void addLayoutComponent(String string, Component comp) 223: { 224: // Do nothing. 225: } 226: 227: /** 228: * This method returns whether the children will be centered. 229: * 230: * @return Whether the children will be centered. 231: */ 232: public boolean getCentersChildren() 233: { 234: return centersChildren; 235: } 236: 237: /** 238: * This method returns the amount of space between components. 239: * 240: * @return The amount of space between components. 241: */ 242: public int getPadding() 243: { 244: return padding; 245: } 246: 247: /** 248: * This method returns whether all components will share widths (set to 249: * largest width). 250: * 251: * @return Whether all components will share widths. 252: */ 253: public boolean getSyncAllWidths() 254: { 255: return syncAllWidths; 256: } 257: 258: /** 259: * This method lays out the given container. 260: * 261: * @param container The container to lay out. 262: */ 263: public void layoutContainer(Container container) 264: { 265: Component[] buttonList = container.getComponents(); 266: int x = container.getInsets().left; 267: if (getCentersChildren()) 268: x += (int) ((double) (container.getSize().width) / 2 269: - (double) (buttonRowLength(container)) / 2); 270: for (int i = 0; i < buttonList.length; i++) 271: { 272: Dimension dims = buttonList[i].getPreferredSize(); 273: if (syncAllWidths) 274: { 275: buttonList[i].setBounds(x, 0, widthOfWidestButton, dims.height); 276: x += widthOfWidestButton + getPadding(); 277: } 278: else 279: { 280: buttonList[i].setBounds(x, 0, dims.width, dims.height); 281: x += dims.width + getPadding(); 282: } 283: } 284: } 285: 286: /** 287: * This method returns the width of the given container taking into 288: * consideration the padding and syncAllWidths. 289: * 290: * @param c The container to calculate width for. 291: * 292: * @return The width of the given container. 293: */ 294: private int buttonRowLength(Container c) 295: { 296: Component[] buttonList = c.getComponents(); 297: 298: int buttonLength = 0; 299: int widest = 0; 300: int tallest = 0; 301: 302: for (int i = 0; i < buttonList.length; i++) 303: { 304: Dimension dims = buttonList[i].getPreferredSize(); 305: buttonLength += dims.width + getPadding(); 306: widest = Math.max(widest, dims.width); 307: tallest = Math.max(tallest, dims.height); 308: } 309: 310: widthOfWidestButton = widest; 311: tallestButton = tallest; 312: 313: int width; 314: if (getSyncAllWidths()) 315: width = widest * buttonList.length 316: + getPadding() * (buttonList.length - 1); 317: else 318: width = buttonLength; 319: 320: Insets insets = c.getInsets(); 321: width += insets.left + insets.right; 322: 323: return width; 324: } 325: 326: /** 327: * This method returns the minimum layout size for the given container. 328: * 329: * @param c The container to measure. 330: * 331: * @return The minimum layout size. 332: */ 333: public Dimension minimumLayoutSize(Container c) 334: { 335: return preferredLayoutSize(c); 336: } 337: 338: /** 339: * This method returns the preferred size of the given container. 340: * 341: * @param c The container to measure. 342: * 343: * @return The preferred size. 344: */ 345: public Dimension preferredLayoutSize(Container c) 346: { 347: int w = buttonRowLength(c); 348: 349: return new Dimension(w, tallestButton); 350: } 351: 352: /** 353: * This method removes the given component from the layout manager's 354: * knowledge. 355: * 356: * @param c The component to remove. 357: */ 358: public void removeLayoutComponent(Component c) 359: { 360: // Do nothing. 361: } 362: 363: /** 364: * This method sets whether the children will be centered. 365: * 366: * @param newValue Whether the children will be centered. 367: */ 368: public void setCentersChildren(boolean newValue) 369: { 370: centersChildren = newValue; 371: } 372: 373: /** 374: * This method sets the amount of space between each component. 375: * 376: * @param newPadding The padding between components. 377: */ 378: public void setPadding(int newPadding) 379: { 380: padding = newPadding; 381: } 382: 383: /** 384: * This method sets whether the widths will be synced. 385: * 386: * @param newValue Whether the widths will be synced. 387: */ 388: public void setSyncAllWidths(boolean newValue) 389: { 390: syncAllWidths = newValue; 391: } 392: } 393: 394: /** 395: * This helper class handles property change events from the JOptionPane. 396: * 397: * @specnote Apparently this class was intended to be protected, 398: * but was made public by a compiler bug and is now 399: * public for compatibility. 400: */ 401: public class PropertyChangeHandler implements PropertyChangeListener 402: { 403: /** 404: * This method is called when one of the properties of the JOptionPane 405: * changes. 406: * 407: * @param e The PropertyChangeEvent. 408: */ 409: public void propertyChange(PropertyChangeEvent e) 410: { 411: String property = e.getPropertyName(); 412: if (property.equals(JOptionPane.ICON_PROPERTY) 413: || property.equals(JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY) 414: || property.equals(JOptionPane.INITIAL_VALUE_PROPERTY) 415: || property.equals(JOptionPane.MESSAGE_PROPERTY) 416: || property.equals(JOptionPane.MESSAGE_TYPE_PROPERTY) 417: || property.equals(JOptionPane.OPTION_TYPE_PROPERTY) 418: || property.equals(JOptionPane.OPTIONS_PROPERTY) 419: || property.equals(JOptionPane.WANTS_INPUT_PROPERTY)) 420: { 421: uninstallComponents(); 422: installComponents(); 423: optionPane.validate(); 424: } 425: } 426: } 427: 428: /** 429: * The minimum width for JOptionPanes. 430: */ 431: public static final int MinimumWidth = 262; 432: 433: /** 434: * The minimum height for JOptionPanes. 435: */ 436: public static final int MinimumHeight = 90; 437: 438: /** Whether the JOptionPane contains custom components. */ 439: protected boolean hasCustomComponents; 440: 441: // The initialFocusComponent seems to always be set to a button (even if 442: // I try to set initialSelectionValue). This is different from what the 443: // javadocs state (which should switch this reference to the input component 444: // if one is present since that is what's going to get focus). 445: 446: /** 447: * The button that will receive focus based on initialValue when no input 448: * component is present. If an input component is present, then the input 449: * component will receive focus instead. 450: */ 451: protected Component initialFocusComponent; 452: 453: /** The component that receives input when the JOptionPane needs it. */ 454: protected JComponent inputComponent; 455: 456: /** The minimum dimensions of the JOptionPane. */ 457: protected Dimension minimumSize; 458: 459: /** The propertyChangeListener for the JOptionPane. */ 460: protected PropertyChangeListener propertyChangeListener; 461: 462: /** The JOptionPane this UI delegate is used for. */ 463: protected JOptionPane optionPane; 464: 465: /** The size of the icons. */ 466: private static final int ICON_SIZE = 36; 467: 468: /** The string used to describe OK buttons. */ 469: private static final String OK_STRING = "OK"; 470: 471: /** The string used to describe Yes buttons. */ 472: private static final String YES_STRING = "Yes"; 473: 474: /** The string used to describe No buttons. */ 475: private static final String NO_STRING = "No"; 476: 477: /** The string used to describe Cancel buttons. */ 478: private static final String CANCEL_STRING = "Cancel"; 479: 480: /** The container for the message area. 481: * This is package-private to avoid an accessor method. */ 482: transient Container messageAreaContainer; 483: 484: /** The container for the buttons. 485: * This is package-private to avoid an accessor method. */ 486: transient Container buttonContainer; 487: 488: /** 489: * A helper class that implements Icon. This is used temporarily until 490: * ImageIcons are fixed. 491: */ 492: private static class MessageIcon implements Icon 493: { 494: /** 495: * This method returns the width of the icon. 496: * 497: * @return The width of the icon. 498: */ 499: public int getIconWidth() 500: { 501: return ICON_SIZE; 502: } 503: 504: /** 505: * This method returns the height of the icon. 506: * 507: * @return The height of the icon. 508: */ 509: public int getIconHeight() 510: { 511: return ICON_SIZE; 512: } 513: 514: /** 515: * This method paints the icon as a part of the given component using the 516: * given graphics and the given x and y position. 517: * 518: * @param c The component that owns this icon. 519: * @param g The Graphics object to paint with. 520: * @param x The x coordinate. 521: * @param y The y coordinate. 522: */ 523: public void paintIcon(Component c, Graphics g, int x, int y) 524: { 525: // Nothing to do here. 526: } 527: } 528: 529: /** The icon displayed for ERROR_MESSAGE. */ 530: private static MessageIcon errorIcon = new MessageIcon() 531: { 532: public void paintIcon(Component c, Graphics g, int x, int y) 533: { 534: Polygon oct = new Polygon(new int[] { 0, 0, 9, 27, 36, 36, 27, 9 }, 535: new int[] { 9, 27, 36, 36, 27, 9, 0, 0 }, 8); 536: g.translate(x, y); 537: 538: Color saved = g.getColor(); 539: g.setColor(Color.RED); 540: 541: g.fillPolygon(oct); 542: 543: g.setColor(Color.BLACK); 544: g.drawRect(13, 16, 10, 4); 545: 546: g.setColor(saved); 547: g.translate(-x, -y); 548: } 549: }; 550: 551: /** The icon displayed for INFORMATION_MESSAGE. */ 552: private static MessageIcon infoIcon = new MessageIcon() 553: { 554: public void paintIcon(Component c, Graphics g, int x, int y) 555: { 556: g.translate(x, y); 557: Color saved = g.getColor(); 558: 559: // Should be purple. 560: g.setColor(Color.RED); 561: 562: g.fillOval(0, 0, ICON_SIZE, ICON_SIZE); 563: 564: g.setColor(Color.BLACK); 565: g.drawOval(16, 6, 4, 4); 566: 567: Polygon bottomI = new Polygon(new int[] { 15, 15, 13, 13, 23, 23, 21, 21 }, 568: new int[] { 12, 28, 28, 30, 30, 28, 28, 12 }, 569: 8); 570: g.drawPolygon(bottomI); 571: 572: g.setColor(saved); 573: g.translate(-x, -y); 574: } 575: }; 576: 577: /** The icon displayed for WARNING_MESSAGE. */ 578: private static MessageIcon warningIcon = new MessageIcon() 579: { 580: public void paintIcon(Component c, Graphics g, int x, int y) 581: { 582: g.translate(x, y); 583: Color saved = g.getColor(); 584: g.setColor(Color.YELLOW); 585: 586: Polygon triangle = new Polygon(new int[] { 0, 18, 36 }, 587: new int[] { 36, 0, 36 }, 3); 588: g.fillPolygon(triangle); 589: 590: g.setColor(Color.BLACK); 591: 592: Polygon excl = new Polygon(new int[] { 15, 16, 20, 21 }, 593: new int[] { 8, 26, 26, 8 }, 4); 594: g.drawPolygon(excl); 595: g.drawOval(16, 30, 4, 4); 596: 597: g.setColor(saved); 598: g.translate(-x, -y); 599: } 600: }; 601: 602: /** The icon displayed for MESSAGE_ICON. */ 603: private static MessageIcon questionIcon = new MessageIcon() 604: { 605: public void paintIcon(Component c, Graphics g, int x, int y) 606: { 607: g.translate(x, y); 608: Color saved = g.getColor(); 609: g.setColor(Color.GREEN); 610: 611: g.fillRect(0, 0, ICON_SIZE, ICON_SIZE); 612: 613: g.setColor(Color.BLACK); 614: 615: g.drawOval(11, 2, 16, 16); 616: g.drawOval(14, 5, 10, 10); 617: 618: g.setColor(Color.GREEN); 619: g.fillRect(0, 10, ICON_SIZE, ICON_SIZE - 10); 620: 621: g.setColor(Color.BLACK); 622: 623: g.drawLine(11, 10, 14, 10); 624: 625: g.drawLine(24, 10, 17, 22); 626: g.drawLine(27, 10, 20, 22); 627: g.drawLine(17, 22, 20, 22); 628: 629: g.drawOval(17, 25, 3, 3); 630: 631: g.setColor(saved); 632: g.translate(-x, -y); 633: } 634: }; 635: 636: /** 637: * Creates a new BasicOptionPaneUI object. 638: */ 639: public BasicOptionPaneUI() 640: { 641: // Nothing to do here. 642: } 643: 644: /** 645: * This method is messaged to add the buttons to the given container. 646: * 647: * @param container The container to add components to. 648: * @param buttons The buttons to add. (If it is an instance of component, 649: * the Object is added directly. If it is an instance of Icon, it is 650: * packed into a label and added. For all other cases, the string 651: * representation of the Object is retreived and packed into a 652: * label.) 653: * @param initialIndex The index of the component that is the initialValue. 654: */ 655: protected void addButtonComponents(Container container, Object[] buttons, 656: int initialIndex) 657: { 658: if (buttons == null) 659: return; 660: for (int i = 0; i < buttons.length; i++) 661: { 662: if (buttons[i] != null) 663: { 664: Component toAdd; 665: if (buttons[i] instanceof Component) 666: toAdd = (Component) buttons[i]; 667: else 668: { 669: if (buttons[i] instanceof Icon) 670: toAdd = new JButton((Icon) buttons[i]); 671: else 672: toAdd = new JButton(buttons[i].toString()); 673: hasCustomComponents = true; 674: } 675: if (toAdd instanceof JButton) 676: ((JButton) toAdd).addActionListener(createButtonActionListener(i)); 677: if (i == initialIndex) 678: initialFocusComponent = toAdd; 679: container.add(toAdd); 680: } 681: } 682: selectInitialValue(optionPane); 683: } 684: 685: /** 686: * This method adds the appropriate icon the given container. 687: * 688: * @param top The container to add an icon to. 689: */ 690: protected void addIcon(Container top) 691: { 692: JLabel iconLabel = null; 693: Icon icon = getIcon(); 694: if (icon != null) 695: { 696: iconLabel = new JLabel(icon); 697: configureLabel(iconLabel); 698: top.add(iconLabel, BorderLayout.WEST); 699: } 700: } 701: 702: /** 703: * A helper method that returns an instance of GridBagConstraints to be used 704: * for creating the message area. 705: * 706: * @return An instance of GridBagConstraints. 707: */ 708: private static GridBagConstraints createConstraints() 709: { 710: GridBagConstraints constraints = new GridBagConstraints(); 711: constraints.gridx = GridBagConstraints.REMAINDER; 712: constraints.gridy = GridBagConstraints.REMAINDER; 713: constraints.gridwidth = 0; 714: constraints.anchor = GridBagConstraints.LINE_START; 715: constraints.fill = GridBagConstraints.NONE; 716: constraints.insets = new Insets(0, 0, 3, 0); 717: 718: return constraints; 719: } 720: 721: /** 722: * This method creates the proper object (if necessary) to represent msg. 723: * (If msg is an instance of Component, it will add it directly. If it is 724: * an icon, then it will pack it in a label and add it. Otherwise, it gets 725: * treated as a string. If the string is longer than maxll, a box is 726: * created and the burstStringInto is called with the box as the container. 727: * The box is then added to the given container. Otherwise, the string is 728: * packed in a label and placed in the given container.) This method is 729: * also used for adding the inputComponent to the container. 730: * 731: * @param container The container to add to. 732: * @param cons The constraints when adding. 733: * @param msg The message to add. 734: * @param maxll The max line length. 735: * @param internallyCreated Whether the msg is internally created. 736: */ 737: protected void addMessageComponents(Container container, 738: GridBagConstraints cons, Object msg, 739: int maxll, boolean internallyCreated) 740: { 741: if (msg == null) 742: return; 743: hasCustomComponents = internallyCreated; 744: if (msg instanceof Object[]) 745: { 746: Object[] arr = (Object[]) msg; 747: for (int i = 0; i < arr.length; i++) 748: addMessageComponents(container, cons, arr[i], maxll, 749: internallyCreated); 750: return; 751: } 752: else if (msg instanceof Component) 753: { 754: container.add((Component) msg, cons); 755: cons.gridy++; 756: } 757: else if (msg instanceof Icon) 758: { 759: JLabel label = new JLabel((Icon) msg); 760: configureLabel(label); 761: container.add(label, cons); 762: cons.gridy++; 763: } 764: else 765: { 766: // Undocumented behaviour. 767: // if msg.toString().length greater than maxll 768: // it will create a box and burst the string. 769: // otherwise, it will just create a label and re-call 770: // this method with the label o.O 771: if (msg.toString().length() > maxll || msg.toString().contains("\n")) 772: { 773: Box tmp = new Box(BoxLayout.Y_AXIS); 774: burstStringInto(tmp, msg.toString(), maxll); 775: addMessageComponents(container, cons, tmp, maxll, true); 776: } 777: else 778: { 779: JLabel label = new JLabel(msg.toString()); 780: configureLabel(label); 781: addMessageComponents(container, cons, label, maxll, true); 782: } 783: } 784: } 785: 786: /** 787: * This method creates instances of d (recursively if necessary based on 788: * maxll) and adds to c. 789: * 790: * @param c The container to add to. 791: * @param d The string to burst. 792: * @param maxll The max line length. 793: */ 794: protected void burstStringInto(Container c, String d, int maxll) 795: { 796: if (d == null || c == null) 797: return; 798: 799: int newlineIndex = d.indexOf('\n'); 800: String line; 801: String remainder; 802: if (newlineIndex >= 0 && newlineIndex < maxll) 803: { 804: line = d.substring(0, newlineIndex); 805: remainder = d.substring(newlineIndex + 1); 806: } 807: else 808: { 809: line = d.substring(0, maxll); 810: remainder = d.substring(maxll); 811: } 812: JLabel label = new JLabel(line); 813: configureLabel(label); 814: c.add(label); 815: 816: // If there is nothing left to burst, then we can stop. 817: if (remainder.length() == 0) 818: return; 819: 820: // Recursively call ourselves to burst the remainder of the string, 821: if (remainder.length() > maxll || remainder.contains("\n")) 822: burstStringInto(c, remainder, maxll); 823: else 824: { 825: // Add the remainder to the container and be done. 826: JLabel l = new JLabel(remainder); 827: configureLabel(l); 828: c.add(l); 829: } 830: } 831: 832: /** 833: * This method returns true if the given JOptionPane contains custom 834: * components. 835: * 836: * @param op The JOptionPane to check. 837: * 838: * @return True if the JOptionPane contains custom components. 839: */ 840: public boolean containsCustomComponents(JOptionPane op) 841: { 842: return hasCustomComponents; 843: } 844: 845: /** 846: * This method creates a button action listener for the given button index. 847: * 848: * @param buttonIndex The index of the button in components. 849: * 850: * @return A new ButtonActionListener. 851: */ 852: protected ActionListener createButtonActionListener(int buttonIndex) 853: { 854: return new ButtonActionListener(buttonIndex); 855: } 856: 857: /** 858: * This method creates the button area. 859: * 860: * @return A new Button Area. 861: */ 862: protected Container createButtonArea() 863: { 864: JPanel buttonPanel = new JPanel(); 865: Border b = UIManager.getBorder("OptionPane.buttonAreaBorder"); 866: if (b != null) 867: buttonPanel.setBorder(b); 868: 869: buttonPanel.setLayout(createLayoutManager()); 870: addButtonComponents(buttonPanel, getButtons(), getInitialValueIndex()); 871: 872: return buttonPanel; 873: } 874: 875: /** 876: * This method creates a new LayoutManager for the button area. 877: * 878: * @return A new LayoutManager for the button area. 879: */ 880: protected LayoutManager createLayoutManager() 881: { 882: return new ButtonAreaLayout(getSizeButtonsToSameWidth(), 6); 883: } 884: 885: /** 886: * This method creates the message area. 887: * 888: * @return A new message area. 889: */ 890: protected Container createMessageArea() 891: { 892: JPanel messageArea = new JPanel(); 893: Border messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder"); 894: if (messageBorder != null) 895: messageArea.setBorder(messageBorder); 896: 897: messageArea.setLayout(new BorderLayout()); 898: addIcon(messageArea); 899: 900: JPanel rightSide = new JPanel(); 901: rightSide.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 902: rightSide.setLayout(new GridBagLayout()); 903: GridBagConstraints con = createConstraints(); 904: 905: addMessageComponents(rightSide, con, getMessage(), 906: getMaxCharactersPerLineCount(), false); 907: 908: if (optionPane.getWantsInput()) 909: { 910: Object[] selection = optionPane.getSelectionValues(); 911: 912: if (selection == null) 913: inputComponent = new JTextField(15); 914: else if (selection.length < 20) 915: inputComponent = new JComboBox(selection); 916: else 917: inputComponent = new JList(selection); 918: if (inputComponent != null) 919: { 920: addMessageComponents(rightSide, con, inputComponent, 921: getMaxCharactersPerLineCount(), false); 922: resetSelectedValue(); 923: selectInitialValue(optionPane); 924: } 925: } 926: 927: messageArea.add(rightSide, BorderLayout.CENTER); 928: 929: return messageArea; 930: } 931: 932: /** 933: * This method creates a new PropertyChangeListener for listening to the 934: * JOptionPane. 935: * 936: * @return A new PropertyChangeListener. 937: */ 938: protected PropertyChangeListener createPropertyChangeListener() 939: { 940: return new PropertyChangeHandler(); 941: } 942: 943: /** 944: * This method creates a Container that will separate the message and button 945: * areas. 946: * 947: * @return A Container that will separate the message and button areas. 948: */ 949: protected Container createSeparator() 950: { 951: // The reference implementation returns null here. When overriding 952: // to return something non-null, the component gets added between 953: // the message area and the button area. See installComponents(). 954: return null; 955: } 956: 957: /** 958: * This method creates a new BasicOptionPaneUI for the given component. 959: * 960: * @param x The component to create a UI for. 961: * 962: * @return A new BasicOptionPaneUI. 963: */ 964: public static ComponentUI createUI(JComponent x) 965: { 966: return new BasicOptionPaneUI(); 967: } 968: 969: /** 970: * This method returns the buttons for the JOptionPane. If no options are 971: * set, a set of options will be created based upon the optionType. 972: * 973: * @return The buttons that will be added. 974: */ 975: protected Object[] getButtons() 976: { 977: if (optionPane.getOptions() != null) 978: return optionPane.getOptions(); 979: switch (optionPane.getOptionType()) 980: { 981: case JOptionPane.YES_NO_OPTION: 982: return new Object[] { YES_STRING, NO_STRING }; 983: case JOptionPane.YES_NO_CANCEL_OPTION: 984: return new Object[] { YES_STRING, NO_STRING, CANCEL_STRING }; 985: case JOptionPane.OK_CANCEL_OPTION: 986: return new Object[] { OK_STRING, CANCEL_STRING }; 987: case JOptionPane.DEFAULT_OPTION: 988: return (optionPane.getWantsInput()) ? 989: new Object[] { OK_STRING, CANCEL_STRING } : 990: (optionPane.getMessageType() == JOptionPane.QUESTION_MESSAGE) ? 991: new Object[] { YES_STRING, NO_STRING, CANCEL_STRING } : 992: // ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE 993: new Object[] { OK_STRING }; 994: } 995: return null; 996: } 997: 998: /** 999: * This method will return the icon the user has set or the icon that will 1000: * be used based on message type. 1001: * 1002: * @return The icon to use in the JOptionPane. 1003: */ 1004: protected Icon getIcon() 1005: { 1006: if (optionPane.getIcon() != null) 1007: return optionPane.getIcon(); 1008: else 1009: return getIconForType(optionPane.getMessageType()); 1010: } 1011: 1012: /** 1013: * This method returns the icon for the given messageType. 1014: * 1015: * @param messageType The type of message. 1016: * 1017: * @return The icon for the given messageType. 1018: */ 1019: protected Icon getIconForType(int messageType) 1020: { 1021: Icon tmp = null; 1022: switch (messageType) 1023: { 1024: case JOptionPane.ERROR_MESSAGE: 1025: tmp = errorIcon; 1026: break; 1027: case JOptionPane.INFORMATION_MESSAGE: 1028: tmp = infoIcon; 1029: break; 1030: case JOptionPane.WARNING_MESSAGE: 1031: tmp = warningIcon; 1032: break; 1033: case JOptionPane.QUESTION_MESSAGE: 1034: tmp = questionIcon; 1035: break; 1036: } 1037: return tmp; 1038: // FIXME: Don't cast till the default icons are in. 1039: // return new IconUIResource(tmp); 1040: } 1041: 1042: /** 1043: * This method returns the index of the initialValue in the options array. 1044: * 1045: * @return The index of the initalValue. 1046: */ 1047: protected int getInitialValueIndex() 1048: { 1049: Object[] buttons = getButtons(); 1050: 1051: if (buttons == null) 1052: return -1; 1053: 1054: Object select = optionPane.getInitialValue(); 1055: 1056: for (int i = 0; i < buttons.length; i++) 1057: { 1058: if (select == buttons[i]) 1059: return i; 1060: } 1061: return 0; 1062: } 1063: 1064: /** 1065: * This method returns the maximum number of characters that should be 1066: * placed on a line. 1067: * 1068: * @return The maximum number of characteres that should be placed on a 1069: * line. 1070: */ 1071: protected int getMaxCharactersPerLineCount() 1072: { 1073: return optionPane.getMaxCharactersPerLineCount(); 1074: } 1075: 1076: /** 1077: * This method returns the maximum size. 1078: * 1079: * @param c The JComponent to measure. 1080: * 1081: * @return The maximum size. 1082: */ 1083: public Dimension getMaximumSize(JComponent c) 1084: { 1085: return getPreferredSize(c); 1086: } 1087: 1088: /** 1089: * This method returns the message of the JOptionPane. 1090: * 1091: * @return The message. 1092: */ 1093: protected Object getMessage() 1094: { 1095: return optionPane.getMessage(); 1096: } 1097: 1098: /** 1099: * This method returns the minimum size of the JOptionPane. 1100: * 1101: * @return The minimum size. 1102: */ 1103: public Dimension getMinimumOptionPaneSize() 1104: { 1105: return minimumSize; 1106: } 1107: 1108: /** 1109: * This method returns the minimum size. 1110: * 1111: * @param c The JComponent to measure. 1112: * 1113: * @return The minimum size. 1114: */ 1115: public Dimension getMinimumSize(JComponent c) 1116: { 1117: return getPreferredSize(c); 1118: } 1119: 1120: /** 1121: * This method returns the preferred size of the JOptionPane. The preferred 1122: * size is the maximum of the size desired by the layout and the minimum 1123: * size. 1124: * 1125: * @param c The JComponent to measure. 1126: * 1127: * @return The preferred size. 1128: */ 1129: public Dimension getPreferredSize(JComponent c) 1130: { 1131: Dimension d = optionPane.getLayout().preferredLayoutSize(optionPane); 1132: Dimension d2 = getMinimumOptionPaneSize(); 1133: 1134: int w = Math.max(d.width, d2.width); 1135: int h = Math.max(d.height, d2.height); 1136: return new Dimension(w, h); 1137: } 1138: 1139: /** 1140: * This method returns whether all buttons should have the same width. 1141: * 1142: * @return Whether all buttons should have the same width. 1143: */ 1144: protected boolean getSizeButtonsToSameWidth() 1145: { 1146: return true; 1147: } 1148: 1149: /** 1150: * This method installs components for the JOptionPane. 1151: */ 1152: protected void installComponents() 1153: { 1154: // First thing is the message area. 1155: optionPane.add(createMessageArea()); 1156: 1157: // Add separator when createSeparator() is overridden to return 1158: // something other than null. 1159: Container sep = createSeparator(); 1160: if (sep != null) 1161: optionPane.add(sep); 1162: 1163: // Last thing is the button area. 1164: optionPane.add(createButtonArea()); 1165: } 1166: 1167: /** 1168: * This method installs defaults for the JOptionPane. 1169: */ 1170: protected void installDefaults() 1171: { 1172: LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background", 1173: "OptionPane.foreground", 1174: "OptionPane.font"); 1175: LookAndFeel.installBorder(optionPane, "OptionPane.border"); 1176: optionPane.setOpaque(true); 1177: 1178: minimumSize = UIManager.getDimension("OptionPane.minimumSize"); 1179: 1180: // FIXME: Image icons don't seem to work properly right now. 1181: // Once they do, replace the synthetic icons with these ones. 1182: 1183: /* 1184: warningIcon = (IconUIResource) defaults.getIcon("OptionPane.warningIcon"); 1185: infoIcon = (IconUIResource) defaults.getIcon("OptionPane.informationIcon"); 1186: errorIcon = (IconUIResource) defaults.getIcon("OptionPane.errorIcon"); 1187: questionIcon = (IconUIResource) defaults.getIcon("OptionPane.questionIcon"); 1188: */ 1189: } 1190: 1191: /** 1192: * This method installs keyboard actions for the JOptionpane. 1193: */ 1194: protected void installKeyboardActions() 1195: { 1196: // Install the input map. 1197: Object[] bindings = 1198: (Object[]) SharedUIDefaults.get("OptionPane.windowBindings"); 1199: InputMap inputMap = LookAndFeel.makeComponentInputMap(optionPane, 1200: bindings); 1201: SwingUtilities.replaceUIInputMap(optionPane, 1202: JComponent.WHEN_IN_FOCUSED_WINDOW, 1203: inputMap); 1204: 1205: // FIXME: The JDK uses a LazyActionMap for parentActionMap 1206: SwingUtilities.replaceUIActionMap(optionPane, getActionMap()); 1207: } 1208: 1209: /** 1210: * Fetches the action map from the UI defaults, or create a new one 1211: * if the action map hasn't been initialized. 1212: * 1213: * @return the action map 1214: */ 1215: private ActionMap getActionMap() 1216: { 1217: ActionMap am = (ActionMap) UIManager.get("OptionPane.actionMap"); 1218: if (am == null) 1219: { 1220: am = createDefaultActions(); 1221: UIManager.getLookAndFeelDefaults().put("OptionPane.actionMap", am); 1222: } 1223: return am; 1224: } 1225: 1226: private ActionMap createDefaultActions() 1227: { 1228: ActionMapUIResource am = new ActionMapUIResource(); 1229: Action action = new OptionPaneCloseAction(); 1230: 1231: am.put("close", action); 1232: return am; 1233: } 1234: 1235: /** 1236: * This method installs listeners for the JOptionPane. 1237: */ 1238: protected void installListeners() 1239: { 1240: propertyChangeListener = createPropertyChangeListener(); 1241: 1242: optionPane.addPropertyChangeListener(propertyChangeListener); 1243: } 1244: 1245: /** 1246: * This method installs the UI for the JOptionPane. 1247: * 1248: * @param c The JComponent to install the UI for. 1249: */ 1250: public void installUI(JComponent c) 1251: { 1252: if (c instanceof JOptionPane) 1253: { 1254: optionPane = (JOptionPane) c; 1255: 1256: installDefaults(); 1257: installComponents(); 1258: installListeners(); 1259: installKeyboardActions(); 1260: } 1261: } 1262: 1263: /** 1264: * Changes the inputValue property in the JOptionPane based on the current 1265: * value of the inputComponent. 1266: */ 1267: protected void resetInputValue() 1268: { 1269: if (optionPane.getWantsInput() && inputComponent != null) 1270: { 1271: Object output = null; 1272: if (inputComponent instanceof JTextField) 1273: output = ((JTextField) inputComponent).getText(); 1274: else if (inputComponent instanceof JComboBox) 1275: output = ((JComboBox) inputComponent).getSelectedItem(); 1276: else if (inputComponent instanceof JList) 1277: output = ((JList) inputComponent).getSelectedValue(); 1278: 1279: if (output != null) 1280: optionPane.setInputValue(output); 1281: } 1282: } 1283: 1284: /** 1285: * This method requests focus to the inputComponent (if one is present) and 1286: * the initialFocusComponent otherwise. 1287: * 1288: * @param op The JOptionPane. 1289: */ 1290: public void selectInitialValue(JOptionPane op) 1291: { 1292: if (inputComponent != null) 1293: { 1294: inputComponent.requestFocus(); 1295: return; 1296: } 1297: if (initialFocusComponent != null) 1298: initialFocusComponent.requestFocus(); 1299: } 1300: 1301: /** 1302: * This method resets the value in the inputComponent to the 1303: * initialSelectionValue property. 1304: * This is package-private to avoid an accessor method. 1305: */ 1306: void resetSelectedValue() 1307: { 1308: if (inputComponent != null) 1309: { 1310: Object init = optionPane.getInitialSelectionValue(); 1311: if (init == null) 1312: return; 1313: if (inputComponent instanceof JTextField) 1314: ((JTextField) inputComponent).setText((String) init); 1315: else if (inputComponent instanceof JComboBox) 1316: ((JComboBox) inputComponent).setSelectedItem(init); 1317: else if (inputComponent instanceof JList) 1318: { 1319: // ((JList) inputComponent).setSelectedValue(init, true); 1320: } 1321: } 1322: } 1323: 1324: /** 1325: * This method uninstalls all the components in the JOptionPane. 1326: */ 1327: protected void uninstallComponents() 1328: { 1329: optionPane.removeAll(); 1330: buttonContainer = null; 1331: messageAreaContainer = null; 1332: } 1333: 1334: /** 1335: * This method uninstalls the defaults for the JOptionPane. 1336: */ 1337: protected void uninstallDefaults() 1338: { 1339: optionPane.setFont(null); 1340: optionPane.setForeground(null); 1341: optionPane.setBackground(null); 1342: 1343: minimumSize = null; 1344: 1345: // FIXME: ImageIcons don't seem to work properly 1346: 1347: /* 1348: warningIcon = null; 1349: errorIcon = null; 1350: questionIcon = null; 1351: infoIcon = null; 1352: */ 1353: } 1354: 1355: /** 1356: * This method uninstalls keyboard actions for the JOptionPane. 1357: */ 1358: protected void uninstallKeyboardActions() 1359: { 1360: SwingUtilities.replaceUIInputMap(optionPane, JComponent. 1361: WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); 1362: SwingUtilities.replaceUIActionMap(optionPane, null); 1363: } 1364: 1365: /** 1366: * This method uninstalls listeners for the JOptionPane. 1367: */ 1368: protected void uninstallListeners() 1369: { 1370: optionPane.removePropertyChangeListener(propertyChangeListener); 1371: propertyChangeListener = null; 1372: } 1373: 1374: /** 1375: * This method uninstalls the UI for the given JComponent. 1376: * 1377: * @param c The JComponent to uninstall for. 1378: */ 1379: public void uninstallUI(JComponent c) 1380: { 1381: uninstallKeyboardActions(); 1382: uninstallListeners(); 1383: uninstallComponents(); 1384: uninstallDefaults(); 1385: 1386: optionPane = null; 1387: } 1388: 1389: /** 1390: * Applies the proper UI configuration to labels that are added to 1391: * the OptionPane. 1392: * 1393: * @param l the label to configure 1394: */ 1395: private void configureLabel(JLabel l) 1396: { 1397: Color c = UIManager.getColor("OptionPane.messageForeground"); 1398: if (c != null) 1399: l.setForeground(c); 1400: Font f = UIManager.getFont("OptionPane.messageFont"); 1401: if (f != null) 1402: l.setFont(f); 1403: } 1404: }