int (Graphics2D g, int textStart, boolean focusAroundIcon) { synchronized (myFragments) { // If there is no icon, then we have to add left internal padding if (textStart == 0) { textStart = myIpad.left; } float offset = textStart; if (myBorder != null) { offset += myBorder.getBorderInsets(this).left; } offset += getInsets().left; final class Frag { private final int index; private final float start; private final float end; private final float baseLine; private final Font font; private final Frag next; private Frag(int index, float start, float end, float baseLine, @NotNull Font font, Frag next) { this.index = index; this.start = start; this.end = end; this.baseLine = baseLine; this.font = font; this.next = next; } } int height = getHeight(); applyAdditionalHints(g); final Font baseFont = getBaseFont(); g.setFont(baseFont); offset += computeTextAlignShift(); int baseSize = baseFont.getSize(); FontMetrics baseMetrics = g.getFontMetrics(); Rectangle area = computePaintArea(); final int textBaseline = area.y + getTextBaseLine(baseMetrics, area.height); boolean wasSmaller = false; Frag secondPassFrag = null; for (int i = 0; i < myFragments.size(); i++) { ColoredFragment fragment = myFragments.get(i); final SimpleTextAttributes attributes = fragment.attributes; Font font = g.getFont(); boolean isSmaller = attributes.isSmaller(); if (font.getStyle() != attributes.getFontStyle() || isSmaller != wasSmaller) { // derive font only if it is necessary font = deriveFont(font, attributes.getFontStyle(), isSmaller ? UIUtil.getFontSize(UIUtil.FontSize.SMALL) : baseSize); } wasSmaller = isSmaller; g.setFont(font); final FontMetrics metrics = g.getFontMetrics(font); float fragmentWidth = computeStringWidth(fragment, i, font); final int fragmentPadding = fragment.padding; boolean secondPass = attributes.isSearchMatch() || attributes.isClickable(); final Color bgColor = secondPass ? null : attributes.getBgColor(); if ((attributes.isOpaque() || isOpaque()) && bgColor != null) { doPaintFragmentBackground(g, i, bgColor, (int)offset, 0, (int)fragmentWidth, height); } Color color; color = isEnabled() ? getActiveTextColor(attributes.getFgColor()) : NamedColorUtil.getInactiveTextColor(); g.setColor(color); final int fragmentAlignment = fragment.alignment; final float endOffset; if (fragmentPadding > 0 && fragmentPadding > fragmentWidth) { endOffset = fragmentPadding; if (fragmentAlignment == SwingConstants.RIGHT || fragmentAlignment == SwingConstants.TRAILING) { offset = fragmentPadding - fragmentWidth; } } else { endOffset = offset + fragmentWidth; } if (offset < area.getMaxX()) { if (!secondPass) { if (shouldDrawDimmed()) { color = ColorUtil.dimmer(color); } g.setColor(color); doDrawString(g, fragment, i, offset, textBaseline); // for some reason strokeState here may be incorrect, resetting the stroke helps g.setStroke(g.getStroke()); drawTextAttributes(g, attributes, (int)offset, textBaseline, (int)fragmentWidth, metrics, font); } if (secondPass) { secondPassFrag = new Frag(i, offset, offset + fragmentWidth, textBaseline, font, secondPassFrag); } } offset = endOffset; } // Paint focus border around the text and icon (if necessary) if (myPaintFocusBorder && myBorder != null) { int width = getWidth(); if (focusAroundIcon) { myBorder.paintBorder(this, g, 0, 0, width, height); } else { myBorder.paintBorder(this, g, textStart, 0, width - textStart, height); } } // draw search matches after all for (Frag frag = secondPassFrag; frag != null; frag = frag.next) { float x1 = frag.start; float x2 = frag.end; float baseline = frag.baseLine; ColoredFragment fragment = myFragments.get(frag.index); String text = fragment.text; SimpleTextAttributes attributes = fragment.attributes; Color fgColor; if (attributes.isSearchMatch()) { fgColor = new JBColor(Gray._50, Gray._0); UIUtil.drawSearchMatch(g, x1, x2, height); } else if (attributes.isClickable()) { boolean selected = UIUtil.getTreeSelectionBackground(true) == getBackground(); fgColor = ObjectUtils.notNull(attributes.getFgColor(), UIUtil.getLabelForeground()); drawClickableFrag(g, x1, x2, height, selected, attributes.isHovered(), attributes.isNoBorder()); } else { continue; } g.setFont(frag.font); g.setColor(fgColor); if (!drawWithClipping(frag.index, g, frag.font, frag.start, frag.end, baseline)) { g.drawString(text, x1, baseline); } int fragmentWidth = (int)(x2 - x1); drawTextAttributes(g, attributes, (int)x1, (int)baseline, fragmentWidth, g.getFontMetrics(), g.getFont()); } return (int)offset; } }