Source for javax.swing.plaf.basic.BasicOptionPaneUI

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