public void paint(Graphics g, JComponent c) {
    AbstractButton b = (AbstractButton) c;
    ButtonModel model = b.getModel();

    String text = layout(b, SwingUtilities2.getFontMetrics(b, g), b.getWidth(), b.getHeight());

    clearTextShiftOffset();

    // perform UI specific press action, e.g. Windows L&F shifts text
    if (model.isArmed() && model.isPressed()) {
      paintButtonPressed(g, b);
    }

    // Paint the Icon
    if (b.getIcon() != null) {
      paintIcon(g, c, iconRect);
    }

    if (text != null && !text.equals("")) {
      View v = (View) c.getClientProperty(BasicHTML.propertyKey);
      if (v != null) {
        v.paint(g, textRect);
      } else {
        paintText(g, b, textRect, text);
      }
    }

    if (b.isFocusPainted() && b.hasFocus()) {
      // paint UI specific focus
      paintFocus(g, b, viewRect, textRect, iconRect);
    }
  }
  /**
   * This checks to see if updateLayout() needs to be called. Generally this returns true if the
   * width, height, or segment position has changed.
   */
  protected boolean isLayoutValid(AbstractButton button) {
    int horizontalPosition = getHorizontalPosition(button);
    int verticalPosition = getVerticalPosition(button);
    int width = button.getWidth();
    int height = button.getHeight();

    String key = width + " " + height + " " + horizontalPosition + " " + verticalPosition;

    String oldKey = (String) button.getClientProperty("FilledButtonUI.validationKey");
    if (oldKey == null) return false;

    if (oldKey.equals(key)) return true;

    return false;
  }
  public void layoutComponents() {
    setLayout(new BorderLayout());
    add(main, BorderLayout.CENTER);

    if (popperIsVisible) {
      if (getPopperButtonLocation() == RIGHT) {
        popper.setPreferredSize(new Dimension(14, main.getHeight()));
        add(popper, BorderLayout.EAST);
      } else if (getPopperButtonLocation() == BOTTOM) {
        popper.setPreferredSize(new Dimension(main.getWidth(), 14));
        add(popper, BorderLayout.SOUTH);

        setPopperArrowDirection(DOWN);
        setPopupLocation(popper.getX(), popper.getY() + popper.getHeight() + 5);
      }
    }

    Utilities.updateView(this);
  }
  protected void updateLayout(AbstractButton button, ButtonInfo buttonInfo) {
    Shape customShape = (Shape) button.getClientProperty(SHAPE);
    int width = button.getWidth();
    int height = button.getHeight();
    int horizontalPosition = getHorizontalPosition(button);
    int verticalPosition = getVerticalPosition(button);

    String key = width + " " + height + " " + horizontalPosition + " " + verticalPosition;
    button.putClientProperty("FilledButtonUI.validationKey", key);

    int dx = 0;
    int dy = 0;

    if (getFocusPainting(button) == PaintFocus.OUTSIDE
        || getFocusPainting(button) == PaintFocus.BOTH) {
      if (horizontalPosition == POS_LEFT || horizontalPosition == POS_ONLY) {
        dx += focusSize;
        width -= focusSize;
      }
      if (horizontalPosition == POS_RIGHT || horizontalPosition == POS_ONLY) {
        width -= focusSize;
      }
      if (verticalPosition == POS_TOP || verticalPosition == POS_ONLY) {
        dy += focusSize;
        height -= focusSize;
      }
      if (verticalPosition == POS_BOTTOM || verticalPosition == POS_ONLY) {
        height -= focusSize;
      }
    } else {
      if ((verticalPosition == POS_BOTTOM || verticalPosition == POS_ONLY)
          && fill.getShadowHighlight(button) != null) {
        height--;
      }
    }

    ButtonInfo info = getButtonInfo(button);
    boolean showSeparators = isShowingSeparators(button);

    shape.getShape(
        info.fill,
        info.stroke,
        width,
        height,
        horizontalPosition,
        verticalPosition,
        showSeparators,
        customShape);

    AffineTransform translation = AffineTransform.getTranslateInstance(dx, dy);
    info.fill.transform(translation);
    info.stroke.transform(translation);

    Font font = button.getFont();
    if (font == null) font = new Font("Default", 0, 12);
    FontMetrics fm = button.getFontMetrics(font);

    info.viewRect.x =
        info.viewRect.y =
            info.textRect.x = info.textRect.y = info.textRect.width = info.textRect.height = 0;
    info.iconRect.x = info.iconRect.y = info.iconRect.width = info.iconRect.height = 0;
    info.viewRect.width = Short.MAX_VALUE;
    info.viewRect.height = Short.MAX_VALUE;

    SwingUtilities.layoutCompoundLabel(
        fm,
        button.getText(),
        button.getIcon(),
        button.getVerticalAlignment(),
        button.getHorizontalAlignment(),
        button.getVerticalTextPosition(),
        button.getHorizontalTextPosition(),
        info.viewRect,
        info.iconRect,
        info.textRect,
        button.getIconTextGap());

    Insets textInsets = getTextPadding();
    Insets iconInsets = getIconPadding();

    Rectangle tempTextRect = new Rectangle(info.textRect);
    Rectangle tempIconRect = new Rectangle(info.iconRect);
    if (info.textRect.width > 0) {
      tempTextRect.y -= textInsets.top;
      tempTextRect.x -= textInsets.left;
      tempTextRect.width += textInsets.left + textInsets.right;
      tempTextRect.height += textInsets.top + textInsets.bottom;
    }
    if (info.iconRect.width > 0) {
      tempIconRect.y -= iconInsets.top;
      tempIconRect.x -= iconInsets.left;
      tempIconRect.width += iconInsets.left + iconInsets.right;
      tempIconRect.height += iconInsets.top + iconInsets.bottom;
    }

    Rectangle sum = getSum(new Rectangle[] {tempIconRect, tempTextRect});

    Insets padding = getContentInsets(button);

    float centerX, centerY;
    if (button.getHorizontalAlignment() == SwingConstants.LEFT
        || button.getHorizontalAlignment() == SwingConstants.LEADING) {
      centerX = padding.left + sum.width / 2;
    } else if (button.getHorizontalAlignment() == SwingConstants.RIGHT
        || button.getHorizontalAlignment() == SwingConstants.TRAILING) {
      centerX = button.getWidth() - padding.right - sum.width / 2;
    } else {
      centerX = ((button.getWidth() - padding.left - padding.right)) / 2f;
    }
    // TODO: also take into account vertical alignment:
    centerY = ((button.getHeight() - padding.top - padding.bottom)) / 2f;

    float shiftX = centerX - (sum.width) / 2f - sum.x + padding.left;
    float shiftY = centerY - (sum.height) / 2f - sum.y + padding.top;

    if (customShape == null) {
      if (button.getVerticalAlignment() == SwingConstants.CENTER
          && button.getVerticalTextPosition() == SwingConstants.CENTER
          && info.textRect.width > 0) {
        int unusedAscent = getUnusedAscent(fm, font);
        int ascent = fm.getAscent() - unusedAscent;

        shiftY =
            (int) (-sum.y + centerY - ascent / 2 - unusedAscent + padding.top - textInsets.top);
      }
    }

    info.iconRect.setFrame(
        info.iconRect.x + shiftX,
        info.iconRect.y + shiftY,
        info.iconRect.width,
        info.iconRect.height);
    info.textRect.setRect(
        (int) (info.textRect.x + shiftX + .5f),
        (int) (info.textRect.y + shiftY + .5f),
        info.textRect.width,
        info.textRect.height);

    info.updateFillBounds();
  }
  /**
   * This calls the other relevant <code>paint...()</code> methods in this object. The layering of
   * the focus varies based on whether it should be painted outside or inside the filled shape, but
   * otherwise the layers are:
   *
   * <ul>
   *   <li>Filling the bounds with <code>button.getBackground()</code> (if <code>button.isOpaque()
   *       </code> is true).
   *   <li>If <code>getShadowHighlight()</code> is non-null, painting the stroke/border 1 pixel
   *       below its usual location.
   *   <li><code>paintBackground(g)</code>
   *   <li><code>paintEffects(g,false)</code>
   *   <li><code>paintIcon(g)</code>
   *   <li><code>paintText(g)</code>
   *   <LI><code>paintForeground(g)</code>
   *   <LI><code>paintEffects(g,true)</code>
   * </ul>
   */
  @Override
  public void paint(Graphics g0, JComponent c) {
    AbstractButton button = (AbstractButton) c;

    if (isLayoutValid(button) == false) updateLayout(button, getButtonInfo(button));

    if (button.isOpaque()) {
      g0.setColor(button.getBackground());
      g0.fillRect(0, 0, button.getWidth(), button.getHeight());
    }

    Graphics2D g = new OptimizedGraphics2D((Graphics2D) g0);

    g.setComposite(getComposite(button));

    ButtonInfo info = getButtonInfo(button);

    Color highlight = fill.getShadowHighlight(button);
    if (highlight != null && isStrokePainted(button)) {
      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g.translate(0, 1);
      g.setColor(highlight);
      g.draw(info.fill);
      g.translate(0, -1);
    }

    PaintFocus focus = getFocusPainting(button);
    boolean hasFocus = hasFocus(button);
    if (Boolean.FALSE.equals(hasFocus) || button.isFocusPainted() == false) focus = PaintFocus.NONE;

    // this shouldn't be an issue, but just in case:
    if (isEnabled(button) == false) focus = PaintFocus.NONE;

    if (focus == PaintFocus.OUTSIDE) {
      if (isFillOpaque()) {
        // the opaque fill will overwrite the inner part of
        // this stroke...
        PlafPaintUtils.paintFocus(g, info.stroke, focusSize);
      } else {
        // this still has some rendering quirks in
        // Quartz (remove the clipping to study closely)
        // ... but other than the top horizontal & vertical
        // line it's OK.  And even those are ... partly there.
        Graphics2D focusG = (Graphics2D) g.create();
        GeneralPath outsideClip = new GeneralPath(Path2D.WIND_EVEN_ODD);
        outsideClip.append(new Rectangle(0, 0, button.getWidth(), button.getHeight()), false);
        outsideClip.append(info.fill, false);
        focusG.clip(outsideClip);
        PlafPaintUtils.paintFocus(focusG, info.stroke, focusSize);
        focusG.dispose();
      }
    }
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);

    paintBackground(g, info);
    paintEffects(g, info, true);

    g.setStroke(new BasicStroke(1));
    if (focus == PaintFocus.INSIDE) {
      Graphics2D focusG = (Graphics2D) g.create();
      focusG.clip(info.fill);
      PlafPaintUtils.paintFocus(focusG, info.stroke, focusSize);
      focusG.dispose();
      paintStroke(g, info);
    } else if (focus == PaintFocus.BOTH) {
      paintStroke(g, info);
      PlafPaintUtils.paintFocus(g, info.stroke, focusSize);
    } else {
      paintStroke(g, info);
    }

    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    paintIcon(g, info);
    paintText(g, info);

    g.setComposite(isEnabled(button) ? AlphaComposite.SrcOver : SRC_OVER_TRANSLUCENT);
    paintForeground(g, info);
    paintEffects(g, info, false);
  }