/*
* @(#)BasicStyledLabelUI.java 6/8/2012
*
* Copyright 2002 - 2012 JIDE Software Inc. All rights reserved.
*/
package com.jidesoft.plaf.basic;
import com.jidesoft.plaf.UIDefaultsLookup;
import com.jidesoft.swing.JideSwingUtilities;
import com.jidesoft.swing.StyleRange;
import com.jidesoft.swing.StyledLabel;
import com.jidesoft.swing.FontUtils;
import com.sun.java.swing.plaf.windows.WindowsLookAndFeel;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicLabelUI;
import javax.swing.text.View;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class BasicStyledLabelUI extends BasicLabelUI implements SwingConstants {
public static Comparator<StyleRange> _comparator;
protected static BasicStyledLabelUI styledLabelUI = new BasicStyledLabelUI();
@SuppressWarnings({"UnusedDeclaration"})
public static ComponentUI createUI(JComponent c) {
return styledLabelUI;
}
class StyledText {
StyleRange styleRange;
String text;
public StyledText(String text) {
this.text = text;
}
public StyledText(String text, StyleRange styleRange) {
this.text = text;
this.styleRange = styleRange;
}
}
private final List<StyledText> _styledTexts = new ArrayList<StyledText>();
private int _preferredRowCount = 1;
@Override
public void propertyChange(PropertyChangeEvent e) {
super.propertyChange(e);
if (StyledLabel.PROPERTY_STYLE_RANGE.equals(e.getPropertyName())) {
synchronized (_styledTexts) {
_styledTexts.clear();
}
if (e.getSource() instanceof StyledLabel) {
((StyledLabel) e.getSource()).revalidate();
((StyledLabel) e.getSource()).repaint();
}
}
else if (StyledLabel.PROPERTY_IGNORE_COLOR_SETTINGS.equals(e.getPropertyName())) {
if (e.getSource() instanceof StyledLabel) {
((StyledLabel) e.getSource()).repaint();
}
}
}
@Override
protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY) {
View v = (l != null) ? (View) l.getClientProperty("html") : null;
if (v != null) {
super.paintEnabledText(l, g, s, textX, textY);
}
else {
paintStyledText((StyledLabel) l, g, textX, textY);
}
}
@Override
protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY) {
View v = (l != null) ? (View) l.getClientProperty("html") : null;
if (v != null) {
super.paintDisabledText(l, g, s, textX, textY);
}
else {
paintStyledText((StyledLabel) l, g, textX, textY);
}
}
protected void buildStyledText(StyledLabel label) {
synchronized (_styledTexts) {
_styledTexts.clear();
StyleRange[] styleRanges = label.getStyleRanges();
if (_comparator == null) {
_comparator = new Comparator<StyleRange>() {
public int compare(StyleRange r1, StyleRange r2) {
if (r1.getStart() < r2.getStart()) {
return -1;
}
else if (r1.getStart() > r2.getStart()) {
return 1;
}
else {
return 0;
}
}
};
}
Arrays.sort(styleRanges, _comparator);
String s = label.getText();
if (s != null && s.length() > 0) { // do not do anything if the text is empty
int index = 0;
for (StyleRange styleRange : styleRanges) {
if (index >= s.length()) {
break;
}
if (styleRange.getStart() > index) { // fill in the gap
String text = s.substring(index, Math.min(styleRange.getStart(), s.length()));
StyleRange newRange = new StyleRange(index, styleRange.getStart() - index, -1);
addStyledTexts(text, newRange, label);
index = styleRange.getStart();
}
if (styleRange.getStart() == index) { // exactly on
if (styleRange.getLength() == -1) {
String text = s.substring(index);
addStyledTexts(text, styleRange, label);
index = s.length();
}
else {
String text = s.substring(index, Math.min(index + styleRange.getLength(), s.length()));
addStyledTexts(text, styleRange, label);
index += styleRange.getLength();
}
}
else if (styleRange.getStart() < index) { // overlap
// ignore
}
}
if (index < s.length()) {
String text = s.substring(index, s.length());
StyleRange range = new StyleRange(index, s.length() - index, -1);
addStyledTexts(text, range, label);
}
}
}
}
private void addStyledTexts(String text, StyleRange range, StyledLabel label) {
range = new StyleRange(range); // keep the passed-in parameter no change
int index1 = text.indexOf('\r');
int index2 = text.indexOf('\n');
while (index1 >= 0 || index2 >= 0) {
int index = index1 >= 0 ? index1 : -1;
if (index2 >= 0 && (index2 < index1 || index < 0)) {
index = index2;
}
String subString = text.substring(0, index);
StyleRange newRange = new StyleRange(range);
newRange.setStart(range.getStart());
newRange.setLength(index);
_styledTexts.add(new StyledText(subString, newRange));
int length = 1;
if (text.charAt(index) == '\r' && index + 1 < text.length() && text.charAt(index + 1) == '\n') {
length++;
}
newRange = new StyleRange(range);
newRange.setStart(range.getStart() + index);
newRange.setLength(length);
_styledTexts.add(new StyledText(text.substring(index, index + length), newRange));
text = text.substring(index + length);
range.setStart(range.getStart() + index + length);
range.setLength(range.getLength() - index - length);
index1 = text.indexOf('\r');
index2 = text.indexOf('\n');
}
if (text.length() > 0) {
_styledTexts.add(new StyledText(text, range));
}
}
private boolean _gettingPreferredSize;
@Override
public Dimension getPreferredSize(JComponent c) {
_gettingPreferredSize = true;
Dimension preferredSize;
try {
preferredSize = super.getPreferredSize(c);
}
finally {
_gettingPreferredSize = false;
}
return preferredSize;
}
@Override
protected String layoutCL(JLabel label, FontMetrics fontMetrics, String text, Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR) {
Dimension size = null;
if (label instanceof StyledLabel) {
int oldPreferredWidth = ((StyledLabel) label).getPreferredWidth();
int oldRows = ((StyledLabel) label).getRows();
try {
if (((StyledLabel) label).isLineWrap() && label.getWidth() > 0) {
((StyledLabel) label).setPreferredWidth(label.getWidth());
}
size = getPreferredSize((StyledLabel) label);
if (oldPreferredWidth > 0 && oldPreferredWidth < label.getWidth()) {
((StyledLabel) label).setPreferredWidth(oldPreferredWidth);
size = getPreferredSize((StyledLabel) label);
}
else if (((StyledLabel) label).isLineWrap() && ((StyledLabel) label).getMinRows() > 0) {
((StyledLabel) label).setPreferredWidth(0);
((StyledLabel) label).setRows(0);
Dimension minSize = getPreferredSize((StyledLabel) label);
if (minSize.height > size.height) {
size = minSize;
}
}
}
finally {
((StyledLabel) label).setPreferredWidth(oldPreferredWidth);
((StyledLabel) label).setRows(oldRows);
}
}
else {
size = label.getPreferredSize();
}
textR.width = size.width;
textR.height = size.height;
if (label.getIcon() != null) {
textR.width -= label.getIcon().getIconWidth() + label.getIconTextGap();
}
return layoutCompoundLabel(
label,
fontMetrics,
text,
icon,
label.getVerticalAlignment(),
label.getHorizontalAlignment(),
label.getVerticalTextPosition(),
label.getHorizontalTextPosition(),
viewR,
iconR,
textR,
label.getIconTextGap());
}
/**
* Gets the preferred size of the text portion of the StyledLabel including the icon.
*
* @param label the StyledLabel
* @return the preferred size.
*/
protected Dimension getPreferredSize(StyledLabel label) {
buildStyledText(label);
Font font = getFont(label);
FontMetrics fm = label.getFontMetrics(font);
FontMetrics fm2;
int defaultFontSize = font.getSize();
boolean lineWrap = label.isLineWrap() || (label.getText() != null && (label.getText().contains("\r") || label.getText().contains("\n")));
synchronized (_styledTexts) {
StyledText[] texts = _styledTexts.toArray(new StyledText[_styledTexts.size()]);
// get maximum row height first by comparing all fonts of styled texts
int maxRowHeight = fm.getHeight();
for (StyledText styledText : texts) {
StyleRange style = styledText.styleRange;
int size = (style != null && (style.isSuperscript() || style.isSubscript())) ? Math.round((float) defaultFontSize / style.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label);
int styleHeight = fm.getHeight();
if (style != null && ((style.getFontStyle() != -1 && font.getStyle() != style.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, style.getFontStyle() == -1 ? font.getStyle() : style.getFontStyle(), size);
fm2 = label.getFontMetrics(font);
styleHeight = fm2.getHeight();
}
styleHeight++;
/*
if (style != null) {
if (style.isWaved()) {
styleHeight += 4;
}
else if (style.isDotted()) {
styleHeight += 3;
}
else if (style.isUnderlined()) {
styleHeight += 2;
}
}
*/
maxRowHeight = Math.max(maxRowHeight, styleHeight);
}
int naturalRowCount = 1;
int nextRowStartIndex = 0;
int width = 0;
int maxWidth = 0;
List<Integer> lineWidths = new ArrayList<Integer>();
// get one line width
for (StyledText styledText : _styledTexts) {
StyleRange style = styledText.styleRange;
int size = (style != null &&
(style.isSuperscript() || style.isSubscript())) ? Math.round((float) defaultFontSize / style.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label);
String s = styledText.text.substring(nextRowStartIndex);
if (s.startsWith("\r") || s.startsWith("\n")) {
lineWidths.add(width);
maxWidth = Math.max(width, maxWidth);
width = 0;
naturalRowCount++;
if (label.getMaxRows() > 0 && naturalRowCount >= label.getMaxRows()) {
break;
}
continue;
}
if (style != null && ((style.getFontStyle() != -1 && font.getStyle() != style.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, style.getFontStyle() == -1 ? font.getStyle() : style.getFontStyle(), size);
fm2 = label.getFontMetrics(font);
width += fm2.stringWidth(s);
}
else {
// fm2 = fm;
width += fm.stringWidth(s);
}
}
lineWidths.add(width);
maxWidth = Math.max(width, maxWidth);
int maxLineWidth = maxWidth;
_preferredRowCount = naturalRowCount;
// if getPreferredWidth() is not set but getRows() is set, get maximum width and row count based on the required rows.
if (lineWrap && label.getPreferredWidth() <= 0 && label.getRows() > 0) {
maxWidth = getMaximumWidth(label, maxWidth, naturalRowCount, label.getRows());
}
// if calculated maximum width is larger than label's maximum size, wrap again to get the updated row count and use the label's maximum width as the maximum width.
int preferredWidth = label.getPreferredWidth();
if (preferredWidth > 0 && label.getInsets() != null) {
preferredWidth -= label.getInsets().left + label.getInsets().right;
}
if (label.getIcon() != null && label.getHorizontalTextPosition() != SwingConstants.CENTER) {
preferredWidth -= label.getIcon().getIconWidth() + label.getIconTextGap();
}
if (lineWrap && preferredWidth > 0 && maxWidth > preferredWidth) {
maxWidth = getLayoutWidth(label, preferredWidth);
}
// label.getPreferredWidth() <= 0 && label.getMaxRows() > 0 && rowCount > label.getMaxRows(), recalculate the maximum width according to the maximum rows
if (lineWrap && label.getMaxRows() > 0 && _preferredRowCount > label.getMaxRows()) {
if (label.getPreferredWidth() <= 0) {
maxWidth = getMaximumWidth(label, maxWidth, naturalRowCount, label.getMaxRows());
}
else {
_preferredRowCount = label.getMaxRows();
}
}
// label.getPreferredWidth() <= 0 && label.getMinRows() > 0 && rowCount < label.getMinRows(), recalculate the maximum width according to the minimum rows
if (lineWrap && label.getPreferredWidth() <= 0 && label.getMinRows() > 0 && _preferredRowCount < label.getMinRows()) {
maxWidth = getMaximumWidth(label, maxWidth, naturalRowCount, label.getMinRows());
}
if (_gettingPreferredSize && label.getRows() > 0 && _preferredRowCount > label.getRows() && (label.getPreferredWidth() <= 0 || label.getPreferredWidth() >= maxLineWidth || naturalRowCount > label.getRows())) {
_preferredRowCount = label.getRows();
maxLineWidth = 0;
for (int i = 0; i < lineWidths.size() && i < _preferredRowCount; i++) {
maxLineWidth = Math.max(maxLineWidth, lineWidths.get(i));
}
}
Dimension dimension = new Dimension(Math.min(maxWidth, maxLineWidth), (maxRowHeight + Math.max(0, label.getRowGap())) * _preferredRowCount);
if (label.getIcon() != null) {
dimension = new Dimension(dimension.width + label.getIconTextGap() + label.getIcon().getIconWidth(), dimension.height);
}
return dimension;
}
}
private int getLayoutWidth(StyledLabel label, int maxWidth) {
int nextRowStartIndex;
Font font = getFont(label);
int defaultFontSize = font.getSize();
FontMetrics fm = label.getFontMetrics(font);
FontMetrics fm2;
nextRowStartIndex = 0;
int x = 0;
_preferredRowCount = 1;
for (int i = 0; i < _styledTexts.size(); i++) {
StyledText styledText = _styledTexts.get(i);
StyleRange style = styledText.styleRange;
if (styledText.text.contains("\r") || styledText.text.contains("\n")) {
x = 0;
_preferredRowCount++;
continue;
}
int size = (style != null &&
(style.isSuperscript() || style.isSubscript())) ? Math.round((float) defaultFontSize / style.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label); // cannot omit this one
if (style != null && ((style.getFontStyle() != -1 && font.getStyle() != style.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, style.getFontStyle() == -1 ? font.getStyle() : style.getFontStyle(), size);
fm2 = label.getFontMetrics(font);
}
else {
fm2 = fm;
}
String s = styledText.text.substring(nextRowStartIndex);
int strWidth = fm2.stringWidth(s);
boolean wrapped = false;
int widthLeft = maxWidth - x;
if (widthLeft < strWidth) {
wrapped = true;
int availLength = s.length() * widthLeft / strWidth + 1;
int nextWordStartIndex;
int nextRowStartIndexInSubString = 0;
boolean needBreak = false;
boolean needContinue = false;
int loopCount = 0;
do {
String subString = s.substring(0, Math.min(availLength, s.length()));
int firstRowWordEndIndex = findFirstRowWordEndIndex(subString);
nextWordStartIndex = firstRowWordEndIndex < 0 ? 0 : findNextWordStartIndex(s, firstRowWordEndIndex);
if (firstRowWordEndIndex < 0) {
if (x != 0) {
x = 0;
i--;
_preferredRowCount++;
if (label.getMaxRows() > 0 && _preferredRowCount >= label.getMaxRows()) {
needBreak = true;
}
needContinue = true;
break;
}
else {
firstRowWordEndIndex = 0;
nextWordStartIndex = Math.min(s.length(), availLength);
}
}
nextRowStartIndexInSubString = firstRowWordEndIndex + 1;
String subStringThisRow = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(subStringThisRow);
if (strWidth > widthLeft) {
availLength = subString.length() * widthLeft / strWidth;
}
loopCount++;
if (loopCount > 50) {
System.err.println("Painting Styled Label Error: " + styledText);
break;
}
} while (strWidth > widthLeft && availLength > 0);
if (needBreak) {
break;
}
if (needContinue) {
continue;
}
while (nextRowStartIndexInSubString < nextWordStartIndex) {
strWidth += fm2.charWidth(s.charAt(nextRowStartIndexInSubString));
if (strWidth >= widthLeft) {
break;
}
nextRowStartIndexInSubString++;
}
String subStringThisRow = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(subStringThisRow);
while (nextRowStartIndexInSubString < nextWordStartIndex) {
strWidth += fm2.charWidth(s.charAt(nextRowStartIndexInSubString));
if (strWidth >= widthLeft) {
break;
}
nextRowStartIndexInSubString++;
}
s = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(s);
nextRowStartIndex += nextRowStartIndexInSubString;
}
else {
nextRowStartIndex = 0;
}
if (wrapped) {
_preferredRowCount++;
x = 0;
i--;
}
else {
x += strWidth;
}
}
return maxWidth;
}
private int getMaximumWidth(StyledLabel label, int maxWidth, int naturalRowCount, int limitedRows) {
int textWidth = label.getWidth() - label.getInsets().left - label.getInsets().right;
if (label.getIcon() != null) {
textWidth -= label.getIcon().getIconWidth() + label.getIconTextGap();
}
if (naturalRowCount > 1) {
int proposedMaxWidthMin = 1;
int proposedMaxWidthMax = maxWidth;
_preferredRowCount = naturalRowCount;
while (proposedMaxWidthMin < proposedMaxWidthMax) {
int middle = (proposedMaxWidthMax + proposedMaxWidthMin) / 2;
maxWidth = getLayoutWidth(label, middle);
if (_preferredRowCount > limitedRows) {
proposedMaxWidthMin = middle + 1;
_preferredRowCount = naturalRowCount;
}
else {
proposedMaxWidthMax = middle - 1;
}
}
return maxWidth + maxWidth / 20;
}
int estimatedWidth = maxWidth / limitedRows + 1;
int x = 0;
int nextRowStartIndex = 0;
Font font = getFont(label);
FontMetrics fm = label.getFontMetrics(font);
int defaultFontSize = font.getSize();
FontMetrics fm2;
for (int i = 0; i < _styledTexts.size(); i++) {
StyledText styledText = _styledTexts.get(i);
StyleRange style = styledText.styleRange;
int size = (style != null && (style.isSuperscript() || style.isSubscript())) ? Math.round((float) defaultFontSize / style.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label);
if (style != null && ((style.getFontStyle() != -1 && font.getStyle() != style.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, style.getFontStyle() == -1 ? font.getStyle() : style.getFontStyle(), size);
fm2 = label.getFontMetrics(font);
}
else {
fm2 = fm;
}
String s = styledText.text.substring(nextRowStartIndex);
int strWidth = fm2.stringWidth(s);
int widthLeft = estimatedWidth - x;
if (widthLeft < strWidth) {
int availLength = s.length() * widthLeft / strWidth + 1;
String subString = s.substring(0, Math.min(availLength, s.length()));
int firstRowWordEndIndex = findFirstRowWordEndIndex(subString);
int nextWordStartIndex = findNextWordStartIndex(s, firstRowWordEndIndex);
if (firstRowWordEndIndex < 0) {
if (nextWordStartIndex < s.length()) {
firstRowWordEndIndex = findFirstRowWordEndIndex(s.substring(0, nextWordStartIndex));
}
else {
firstRowWordEndIndex = nextWordStartIndex;
}
}
int nextRowStartIndexInSubString = firstRowWordEndIndex + 1;
String subStringThisRow = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(subStringThisRow);
while (nextRowStartIndexInSubString < nextWordStartIndex) {
strWidth += fm2.charWidth(s.charAt(nextRowStartIndexInSubString));
nextRowStartIndexInSubString++;
if (strWidth >= widthLeft) {
break;
}
}
s = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(s);
nextRowStartIndex += nextRowStartIndexInSubString;
if (x + strWidth >= maxWidth) {
x = Math.max(x, strWidth);
break;
}
if (x + strWidth >= estimatedWidth) {
x += strWidth;
break;
}
i--;
}
x += strWidth;
}
int paintWidth = x;
if (label.getInsets() != null) {
paintWidth += label.getInsets().left + label.getInsets().right;
}
int paintRows = internalPaintStyledText(label, null, 0, 0, paintWidth);
if (paintRows != limitedRows) {
maxWidth = Math.min(maxWidth, textWidth);
while (paintRows > limitedRows && paintWidth < maxWidth) {
paintWidth += 2;
paintRows = internalPaintStyledText(label, null, 0, 0, paintWidth);
}
while (paintRows < limitedRows && paintWidth > 0) {
paintWidth -= 2;
paintRows = internalPaintStyledText(label, null, 0, 0, paintWidth);
}
x = paintWidth;
if (label.getInsets() != null) {
x -= label.getInsets().left + label.getInsets().right;
}
}
_preferredRowCount = limitedRows;
return x;
}
/**
* Gets the font from the label.
*
* @param label the label.
* @return the font. If label's getFont is null, we will use Label.font instead.
*/
protected Font getFont(StyledLabel label) {
Font font = label.getFont();
if (font == null) {
font = UIDefaultsLookup.getFont("Label.font");
}
return font;
}
protected void paintStyledText(StyledLabel label, Graphics g, int textX, int textY) {
label.setTruncated(false);
int paintWidth = label.getWidth();
if (label.isLineWrap()) {
int oldPreferredWidth = label.getPreferredWidth();
int oldRows = label.getRows();
try {
label.setRows(0);
paintWidth = getPreferredSize(label).width;
label.setPreferredWidth(oldPreferredWidth > 0 ? Math.min(label.getWidth(), oldPreferredWidth) : label.getWidth());
Dimension sizeOnWidth = getPreferredSize(label);
if (sizeOnWidth.width < paintWidth) {
paintWidth = sizeOnWidth.width;
}
}
finally {
label.setPreferredWidth(oldPreferredWidth);
label.setRows(oldRows);
}
}
Color oldColor = g.getColor();
int textWidth = label.getWidth();
if (label.getInsets() != null) {
textWidth -= label.getInsets().left + label.getInsets().right;
}
if (label.getIcon() != null && label.getHorizontalTextPosition() != SwingConstants.CENTER) {
textWidth -= label.getIcon().getIconWidth() + label.getIconTextGap();
}
paintWidth = Math.min(paintWidth, textWidth);
internalPaintStyledText(label, g, textX, textY, paintWidth);
g.setColor(oldColor);
}
private int internalPaintStyledText(StyledLabel label, Graphics g, int textX, int textY, int paintWidth) {
int labelHeight = label.getHeight();
if (labelHeight <= 0) {
labelHeight = Integer.MAX_VALUE;
}
Insets insets = label.getInsets();
if (insets != null) {
labelHeight -= insets.top + insets.bottom;
}
int leftMostX = 0;
if (insets != null) {
leftMostX += insets.left;
}
if (label.getIcon() != null) {
int horizontalTextPosition = label.getHorizontalTextPosition();
if ((horizontalTextPosition == SwingConstants.TRAILING && label.getComponentOrientation().isLeftToRight()) ||(horizontalTextPosition == SwingConstants.LEADING && !label.getComponentOrientation().isLeftToRight())) {
horizontalTextPosition = SwingConstants.RIGHT;
}
if (horizontalTextPosition == SwingConstants.RIGHT) {
leftMostX += label.getIcon().getIconWidth() + label.getIconTextGap();
}
}
int startX = textX < leftMostX ? leftMostX : textX;
int y;
int endX = paintWidth + startX;
int x = startX;
int mnemonicIndex = label.getDisplayedMnemonicIndex();
if (UIManager.getLookAndFeel() instanceof WindowsLookAndFeel &&
WindowsLookAndFeel.isMnemonicHidden()) {
mnemonicIndex = -1;
}
int charDisplayed = 0;
boolean displayMnemonic;
int mneIndex = 0;
Font font = getFont(label);
FontMetrics fm = label.getFontMetrics(font);
FontMetrics fm2;
FontMetrics nextFm2 = null;
int defaultFontSize = font.getSize();
synchronized (_styledTexts) {
String nextS;
int maxRowHeight = fm.getHeight();
int minStartY = fm.getAscent();
int horizontalAlignment = label.getHorizontalAlignment();
switch (horizontalAlignment) {
case LEADING:
horizontalAlignment = label.getComponentOrientation().isLeftToRight() ? LEFT : RIGHT;
break;
case TRAILING:
horizontalAlignment = label.getComponentOrientation().isLeftToRight() ? RIGHT : LEFT;
break;
}
for (StyledText styledText : _styledTexts) {
StyleRange style = styledText.styleRange;
int size = (style != null && (style.isSuperscript() || style.isSubscript())) ? Math.round((float) defaultFontSize / style.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label);
if (style != null && ((style.getFontStyle() != -1 && font.getStyle() != style.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, style.getFontStyle() == -1 ? font.getStyle() : style.getFontStyle(), size);
fm2 = label.getFontMetrics(font);
maxRowHeight = Math.max(maxRowHeight, fm2.getHeight());
minStartY = Math.max(minStartY, fm2.getAscent());
}
}
boolean lineWrap = label.isLineWrap();
if (!lineWrap) {
for (StyledText styledText : _styledTexts) {
if (styledText.text.endsWith("\n")) {
lineWrap = true;
break;
}
}
}
if (lineWrap && textY < minStartY) {
textY = minStartY;
}
int nextRowStartIndex = 0;
int rowCount = 0;
int rowStartOffset = 0;
for (int i = 0; i < _styledTexts.size(); i++) {
StyledText styledText = _styledTexts.get(i);
StyleRange style = styledText.styleRange;
if (mnemonicIndex >= 0 && styledText.text.length() - nextRowStartIndex > mnemonicIndex - charDisplayed) {
displayMnemonic = true;
mneIndex = mnemonicIndex - charDisplayed;
}
else {
displayMnemonic = false;
}
charDisplayed += styledText.text.length() - nextRowStartIndex;
if (styledText.text.contains("\r") || styledText.text.contains("\n")) {
boolean lastRow = (label.getMaxRows() > 0 && rowCount >= label.getMaxRows() - 1) || textY + maxRowHeight + Math.max(0, label.getRowGap()) > labelHeight;
if (horizontalAlignment != LEFT && g != null) {
if (lastRow && i != _styledTexts.size() - 1) {
x += fm.stringWidth("...");
}
paintRow(label, g, startX, x, endX, textY, rowStartOffset, style.getStart() + styledText.text.length(), lastRow);
}
rowStartOffset = style.getStart();
nextRowStartIndex = 0;
nextFm2 = null;
if (!lastRow) {
rowStartOffset += style.getLength();
rowCount++;
x = startX;
textY += maxRowHeight + Math.max(0, label.getRowGap());
continue; // continue to paint "..." if lastRow is true
}
else if (horizontalAlignment != LEFT && g != null) {
break;
}
}
y = textY;
if (nextFm2 == null) {
int size = (style != null &&
(style.isSuperscript() || style.isSubscript())) ? Math.round((float) defaultFontSize / style.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label);
if (style != null && ((style.getFontStyle() != -1 && font.getStyle() != style.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, style.getFontStyle() == -1 ? font.getStyle() : style.getFontStyle(), size);
fm2 = label.getFontMetrics(font);
}
else {
fm2 = fm;
}
}
else {
fm2 = nextFm2;
}
if (g != null) {
g.setFont(font);
}
boolean stop = false;
String s = styledText.text.substring(Math.min(nextRowStartIndex, styledText.text.length()));
if (s.contains("\r") || s.contains("\n")) {
s = "...";
stop = true;
}
int strWidth = fm2.stringWidth(s);
boolean wrapped = false;
int widthLeft = endX - x;
if (widthLeft < strWidth && widthLeft >= 0) {
if (label.isLineWrap() && ((label.getMaxRows() > 0 && rowCount < label.getMaxRows() - 1) || label.getMaxRows() <= 0) && y + maxRowHeight + Math.max(0, label.getRowGap()) <= labelHeight) {
wrapped = true;
int availLength = s.length() * widthLeft / strWidth + 1;
int nextWordStartIndex;
int nextRowStartIndexInSubString = 0;
boolean needBreak = false;
boolean needContinue = false;
int loopCount = 0;
do {
String subString = s.substring(0, Math.max(0, Math.min(availLength, s.length())));
int firstRowWordEndIndex = findFirstRowWordEndIndex(subString);
nextWordStartIndex = firstRowWordEndIndex < 0 ? 0 : findNextWordStartIndex(s, firstRowWordEndIndex);
if (firstRowWordEndIndex < 0) {
if (x != startX) {
boolean lastRow = label.getMaxRows() > 0 && rowCount >= label.getMaxRows() - 1;
if (horizontalAlignment != LEFT && g != null) {
paintRow(label, g, startX, x, endX, textY, rowStartOffset, style.getStart() + Math.min(nextRowStartIndex, styledText.text.length()), lastRow);
}
textY += maxRowHeight + Math.max(0, label.getRowGap());
x = startX;
i--;
rowCount++;
rowStartOffset = style.getStart() + Math.min(nextRowStartIndex, styledText.text.length());
if (lastRow) {
needBreak = true;
}
needContinue = true;
break;
}
else {
firstRowWordEndIndex = 0;
nextWordStartIndex = Math.min(s.length(), availLength);
}
}
nextRowStartIndexInSubString = firstRowWordEndIndex + 1;
String subStringThisRow = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(subStringThisRow);
if (strWidth > widthLeft) {
availLength = subString.length() * widthLeft / strWidth;
}
loopCount++;
if (loopCount > 15) {
System.err.println("Painting Styled Label Error: " + styledText);
break;
}
} while (strWidth > widthLeft && availLength > 0);
if (needBreak) {
break;
}
if (needContinue) {
continue;
}
while (nextRowStartIndexInSubString < nextWordStartIndex) {
strWidth += fm2.charWidth(s.charAt(nextRowStartIndexInSubString));
if (strWidth >= widthLeft) {
break;
}
nextRowStartIndexInSubString++;
}
s = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(s);
charDisplayed -= styledText.text.length() - nextRowStartIndex;
if (displayMnemonic) {
if (mnemonicIndex >= 0 && s.length() > mnemonicIndex - charDisplayed) {
displayMnemonic = true;
mneIndex = mnemonicIndex - charDisplayed;
}
else {
displayMnemonic = false;
}
}
charDisplayed += s.length();
nextRowStartIndex += nextRowStartIndexInSubString;
}
else {
// use this method to clip string
s = SwingUtilities.layoutCompoundLabel(label, fm2, s, null, label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(), new Rectangle(x, y, widthLeft, labelHeight), new Rectangle(), new Rectangle(), 0);
strWidth = fm2.stringWidth(s);
}
stop = !lineWrap || y + maxRowHeight + Math.max(0, label.getRowGap()) > labelHeight || (label.getMaxRows() > 0 && rowCount >= label.getMaxRows() - 1);
}
else if (lineWrap) {
nextRowStartIndex = 0;
}
else if (i < _styledTexts.size() - 1) {
StyledText nextStyledText = _styledTexts.get(i + 1);
String nextText = nextStyledText.text;
StyleRange nextStyle = nextStyledText.styleRange;
int size = (nextStyle != null &&
(nextStyle.isSuperscript() || nextStyle.isSubscript())) ? Math.round((float) defaultFontSize / nextStyle.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label);
if (nextStyle != null && ((nextStyle.getFontStyle() != -1 && font.getStyle() != nextStyle.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, nextStyle.getFontStyle() == -1 ? font.getStyle() : nextStyle.getFontStyle(), size);
nextFm2 = label.getFontMetrics(font);
}
else {
nextFm2 = fm;
}
if (nextFm2.stringWidth(nextText) > widthLeft - strWidth) {
nextS = SwingUtilities.layoutCompoundLabel(label, nextFm2, nextText, null, label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(), new Rectangle(x + strWidth, y, widthLeft - strWidth, labelHeight), new Rectangle(), new Rectangle(), 0);
if (nextFm2.stringWidth(nextS) > widthLeft - strWidth) {
s = SwingUtilities.layoutCompoundLabel(label, fm2, s, null, label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(), new Rectangle(x, y, strWidth - 1, labelHeight), new Rectangle(), new Rectangle(), 0);
strWidth = fm2.stringWidth(s);
stop = true;
}
}
}
// start of actual painting
if (rowCount > 0 && x == startX && s.startsWith(" ")) {
s = s.substring(1);
strWidth = fm2.stringWidth(s);
}
if (horizontalAlignment == LEFT && g != null) {
if (style != null && style.isSuperscript()) {
y -= fm.getHeight() - fm2.getHeight();
}
if (style != null && style.getBackgroundColor() != null) {
g.setColor(style.getBackgroundColor());
g.fillRect(x, y - fm2.getHeight(), strWidth, fm2.getHeight() + 4);
}
Color textColor = (style != null && !label.isIgnoreColorSettings() && style.getFontColor() != null) ? style.getFontColor() : label.getForeground();
if (!label.isEnabled()) {
textColor = UIDefaultsLookup.getColor("Label.disabledForeground");
}
g.setColor(textColor);
if (displayMnemonic) {
JideSwingUtilities.drawStringUnderlineCharAt(label, g, s, mneIndex, x, y);
}
else {
JideSwingUtilities.drawString(label, g, s, x, y);
}
if (style != null) {
Stroke oldStroke = ((Graphics2D) g).getStroke();
if (style.getLineStroke() != null) {
((Graphics2D) g).setStroke(style.getLineStroke());
}
if (!label.isIgnoreColorSettings() && style.getLineColor() != null) {
g.setColor(style.getLineColor());
}
if (style.isStrikethrough()) {
int lineY = y + (fm2.getDescent() - fm2.getAscent()) / 2;
g.drawLine(x, lineY, x + strWidth - 1, lineY);
}
if (style.isDoublestrikethrough()) {
int lineY = y + (fm2.getDescent() - fm2.getAscent()) / 2;
g.drawLine(x, lineY - 1, x + strWidth - 1, lineY - 1);
g.drawLine(x, lineY + 1, x + strWidth - 1, lineY + 1);
}
if (style.isUnderlined()) {
int lineY = y + 1;
g.drawLine(x, lineY, x + strWidth - 1, lineY);
}
if (style.isDotted()) {
int dotY = y + 1;
for (int dotX = x; dotX < x + strWidth; dotX += 4) {
g.drawRect(dotX, dotY, 1, 1);
}
}
if (style.isWaved()) {
int waveY = y + 1;
for (int waveX = x; waveX < x + strWidth; waveX += 4) {
if (waveX + 2 <= x + strWidth - 1)
g.drawLine(waveX, waveY + 2, waveX + 2, waveY);
if (waveX + 4 <= x + strWidth - 1)
g.drawLine(waveX + 3, waveY + 1, waveX + 4, waveY + 2);
}
}
if (style.getLineStroke() != null) {
((Graphics2D) g).setStroke(oldStroke);
}
}
}
// end of actual painting
if (stop) {
if (horizontalAlignment != LEFT && g != null) {
x += strWidth;
paintRow(label, g, startX, x, endX, textY, rowStartOffset, label.getText().length(), true);
}
label.setTruncated(true);
break;
}
if (wrapped) {
boolean lastRow = (label.getMaxRows() > 0 && rowCount >= label.getMaxRows() - 1) || textY + maxRowHeight + Math.max(0, label.getRowGap()) > labelHeight;
if (horizontalAlignment != LEFT && g != null) {
x += strWidth;
paintRow(label, g, startX, x, endX, textY, rowStartOffset, style.getStart() + Math.min(nextRowStartIndex, styledText.text.length()), lastRow);
}
textY += maxRowHeight + Math.max(0, label.getRowGap());
x = startX;
i--;
rowCount++;
rowStartOffset = style.getStart() + Math.min(nextRowStartIndex, styledText.text.length());
if (lastRow) {
break;
}
}
else {
x += strWidth;
}
if (i == _styledTexts.size() - 1) {
if (horizontalAlignment != LEFT && g != null) {
paintRow(label, g, startX, x, endX, textY, rowStartOffset, -1, true);
}
}
}
return (int) Math.ceil((double) textY / maxRowHeight);
}
}
private void paintRow(StyledLabel label, Graphics g, int leftAlignmentX, int thisLineEndX, int rightMostX, int textY, int startOffset, int endOffset, boolean lastRow) {
if (g == null) {
return;
}
int horizontalTextPosition = label.getHorizontalTextPosition();
int horizontalAlignment = label.getHorizontalAlignment();
if ((horizontalTextPosition == SwingConstants.TRAILING && !label.getComponentOrientation().isLeftToRight()) ||(horizontalTextPosition == SwingConstants.LEADING && label.getComponentOrientation().isLeftToRight())) {
horizontalTextPosition = SwingConstants.LEFT;
}
if ((horizontalTextPosition == SwingConstants.LEADING && !label.getComponentOrientation().isLeftToRight()) ||(horizontalTextPosition == SwingConstants.TRAILING && label.getComponentOrientation().isLeftToRight())) {
horizontalTextPosition = SwingConstants.RIGHT;
}
if ((horizontalAlignment == SwingConstants.TRAILING && !label.getComponentOrientation().isLeftToRight()) ||(horizontalAlignment == SwingConstants.LEADING && label.getComponentOrientation().isLeftToRight())) {
horizontalAlignment = SwingConstants.LEFT;
}
if ((horizontalAlignment == SwingConstants.LEADING && !label.getComponentOrientation().isLeftToRight()) ||(horizontalAlignment == SwingConstants.TRAILING && label.getComponentOrientation().isLeftToRight())) {
horizontalAlignment = SwingConstants.RIGHT;
}
Insets insets = label.getInsets();
int textX = leftAlignmentX;
int paintWidth = thisLineEndX - leftAlignmentX;
if (horizontalAlignment == RIGHT) {
paintWidth = thisLineEndX - textX;
textX = label.getWidth() - paintWidth;
if (insets != null) {
textX -= insets.right;
}
if (label.getIcon() != null && horizontalTextPosition == SwingConstants.LEFT) {
textX -= label.getIcon().getIconWidth() + label.getIconTextGap();
}
}
else if (horizontalAlignment == CENTER) {
int leftMostX = 0;
if (horizontalTextPosition == SwingConstants.RIGHT && label.getIcon() != null) {
leftMostX += label.getIcon().getIconWidth() + label.getIconTextGap();
}
int labelWidth = label.getWidth();
if (insets != null) {
labelWidth -= insets.right + insets.left;
leftMostX += insets.left;
}
if (label.getIcon() != null && horizontalTextPosition != SwingConstants.CENTER) {
labelWidth -= label.getIcon().getIconWidth() + label.getIconTextGap();
}
textX = leftMostX + (labelWidth - paintWidth) / 2;
}
paintWidth = Math.min(paintWidth, rightMostX - leftAlignmentX);
int mnemonicIndex = label.getDisplayedMnemonicIndex();
if (UIManager.getLookAndFeel() instanceof WindowsLookAndFeel &&
WindowsLookAndFeel.isMnemonicHidden()) {
mnemonicIndex = -1;
}
int charDisplayed = 0;
boolean displayMnemonic;
int mneIndex = 0;
Font font = getFont(label);
FontMetrics fm = label.getFontMetrics(font);
FontMetrics fm2;
FontMetrics nextFm2 = null;
int defaultFontSize = font.getSize();
int x = textX;
for (int i = 0; i < _styledTexts.size() && (endOffset < 0 || charDisplayed < endOffset); i++) {
StyledText styledText = _styledTexts.get(i);
StyleRange style = styledText.styleRange;
int length = style.getLength();
if (length < 0) {
length = styledText.text.length();
}
if (style.getStart() + length <= startOffset) {
charDisplayed += length;
continue;
}
int nextRowStartIndex = style.getStart() >= startOffset ? 0 : startOffset - style.getStart();
charDisplayed += nextRowStartIndex;
if (mnemonicIndex >= 0 && styledText.text.length() - nextRowStartIndex > mnemonicIndex - charDisplayed) {
displayMnemonic = true;
mneIndex = mnemonicIndex - charDisplayed;
}
else {
displayMnemonic = false;
}
int paintLength = styledText.text.length() - nextRowStartIndex;
if (endOffset >= 0 && charDisplayed + paintLength >= endOffset) {
paintLength = endOffset - charDisplayed;
}
charDisplayed += paintLength;
int y = textY;
if (nextFm2 == null) {
int size = (style != null &&
(style.isSuperscript() || style.isSubscript())) ? Math.round((float) defaultFontSize / style.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label);
if (style != null && ((style.getFontStyle() != -1 && font.getStyle() != style.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, style.getFontStyle() == -1 ? font.getStyle() : style.getFontStyle(), size);
fm2 = label.getFontMetrics(font);
}
else {
fm2 = fm;
}
}
else {
fm2 = nextFm2;
}
g.setFont(font);
String s = styledText.text.substring(Math.min(nextRowStartIndex, styledText.text.length()));
if (startOffset > 0 && x == textX && s.startsWith(" ")) {
s = s.substring(1);
}
if (s.length() > paintLength) {
s = s.substring(0, paintLength);
}
if (s.contains("\r") || s.contains("\n")) {
if (styledText.styleRange.getStart() + styledText.styleRange.getLength() >= endOffset) {
break;
}
s = "...";
}
int strWidth = fm2.stringWidth(s);
int widthLeft = paintWidth + textX - x;
if (widthLeft < strWidth) {
if (strWidth <= 0) {
return;
}
if (label.isLineWrap() && !lastRow) {
int availLength = s.length() * widthLeft / strWidth + 1;
int nextWordStartIndex;
int nextRowStartIndexInSubString;
int loopCount = 0;
do {
String subString = s.substring(0, Math.max(0, Math.min(availLength, s.length())));
int firstRowWordEndIndex = findFirstRowWordEndIndex(subString);
nextWordStartIndex = firstRowWordEndIndex < 0 ? 0 : findNextWordStartIndex(s, firstRowWordEndIndex);
if (firstRowWordEndIndex < 0) {
if (x == textX) {
firstRowWordEndIndex = 0;
nextWordStartIndex = Math.min(s.length(), availLength);
}
}
nextRowStartIndexInSubString = firstRowWordEndIndex + 1;
String subStringThisRow = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(subStringThisRow);
if (strWidth > widthLeft) {
availLength = subString.length() * widthLeft / strWidth;
}
loopCount++;
if (loopCount > 50) {
System.err.println("Painting Styled Label Error: " + styledText);
break;
}
}
while (strWidth > widthLeft && availLength > 0);
while (nextRowStartIndexInSubString < nextWordStartIndex) {
strWidth += fm2.charWidth(s.charAt(nextRowStartIndexInSubString));
if (strWidth >= widthLeft) {
break;
}
nextRowStartIndexInSubString++;
}
s = s.substring(0, Math.min(nextRowStartIndexInSubString, s.length()));
strWidth = fm2.stringWidth(s);
charDisplayed -= styledText.text.length() - nextRowStartIndex;
if (displayMnemonic) {
if (mnemonicIndex >= 0 && s.length() > mnemonicIndex - charDisplayed) {
displayMnemonic = true;
mneIndex = mnemonicIndex - charDisplayed;
}
else {
displayMnemonic = false;
}
}
charDisplayed += s.length();
nextRowStartIndex += nextRowStartIndexInSubString;
}
else {
// use this method to clip string
s = SwingUtilities.layoutCompoundLabel(label, fm2, s, null, label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(), new Rectangle(x, y, widthLeft, label.getHeight()), new Rectangle(), new Rectangle(), 0);
strWidth = fm2.stringWidth(s);
}
}
else if (label.isLineWrap()) {
nextRowStartIndex = 0;
}
else if (i < _styledTexts.size() - 1) {
StyledText nextStyledText = _styledTexts.get(i + 1);
String nextText = nextStyledText.text;
StyleRange nextStyle = nextStyledText.styleRange;
int size = (nextStyle != null &&
(nextStyle.isSuperscript() || nextStyle.isSubscript())) ? Math.round((float) defaultFontSize / nextStyle.getFontShrinkRatio()) : defaultFontSize;
font = getFont(label);
if (nextStyle != null && ((nextStyle.getFontStyle() != -1 && font.getStyle() != nextStyle.getFontStyle()) || font.getSize() != size)) {
font = FontUtils.getCachedDerivedFont(font, nextStyle.getFontStyle() == -1 ? font.getStyle() : nextStyle.getFontStyle(), size);
nextFm2 = label.getFontMetrics(font);
}
else {
nextFm2 = fm;
}
if (nextFm2.stringWidth(nextText) > widthLeft - strWidth) {
String nextS = SwingUtilities.layoutCompoundLabel(label, nextFm2, nextText, null, label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(), new Rectangle(x + strWidth, y, widthLeft - strWidth, label.getHeight()), new Rectangle(), new Rectangle(), 0);
if (nextFm2.stringWidth(nextS) > widthLeft - strWidth) {
s = SwingUtilities.layoutCompoundLabel(label, fm2, s, null, label.getVerticalAlignment(), label.getHorizontalAlignment(),
label.getVerticalTextPosition(), label.getHorizontalTextPosition(), new Rectangle(x, y, strWidth - 1, label.getHeight()), new Rectangle(), new Rectangle(), 0);
strWidth = fm2.stringWidth(s);
}
}
}
// start of actual painting
if (style != null && style.isSuperscript()) {
y -= fm.getHeight() - fm2.getHeight();
}
if (style != null && style.getBackgroundColor() != null) {
g.setColor(style.getBackgroundColor());
g.fillRect(x, y - fm2.getHeight(), strWidth, fm2.getHeight() + 4);
}
Color textColor = (style != null && !label.isIgnoreColorSettings() && style.getFontColor() != null) ? style.getFontColor() : label.getForeground();
if (!label.isEnabled()) {
textColor = UIDefaultsLookup.getColor("Label.disabledForeground");
}
g.setColor(textColor);
if (displayMnemonic) {
JideSwingUtilities.drawStringUnderlineCharAt(label, g, s, mneIndex, x, y);
}
else {
JideSwingUtilities.drawString(label, g, s, x, y);
}
if (style != null) {
Stroke oldStroke = ((Graphics2D) g).getStroke();
if (style.getLineStroke() != null) {
((Graphics2D) g).setStroke(style.getLineStroke());
}
if (!label.isIgnoreColorSettings() && style.getLineColor() != null) {
g.setColor(style.getLineColor());
}
if (style.isStrikethrough()) {
int lineY = y + (fm2.getDescent() - fm2.getAscent()) / 2;
g.drawLine(x, lineY, x + strWidth - 1, lineY);
}
if (style.isDoublestrikethrough()) {
int lineY = y + (fm2.getDescent() - fm2.getAscent()) / 2;
g.drawLine(x, lineY - 1, x + strWidth - 1, lineY - 1);
g.drawLine(x, lineY + 1, x + strWidth - 1, lineY + 1);
}
if (style.isUnderlined()) {
int lineY = y + 1;
g.drawLine(x, lineY, x + strWidth - 1, lineY);
}
if (style.isDotted()) {
int dotY = y + 1;
for (int dotX = x; dotX < x + strWidth; dotX += 4) {
g.drawRect(dotX, dotY, 1, 1);
}
}
if (style.isWaved()) {
int waveY = y + 1;
for (int waveX = x; waveX < x + strWidth; waveX += 4) {
if (waveX + 2 <= x + strWidth - 1)
g.drawLine(waveX, waveY + 2, waveX + 2, waveY);
if (waveX + 4 <= x + strWidth - 1)
g.drawLine(waveX + 3, waveY + 1, waveX + 4, waveY + 2);
}
}
if (style.getLineStroke() != null) {
((Graphics2D) g).setStroke(oldStroke);
}
}
// end of actual painting
x += strWidth;
}
}
private int findNextWordStartIndex(String string, int firstRowEndIndex) {
boolean skipFirstWord = firstRowEndIndex < 0;
for (int i = firstRowEndIndex + 1; i < string.length(); i++) {
char c = string.charAt(i);
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
if (!skipFirstWord) {
return i;
}
}
else {
skipFirstWord = false;
}
}
return string.length();
}
private int findFirstRowWordEndIndex(String string) {
boolean spaceFound = false;
for (int i = string.length() - 1; i >= 0; i--) {
char c = string.charAt(i);
if (!spaceFound) {
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
spaceFound = true;
}
}
else {
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
return i;
}
}
}
return -1;
}
/**
* Compute and return the location of the icons origin, the location of origin of the text baseline, and a possibly
* clipped version of the compound labels string. Locations are computed relative to the viewR rectangle. The
* JComponents orientation (LEADING/TRAILING) will also be taken into account and translated into LEFT/RIGHT values
* accordingly.
*
* @param c the component
* @param fm the font metrics
* @param text the text
* @param icon the icon
* @param verticalAlignment vertical alignment mode
* @param horizontalAlignment horizontal alignment mode
* @param verticalTextPosition vertical text position
* @param horizontalTextPosition horizontal text position
* @param viewR view rectangle
* @param iconR icon rectangle
* @param textR text rectangle
* @param textIconGap the gap between text and icon
* @return the layout string
*/
public static String layoutCompoundLabel(JComponent c,
FontMetrics fm,
String text,
Icon icon,
int verticalAlignment,
int horizontalAlignment,
int verticalTextPosition,
int horizontalTextPosition,
Rectangle viewR,
Rectangle iconR,
Rectangle textR,
int textIconGap) {
boolean orientationIsLeftToRight = true;
int hAlign = horizontalAlignment;
int hTextPos = horizontalTextPosition;
if (c != null) {
if (!(c.getComponentOrientation().isLeftToRight())) {
orientationIsLeftToRight = false;
}
}
// Translate LEADING/TRAILING values in horizontalAlignment
// to LEFT/RIGHT values depending on the components orientation
switch (horizontalAlignment) {
case LEADING:
hAlign = (orientationIsLeftToRight) ? LEFT : RIGHT;
break;
case TRAILING:
hAlign = (orientationIsLeftToRight) ? RIGHT : LEFT;
break;
}
// Translate LEADING/TRAILING values in horizontalTextPosition
// to LEFT/RIGHT values depending on the components orientation
switch (horizontalTextPosition) {
case LEADING:
hTextPos = (orientationIsLeftToRight) ? LEFT : RIGHT;
break;
case TRAILING:
hTextPos = (orientationIsLeftToRight) ? RIGHT : LEFT;
break;
}
return layoutCompoundLabelImpl(c,
fm,
text,
icon,
verticalAlignment,
hAlign,
verticalTextPosition,
hTextPos,
viewR,
iconR,
textR,
textIconGap);
}
/**
* Compute and return the location of the icons origin, the location of origin of the text baseline, and a possibly
* clipped version of the compound labels string. Locations are computed relative to the viewR rectangle. This
* layoutCompoundLabel() does not know how to handle LEADING/TRAILING values in horizontalTextPosition (they will
* default to RIGHT) and in horizontalAlignment (they will default to CENTER). Use the other version of
* layoutCompoundLabel() instead.
*
* @param fm the font metrics
* @param text the text
* @param icon the icon
* @param verticalAlignment vertical alignment mode
* @param horizontalAlignment horizontal alignment mode
* @param verticalTextPosition vertical text position
* @param horizontalTextPosition horizontal text position
* @param viewR view rectangle
* @param iconR icon rectangle
* @param textR text rectangle
* @param textIconGap the gap between text and icon
* @return the layout string
*/
public static String layoutCompoundLabel(
FontMetrics fm,
String text,
Icon icon,
int verticalAlignment,
int horizontalAlignment,
int verticalTextPosition,
int horizontalTextPosition,
Rectangle viewR,
Rectangle iconR,
Rectangle textR,
int textIconGap) {
return layoutCompoundLabelImpl(null, fm, text, icon,
verticalAlignment,
horizontalAlignment,
verticalTextPosition,
horizontalTextPosition,
viewR, iconR, textR, textIconGap);
}
/**
* Compute and return the location of the icons origin, the location of origin of the text baseline, and a possibly
* clipped version of the compound labels string. Locations are computed relative to the viewR rectangle. This
* layoutCompoundLabel() does not know how to handle LEADING/TRAILING values in horizontalTextPosition (they will
* default to RIGHT) and in horizontalAlignment (they will default to CENTER). Use the other version of
* layoutCompoundLabel() instead.
*
* @param c the component
* @param fm the font metrics
* @param text the text
* @param icon the icon
* @param verticalAlignment vertical alignment mode
* @param horizontalAlignment horizontal alignment mode
* @param verticalTextPosition vertical text position
* @param horizontalTextPosition horizontal text position
* @param viewR view rectangle
* @param iconR icon rectangle
* @param textR text rectangle
* @param textIconGap the gap between text and icon
* @return the layout string
*/
@SuppressWarnings({"UnusedDeclaration"})
private static String layoutCompoundLabelImpl(
JComponent c,
FontMetrics fm,
String text,
Icon icon,
int verticalAlignment,
int horizontalAlignment,
int verticalTextPosition,
int horizontalTextPosition,
Rectangle viewR,
Rectangle iconR,
Rectangle textR,
int textIconGap) {
/* Initialize the icon bounds rectangle iconR.
*/
if (icon != null) {
iconR.width = icon.getIconWidth();
iconR.height = icon.getIconHeight();
}
else {
iconR.width = iconR.height = 0;
}
/* Initialize the text bounds rectangle textR. If a null
* or and empty String was specified we substitute "" here
* and use 0,0,0,0 for textR.
*/
boolean textIsEmpty = (text == null) || text.equals("");
int lsb = 0;
/* Unless both text and icon are non-null, we effectively ignore
* the value of textIconGap.
*/
int gap;
View v;
if (textIsEmpty) {
textR.width = textR.height = 0;
text = "";
gap = 0;
}
else {
int availTextWidth;
gap = (icon == null) ? 0 : textIconGap;
if (horizontalTextPosition == CENTER) {
availTextWidth = viewR.width;
}
else {
availTextWidth = viewR.width - (iconR.width + gap);
}
v = (c != null) ? (View) c.getClientProperty("html") : null;
if (v != null) {
textR.width = Math.min(availTextWidth, (int) v.getPreferredSpan(View.X_AXIS));
textR.height = (int) v.getPreferredSpan(View.Y_AXIS);
}
else {
// this is only place that is changed for StyledLabel
// textR.width = SwingUtilities2.stringWidth(c, fm, text);
// lsb = SwingUtilities2.getLeftSideBearing(c, fm, text);
// if (lsb < 0) {
// // If lsb is negative, add it to the width and later
// // adjust the x location. This gives more space than is
// // actually needed.
// // This is done like this for two reasons:
// // 1. If we set the width to the actual bounds all
// // callers would have to account for negative lsb
// // (pref size calculations ONLY look at width of
// // textR)
// // 2. You can do a drawString at the returned location
// // and the text won't be clipped.
// textR.width -= lsb;
// }
// if (textR.width > availTextWidth) {
// text = SwingUtilities2.clipString(c, fm, text,
// availTextWidth);
// textR.width = SwingUtilities2.stringWidth(c, fm, text);
// }
// textR.height = fm.getHeight();
}
}
/* Compute textR.x,y given the verticalTextPosition and
* horizontalTextPosition properties
*/
if (verticalTextPosition == TOP) {
if (horizontalTextPosition != CENTER) {
textR.y = 0;
}
else {
textR.y = -(textR.height + gap);
}
}
else if (verticalTextPosition == CENTER) {
textR.y = (iconR.height / 2) - (textR.height / 2);
}
else { // (verticalTextPosition == BOTTOM)
if (horizontalTextPosition != CENTER) {
textR.y = iconR.height - textR.height;
}
else {
textR.y = (iconR.height + gap);
}
}
if (horizontalTextPosition == LEFT) {
textR.x = -(textR.width + gap);
}
else if (horizontalTextPosition == CENTER) {
textR.x = (iconR.width / 2) - (textR.width / 2);
}
else { // (horizontalTextPosition == RIGHT)
textR.x = (iconR.width + gap);
}
/* labelR is the rectangle that contains iconR and textR.
* Move it to its proper position given the labelAlignment
* properties.
*
* To avoid actually allocating a Rectangle, Rectangle.union
* has been inlined below.
*/
int labelR_x = Math.min(iconR.x, textR.x);
int labelR_width = Math.max(iconR.x + iconR.width,
textR.x + textR.width) - labelR_x;
int labelR_y = Math.min(iconR.y, textR.y);
int labelR_height = Math.max(iconR.y + iconR.height,
textR.y + textR.height) - labelR_y;
int dx, dy;
if (verticalAlignment == TOP) {
dy = viewR.y - labelR_y;
}
else if (verticalAlignment == CENTER) {
dy = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2));
}
else { // (verticalAlignment == BOTTOM)
dy = (viewR.y + viewR.height) - (labelR_y + labelR_height);
}
if (horizontalAlignment == LEFT) {
dx = viewR.x - labelR_x;
}
else if (horizontalAlignment == RIGHT) {
dx = (viewR.x + viewR.width) - (labelR_x + labelR_width);
}
else { // (horizontalAlignment == CENTER)
dx = (viewR.x + (viewR.width / 2)) -
(labelR_x + (labelR_width / 2));
}
/* Translate textR and glypyR by dx,dy.
*/
textR.x += dx;
textR.y += dy;
iconR.x += dx;
iconR.y += dy;
if (lsb < 0) {
// lsb is negative. Shift the x location so that the text is
// visually drawn at the right location.
textR.x -= lsb;
}
int maxIconY = viewR.height / 2;
Insets insets = c.getInsets();
int leftMostX = viewR.x;
int rightMostX = viewR.width;
rightMostX -= iconR.width;
if (horizontalTextPosition == SwingConstants.CENTER) {
if (viewR.width < textR.width) {
iconR.x = (leftMostX + rightMostX) / 2;
}
else {
int leftMostTextX = textR.x;
int rightMostTextX = textR.x + textR.width - iconR.width;
iconR.x = textR.x + (textR.width - iconR.width) / 2;
}
}
else if (iconR.x < leftMostX) {
textR.x += leftMostX - iconR.x;
iconR.x = leftMostX;
}
else if (iconR.x > rightMostX && horizontalAlignment != LEFT) {
iconR.x = rightMostX;
textR.x -= iconR.x - rightMostX;
}
if (insets != null) {
maxIconY -= (insets.bottom + insets.top) / 2;
}
if (icon != null) {
maxIconY -= icon.getIconHeight() / 2;
}
if (verticalAlignment == TOP) {
iconR.y = Math.min(maxIconY, iconR.y);
}
else if (verticalAlignment == BOTTOM) {
iconR.y = Math.max(maxIconY, iconR.y);
}
return text;
}
}