001/* TitledBorder.java -- 
002   Copyright (C) 2003, 2004, 2005, 2006,  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.border;
040
041import java.awt.Color;
042import java.awt.Component;
043import java.awt.Dimension;
044import java.awt.Font;
045import java.awt.FontMetrics;
046import java.awt.Graphics;
047import java.awt.Insets;
048import java.awt.Point;
049import java.awt.Rectangle;
050
051import javax.swing.SwingUtilities;
052import 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 */
060public 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}