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.LookAndFeel;
  74: import javax.swing.SwingUtilities;
  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:               // We do nothing if attempt has been vetoed.
 151:             }
 152:         }
 153:     }
 154:   }
 155: 
 156:   /**
 157:    * This helper layout manager is responsible for the layout of the button
 158:    * area. The button area is the panel that holds the buttons which
 159:    * represent the options.
 160:    *
 161:    * @specnote Apparently this class was intended to be protected,
 162:    *           but was made public by a compiler bug and is now
 163:    *           public for compatibility.
 164:    */
 165:   public static 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 (syncAllWidths)
 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:     }
 351: 
 352:     /**
 353:      * This method sets the amount of space between each component.
 354:      *
 355:      * @param newPadding The padding between components.
 356:      */
 357:     public void setPadding(int newPadding)
 358:     {
 359:       padding = newPadding;
 360:     }
 361: 
 362:     /**
 363:      * This method sets whether the widths will be synced.
 364:      *
 365:      * @param newValue Whether the widths will be synced.
 366:      */
 367:     public void setSyncAllWidths(boolean newValue)
 368:     {
 369:       syncAllWidths = newValue;
 370:     }
 371:   }
 372: 
 373:   /**
 374:    * This helper class handles property change events from the JOptionPane.
 375:    *
 376:    * @specnote Apparently this class was intended to be protected,
 377:    *           but was made public by a compiler bug and is now
 378:    *           public for compatibility.
 379:    */
 380:   public class PropertyChangeHandler implements PropertyChangeListener
 381:   {
 382:     /**
 383:      * This method is called when one of the properties of the JOptionPane
 384:      * changes.
 385:      *
 386:      * @param e The PropertyChangeEvent.
 387:      */
 388:     public void propertyChange(PropertyChangeEvent e)
 389:     {
 390:       if (e.getPropertyName().equals(JOptionPane.ICON_PROPERTY)
 391:           || e.getPropertyName().equals(JOptionPane.MESSAGE_TYPE_PROPERTY))
 392:     addIcon(messageAreaContainer);
 393:       else if (e.getPropertyName().equals(JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY))
 394:     resetSelectedValue();
 395:       else if (e.getPropertyName().equals(JOptionPane.INITIAL_VALUE_PROPERTY)
 396:                || e.getPropertyName().equals(JOptionPane.OPTIONS_PROPERTY)
 397:                || e.getPropertyName().equals(JOptionPane.OPTION_TYPE_PROPERTY))
 398:         {
 399:       Container newButtons = createButtonArea();
 400:       optionPane.remove(buttonContainer);
 401:       optionPane.add(newButtons);
 402:       buttonContainer = newButtons;
 403:         }
 404: 
 405:       else if (e.getPropertyName().equals(JOptionPane.MESSAGE_PROPERTY)
 406:                || e.getPropertyName().equals(JOptionPane.WANTS_INPUT_PROPERTY)
 407:                || e.getPropertyName().equals(JOptionPane.SELECTION_VALUES_PROPERTY))
 408:         {
 409:           optionPane.remove(messageAreaContainer);
 410:           messageAreaContainer = createMessageArea();
 411:           optionPane.add(messageAreaContainer);
 412:           Container newButtons = createButtonArea();
 413:           optionPane.remove(buttonContainer);
 414:           optionPane.add(newButtons);
 415:           buttonContainer = newButtons;
 416:           optionPane.add(buttonContainer);
 417:         }
 418:       optionPane.invalidate();
 419:       optionPane.repaint();
 420:     }
 421:   }
 422: 
 423:   /**
 424:    * The minimum width for JOptionPanes.
 425:    */
 426:   public static final int MinimumWidth = 262;
 427: 
 428:   /**
 429:    * The minimum height for JOptionPanes.
 430:    */
 431:   public static final int MinimumHeight = 90;
 432: 
 433:   /** Whether the JOptionPane contains custom components. */
 434:   protected boolean hasCustomComponents = false;
 435: 
 436:   // The initialFocusComponent seems to always be set to a button (even if 
 437:   // I try to set initialSelectionValue). This is different from what the 
 438:   // javadocs state (which should switch this reference to the input component 
 439:   // if one is present since that is what's going to get focus). 
 440: 
 441:   /**
 442:    * The button that will receive focus based on initialValue when no input
 443:    * component is present. If an input component is present, then the input
 444:    * component will receive focus instead.
 445:    */
 446:   protected Component initialFocusComponent;
 447: 
 448:   /** The component that receives input when the JOptionPane needs it. */
 449:   protected JComponent inputComponent;
 450: 
 451:   /** The minimum dimensions of the JOptionPane. */
 452:   protected Dimension minimumSize;
 453: 
 454:   /** The propertyChangeListener for the JOptionPane. */
 455:   protected PropertyChangeListener propertyChangeListener;
 456: 
 457:   /** The JOptionPane this UI delegate is used for. */
 458:   protected JOptionPane optionPane;
 459: 
 460:   /** The size of the icons. */
 461:   // FIXME: wrong name for a constant.
 462:   private static final int iconSize = 36;
 463: 
 464:   /** The foreground color for the message area. */
 465:   private transient Color messageForeground;
 466: 
 467:   /** The border around the message area. */
 468:   private transient Border messageBorder;
 469: 
 470:   /** The border around the button area. */
 471:   private transient Border buttonBorder;
 472: 
 473:   /** The string used to describe OK buttons. */
 474:   private static final String OK_STRING = "OK";
 475: 
 476:   /** The string used to describe Yes buttons. */
 477:   private static final String YES_STRING = "Yes";
 478: 
 479:   /** The string used to describe No buttons. */
 480:   private static final String NO_STRING = "No";
 481: 
 482:   /** The string used to describe Cancel buttons. */
 483:   private static final String CANCEL_STRING = "Cancel";
 484: 
 485:   /** The container for the message area.
 486:    * This is package-private to avoid an accessor method. */
 487:   transient Container messageAreaContainer;
 488: 
 489:   /** The container for the buttons.
 490:    * This is package-private to avoid an accessor method.  */
 491:   transient Container buttonContainer;
 492: 
 493:   /**
 494:    * A helper class that implements Icon. This is used temporarily until
 495:    * ImageIcons are fixed.
 496:    */
 497:   private static class MessageIcon implements Icon
 498:   {
 499:     /**
 500:      * This method returns the width of the icon.
 501:      *
 502:      * @return The width of the icon.
 503:      */
 504:     public int getIconWidth()
 505:     {
 506:       return iconSize;
 507:     }
 508: 
 509:     /**
 510:      * This method returns the height of the icon.
 511:      *
 512:      * @return The height of the icon.
 513:      */
 514:     public int getIconHeight()
 515:     {
 516:       return iconSize;
 517:     }
 518: 
 519:     /**
 520:      * This method paints the icon as a part of the given component using the
 521:      * given graphics and the given x and y position.
 522:      *
 523:      * @param c The component that owns this icon.
 524:      * @param g The Graphics object to paint with.
 525:      * @param x The x coordinate.
 526:      * @param y The y coordinate.
 527:      */
 528:     public void paintIcon(Component c, Graphics g, int x, int y)
 529:     {
 530:       // Nothing to do here.
 531:     }
 532:   }
 533: 
 534:   /** The icon displayed for ERROR_MESSAGE. */
 535:   private static MessageIcon errorIcon = new MessageIcon()
 536:     {
 537:       public void paintIcon(Component c, Graphics g, int x, int y)
 538:       {
 539:     Polygon oct = new Polygon(new int[] { 0, 0, 9, 27, 36, 36, 27, 9 },
 540:                               new int[] { 9, 27, 36, 36, 27, 9, 0, 0 }, 8);
 541:     g.translate(x, y);
 542: 
 543:     Color saved = g.getColor();
 544:     g.setColor(Color.RED);
 545: 
 546:     g.fillPolygon(oct);
 547: 
 548:     g.setColor(Color.BLACK);
 549:     g.drawRect(13, 16, 10, 4);
 550: 
 551:     g.setColor(saved);
 552:     g.translate(-x, -y);
 553:       }
 554:     };
 555: 
 556:   /** The icon displayed for INFORMATION_MESSAGE. */
 557:   private static MessageIcon infoIcon = new MessageIcon()
 558:     {
 559:       public void paintIcon(Component c, Graphics g, int x, int y)
 560:       {
 561:     g.translate(x, y);
 562:     Color saved = g.getColor();
 563: 
 564:     // Should be purple.
 565:     g.setColor(Color.RED);
 566: 
 567:     g.fillOval(0, 0, iconSize, iconSize);
 568: 
 569:     g.setColor(Color.BLACK);
 570:     g.drawOval(16, 6, 4, 4);
 571: 
 572:     Polygon bottomI = new Polygon(new int[] { 15, 15, 13, 13, 23, 23, 21, 21 },
 573:                                   new int[] { 12, 28, 28, 30, 30, 28, 28, 12 },
 574:                                   8);
 575:     g.drawPolygon(bottomI);
 576: 
 577:     g.setColor(saved);
 578:     g.translate(-x, -y);
 579:       }
 580:     };
 581: 
 582:   /** The icon displayed for WARNING_MESSAGE. */
 583:   private static MessageIcon warningIcon = new MessageIcon()
 584:     {
 585:       public void paintIcon(Component c, Graphics g, int x, int y)
 586:       {
 587:     g.translate(x, y);
 588:     Color saved = g.getColor();
 589:     g.setColor(Color.YELLOW);
 590: 
 591:     Polygon triangle = new Polygon(new int[] { 0, 18, 36 },
 592:                                    new int[] { 36, 0, 36 }, 3);
 593:     g.fillPolygon(triangle);
 594: 
 595:     g.setColor(Color.BLACK);
 596: 
 597:     Polygon excl = new Polygon(new int[] { 15, 16, 20, 21 },
 598:                                new int[] { 8, 26, 26, 8 }, 4);
 599:     g.drawPolygon(excl);
 600:     g.drawOval(16, 30, 4, 4);
 601: 
 602:     g.setColor(saved);
 603:     g.translate(-x, -y);
 604:       }
 605:     };
 606: 
 607:   /** The icon displayed for MESSAGE_ICON. */
 608:   private static MessageIcon questionIcon = new MessageIcon()
 609:     {
 610:       public void paintIcon(Component c, Graphics g, int x, int y)
 611:       {
 612:     g.translate(x, y);
 613:     Color saved = g.getColor();
 614:     g.setColor(Color.GREEN);
 615: 
 616:     g.fillRect(0, 0, iconSize, iconSize);
 617: 
 618:     g.setColor(Color.BLACK);
 619: 
 620:     g.drawOval(11, 2, 16, 16);
 621:     g.drawOval(14, 5, 10, 10);
 622: 
 623:     g.setColor(Color.GREEN);
 624:     g.fillRect(0, 10, iconSize, iconSize - 10);
 625: 
 626:     g.setColor(Color.BLACK);
 627: 
 628:     g.drawLine(11, 10, 14, 10);
 629: 
 630:     g.drawLine(24, 10, 17, 22);
 631:     g.drawLine(27, 10, 20, 22);
 632:     g.drawLine(17, 22, 20, 22);
 633: 
 634:     g.drawOval(17, 25, 3, 3);
 635: 
 636:     g.setColor(saved);
 637:     g.translate(-x, -y);
 638:       }
 639:     };
 640: 
 641:   // FIXME: Uncomment when the ImageIcons are fixed.
 642: 
 643:   /*  IconUIResource warningIcon, questionIcon, infoIcon, errorIcon;*/
 644: 
 645:   /**
 646:    * Creates a new BasicOptionPaneUI object.
 647:    */
 648:   public BasicOptionPaneUI()
 649:   {
 650:     // Nothing to do here.
 651:   }
 652: 
 653:   /**
 654:    * This method is messaged to add the buttons to the given container.
 655:    *
 656:    * @param container The container to add components to.
 657:    * @param buttons The buttons to add. (If it is an instance of component,
 658:    *        the Object is added directly. If it is an instance of Icon, it is
 659:    *        packed into a label and added. For all other cases, the string
 660:    *        representation of the Object is retreived and packed into a
 661:    *        label.)
 662:    * @param initialIndex The index of the component that is the initialValue.
 663:    */
 664:   protected void addButtonComponents(Container container, Object[] buttons,
 665:                                      int initialIndex)
 666:   {
 667:     if (buttons == null)
 668:       return;
 669:     for (int i = 0; i < buttons.length; i++)
 670:       {
 671:     if (buttons[i] != null)
 672:       {
 673:         Component toAdd;
 674:         if (buttons[i] instanceof Component)
 675:           toAdd = (Component) buttons[i];
 676:         else
 677:           {
 678:         if (buttons[i] instanceof Icon)
 679:           toAdd = new JButton((Icon) buttons[i]);
 680:         else
 681:           toAdd = new JButton(buttons[i].toString());
 682:         hasCustomComponents = true;
 683:           }
 684:         if (toAdd instanceof JButton)
 685:           ((JButton) toAdd).addActionListener(createButtonActionListener(i));
 686:         if (i == initialIndex)
 687:           initialFocusComponent = toAdd;
 688:         container.add(toAdd);
 689:       }
 690:       }
 691:     selectInitialValue(optionPane);
 692:   }
 693: 
 694:   /**
 695:    * This method adds the appropriate icon the given container.
 696:    *
 697:    * @param top The container to add an icon to.
 698:    */
 699:   protected void addIcon(Container top)
 700:   {
 701:     JLabel iconLabel = null;
 702:     Icon icon = getIcon();
 703:     if (icon != null)
 704:       {
 705:     iconLabel = new JLabel(icon);
 706:     top.add(iconLabel, BorderLayout.WEST);
 707:       }
 708:   }
 709: 
 710:   /**
 711:    * A helper method that returns an instance of GridBagConstraints to be used
 712:    * for creating the message area.
 713:    *
 714:    * @return An instance of GridBagConstraints.
 715:    */
 716:   private static GridBagConstraints createConstraints()
 717:   {
 718:     GridBagConstraints constraints = new GridBagConstraints();
 719:     constraints.gridx = GridBagConstraints.REMAINDER;
 720:     constraints.gridy = GridBagConstraints.REMAINDER;
 721:     constraints.gridwidth = 0;
 722:     constraints.anchor = GridBagConstraints.LINE_START;
 723:     constraints.fill = GridBagConstraints.NONE;
 724:     constraints.insets = new Insets(0, 0, 3, 0);
 725: 
 726:     return constraints;
 727:   }
 728: 
 729:   /**
 730:    * This method creates the proper object (if necessary) to represent msg.
 731:    * (If msg is an instance of Component, it will add it directly. If it is
 732:    * an icon, then it will pack it in a label and add it. Otherwise, it gets
 733:    * treated as a string. If the string is longer than maxll, a box is
 734:    * created and the burstStringInto is called with the box as the container.
 735:    * The box is then added to the given container. Otherwise, the string is
 736:    * packed in a label and placed in the given container.) This method is
 737:    * also used for adding the inputComponent to the container.
 738:    *
 739:    * @param container The container to add to.
 740:    * @param cons The constraints when adding.
 741:    * @param msg The message to add.
 742:    * @param maxll The max line length.
 743:    * @param internallyCreated Whether the msg is internally created.
 744:    */
 745:   protected void addMessageComponents(Container container,
 746:                                       GridBagConstraints cons, Object msg,
 747:                                       int maxll, boolean internallyCreated)
 748:   {
 749:     if (msg == null)
 750:       return;
 751:     hasCustomComponents = internallyCreated;
 752:     if (msg instanceof Object[])
 753:       {
 754:     Object[] arr = (Object[]) msg;
 755:     for (int i = 0; i < arr.length; i++)
 756:       addMessageComponents(container, cons, arr[i], maxll,
 757:                            internallyCreated);
 758:     return;
 759:       }
 760:     else if (msg instanceof Component)
 761:       {
 762:     container.add((Component) msg, cons);
 763:     cons.gridy++;
 764:       }
 765:     else if (msg instanceof Icon)
 766:       {
 767:     container.add(new JLabel((Icon) msg), cons);
 768:     cons.gridy++;
 769:       }
 770:     else
 771:       {
 772:     // Undocumented behaviour.
 773:     // if msg.toString().length greater than maxll
 774:     // it will create a box and burst the string.
 775:     // otherwise, it will just create a label and re-call 
 776:     // this method with the label o.O
 777:     if (msg.toString().length() > maxll || msg.toString().contains("\n"))
 778:       {
 779:         Box tmp = new Box(BoxLayout.Y_AXIS);
 780:         burstStringInto(tmp, msg.toString(), maxll);
 781:         addMessageComponents(container, cons, tmp, maxll, true);
 782:       }
 783:     else
 784:       addMessageComponents(container, cons, new JLabel(msg.toString()),
 785:                            maxll, true);
 786:       }
 787:   }
 788: 
 789:   /**
 790:    * This method creates instances of d (recursively if necessary based on
 791:    * maxll) and adds to c.
 792:    *
 793:    * @param c The container to add to.
 794:    * @param d The string to burst.
 795:    * @param maxll The max line length.
 796:    */
 797:   protected void burstStringInto(Container c, String d, int maxll)
 798:   {
 799:     if (d == null || c == null)
 800:       return;
 801: 
 802:     int newlineIndex = d.indexOf('\n');
 803:     String line;
 804:     String remainder;
 805:     if (newlineIndex >= 0 && newlineIndex < maxll)
 806:       {
 807:         line = d.substring(0, newlineIndex);
 808:         remainder = d.substring(newlineIndex + 1);
 809:       }
 810:     else
 811:       {
 812:         line = d.substring(0, maxll);
 813:         remainder = d.substring(maxll);
 814:       }
 815:     JLabel label = new JLabel(line);
 816:     c.add(label);
 817: 
 818:     // If there is nothing left to burst, then we can stop.
 819:     if (remainder.length() == 0)
 820:       return;
 821: 
 822:     // Recursivly call ourselves to burst the remainder of the string, 
 823:     if ((remainder.length() > maxll || remainder.contains("\n")))
 824:       burstStringInto(c, remainder, maxll);
 825:     else
 826:       // Add the remainder to the container and be done.
 827:       c.add(new JLabel(remainder)); 
 828:   }
 829: 
 830:   /**
 831:    * This method returns true if the given JOptionPane contains custom
 832:    * components.
 833:    *
 834:    * @param op The JOptionPane to check.
 835:    *
 836:    * @return True if the JOptionPane contains custom components.
 837:    */
 838:   public boolean containsCustomComponents(JOptionPane op)
 839:   {
 840:     return hasCustomComponents;
 841:   }
 842: 
 843:   /**
 844:    * This method creates a button action listener for the given button index.
 845:    *
 846:    * @param buttonIndex The index of the button in components.
 847:    *
 848:    * @return A new ButtonActionListener.
 849:    */
 850:   protected ActionListener createButtonActionListener(int buttonIndex)
 851:   {
 852:     return new ButtonActionListener(buttonIndex);
 853:   }
 854: 
 855:   /**
 856:    * This method creates the button area.
 857:    *
 858:    * @return A new Button Area.
 859:    */
 860:   protected Container createButtonArea()
 861:   {
 862:     JPanel buttonPanel = new JPanel();
 863: 
 864:     buttonPanel.setLayout(createLayoutManager());
 865:     addButtonComponents(buttonPanel, getButtons(), getInitialValueIndex());
 866: 
 867:     return buttonPanel;
 868:   }
 869: 
 870:   /**
 871:    * This method creates a new LayoutManager for the button area.
 872:    *
 873:    * @return A new LayoutManager for the button area.
 874:    */
 875:   protected LayoutManager createLayoutManager()
 876:   {
 877:     return new ButtonAreaLayout(getSizeButtonsToSameWidth(), 6);
 878:   }
 879: 
 880:   /**
 881:    * This method creates the message area.
 882:    *
 883:    * @return A new message area.
 884:    */
 885:   protected Container createMessageArea()
 886:   {
 887:     JPanel messageArea = new JPanel();
 888:     messageArea.setLayout(new BorderLayout());
 889:     addIcon(messageArea);
 890: 
 891:     JPanel rightSide = new JPanel();
 892:     rightSide.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
 893:     rightSide.setLayout(new GridBagLayout());
 894:     GridBagConstraints con = createConstraints();
 895:     
 896:     addMessageComponents(rightSide, con, getMessage(),
 897:                          getMaxCharactersPerLineCount(), false);
 898: 
 899:     if (optionPane.getWantsInput())
 900:       {
 901:     Object[] selection = optionPane.getSelectionValues();
 902: 
 903:     if (selection == null)
 904:           inputComponent = new JTextField(15);
 905:     else if (selection.length < 20)
 906:           inputComponent = new JComboBox(selection);
 907:     else
 908:       inputComponent = new JList(selection);
 909:     if (inputComponent != null)
 910:       {
 911:         addMessageComponents(rightSide, con, inputComponent,
 912:                                  getMaxCharactersPerLineCount(), false);
 913:         resetSelectedValue();
 914:         selectInitialValue(optionPane);
 915:       }
 916:       }
 917: 
 918:     messageArea.add(rightSide, BorderLayout.CENTER);
 919: 
 920:     return messageArea;
 921:   }
 922: 
 923:   /**
 924:    * This method creates a new PropertyChangeListener for listening to the
 925:    * JOptionPane.
 926:    *
 927:    * @return A new PropertyChangeListener.
 928:    */
 929:   protected PropertyChangeListener createPropertyChangeListener()
 930:   {
 931:     return new PropertyChangeHandler();
 932:   }
 933: 
 934:   /**
 935:    * This method creates a Container that will separate the message and button
 936:    * areas.
 937:    *
 938:    * @return A Container that will separate the message and button areas.
 939:    */
 940:   protected Container createSeparator()
 941:   {
 942:     // FIXME: Figure out what this method is supposed to return and where
 943:     // this should be added to the OptionPane.
 944:     return null;
 945:   }
 946: 
 947:   /**
 948:    * This method creates a new BasicOptionPaneUI for the given component.
 949:    *
 950:    * @param x The component to create a UI for.
 951:    *
 952:    * @return A new BasicOptionPaneUI.
 953:    */
 954:   public static ComponentUI createUI(JComponent x)
 955:   {
 956:     return new BasicOptionPaneUI();
 957:   }
 958: 
 959:   /**
 960:    * This method returns the buttons for the JOptionPane. If no options are
 961:    * set, a set of options will be created based upon the optionType.
 962:    *
 963:    * @return The buttons that will be added.
 964:    */
 965:   protected Object[] getButtons()
 966:   {
 967:     if (optionPane.getOptions() != null)
 968:       return optionPane.getOptions();
 969:     switch (optionPane.getOptionType())
 970:       {
 971:       case JOptionPane.YES_NO_OPTION:
 972:     return new Object[] { YES_STRING, NO_STRING };
 973:       case JOptionPane.YES_NO_CANCEL_OPTION:
 974:     return new Object[] { YES_STRING, NO_STRING, CANCEL_STRING };
 975:       case JOptionPane.OK_CANCEL_OPTION:
 976:     return new Object[] { OK_STRING, CANCEL_STRING };
 977:       case JOptionPane.DEFAULT_OPTION:
 978:         return (optionPane.getWantsInput() ) ?
 979:                new Object[] { OK_STRING, CANCEL_STRING } :
 980:                ( optionPane.getMessageType() == JOptionPane.QUESTION_MESSAGE ) ?
 981:                new Object[] { YES_STRING, NO_STRING, CANCEL_STRING } :
 982:                // ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE
 983:                new Object[] { OK_STRING };
 984:       }
 985:     return null;
 986:   }
 987: 
 988:   /**
 989:    * This method will return the icon the user has set or the icon that will
 990:    * be used based on message type.
 991:    *
 992:    * @return The icon to use in the JOptionPane.
 993:    */
 994:   protected Icon getIcon()
 995:   {
 996:     if (optionPane.getIcon() != null)
 997:       return optionPane.getIcon();
 998:     else
 999:       return getIconForType(optionPane.getMessageType());
1000:   }
1001: 
1002:   /**
1003:    * This method returns the icon for the given messageType.
1004:    *
1005:    * @param messageType The type of message.
1006:    *
1007:    * @return The icon for the given messageType.
1008:    */
1009:   protected Icon getIconForType(int messageType)
1010:   {
1011:     Icon tmp = null;
1012:     switch (messageType)
1013:       {
1014:       case JOptionPane.ERROR_MESSAGE:
1015:     tmp = errorIcon;
1016:     break;
1017:       case JOptionPane.INFORMATION_MESSAGE:
1018:     tmp = infoIcon;
1019:     break;
1020:       case JOptionPane.WARNING_MESSAGE:
1021:     tmp = warningIcon;
1022:     break;
1023:       case JOptionPane.QUESTION_MESSAGE:
1024:     tmp = questionIcon;
1025:     break;
1026:       }
1027:     return tmp;
1028:     // FIXME: Don't cast till the default icons are in.
1029:     // return new IconUIResource(tmp);
1030:   }
1031: 
1032:   /**
1033:    * This method returns the index of the initialValue in the options array.
1034:    *
1035:    * @return The index of the initalValue.
1036:    */
1037:   protected int getInitialValueIndex()
1038:   {
1039:     Object[] buttons = getButtons();
1040: 
1041:     if (buttons == null)
1042:       return -1;
1043: 
1044:     Object select = optionPane.getInitialValue();
1045: 
1046:     for (int i = 0; i < buttons.length; i++)
1047:       {
1048:     if (select == buttons[i])
1049:       return i;
1050:       }
1051:     return 0;
1052:   }
1053: 
1054:   /**
1055:    * This method returns the maximum number of characters that should be
1056:    * placed on a line.
1057:    *
1058:    * @return The maximum number of characteres that should be placed on a
1059:    *         line.
1060:    */
1061:   protected int getMaxCharactersPerLineCount()
1062:   {
1063:     return optionPane.getMaxCharactersPerLineCount();
1064:   }
1065: 
1066:   /**
1067:    * This method returns the maximum size.
1068:    *
1069:    * @param c The JComponent to measure.
1070:    *
1071:    * @return The maximum size.
1072:    */
1073:   public Dimension getMaximumSize(JComponent c)
1074:   {
1075:     return getPreferredSize(c);
1076:   }
1077: 
1078:   /**
1079:    * This method returns the message of the JOptionPane.
1080:    *
1081:    * @return The message.
1082:    */
1083:   protected Object getMessage()
1084:   {
1085:     return optionPane.getMessage();
1086:   }
1087: 
1088:   /**
1089:    * This method returns the minimum size of the JOptionPane.
1090:    *
1091:    * @return The minimum size.
1092:    */
1093:   public Dimension getMinimumOptionPaneSize()
1094:   {
1095:     return minimumSize;
1096:   }
1097: 
1098:   /**
1099:    * This method returns the minimum size.
1100:    *
1101:    * @param c The JComponent to measure.
1102:    *
1103:    * @return The minimum size.
1104:    */
1105:   public Dimension getMinimumSize(JComponent c)
1106:   {
1107:     return getPreferredSize(c);
1108:   }
1109: 
1110:   /**
1111:    * This method returns the preferred size of the JOptionPane. The preferred
1112:    * size is the maximum of the size desired by the layout and the minimum
1113:    * size.
1114:    *
1115:    * @param c The JComponent to measure.
1116:    *
1117:    * @return The preferred size.
1118:    */
1119:   public Dimension getPreferredSize(JComponent c)
1120:   {
1121:     Dimension d = optionPane.getLayout().preferredLayoutSize(optionPane);
1122:     Dimension d2 = getMinimumOptionPaneSize();
1123: 
1124:     int w = Math.max(d.width, d2.width);
1125:     int h = Math.max(d.height, d2.height);
1126:     return new Dimension(w, h);
1127:   }
1128: 
1129:   /**
1130:    * This method returns whether all buttons should have the same width.
1131:    *
1132:    * @return Whether all buttons should have the same width.
1133:    */
1134:   protected boolean getSizeButtonsToSameWidth()
1135:   {
1136:     return true;
1137:   }
1138: 
1139:   /**
1140:    * This method installs components for the JOptionPane.
1141:    */
1142:   protected void installComponents()
1143:   {
1144:     // reset it.
1145:     hasCustomComponents = false;
1146:     Container msg = createMessageArea();
1147:     if (msg != null)
1148:       {
1149:     ((JComponent) msg).setBorder(messageBorder);
1150:     msg.setForeground(messageForeground);
1151:     messageAreaContainer = msg;
1152:     optionPane.add(msg);
1153:       }
1154: 
1155:     // FIXME: Figure out if the separator should be inserted here or what
1156:     // this thing is supposed to do. Note: The JDK does NOT insert another
1157:     // component at this place. The JOptionPane only has two panels in it
1158:     // and there actually are applications that depend on this beeing so.
1159:     Container sep = createSeparator();
1160:     if (sep != null)
1161:       optionPane.add(sep);
1162: 
1163:     Container button = createButtonArea();
1164:     if (button != null)
1165:       {
1166:     ((JComponent) button).setBorder(buttonBorder);
1167:     buttonContainer = button;
1168:     optionPane.add(button);
1169:       }
1170: 
1171:     optionPane.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11));
1172:     optionPane.invalidate();
1173:   }
1174: 
1175:   /**
1176:    * This method installs defaults for the JOptionPane.
1177:    */
1178:   protected void installDefaults()
1179:   {
1180:     LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background",
1181:                                      "OptionPane.foreground",
1182:                                      "OptionPane.font");
1183:     LookAndFeel.installBorder(optionPane, "OptionPane.border");
1184:     optionPane.setOpaque(true);
1185: 
1186:     messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder");
1187:     messageForeground = UIManager.getColor("OptionPane.messageForeground");
1188:     buttonBorder = UIManager.getBorder("OptionPane.buttonAreaBorder");
1189: 
1190:     minimumSize = UIManager.getDimension("OptionPane.minimumSize");
1191: 
1192:     // FIXME: Image icons don't seem to work properly right now.
1193:     // Once they do, replace the synthetic icons with these ones.
1194: 
1195:     /*
1196:     warningIcon = (IconUIResource) defaults.getIcon("OptionPane.warningIcon");
1197:     infoIcon = (IconUIResource) defaults.getIcon("OptionPane.informationIcon");
1198:     errorIcon = (IconUIResource) defaults.getIcon("OptionPane.errorIcon");
1199:     questionIcon = (IconUIResource) defaults.getIcon("OptionPane.questionIcon");
1200:     */
1201:   }
1202: 
1203:   /**
1204:    * This method installs keyboard actions for the JOptionpane.
1205:    */
1206:   protected void installKeyboardActions()
1207:   {
1208:     // FIXME: implement.
1209:   }
1210: 
1211:   /**
1212:    * This method installs listeners for the JOptionPane.
1213:    */
1214:   protected void installListeners()
1215:   {
1216:     propertyChangeListener = createPropertyChangeListener();
1217: 
1218:     optionPane.addPropertyChangeListener(propertyChangeListener);
1219:   }
1220: 
1221:   /**
1222:    * This method installs the UI for the JOptionPane.
1223:    *
1224:    * @param c The JComponent to install the UI for.
1225:    */
1226:   public void installUI(JComponent c)
1227:   {
1228:     if (c instanceof JOptionPane)
1229:       {
1230:     optionPane = (JOptionPane) c;
1231: 
1232:     installDefaults();
1233:     installComponents();
1234:     installListeners();
1235:     installKeyboardActions();
1236:       }
1237:   }
1238: 
1239:   /**
1240:    * Changes the inputValue property in the JOptionPane based on the current
1241:    * value of the inputComponent.
1242:    */
1243:   protected void resetInputValue()
1244:   {
1245:     if (optionPane.getWantsInput() && inputComponent != null)
1246:       {
1247:     Object output = null;
1248:     if (inputComponent instanceof JTextField)
1249:       output = ((JTextField) inputComponent).getText();
1250:     else if (inputComponent instanceof JComboBox)
1251:       output = ((JComboBox) inputComponent).getSelectedItem();
1252:     else if (inputComponent instanceof JList)
1253:       output = ((JList) inputComponent).getSelectedValue();
1254: 
1255:     if (output != null)
1256:       optionPane.setInputValue(output);
1257:       }
1258:   }
1259: 
1260:   /**
1261:    * This method requests focus to the inputComponent (if one is present) and
1262:    * the initialFocusComponent otherwise.
1263:    *
1264:    * @param op The JOptionPane.
1265:    */
1266:   public void selectInitialValue(JOptionPane op)
1267:   {
1268:     if (inputComponent != null)
1269:       {
1270:     inputComponent.requestFocus();
1271:     return;
1272:       }
1273:     if (initialFocusComponent != null)
1274:       initialFocusComponent.requestFocus();
1275:   }
1276: 
1277:   /**
1278:    * This method resets the value in the inputComponent to the
1279:    * initialSelectionValue property.
1280:    * This is package-private to avoid an accessor method.
1281:    */
1282:   void resetSelectedValue()
1283:   {
1284:     if (inputComponent != null)
1285:       {
1286:     Object init = optionPane.getInitialSelectionValue();
1287:     if (init == null)
1288:       return;
1289:     if (inputComponent instanceof JTextField)
1290:       ((JTextField) inputComponent).setText((String) init);
1291:     else if (inputComponent instanceof JComboBox)
1292:       ((JComboBox) inputComponent).setSelectedItem(init);
1293:     else if (inputComponent instanceof JList)
1294:       {
1295:         //  ((JList) inputComponent).setSelectedValue(init, true);
1296:       }
1297:       }
1298:   }
1299: 
1300:   /**
1301:    * This method uninstalls all the components in the JOptionPane.
1302:    */
1303:   protected void uninstallComponents()
1304:   {
1305:     optionPane.removeAll();
1306:     buttonContainer = null;
1307:     messageAreaContainer = null;
1308:   }
1309: 
1310:   /**
1311:    * This method uninstalls the defaults for the JOptionPane.
1312:    */
1313:   protected void uninstallDefaults()
1314:   {
1315:     optionPane.setFont(null);
1316:     optionPane.setForeground(null);
1317:     optionPane.setBackground(null);
1318: 
1319:     minimumSize = null;
1320: 
1321:     messageBorder = null;
1322:     buttonBorder = null;
1323:     messageForeground = null;
1324: 
1325:     // FIXME: ImageIcons don't seem to work properly
1326: 
1327:     /*
1328:     warningIcon = null;
1329:     errorIcon = null;
1330:     questionIcon = null;
1331:     infoIcon = null;
1332:     */
1333:   }
1334: 
1335:   /**
1336:    * This method uninstalls keyboard actions for the JOptionPane.
1337:    */
1338:   protected void uninstallKeyboardActions()
1339:   {
1340:     // FIXME: implement.
1341:   }
1342: 
1343:   /**
1344:    * This method uninstalls listeners for the JOptionPane.
1345:    */
1346:   protected void uninstallListeners()
1347:   {
1348:     optionPane.removePropertyChangeListener(propertyChangeListener);
1349:     propertyChangeListener = null;
1350:   }
1351: 
1352:   /**
1353:    * This method uninstalls the UI for the given JComponent.
1354:    *
1355:    * @param c The JComponent to uninstall for.
1356:    */
1357:   public void uninstallUI(JComponent c)
1358:   {
1359:     uninstallKeyboardActions();
1360:     uninstallListeners();
1361:     uninstallComponents();
1362:     uninstallDefaults();
1363: 
1364:     optionPane = null;
1365:   }
1366: }