1 /*
   2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.border;
  26 
  27 import java.awt.Color;
  28 import java.awt.Component;
  29 import java.awt.Dimension;
  30 import java.awt.Font;
  31 import java.awt.Graphics;
  32 import java.awt.Graphics2D;
  33 import java.awt.Insets;
  34 import java.awt.Rectangle;
  35 import java.awt.geom.Path2D;
  36 import java.beans.ConstructorProperties;
  37 import java.beans.PropertyChangeEvent;
  38 import java.beans.PropertyChangeListener;
  39 import java.lang.ref.WeakReference;
  40 import javax.swing.JComponent;
  41 import javax.swing.JLabel;
  42 import javax.swing.UIManager;
  43 import javax.swing.plaf.basic.BasicHTML;
  44 
  45 import jdk.internal.ref.CleanerFactory;
  46 
  47 /**
  48  * A class which implements an arbitrary border
  49  * with the addition of a String title in a
  50  * specified position and justification.
  51  * <p>
  52  * If the border, font, or color property values are not
  53  * specified in the constructor or by invoking the appropriate
  54  * set methods, the property values will be defined by the current
  55  * look and feel, using the following property names in the
  56  * Defaults Table:
  57  * <ul>
  58  * <li>&quot;TitledBorder.border&quot;
  59  * <li>&quot;TitledBorder.font&quot;
  60  * <li>&quot;TitledBorder.titleColor&quot;
  61  * </ul>
  62  * <p>
  63  * <strong>Warning:</strong>
  64  * Serialized objects of this class will not be compatible with
  65  * future Swing releases. The current serialization support is
  66  * appropriate for short term storage or RMI between applications running
  67  * the same version of Swing.  As of 1.4, support for long term storage
  68  * of all JavaBeans&trade;
  69  * has been added to the <code>java.beans</code> package.
  70  * Please see {@link java.beans.XMLEncoder}.
  71  *
  72  * @author David Kloba
  73  * @author Amy Fowler
  74  */
  75 @SuppressWarnings("serial")
  76 public class TitledBorder extends AbstractBorder
  77 {
  78     /**
  79      * The title the border should display.
  80      */
  81     protected String title;
  82     /**
  83      * The border.
  84      */
  85     protected Border border;
  86     /**
  87      * The position for the title.
  88      */
  89     protected int titlePosition;
  90     /**
  91      * The justification for the title.
  92      */
  93     protected int titleJustification;
  94     /**
  95      * The font for rendering the title.
  96      */
  97     protected Font titleFont;
  98     /**
  99      * The color of the title.
 100      */
 101     protected Color titleColor;
 102 
 103     private final JLabel label;
 104 
 105     /**
 106      * Use the default vertical orientation for the title text.
 107      */
 108     public static final int     DEFAULT_POSITION        = 0;
 109     /** Position the title above the border's top line. */
 110     public static final int     ABOVE_TOP               = 1;
 111     /** Position the title in the middle of the border's top line. */
 112     public static final int     TOP                     = 2;
 113     /** Position the title below the border's top line. */
 114     public static final int     BELOW_TOP               = 3;
 115     /** Position the title above the border's bottom line. */
 116     public static final int     ABOVE_BOTTOM            = 4;
 117     /** Position the title in the middle of the border's bottom line. */
 118     public static final int     BOTTOM                  = 5;
 119     /** Position the title below the border's bottom line. */
 120     public static final int     BELOW_BOTTOM            = 6;
 121 
 122     /**
 123      * Use the default justification for the title text.
 124      */
 125     public static final int     DEFAULT_JUSTIFICATION   = 0;
 126     /** Position title text at the left side of the border line. */
 127     public static final int     LEFT                    = 1;
 128     /** Position title text in the center of the border line. */
 129     public static final int     CENTER                  = 2;
 130     /** Position title text at the right side of the border line. */
 131     public static final int     RIGHT                   = 3;
 132     /** Position title text at the left side of the border line
 133      *  for left to right orientation, at the right side of the
 134      *  border line for right to left orientation.
 135      */
 136     public static final int     LEADING = 4;
 137     /** Position title text at the right side of the border line
 138      *  for left to right orientation, at the left side of the
 139      *  border line for right to left orientation.
 140      */
 141     public static final int     TRAILING = 5;
 142 
 143     /**
 144      * Space between the border and the component's edge
 145      */
 146     protected static final int EDGE_SPACING = 2;
 147 
 148     /**
 149      * Space between the border and text
 150      */
 151     protected static final int TEXT_SPACING = 2;
 152 
 153     /**
 154      * Horizontal inset of text that is left or right justified
 155      */
 156     protected static final int TEXT_INSET_H = 5;
 157 
 158     /**
 159      * Creates a TitledBorder instance.
 160      *
 161      * @param title  the title the border should display
 162      */
 163     public TitledBorder(String title) {
 164         this(null, title, LEADING, DEFAULT_POSITION, null, null);
 165     }
 166 
 167     /**
 168      * Creates a TitledBorder instance with the specified border
 169      * and an empty title.
 170      *
 171      * @param border  the border
 172      */
 173     public TitledBorder(Border border) {
 174         this(border, "", LEADING, DEFAULT_POSITION, null, null);
 175     }
 176 
 177     /**
 178      * Creates a TitledBorder instance with the specified border
 179      * and title.
 180      *
 181      * @param border  the border
 182      * @param title  the title the border should display
 183      */
 184     public TitledBorder(Border border, String title) {
 185         this(border, title, LEADING, DEFAULT_POSITION, null, null);
 186     }
 187 
 188     /**
 189      * Creates a TitledBorder instance with the specified border,
 190      * title, title-justification, and title-position.
 191      *
 192      * @param border  the border
 193      * @param title  the title the border should display
 194      * @param titleJustification the justification for the title
 195      * @param titlePosition the position for the title
 196      */
 197     public TitledBorder(Border border,
 198                         String title,
 199                         int titleJustification,
 200                         int titlePosition) {
 201         this(border, title, titleJustification,
 202              titlePosition, null, null);
 203     }
 204 
 205     /**
 206      * Creates a TitledBorder instance with the specified border,
 207      * title, title-justification, title-position, and title-font.
 208      *
 209      * @param border  the border
 210      * @param title  the title the border should display
 211      * @param titleJustification the justification for the title
 212      * @param titlePosition the position for the title
 213      * @param titleFont the font for rendering the title
 214      */
 215     public TitledBorder(Border border,
 216                         String title,
 217                         int titleJustification,
 218                         int titlePosition,
 219                         Font titleFont) {
 220         this(border, title, titleJustification,
 221              titlePosition, titleFont, null);
 222     }
 223 
 224     /**
 225      * Creates a TitledBorder instance with the specified border,
 226      * title, title-justification, title-position, title-font, and
 227      * title-color.
 228      *
 229      * @param border  the border
 230      * @param title  the title the border should display
 231      * @param titleJustification the justification for the title
 232      * @param titlePosition the position for the title
 233      * @param titleFont the font of the title
 234      * @param titleColor the color of the title
 235      */
 236     @ConstructorProperties({"border", "title", "titleJustification", "titlePosition", "titleFont", "titleColor"})
 237     public TitledBorder(Border border,
 238                         String title,
 239                         int titleJustification,
 240                         int titlePosition,
 241                         Font titleFont,
 242                         Color titleColor) {
 243         this.title = title;
 244         this.border = border;
 245         this.titleFont = titleFont;
 246         this.titleColor = titleColor;
 247 
 248         setTitleJustification(titleJustification);
 249         setTitlePosition(titlePosition);
 250 
 251         this.label = new JLabel();
 252         this.label.setOpaque(false);
 253         this.label.putClientProperty(BasicHTML.propertyKey, null);
 254         installPropertyChangeListeners();
 255     }
 256 
 257     /**
 258      * Paints the border for the specified component with the
 259      * specified position and size.
 260      * @param c the component for which this border is being painted
 261      * @param g the paint graphics
 262      * @param x the x position of the painted border
 263      * @param y the y position of the painted border
 264      * @param width the width of the painted border
 265      * @param height the height of the painted border
 266      */
 267     public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
 268         Border border = getBorder();
 269         String title = getTitle();
 270         if ((title != null) && !title.isEmpty()) {
 271             int edge = (border instanceof TitledBorder) ? 0 : EDGE_SPACING;
 272             JLabel label = getLabel(c);
 273             Dimension size = label.getPreferredSize();
 274             Insets insets = getBorderInsets(border, c, new Insets(0, 0, 0, 0));
 275 
 276             int borderX = x + edge;
 277             int borderY = y + edge;
 278             int borderW = width - edge - edge;
 279             int borderH = height - edge - edge;
 280 
 281             int labelY = y;
 282             int labelH = size.height;
 283             int position = getPosition();
 284             switch (position) {
 285                 case ABOVE_TOP:
 286                     insets.left = 0;
 287                     insets.right = 0;
 288                     borderY += labelH - edge;
 289                     borderH -= labelH - edge;
 290                     break;
 291                 case TOP:
 292                     insets.top = edge + insets.top/2 - labelH/2;
 293                     if (insets.top < edge) {
 294                         borderY -= insets.top;
 295                         borderH += insets.top;
 296                     }
 297                     else {
 298                         labelY += insets.top;
 299                     }
 300                     break;
 301                 case BELOW_TOP:
 302                     labelY += insets.top + edge;
 303                     break;
 304                 case ABOVE_BOTTOM:
 305                     labelY += height - labelH - insets.bottom - edge;
 306                     break;
 307                 case BOTTOM:
 308                     labelY += height - labelH;
 309                     insets.bottom = edge + (insets.bottom - labelH) / 2;
 310                     if (insets.bottom < edge) {
 311                         borderH += insets.bottom;
 312                     }
 313                     else {
 314                         labelY -= insets.bottom;
 315                     }
 316                     break;
 317                 case BELOW_BOTTOM:
 318                     insets.left = 0;
 319                     insets.right = 0;
 320                     labelY += height - labelH;
 321                     borderH -= labelH - edge;
 322                     break;
 323             }
 324             insets.left += edge + TEXT_INSET_H;
 325             insets.right += edge + TEXT_INSET_H;
 326 
 327             int labelX = x;
 328             int labelW = width - insets.left - insets.right;
 329             if (labelW > size.width) {
 330                 labelW = size.width;
 331             }
 332             switch (getJustification(c)) {
 333                 case LEFT:
 334                     labelX += insets.left;
 335                     break;
 336                 case RIGHT:
 337                     labelX += width - insets.right - labelW;
 338                     break;
 339                 case CENTER:
 340                     labelX += (width - labelW) / 2;
 341                     break;
 342             }
 343 
 344             if (border != null) {
 345                 if ((position != TOP) && (position != BOTTOM)) {
 346                     border.paintBorder(c, g, borderX, borderY, borderW, borderH);
 347                 }
 348                 else {
 349                     Graphics g2 = g.create();
 350                     if (g2 instanceof Graphics2D) {
 351                         Graphics2D g2d = (Graphics2D) g2;
 352                         Path2D path = new Path2D.Float();
 353                         path.append(new Rectangle(borderX, borderY, borderW, labelY - borderY), false);
 354                         path.append(new Rectangle(borderX, labelY, labelX - borderX - TEXT_SPACING, labelH), false);
 355                         path.append(new Rectangle(labelX + labelW + TEXT_SPACING, labelY, borderX - labelX + borderW - labelW - TEXT_SPACING, labelH), false);
 356                         path.append(new Rectangle(borderX, labelY + labelH, borderW, borderY - labelY + borderH - labelH), false);
 357                         g2d.clip(path);
 358                     }
 359                     border.paintBorder(c, g2, borderX, borderY, borderW, borderH);
 360                     g2.dispose();
 361                 }
 362             }
 363             g.translate(labelX, labelY);
 364             label.setSize(labelW, labelH);
 365             label.paint(g);
 366             g.translate(-labelX, -labelY);
 367         }
 368         else if (border != null) {
 369             border.paintBorder(c, g, x, y, width, height);
 370         }
 371     }
 372 
 373     /**
 374      * Reinitialize the insets parameter with this Border's current Insets.
 375      * @param c the component for which this border insets value applies
 376      * @param insets the object to be reinitialized
 377      */
 378     public Insets getBorderInsets(Component c, Insets insets) {
 379         Border border = getBorder();
 380         insets = getBorderInsets(border, c, insets);
 381 
 382         String title = getTitle();
 383         if ((title != null) && !title.isEmpty()) {
 384             int edge = (border instanceof TitledBorder) ? 0 : EDGE_SPACING;
 385             JLabel label = getLabel(c);
 386             Dimension size = label.getPreferredSize();
 387 
 388             switch (getPosition()) {
 389                 case ABOVE_TOP:
 390                     insets.top += size.height - edge;
 391                     break;
 392                 case TOP: {
 393                     if (insets.top < size.height) {
 394                         insets.top = size.height - edge;
 395                     }
 396                     break;
 397                 }
 398                 case BELOW_TOP:
 399                     insets.top += size.height;
 400                     break;
 401                 case ABOVE_BOTTOM:
 402                     insets.bottom += size.height;
 403                     break;
 404                 case BOTTOM: {
 405                     if (insets.bottom < size.height) {
 406                         insets.bottom = size.height - edge;
 407                     }
 408                     break;
 409                 }
 410                 case BELOW_BOTTOM:
 411                     insets.bottom += size.height - edge;
 412                     break;
 413             }
 414             insets.top += edge + TEXT_SPACING;
 415             insets.left += edge + TEXT_SPACING;
 416             insets.right += edge + TEXT_SPACING;
 417             insets.bottom += edge + TEXT_SPACING;
 418         }
 419         return insets;
 420     }
 421 
 422     /**
 423      * Returns whether or not the border is opaque.
 424      */
 425     public boolean isBorderOpaque() {
 426         return false;
 427     }
 428 
 429     /**
 430      * Returns the title of the titled border.
 431      *
 432      * @return the title of the titled border
 433      */
 434     public String getTitle() {
 435         return title;
 436     }
 437 
 438     /**
 439      * Returns the border of the titled border.
 440      *
 441      * @return the border of the titled border
 442      */
 443     public Border getBorder() {
 444         return border != null
 445                 ? border
 446                 : UIManager.getBorder("TitledBorder.border");
 447     }
 448 
 449     /**
 450      * Returns the title-position of the titled border.
 451      *
 452      * @return the title-position of the titled border
 453      */
 454     public int getTitlePosition() {
 455         return titlePosition;
 456     }
 457 
 458     /**
 459      * Returns the title-justification of the titled border.
 460      *
 461      * @return the title-justification of the titled border
 462      */
 463     public int getTitleJustification() {
 464         return titleJustification;
 465     }
 466 
 467     /**
 468      * Returns the title-font of the titled border.
 469      *
 470      * @return the title-font of the titled border
 471      */
 472     public Font getTitleFont() {
 473         return titleFont == null ? UIManager.getFont("TitledBorder.font") : titleFont;
 474     }
 475 
 476     /**
 477      * Returns the title-color of the titled border.
 478      *
 479      * @return the title-color of the titled border
 480      */
 481     public Color getTitleColor() {
 482         return titleColor == null ? UIManager.getColor("TitledBorder.titleColor") : titleColor;
 483     }
 484 
 485 
 486     // REMIND(aim): remove all or some of these set methods?
 487 
 488     /**
 489      * Sets the title of the titled border.
 490      * @param title  the title for the border
 491      */
 492     public void setTitle(String title) {
 493         this.title = title;
 494     }
 495 
 496     /**
 497      * Sets the border of the titled border.
 498      * @param border the border
 499      */
 500     public void setBorder(Border border) {
 501         this.border = border;
 502     }
 503 
 504     /**
 505      * Sets the title-position of the titled border.
 506      * @param titlePosition the position for the border
 507      */
 508     public void setTitlePosition(int titlePosition) {
 509         switch (titlePosition) {
 510             case ABOVE_TOP:
 511             case TOP:
 512             case BELOW_TOP:
 513             case ABOVE_BOTTOM:
 514             case BOTTOM:
 515             case BELOW_BOTTOM:
 516             case DEFAULT_POSITION:
 517                 this.titlePosition = titlePosition;
 518                 break;
 519             default:
 520                 throw new IllegalArgumentException(titlePosition +
 521                         " is not a valid title position.");
 522         }
 523     }
 524 
 525     /**
 526      * Sets the title-justification of the titled border.
 527      * @param titleJustification the justification for the border
 528      */
 529     public void setTitleJustification(int titleJustification) {
 530         switch (titleJustification) {
 531             case DEFAULT_JUSTIFICATION:
 532             case LEFT:
 533             case CENTER:
 534             case RIGHT:
 535             case LEADING:
 536             case TRAILING:
 537                 this.titleJustification = titleJustification;
 538                 break;
 539             default:
 540                 throw new IllegalArgumentException(titleJustification +
 541                         " is not a valid title justification.");
 542         }
 543     }
 544 
 545     /**
 546      * Sets the title-font of the titled border.
 547      * @param titleFont the font for the border title
 548      */
 549     public void setTitleFont(Font titleFont) {
 550         this.titleFont = titleFont;
 551     }
 552 
 553     /**
 554      * Sets the title-color of the titled border.
 555      * @param titleColor the color for the border title
 556      */
 557     public void setTitleColor(Color titleColor) {
 558         this.titleColor = titleColor;
 559     }
 560 
 561     /**
 562      * Returns the minimum dimensions this border requires
 563      * in order to fully display the border and title.
 564      * @param c the component where this border will be drawn
 565      * @return the {@code Dimension} object
 566      */
 567     public Dimension getMinimumSize(Component c) {
 568         Insets insets = getBorderInsets(c);
 569         Dimension minSize = new Dimension(insets.right+insets.left,
 570                                           insets.top+insets.bottom);
 571         String title = getTitle();
 572         if ((title != null) && !title.isEmpty()) {
 573             JLabel label = getLabel(c);
 574             Dimension size = label.getPreferredSize();
 575 
 576             int position = getPosition();
 577             if ((position != ABOVE_TOP) && (position != BELOW_BOTTOM)) {
 578                 minSize.width += size.width;
 579             }
 580             else if (minSize.width < size.width) {
 581                 minSize.width += size.width;
 582             }
 583         }
 584         return minSize;
 585     }
 586 
 587     /**
 588      * Returns the baseline.
 589      *
 590      * @throws NullPointerException {@inheritDoc}
 591      * @throws IllegalArgumentException {@inheritDoc}
 592      * @see javax.swing.JComponent#getBaseline(int, int)
 593      * @since 1.6
 594      */
 595     public int getBaseline(Component c, int width, int height) {
 596         if (c == null) {
 597             throw new NullPointerException("Must supply non-null component");
 598         }
 599         if (width < 0) {
 600             throw new IllegalArgumentException("Width must be >= 0");
 601         }
 602         if (height < 0) {
 603             throw new IllegalArgumentException("Height must be >= 0");
 604         }
 605         Border border = getBorder();
 606         String title = getTitle();
 607         if ((title != null) && !title.isEmpty()) {
 608             int edge = (border instanceof TitledBorder) ? 0 : EDGE_SPACING;
 609             JLabel label = getLabel(c);
 610             Dimension size = label.getPreferredSize();
 611             Insets insets = getBorderInsets(border, c, new Insets(0, 0, 0, 0));
 612 
 613             int baseline = label.getBaseline(size.width, size.height);
 614             switch (getPosition()) {
 615                 case ABOVE_TOP:
 616                     return baseline;
 617                 case TOP:
 618                     insets.top = edge + (insets.top - size.height) / 2;
 619                     return (insets.top < edge)
 620                             ? baseline
 621                             : baseline + insets.top;
 622                 case BELOW_TOP:
 623                     return baseline + insets.top + edge;
 624                 case ABOVE_BOTTOM:
 625                     return baseline + height - size.height - insets.bottom - edge;
 626                 case BOTTOM:
 627                     insets.bottom = edge + (insets.bottom - size.height) / 2;
 628                     return (insets.bottom < edge)
 629                             ? baseline + height - size.height
 630                             : baseline + height - size.height + insets.bottom;
 631                 case BELOW_BOTTOM:
 632                     return baseline + height - size.height;
 633             }
 634         }
 635         return -1;
 636     }
 637 
 638     /**
 639      * Returns an enum indicating how the baseline of the border
 640      * changes as the size changes.
 641      *
 642      * @throws NullPointerException {@inheritDoc}
 643      * @see javax.swing.JComponent#getBaseline(int, int)
 644      * @since 1.6
 645      */
 646     public Component.BaselineResizeBehavior getBaselineResizeBehavior(
 647             Component c) {
 648         super.getBaselineResizeBehavior(c);
 649         switch (getPosition()) {
 650             case TitledBorder.ABOVE_TOP:
 651             case TitledBorder.TOP:
 652             case TitledBorder.BELOW_TOP:
 653                 return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
 654             case TitledBorder.ABOVE_BOTTOM:
 655             case TitledBorder.BOTTOM:
 656             case TitledBorder.BELOW_BOTTOM:
 657                 return JComponent.BaselineResizeBehavior.CONSTANT_DESCENT;
 658         }
 659         return Component.BaselineResizeBehavior.OTHER;
 660     }
 661 
 662     private int getPosition() {
 663         int position = getTitlePosition();
 664         if (position != DEFAULT_POSITION) {
 665             return position;
 666         }
 667         Object value = UIManager.get("TitledBorder.position");
 668         if (value instanceof Integer) {
 669             int i = (Integer) value;
 670             if ((0 < i) && (i <= 6)) {
 671                 return i;
 672             }
 673         }
 674         else if (value instanceof String) {
 675             String s = (String) value;
 676             if (s.equalsIgnoreCase("ABOVE_TOP")) {
 677                 return ABOVE_TOP;
 678             }
 679             if (s.equalsIgnoreCase("TOP")) {
 680                 return TOP;
 681             }
 682             if (s.equalsIgnoreCase("BELOW_TOP")) {
 683                 return BELOW_TOP;
 684             }
 685             if (s.equalsIgnoreCase("ABOVE_BOTTOM")) {
 686                 return ABOVE_BOTTOM;
 687             }
 688             if (s.equalsIgnoreCase("BOTTOM")) {
 689                 return BOTTOM;
 690             }
 691             if (s.equalsIgnoreCase("BELOW_BOTTOM")) {
 692                 return BELOW_BOTTOM;
 693             }
 694         }
 695         return TOP;
 696     }
 697 
 698     private int getJustification(Component c) {
 699         int justification = getTitleJustification();
 700         if ((justification == LEADING) || (justification == DEFAULT_JUSTIFICATION)) {
 701             return c.getComponentOrientation().isLeftToRight() ? LEFT : RIGHT;
 702         }
 703         if (justification == TRAILING) {
 704             return c.getComponentOrientation().isLeftToRight() ? RIGHT : LEFT;
 705         }
 706         return justification;
 707     }
 708 
 709     /**
 710      * Returns default font of the titled border.
 711      * @return default font of the titled border
 712      * @param c the component
 713      */
 714     protected Font getFont(Component c) {
 715         Font font = getTitleFont();
 716         if (font != null) {
 717             return font;
 718         }
 719         if (c != null) {
 720             font = c.getFont();
 721             if (font != null) {
 722                 return font;
 723             }
 724         }
 725         return new Font(Font.DIALOG, Font.PLAIN, 12);
 726     }
 727 
 728     private Color getColor(Component c) {
 729         Color color = getTitleColor();
 730         if (color != null) {
 731             return color;
 732         }
 733         return (c != null)
 734                 ? c.getForeground()
 735                 : null;
 736     }
 737 
 738     private JLabel getLabel(Component c) {
 739         this.label.setText(getTitle());
 740         this.label.setFont(getFont(c));
 741         this.label.setForeground(getColor(c));
 742         this.label.setComponentOrientation(c.getComponentOrientation());
 743         this.label.setEnabled(c.isEnabled());
 744         return this.label;
 745     }
 746 
 747     private static Insets getBorderInsets(Border border, Component c, Insets insets) {
 748         if (border == null) {
 749             insets.set(0, 0, 0, 0);
 750         }
 751         else if (border instanceof AbstractBorder) {
 752             AbstractBorder ab = (AbstractBorder) border;
 753             insets = ab.getBorderInsets(c, insets);
 754         }
 755         else {
 756             Insets i = border.getBorderInsets(c);
 757             insets.set(i.top, i.left, i.bottom, i.right);
 758         }
 759         return insets;
 760     }
 761 
 762     private void installPropertyChangeListeners() {
 763         final WeakReference<TitledBorder> weakReference = new WeakReference<TitledBorder>(this);
 764         final PropertyChangeListener listener = evt -> {
 765             TitledBorder tb = weakReference.get();
 766             String prop = evt.getPropertyName();
 767             if (tb != null && ("lookAndFeel".equals(prop) || "LabelUI".equals(prop))) {
 768                 tb.label.updateUI();
 769             }
 770         };
 771 
 772         UIManager.addPropertyChangeListener(listener);
 773         UIManager.getDefaults().addPropertyChangeListener(listener);
 774         CleanerFactory.cleaner().register(this, () -> {
 775             UIManager.removePropertyChangeListener(listener);
 776             UIManager.getDefaults().removePropertyChangeListener(listener);
 777         });
 778     }
 779 }