001    /* TitledBorder.java --
002       Copyright (C) 2003, 2004, 2005, 2006,  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.border;
040    
041    import java.awt.Color;
042    import java.awt.Component;
043    import java.awt.Dimension;
044    import java.awt.Font;
045    import java.awt.FontMetrics;
046    import java.awt.Graphics;
047    import java.awt.Insets;
048    import java.awt.Point;
049    import java.awt.Rectangle;
050    
051    import javax.swing.SwingUtilities;
052    import javax.swing.UIManager;
053    
054    
055    /**
056     * A border that paints a title on top of another border.
057     *
058     * @author Sascha Brawer (brawer@dandelis.ch)
059     */
060    public class TitledBorder extends AbstractBorder
061    {
062      /**
063       * A value for the <code>titlePosition</code> property that vertically
064       * positions the title text at the default vertical position, which
065       * is in the middle of the top line of the border.
066       *
067       * @see #getTitlePosition()
068       * @see #setTitlePosition(int)
069       */
070      public static final int DEFAULT_POSITION = 0;
071    
072    
073      /**
074       * A value for the <code>titlePosition</code> property that vertically
075       * positions the title text above the top line of the border.
076       *
077       * @see #getTitlePosition()
078       * @see #setTitlePosition(int)
079       */
080      public static final int ABOVE_TOP = 1;
081    
082    
083      /**
084       * A value for the <code>titlePosition</code> property that vertically
085       * positions the title text at the middle of the top line
086       * of the border.
087       *
088       * @see #getTitlePosition()
089       * @see #setTitlePosition(int)
090       */
091      public static final int TOP = 2;
092    
093    
094      /**
095       * A value for the <code>titlePosition</code> property that vertically
096       * positions the title text below the top line of the border.
097       *
098       * @see #getTitlePosition()
099       * @see #setTitlePosition(int)
100       */
101      public static final int BELOW_TOP = 3;
102    
103    
104      /**
105       * A value for the <code>titlePosition</code> property that vertically
106       * positions the title text above the bottom line of the border.
107       *
108       * @see #getTitlePosition()
109       * @see #setTitlePosition(int)
110       */
111      public static final int ABOVE_BOTTOM = 4;
112    
113    
114      /**
115       * A value for the <code>titlePosition</code> property that vertically
116       * positions the title text at the center of the bottom line
117       * of the border.
118       *
119       * @see #getTitlePosition()
120       * @see #setTitlePosition(int)
121       */
122      public static final int BOTTOM = 5;
123    
124    
125      /**
126       * A value for the <code>titlePosition</code> property that vertically
127       * positions the title text below the bottom line of the border.
128       *
129       * @see #getTitlePosition()
130       * @see #setTitlePosition(int)
131       */
132      public static final int BELOW_BOTTOM = 6;
133    
134    
135      /**
136       * A value for the <code>titleJustification</code> property that
137       * horizontally aligns the title text with either the left or the
138       * right edge of the border, depending on the orientation of the
139       * component nested into the border. If the component orientation
140       * is left-to-right, the title text is aligned with the left edge;
141       * otherwise, it is aligned with the right edge.  This is the same
142       * behavior as with {@link #LEADING}.
143       *
144       * @see #getTitleJustification()
145       * @see #setTitleJustification(int)
146       * @see java.awt.ComponentOrientation#isLeftToRight()
147       */
148      public static final int DEFAULT_JUSTIFICATION = 0;
149    
150    
151      /**
152       * A value for the <code>titleJustification</code> property that
153       * horizontally aligns the title text with the left-hand edge of
154       * the border.
155       *
156       * @see #getTitleJustification()
157       * @see #setTitleJustification(int)
158       */
159      public static final int LEFT = 1;
160    
161    
162      /**
163       * A value for the <code>titleJustification</code> property that
164       * horizontally aligns the title text with the center of the border.
165       *
166       * @see #getTitleJustification()
167       * @see #setTitleJustification(int)
168       */
169      public static final int CENTER = 2;
170    
171    
172      /**
173       * A value for the <code>titleJustification</code> property that
174       * horizontally aligns the title text with the right-hand edge of
175       * the border.
176       *
177       * @see #getTitleJustification()
178       * @see #setTitleJustification(int)
179       */
180      public static final int RIGHT = 3;
181    
182    
183      /**
184       * A value for the <code>titleJustification</code> property that
185       * horizontally aligns the title text with either the left or the
186       * right edge of the border, depending on the orientation of the
187       * component nested into the border. If the component orientation
188       * is left-to-right, the title text is aligned with the left edge;
189       * otherwise, it is aligned with the right edge. This is the same
190       * behavior as with {@link #DEFAULT_JUSTIFICATION}.
191       *
192       * @see #getTitleJustification()
193       * @see #setTitleJustification(int)
194       * @see java.awt.ComponentOrientation#isLeftToRight()
195       */
196      public static final int LEADING = 4;
197    
198    
199      /**
200       * A value for the <code>titleJustification</code> property that
201       * horizontally aligns the title text with either the right or the
202       * left edge of the border, depending on the orientation of the
203       * component nested into the border. If the component orientation
204       * is left-to-right, the title text is aligned with the right edge;
205       * otherwise, it is aligned with the left edge.
206       *
207       * @see #getTitleJustification()
208       * @see #setTitleJustification(int)
209       * @see java.awt.ComponentOrientation#isLeftToRight()
210       */
211      public static final int TRAILING = 5;
212    
213    
214      /**
215       * The number of pixels between the inside of {@link #border}
216       * and the bordered component.
217       */
218      protected static final int EDGE_SPACING = 2;
219    
220    
221      /**
222       * The number of pixels between the outside of this TitledBorder
223       * and the beginning (if left-aligned) or end (if right-aligned)
224       * of the title text.
225       */
226      protected static final int TEXT_INSET_H = 5;
227    
228    
229      /**
230       * The number of pixels between the title text and {@link #border}.
231       * This value is only relevant if the title text does not intersect
232       * {@link #border}. No intersection occurs if {@link #titlePosition}
233       * is one of {@link #ABOVE_TOP}, {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM},
234       * or {@link #BELOW_BOTTOM}.
235       */
236      protected static final int TEXT_SPACING = 2;
237    
238    
239      /**
240       * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
241       * on MacOS X 10.1.5.
242       */
243      static final long serialVersionUID = 8012999415147721601L;
244    
245    
246      /**
247       * The title, or <code>null</code> to display no title.
248       */
249      protected String title;
250    
251    
252      /**
253       * The border underneath the title. If this value is
254       * <code>null</code>, the border will be retrieved from the {@link
255       * javax.swing.UIManager}&#x2019;s defaults table using the key
256       * <code>TitledBorder.border</code>.
257       */
258      protected Border border;
259    
260    
261      /**
262       * The vertical position of the title text relative to the border,
263       * which is one of {@link #ABOVE_TOP}, {@link #TOP}, {@link
264       * #BELOW_TOP}, {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link
265       * #BELOW_BOTTOM}, or {@link #DEFAULT_POSITION}.
266       */
267      protected int titlePosition;
268    
269    
270      /**
271       * The horizontal alignment of the title text in relation to the
272       * border, which is one of {@link #LEFT}, {@link #CENTER}, {@link
273       * #RIGHT}, {@link #LEADING}, {@link #TRAILING}, or {@link
274       * #DEFAULT_JUSTIFICATION}.
275       */
276      protected int titleJustification;
277    
278    
279      /**
280       * The font for displaying the title text. If this value is
281       * <code>null</code>, the font will be retrieved from the {@link
282       * javax.swing.UIManager}&#x2019;s defaults table using the key
283       * <code>TitledBorder.font</code>.
284       */
285      protected Font titleFont;
286    
287    
288      /**
289       * The color for displaying the title text. If this value is
290       * <code>null</code>, the color will be retrieved from the {@link
291       * javax.swing.UIManager}&#x2019;s defaults table using the key
292       * <code>TitledBorder.titleColor</code>.
293       */
294      protected Color titleColor;
295    
296    
297      /**
298       * Constructs a TitledBorder given the text of its title.
299       *
300       * @param title the title text, or <code>null</code> to use no title text.
301       */
302      public TitledBorder(String title)
303      {
304        this(/* border */ null,
305             title, LEADING, TOP,
306             /* titleFont */ null, /* titleColor */ null);
307      }
308    
309    
310      /**
311       * Constructs an initially untitled TitledBorder given another border.
312       *
313       * @param border the border underneath the title, or <code>null</code>
314       *        to use a default from the current look and feel.
315       */
316      public TitledBorder(Border border)
317      {
318        this(border, /* title */ "", LEADING, TOP,
319             /* titleFont */ null, /* titleColor */ null);
320      }
321    
322    
323      /**
324       * Constructs a TitledBorder given its border and title text.
325       *
326       * @param border the border underneath the title, or <code>null</code>
327       *        to use a default from the current look and feel.
328       *
329       * @param title the title text, or <code>null</code> to use no title
330       *        text.
331       */
332      public TitledBorder(Border border, String title)
333      {
334        this(border, title, LEADING, TOP,
335             /* titleFont */ null, /* titleColor */ null);
336      }
337    
338    
339      /**
340       * Constructs a TitledBorder given its border, title text, horizontal
341       * alignment, and vertical position.
342       *
343       * @param border the border underneath the title, or <code>null</code>
344       *        to use a default from the current look and feel.
345       *
346       * @param title the title text, or <code>null</code> to use no title
347       *        text.
348       *
349       * @param titleJustification the horizontal alignment of the title
350       *        text in relation to the border. The value must be one of
351       *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
352       *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
353    
354       * @param titlePosition the vertical position of the title text
355       *        in relation to the border. The value must be one of
356       *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
357       *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
358       *        or {@link #DEFAULT_POSITION}.
359       *
360       * @throws IllegalArgumentException if <code>titleJustification</code>
361       *         or <code>titlePosition</code> have an unsupported value.
362       */
363      public TitledBorder(Border border, String title, int titleJustification,
364                          int titlePosition)
365      {
366        this(border, title, titleJustification, titlePosition,
367             /* titleFont */ null, /* titleColor */ null);
368      }
369    
370    
371      /**
372       * Constructs a TitledBorder given its border, title text, horizontal
373       * alignment, vertical position, and font.
374       *
375       * @param border the border underneath the title, or <code>null</code>
376       *        to use a default from the current look and feel.
377       *
378       * @param title the title text, or <code>null</code> to use no title
379       *        text.
380       *
381       * @param titleJustification the horizontal alignment of the title
382       *        text in relation to the border. The value must be one of
383       *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
384       *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
385       *
386       * @param titlePosition the vertical position of the title text
387       *        in relation to the border. The value must be one of
388       *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
389       *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
390       *        or {@link #DEFAULT_POSITION}.
391       *
392       * @param titleFont the font for the title text, or <code>null</code>
393       *        to use a default from the current look and feel.
394       *
395       * @throws IllegalArgumentException if <code>titleJustification</code>
396       *         or <code>titlePosition</code> have an unsupported value.
397       */
398      public TitledBorder(Border border, String title, int titleJustification,
399                          int titlePosition, Font titleFont)
400      {
401        this(border, title, titleJustification, titlePosition, titleFont,
402             /* titleColor */ null);
403      }
404    
405    
406      /**
407       * Constructs a TitledBorder given its border, title text, horizontal
408       * alignment, vertical position, font, and color.
409       *
410       * @param border the border underneath the title, or <code>null</code>
411       *        to use a default from the current look and feel.
412       *
413       * @param title the title text, or <code>null</code> to use no title
414       *        text.
415       *
416       * @param titleJustification the horizontal alignment of the title
417       *        text in relation to the border. The value must be one of
418       *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
419       *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
420       *
421       * @param titlePosition the vertical position of the title text
422       *        in relation to the border. The value must be one of
423       *        {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
424       *        {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
425       *        or {@link #DEFAULT_POSITION}.
426       *
427       * @param titleFont the font for the title text, or <code>null</code>
428       *        to use a default from the current look and feel.
429       *
430       * @param titleColor the color for the title text, or <code>null</code>
431       *        to use a default from the current look and feel.
432       *
433       * @throws IllegalArgumentException if <code>titleJustification</code>
434       *         or <code>titlePosition</code> have an unsupported value.
435       */
436      public TitledBorder(Border border, String title, int titleJustification,
437                          int titlePosition, Font titleFont, Color titleColor)
438      {
439        this.border = border;
440        this.title = title;
441    
442        /* Invoking the setter methods ensures that the newly constructed
443         * TitledBorder has valid property values.
444         */
445        setTitleJustification(titleJustification);
446        setTitlePosition(titlePosition);
447    
448        this.titleFont = titleFont;
449        this.titleColor = titleColor;
450      }
451    
452    
453      /**
454       * Paints the border and the title text.
455       *
456       * @param c the component whose border is to be painted.
457       * @param g the graphics for painting.
458       * @param x the horizontal position for painting the border.
459       * @param y the vertical position for painting the border.
460       * @param width the width of the available area for painting the border.
461       * @param height the height of the available area for painting the border.
462       */
463      public void paintBorder(Component c, Graphics  g,
464                              int x, int y, int width, int height)
465      {
466        Rectangle borderRect = new Rectangle(x + EDGE_SPACING, y + EDGE_SPACING,
467                                             width - (EDGE_SPACING * 2),
468                                             height - (EDGE_SPACING * 2));
469        Point textLoc = new Point();
470    
471        // Save color and font.
472        Color savedColor = g.getColor();
473        Font savedFont = g.getFont();
474    
475        // The font metrics.
476        Font font = getFont(c);
477        g.setFont(font);
478        FontMetrics fm = c.getFontMetrics(font);
479    
480        layoutBorderWithTitle(c, fm, borderRect, textLoc);
481        paintBorderWithTitle(c, g, x, y, width, height, borderRect, textLoc, fm);
482    
483        g.setColor(getTitleColor());
484        g.drawString(getTitle(), textLoc.x, textLoc.y);
485        g.setFont(savedFont);
486        g.setColor(savedColor);
487      }
488    
489      /**
490       * Calculates the bounding box of the inner border and the location of the
491       * title string.
492       *
493       * @param c the component on which to paint the border
494       * @param fm the font metrics
495       * @param borderRect output parameter, holds the bounding box of the inner
496       *        border on method exit
497       * @param textLoc output parameter, holds the location of the title text
498       *        on method exit
499       */
500      private void layoutBorderWithTitle(Component c, FontMetrics fm,
501                                         Rectangle borderRect,
502                                         Point textLoc)
503      {
504        Border b = getBorder();
505    
506        // The font metrics.
507        int fontHeight = fm.getHeight();
508        int fontDescent = fm.getDescent();
509        int fontAscent = fm.getAscent();
510        int titleWidth = fm.stringWidth(getTitle());
511    
512        // The base insets.
513        Insets insets;
514        if (b == null)
515          insets = new Insets(0, 0, 0, 0);
516        else
517          insets = b.getBorderInsets(c);
518    
519        // The offset of the border rectangle, dependend on the title placement.
520        int offset;
521    
522        // Layout border and text vertically.
523        int titlePosition = getTitlePosition();
524        switch (titlePosition)
525        {
526          case ABOVE_BOTTOM:
527            textLoc.y = borderRect.y + borderRect.height - insets.bottom
528                         - fontDescent - TEXT_SPACING;
529            break;
530          case BOTTOM:
531            borderRect.height -= fontHeight / 2;
532            textLoc.y = borderRect.y + borderRect.height - fontDescent
533                         + (fontAscent + fontDescent - insets.bottom) / 2;
534            break;
535          case BELOW_BOTTOM:
536            borderRect.height -=  fontHeight;
537            textLoc.y = borderRect.y + borderRect.height + fontAscent
538                         + TEXT_SPACING;
539            break;
540          case ABOVE_TOP:
541            offset = fontAscent + fontDescent
542                     + Math.max(EDGE_SPACING, TEXT_SPACING * 2) - EDGE_SPACING;
543            borderRect.y += offset;
544            borderRect.height -= offset;
545            textLoc.y = borderRect.y - (fontDescent + TEXT_SPACING);
546            break;
547          case BELOW_TOP:
548            textLoc.y = borderRect.y + insets.top + fontAscent + TEXT_SPACING;
549            break;
550          case TOP:
551          case DEFAULT_POSITION:
552          default:
553            offset = Math.max(0, ((fontAscent / 2) + TEXT_SPACING) - EDGE_SPACING);
554            borderRect.y += offset;
555            borderRect.height -= offset;
556            textLoc.y = borderRect.y - fontDescent
557                         + (insets.top + fontAscent + fontDescent) / 2;
558            break;
559        }
560    
561        // Layout border and text horizontally.
562        int justification = getTitleJustification();
563        // Adjust justification for LEADING and TRAILING depending on the direction
564        // of the component.
565        if (c.getComponentOrientation().isLeftToRight())
566          {
567            if (justification == LEADING || justification == DEFAULT_JUSTIFICATION)
568              justification = LEFT;
569            else if (justification == TRAILING)
570              justification = RIGHT;
571          }
572        else
573          {
574            if (justification == LEADING || justification == DEFAULT_JUSTIFICATION)
575              justification = RIGHT;
576            else if (justification == TRAILING)
577              justification = LEFT;
578          }
579    
580        switch (justification)
581        {
582          case CENTER:
583            textLoc.x = borderRect.x + (borderRect.width - titleWidth) / 2;
584            break;
585          case RIGHT:
586            textLoc.x = borderRect.x + borderRect.width - titleWidth
587                         - TEXT_INSET_H - insets.right;
588            break;
589          case LEFT:
590          default:
591            textLoc.x = borderRect.x + TEXT_INSET_H + insets.left;
592        }
593      }
594    
595      /**
596       * Paints the border with the title.
597       *
598       * @param c the component to paint on
599       * @param g the graphics context used for paintin
600       * @param x the upper left corner of the whole border
601       * @param y the upper left corner of the whole border
602       * @param width the width of the whole border
603       * @param height the width of the whole border
604       * @param borderRect the bounding box of the inner border
605       * @param textLoc the location of the border title
606       * @param fm the font metrics of the title
607       */
608      private void paintBorderWithTitle(Component c, Graphics g, int x, int y,
609                                        int width, int height,
610                                        Rectangle borderRect, Point textLoc,
611                                        FontMetrics fm)
612      {
613        Border b = getBorder();
614        int fontDescent = fm.getDescent();
615        int fontAscent = fm.getAscent();
616        int titleWidth = fm.stringWidth(getTitle());
617    
618        if (b != null)
619          {
620            // Paint border in segments, when the title is painted above the
621            // border.
622            if (((titlePosition == TOP || titlePosition == DEFAULT_POSITION)
623                && (borderRect.y > textLoc.y - fontAscent))
624                || (titlePosition == BOTTOM
625                    && borderRect.y + borderRect.height < textLoc.y + fontDescent))
626              {
627                Rectangle clip = new Rectangle();
628                Rectangle saved = g.getClipBounds();
629    
630                // Paint border left from the text.
631                clip.setBounds(saved);
632                SwingUtilities.computeIntersection(x, y, textLoc.x - x - 1,
633                                                   height, clip);
634                if (! clip.isEmpty())
635                  {
636                    g.setClip(clip);
637                    b.paintBorder(c, g, borderRect.x, borderRect.y,
638                                  borderRect.width,
639                                  borderRect.height);
640                  }
641                // Paint border right from the text.
642                clip.setBounds(saved);
643                SwingUtilities.computeIntersection(textLoc.x + titleWidth + 1, y,
644                    x + width - (textLoc.x + titleWidth + 1), height, clip);
645                if (! clip.isEmpty())
646                  {
647                    g.setClip(clip);
648                    b.paintBorder(c, g, borderRect.x, borderRect.y,
649                                  borderRect.width,
650                                  borderRect.height);
651                  }
652    
653                if (titlePosition == TOP || titlePosition == DEFAULT_POSITION)
654                  {
655                    // Paint border below the text.
656                    clip.setBounds(saved);
657                    SwingUtilities.computeIntersection(textLoc.x - 1,
658                                                       textLoc.y + fontDescent,
659                                                       titleWidth + 2,
660                                                       y + height - textLoc.y - fontDescent,
661                                                       clip);
662                    if (! clip.isEmpty())
663                      {
664                        g.setClip(clip);
665                        b.paintBorder(c, g, borderRect.x, borderRect.y,
666                                      borderRect.width,
667                                      borderRect.height);
668                      }
669    
670                  }
671                else
672                  {
673                    // Paint border above the text.
674                    clip.setBounds(saved);
675                    SwingUtilities.computeIntersection(textLoc.x - 1, y,
676                                                       titleWidth + 2,
677                                                       textLoc.y - fontDescent - y,
678                                                       clip);
679                    if (! clip.isEmpty())
680                      {
681                        g.setClip(clip);
682                        b.paintBorder(c, g, borderRect.x, borderRect.y,
683                                      borderRect.width,
684                                      borderRect.height);
685                      }
686    
687                  }
688                g.setClip(saved);
689              }
690            else
691              {
692                b.paintBorder(c, g, borderRect.x, borderRect.y, borderRect.width,
693                              borderRect.height);
694              }
695          }
696      }
697    
698      /**
699       * Measures the width of this border.
700       *
701       * @param c the component whose border is to be measured.
702       *
703       * @return an Insets object whose <code>left</code>, <code>right</code>,
704       *         <code>top</code> and <code>bottom</code> fields indicate the
705       *         width of the border at the respective edge.
706       *
707       * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
708       */
709      public Insets getBorderInsets(Component c)
710      {
711        return getBorderInsets(c, new Insets(0, 0, 0, 0));
712      }
713    
714    
715      /**
716       * Measures the width of this border, storing the results into a
717       * pre-existing Insets object.
718       *
719       * @param insets an Insets object for holding the result values.
720       *        After invoking this method, the <code>left</code>,
721       *        <code>right</code>, <code>top</code> and
722       *        <code>bottom</code> fields indicate the width of the
723       *        border at the respective edge.
724       *
725       * @return the same object that was passed for <code>insets</code>.
726       *
727       * @see #getBorderInsets(Component)
728       */
729      public Insets getBorderInsets(Component c, Insets insets)
730      {
731        // Initialize insets with the insets from our border.
732        Border border = getBorder();
733        if (border != null)
734          {
735            if (border instanceof AbstractBorder)
736              {
737                AbstractBorder aBorder = (AbstractBorder) border;
738                aBorder.getBorderInsets(c, insets);
739              }
740            else
741              {
742                Insets i = border.getBorderInsets(c);
743                insets.top = i.top;
744                insets.bottom = i.bottom;
745                insets.left = i.left;
746                insets.right = i.right;
747              }
748          }
749        else
750          {
751            insets.top = 0;
752            insets.bottom = 0;
753            insets.left = 0;
754            insets.right = 0;
755          }
756    
757        // Add spacing.
758        insets.top += EDGE_SPACING + TEXT_SPACING;
759        insets.bottom += EDGE_SPACING + TEXT_SPACING;
760        insets.left += EDGE_SPACING + TEXT_SPACING;
761        insets.right += EDGE_SPACING + TEXT_SPACING;
762    
763        String title = getTitle();
764        if (c != null && title != null && !title.equals(""))
765          {
766            Font font = getFont(c);
767            FontMetrics fm = c.getFontMetrics(font);
768            int ascent = fm.getAscent();
769            int descent = fm.getDescent();
770            int height = fm.getHeight();
771            switch (getTitlePosition())
772            {
773              case ABOVE_BOTTOM:
774                insets.bottom += ascent + descent + TEXT_SPACING;
775                break;
776              case BOTTOM:
777                insets.bottom += ascent + descent;
778                break;
779              case BELOW_BOTTOM:
780                insets.bottom += height;
781                break;
782              case ABOVE_TOP:
783                insets.top += ascent + descent +
784                              Math.max(EDGE_SPACING, TEXT_SPACING * 2)
785                              - EDGE_SPACING;
786                break;
787              case BELOW_TOP:
788                insets.top += ascent + descent + TEXT_SPACING;
789                break;
790              case TOP:
791              case DEFAULT_POSITION:
792              default:
793                insets.top += ascent + descent;
794            }
795          }
796        return insets;
797      }
798    
799    
800      /**
801       * Returns <code>false</code>, indicating that there are pixels inside
802       * the area of this border where the background shines through.
803       *
804       * @return <code>false</code>.
805       */
806      public boolean isBorderOpaque()
807      {
808        /* Note that the AbstractBorder.isBorderOpaque would also return
809         * false, so there is actually no need to override the inherited
810         * implementation. However, GNU Classpath strives for exact
811         * compatibility with the Sun reference implementation, which
812         * overrides isBorderOpaque for unknown reasons.
813         */
814        return false;
815      }
816    
817    
818      /**
819       * Returns the text of the title.
820       *
821       * @return the title text, or <code>null</code> if no title is
822       *         displayed.
823       */
824      public String getTitle()
825      {
826        return title;
827      }
828    
829    
830      /**
831       * Retrieves the border underneath the title. If no border has been
832       * set, or if it has been set to<code>null</code>, the current
833       * {@link javax.swing.LookAndFeel} will be asked for a border
834       * using the key <code>TitledBorder.border</code>.
835       *
836       * @return a border, or <code>null</code> if the current LookAndFeel
837       *         does not provide a border for the key
838       *         <code>TitledBorder.border</code>.
839       *
840       * @see javax.swing.UIManager#getBorder(Object)
841       */
842      public Border getBorder()
843      {
844        if (border != null)
845          return border;
846    
847        return UIManager.getBorder("TitledBorder.border");
848      }
849    
850    
851      /**
852       * Returns the vertical position of the title text in relation
853       * to the border.
854       *
855       * @return one of the values {@link #ABOVE_TOP}, {@link #TOP},
856       *         {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM}, {@link #BOTTOM},
857       *         {@link #BELOW_BOTTOM}, or {@link #DEFAULT_POSITION}.
858       */
859      public int getTitlePosition()
860      {
861        return titlePosition;
862      }
863    
864    
865      /**
866       * Returns the horizontal alignment of the title text in relation to
867       * the border.
868       *
869       * @return one of the values {@link #LEFT}, {@link #CENTER}, {@link
870       *         #RIGHT}, {@link #LEADING}, {@link #TRAILING}, or {@link
871       *         #DEFAULT_JUSTIFICATION}.
872       */
873      public int getTitleJustification()
874      {
875        return titleJustification;
876      }
877    
878    
879      /**
880       * Retrieves the font for displaying the title text. If no font has
881       * been set, or if it has been set to<code>null</code>, the current
882       * {@link javax.swing.LookAndFeel} will be asked for a font
883       * using the key <code>TitledBorder.font</code>.
884       *
885       * @return a font, or <code>null</code> if the current LookAndFeel
886       *         does not provide a font for the key
887       *         <code>TitledBorder.font</code>.
888       *
889       * @see javax.swing.UIManager#getFont(Object)
890       */
891      public Font getTitleFont()
892      {
893        if (titleFont != null)
894          return titleFont;
895    
896        return UIManager.getFont("TitledBorder.font");
897      }
898    
899    
900      /**
901       * Retrieves the color for displaying the title text. If no color has
902       * been set, or if it has been set to<code>null</code>, the current
903       * {@link javax.swing.LookAndFeel} will be asked for a color
904       * using the key <code>TitledBorder.titleColor</code>.
905       *
906       * @return a color, or <code>null</code> if the current LookAndFeel
907       *         does not provide a color for the key
908       *         <code>TitledBorder.titleColor</code>.
909       *
910       * @see javax.swing.UIManager#getColor(Object)
911       */
912      public Color getTitleColor()
913      {
914        if (titleColor != null)
915          return titleColor;
916    
917        return UIManager.getColor("TitledBorder.titleColor");
918      }
919    
920    
921      /**
922       * Sets the text of the title.
923       *
924       * @param title the new title text, or <code>null</code> for displaying
925       *        no text at all.
926       */
927      public void setTitle(String title)
928      {
929        // Swing borders are not JavaBeans, thus no need to fire an event.
930        this.title = title;
931      }
932    
933    
934      /**
935       * Sets the border underneath the title.
936       *
937       * @param border a border, or <code>null</code> to use the
938       *        border that is supplied by the current LookAndFeel.
939       *
940       * @see #getBorder()
941       */
942      public void setBorder(Border border)
943      {
944        // Swing borders are not JavaBeans, thus no need to fire an event.
945        this.border = border;
946      }
947    
948    
949      /**
950       * Sets the vertical position of the title text in relation
951       * to the border.
952       *
953       * @param titlePosition one of the values {@link #ABOVE_TOP},
954       *        {@link #TOP}, {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM},
955       *        {@link #BOTTOM}, {@link #BELOW_BOTTOM},
956       *        or {@link #DEFAULT_POSITION}.
957       *
958       * @throws IllegalArgumentException if an unsupported value is passed
959       *         for <code>titlePosition</code>.
960       */
961      public void setTitlePosition(int titlePosition)
962      {
963        if ((titlePosition < DEFAULT_POSITION) || (titlePosition > BELOW_BOTTOM))
964          throw new IllegalArgumentException(titlePosition
965              + " is not a valid title position.");
966    
967        // Swing borders are not JavaBeans, thus no need to fire an event.
968        this.titlePosition = titlePosition;
969      }
970    
971    
972      /**
973       * Sets the horizontal alignment of the title text in relation to the border.
974       *
975       * @param titleJustification the new alignment, which must be one of
976       *        {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
977       *        {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
978       *
979       * @throws IllegalArgumentException if an unsupported value is passed
980       *         for <code>titleJustification</code>.
981       */
982      public void setTitleJustification(int titleJustification)
983      {
984        if ((titleJustification < DEFAULT_JUSTIFICATION)
985            || (titleJustification > TRAILING))
986          throw new IllegalArgumentException(titleJustification
987              + " is not a valid title justification.");
988    
989        // Swing borders are not JavaBeans, thus no need to fire an event.
990        this.titleJustification = titleJustification;
991      }
992    
993    
994      /**
995       * Sets the font for displaying the title text.
996       *
997       * @param titleFont the font, or <code>null</code> to use the font
998       *        provided by the current {@link javax.swing.LookAndFeel}.
999       *
1000       * @see #getTitleFont()
1001       */
1002      public void setTitleFont(Font titleFont)
1003      {
1004        // Swing borders are not JavaBeans, thus no need to fire an event.
1005        this.titleFont = titleFont;
1006      }
1007    
1008    
1009      /**
1010       * Sets the color for displaying the title text.
1011       *
1012       * @param titleColor the color, or <code>null</code> to use the color
1013       *        provided by the current {@link javax.swing.LookAndFeel}.
1014       *
1015       * @see #getTitleColor()
1016       */
1017      public void setTitleColor(Color titleColor)
1018      {
1019        // Swing borders are not JavaBeans, thus no need to fire an event.
1020        this.titleColor = titleColor;
1021      }
1022    
1023    
1024      /**
1025       * Calculates the minimum size needed for displaying the border
1026       * and its title.
1027       *
1028       * @param c the Component for which this TitledBorder constitutes
1029       *        a border.
1030       *
1031       * @return The minimum size.
1032       */
1033      public Dimension getMinimumSize(Component c)
1034      {
1035        Insets i = getBorderInsets(c);
1036        Dimension minSize = new Dimension(i.left + i.right, i.top + i.bottom);
1037        Font font = getFont(c);
1038        FontMetrics fm = c.getFontMetrics(font);
1039        int titleWidth = fm.stringWidth(getTitle());
1040        switch (getTitlePosition())
1041        {
1042          case ABOVE_TOP:
1043          case BELOW_BOTTOM:
1044            minSize.width = Math.max(minSize.width, titleWidth);
1045            break;
1046          case BELOW_TOP:
1047          case ABOVE_BOTTOM:
1048          case TOP:
1049          case BOTTOM:
1050          case DEFAULT_POSITION:
1051          default:
1052            minSize.width += titleWidth;
1053        }
1054        return minSize;
1055      }
1056    
1057    
1058      /**
1059       * Returns the font that is used for displaying the title text for
1060       * a given Component.
1061       *
1062       * @param c the Component for which this TitledBorder is the border.
1063       *
1064       * @return The font returned by {@link #getTitleFont()}, or a fallback
1065       *         if {@link #getTitleFont()} returned <code>null</code>.
1066       */
1067      protected Font getFont(Component c)
1068      {
1069        Font f;
1070    
1071        f = getTitleFont();
1072        if (f != null)
1073          return f;
1074    
1075        return new Font("Dialog", Font.PLAIN, 12);
1076      }
1077    
1078    }