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