OSDN Git Service

91bf614340ddb3f76e79343029beb87eded72fe4
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / plaf / basic / BasicOptionPaneUI.java
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 }