Source for javax.swing.plaf.basic.BasicOptionPaneUI

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