public static FontMetrics getFontMetrics(JComponent c, Graphics g, Font f) {
   FontMetrics fm = null;
   if (getJavaVersion() >= 1.6) {
     try {
       Class swingUtilities2Class = Class.forName("sun.swing.SwingUtilities2");
       Class classParams[] = {JComponent.class, Graphics.class, Font.class};
       Method m = swingUtilities2Class.getMethod("getFontMetrics", classParams);
       Object methodParams[] = {c, g, f};
       fm = (FontMetrics) m.invoke(null, methodParams);
     } catch (Exception ex) {
       // Nothing to do
     }
   }
   if (fm == null) {
     if (g == null) {
       if (c != null) {
         g = c.getGraphics();
       }
     }
     if (g != null) {
       if (f != null) {
         fm = g.getFontMetrics(f);
       } else {
         fm = g.getFontMetrics();
       }
     } else if (c != null) {
       if (f != null) {
         fm = c.getFontMetrics(f);
       } else {
         fm = c.getFontMetrics(c.getFont());
       }
     }
   }
   return fm;
 }
  @Override
  public void customizePainter(
      @NotNull JComponent component,
      @NotNull Collection<VcsRef> references,
      @Nullable VcsLogRefManager manager,
      @NotNull Color background,
      @NotNull Color foreground) {
    FontMetrics metrics = component.getFontMetrics(getReferenceFont());
    myHeight =
        metrics.getHeight()
            + RectanglePainter.TOP_TEXT_PADDING
            + RectanglePainter.BOTTOM_TEXT_PADDING;
    myWidth = 2 * PaintParameters.LABEL_PADDING;

    myLabels = ContainerUtil.newArrayList();
    if (manager == null) return;

    List<VcsRef> sorted = ContainerUtil.sorted(references, manager.getLabelsOrderComparator());

    for (Map.Entry<VcsRefType, Collection<VcsRef>> entry :
        ContainerUtil.groupBy(sorted, VcsRef::getType).entrySet()) {
      VcsRef ref = ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(entry.getValue()));
      String text = ref.getName() + (entry.getValue().size() > 1 ? " +" : "");
      myLabels.add(Pair.create(text, entry.getKey().getBackgroundColor()));

      myWidth +=
          myLabelPainter.calculateSize(text, metrics).getWidth() + PaintParameters.LABEL_PADDING;
    }
  }
  public Rectangle getVisualBounds(JComponent c, int type, int width, int height) {
    Rectangle bounds = new Rectangle(0, 0, width, height);
    if (type == VisuallyLayoutable.CLIP_BOUNDS) {
      return bounds;
    }

    AbstractButton b = (AbstractButton) c;

    if (type == VisuallyLayoutable.COMPONENT_BOUNDS
        && b.getBorder() != null
        && b.isBorderPainted()) {
      Border border = b.getBorder();
      if (border instanceof BackgroundBorder) {
        border = ((BackgroundBorder) border).getBackgroundBorder();
        if (border instanceof VisualMargin) {
          InsetsUtil.subtractInto(((VisualMargin) border).getVisualMargin(c), bounds);
        } else if (border instanceof QuaquaButtonBorder) {
          InsetsUtil.subtractInto(((QuaquaButtonBorder) border).getVisualMargin(c), bounds);
        }
      }
      return bounds;
    }

    String text = b.getText();
    boolean isEmpty = (text == null || text.length() == 0);
    if (isEmpty) {
      text = " ";
    }
    Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon();

    if ((icon == null) && (text == null)) {
      return null;
    }

    FontMetrics fm = c.getFontMetrics(c.getFont());
    Insets insets = c.getInsets(viewInsets);

    viewR.x = insets.left;
    viewR.y = insets.top;
    viewR.width = width - (insets.left + insets.right);
    viewR.height = height - (insets.top + insets.bottom);

    iconR.x = iconR.y = iconR.width = iconR.height = 0;
    textR.x = textR.y = textR.width = textR.height = 0;

    String clippedText = layoutCL(b, fm, text, icon, viewR, iconR, textR);

    Rectangle textBounds = Fonts.getPerceivedBounds(text, c.getFont(), c);
    if (isEmpty) {
      textBounds.width = 0;
    }
    int ascent = fm.getAscent();
    textR.x += textBounds.x;
    textR.width = textBounds.width;
    textR.y += ascent + textBounds.y;
    textR.height -= fm.getHeight() - textBounds.height;

    bounds.setBounds(textR);
    return bounds;
  }
  public Dimension getPreferredSize(JComponent c) {
    int fontHeight = 0;
    Font font = c.getFont();
    if (font != null) {
      fontHeight = c.getFontMetrics(font).getHeight();
    }

    return new Dimension(0, fontHeight / 2 + 2);
  }
  private static String getTitle2Text(String fullText, JComponent pathLabel) {
    int labelWidth = pathLabel.getWidth();
    if (fullText == null || fullText.length() == 0) return " ";

    String home = SystemProperties.getUserHome();
    if (FileUtil.startsWith(fullText, home)) {
      fullText = "~" + fullText.substring(home.length());
    }

    while (pathLabel.getFontMetrics(pathLabel.getFont()).stringWidth(fullText) > labelWidth) {
      int sep = fullText.indexOf(File.separatorChar, 4);
      if (sep < 0) return fullText;
      fullText = "..." + fullText.substring(sep);
    }

    return fullText;
  }
  public void ellipsifyValues(int width) {
    int maxKeyWidth = 0;
    int[] maxAdditionalColumnsWidth = new int[additionalColumns];

    for (JComponent keyComp : keyKeyComponentMap.values()) {
      maxKeyWidth = Math.max(maxKeyWidth, keyComp.getPreferredSize().width);
    }

    for (String k : keyValueComponentMap.keySet()) {
      for (int i = 0; i < additionalColumns; i++) {
        JComponent extraComp = getAdditionalColumn(k, i);
        if (extraComp != null) {
          maxAdditionalColumnsWidth[i] =
              Math.max(maxAdditionalColumnsWidth[i], extraComp.getPreferredSize().width);
        }
      }
    }

    width -= maxKeyWidth;
    for (int i : maxAdditionalColumnsWidth) {
      width -= i;
    }

    for (String k : keyValueComponentMap.keySet()) {
      JComponent comp = getComponent(k);
      if (comp != null) {
        int avail = width;
        /*for (int i = 0; i < additionalColumns; i++) {
        JComponent extraComp = getAdditionalColumn(k, i);
        if (extraComp != null) {
        avail -= extraComp.getPreferredSize().width;
        }
        }*/

        if (comp instanceof JLabel) {

          while (avail < comp.getPreferredSize().width) {
            String text = ((JLabel) comp).getText();
            if (text.endsWith("…")) {
              if (text.length() > 2) {
                // Already truncated.
                text = text.substring(0, text.length() - 2);
              } else {
                break; // As short as we can get.  Can't truncate any more.
              }
            } else {
              // Reasonable first truncation is to trim off the last word.
              int spacePos = text.lastIndexOf(' ');
              if (spacePos > 0) {
                text = text.substring(0, spacePos);
              } else {
                FontMetrics fm = comp.getFontMetrics(comp.getFont());

                while (fm.stringWidth(text + "…") > avail) {
                  // causes StringIndexOutOfBoundsException if text is empty.
                  if (text == null || text.length() < 2) {
                    LOG.info("Text is null or empty in KeyValuePairPanel");
                    break;
                  }
                  text = text.substring(0, text.length() - 2);
                }
                // text = text + "…";

                // text = text.substring(0, text.length() - 1);
              }
            }
            ((JLabel) comp).setText(text + "…");
          }
        } else {
          // Can't truncate, but we can force the preferred size.
          comp.setMaximumSize(new Dimension(avail, comp.getPreferredSize().height));
        }
      }
    }
  }
  public static void main(String args[]) {
    JComponent ch = new JComponent() {};
    ch.getAccessibleContext();
    ch.isFocusTraversable();
    ch.setEnabled(false);
    ch.setEnabled(true);
    ch.requestFocus();
    ch.requestFocusInWindow();
    ch.getPreferredSize();
    ch.getMaximumSize();
    ch.getMinimumSize();
    ch.contains(1, 2);
    Component c1 = ch.add(new Component() {});
    Component c2 = ch.add(new Component() {});
    Component c3 = ch.add(new Component() {});
    Insets ins = ch.getInsets();
    ch.getAlignmentY();
    ch.getAlignmentX();
    ch.getGraphics();
    ch.setVisible(false);
    ch.setVisible(true);
    ch.setForeground(Color.red);
    ch.setBackground(Color.red);
    for (String font : Toolkit.getDefaultToolkit().getFontList()) {
      for (int j = 8; j < 17; j++) {
        Font f1 = new Font(font, Font.PLAIN, j);
        Font f2 = new Font(font, Font.BOLD, j);
        Font f3 = new Font(font, Font.ITALIC, j);
        Font f4 = new Font(font, Font.BOLD | Font.ITALIC, j);

        ch.setFont(f1);
        ch.setFont(f2);
        ch.setFont(f3);
        ch.setFont(f4);

        ch.getFontMetrics(f1);
        ch.getFontMetrics(f2);
        ch.getFontMetrics(f3);
        ch.getFontMetrics(f4);
      }
    }
    ch.enable();
    ch.disable();
    ch.reshape(10, 10, 10, 10);
    ch.getBounds(new Rectangle(1, 1, 1, 1));
    ch.getSize(new Dimension(1, 2));
    ch.getLocation(new Point(1, 2));
    ch.getX();
    ch.getY();
    ch.getWidth();
    ch.getHeight();
    ch.isOpaque();
    ch.isValidateRoot();
    ch.isOptimizedDrawingEnabled();
    ch.isDoubleBuffered();
    ch.getComponentCount();
    ch.countComponents();
    ch.getComponent(1);
    ch.getComponent(2);
    Component[] cs = ch.getComponents();
    ch.getLayout();
    ch.setLayout(new FlowLayout());
    ch.doLayout();
    ch.layout();
    ch.invalidate();
    ch.validate();
    ch.remove(0);
    ch.remove(c2);
    ch.removeAll();
    ch.preferredSize();
    ch.minimumSize();
    ch.getComponentAt(1, 2);
    ch.locate(1, 2);
    ch.getComponentAt(new Point(1, 2));
    ch.isFocusCycleRoot(new Container());
    ch.transferFocusBackward();
    ch.setName("goober");
    ch.getName();
    ch.getParent();
    ch.getGraphicsConfiguration();
    ch.getTreeLock();
    ch.getToolkit();
    ch.isValid();
    ch.isDisplayable();
    ch.isVisible();
    ch.isShowing();
    ch.isEnabled();
    ch.enable(false);
    ch.enable(true);
    ch.enableInputMethods(false);
    ch.enableInputMethods(true);
    ch.show();
    ch.show(false);
    ch.show(true);
    ch.hide();
    ch.getForeground();
    ch.isForegroundSet();
    ch.getBackground();
    ch.isBackgroundSet();
    ch.getFont();
    ch.isFontSet();
    Container c = new Container();
    c.add(ch);
    ch.getLocale();
    for (Locale locale : Locale.getAvailableLocales()) ch.setLocale(locale);

    ch.getColorModel();
    ch.getLocation();

    boolean exceptions = false;
    try {
      ch.getLocationOnScreen();
    } catch (IllegalComponentStateException e) {
      exceptions = true;
    }
    if (!exceptions)
      throw new RuntimeException("IllegalComponentStateException did not occur when expected");

    ch.location();
    ch.setLocation(1, 2);
    ch.move(1, 2);
    ch.setLocation(new Point(1, 2));
    ch.getSize();
    ch.size();
    ch.setSize(1, 32);
    ch.resize(1, 32);
    ch.setSize(new Dimension(1, 32));
    ch.resize(new Dimension(1, 32));
    ch.getBounds();
    ch.bounds();
    ch.setBounds(10, 10, 10, 10);
    ch.setBounds(new Rectangle(10, 10, 10, 10));
    ch.isLightweight();
    ch.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
    ch.getCursor();
    ch.isCursorSet();
    ch.inside(1, 2);
    ch.contains(new Point(1, 2));
    ch.isFocusable();
    ch.setFocusable(true);
    ch.setFocusable(false);
    ch.transferFocus();
    ch.getFocusCycleRootAncestor();
    ch.nextFocus();
    ch.transferFocusUpCycle();
    ch.hasFocus();
    ch.isFocusOwner();
    ch.toString();
    ch.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
    ch.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    ch.setComponentOrientation(ComponentOrientation.UNKNOWN);
    ch.getComponentOrientation();
  }