/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ToolTipHelpers.java
* Creation date: Jan 21st 2003.
* By: Ken Wong
*/
package org.openquark.gems.client;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import javax.swing.JComponent;
import org.openquark.cal.compiler.CALDocComment;
import org.openquark.cal.compiler.FunctionalAgent;
import org.openquark.cal.compiler.PolymorphicVarContext;
import org.openquark.cal.compiler.ScopedEntity;
import org.openquark.cal.compiler.ScopedEntityNamingPolicy;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.metadata.ArgumentMetadata;
import org.openquark.cal.metadata.CALFeatureMetadata;
import org.openquark.cal.metadata.ClassMethodMetadata;
import org.openquark.cal.metadata.FunctionMetadata;
import org.openquark.cal.metadata.FunctionalAgentMetadata;
import org.openquark.cal.services.CALWorkspace;
import org.openquark.cal.services.GemEntity;
import org.openquark.gems.client.navigator.NavAddressHelper;
import org.openquark.gems.client.navigator.NavHtmlFactory;
/**
* This class provides methods for constructing commonly used tooltips and general helper methods
* for formatting text to display inside tooltips.
*
* @author Ken Wong
* @author Frank Worsley
*/
public class ToolTipHelpers {
/** The width in pixels at which tooltip lines will be wrapped at. */
private static final int TOOLTIP_MAX_WIDTH = 250;
/**
* @param gem the functional agent gem for which to get a tooltip
* @param parent the component to get the tooltip for
* @param locale the locale for the CALDoc summary (for identifying sentence breaks using BreakIterator).
* @return the HTML formatted tooltip text for a functional agent gem
*/
public static String getFunctionalAgentToolTip(FunctionalAgentGem gem, JComponent parent, Locale locale) {
GemEntity gemEntity = gem.getGemEntity();
FunctionalAgentMetadata metadata = gemEntity.getMetadata(GemCutter.getLocaleFromPreferences());
// Add the name and the description.
// Note: we want to show the fully qualified name in the title, so don't use the naming policy
GemEntity entity = gem.getGemEntity();
// todo-jowong modify below if tooltips are to display minimally qualified module names
String name = metadata.getDisplayName() != null ? entity.getName().getModuleName() + "." + metadata.getDisplayName()
: entity.getName().getQualifiedName();
StringBuilder text = new StringBuilder();
text.append("<html><body><b>");
text.append(name);
text.append("</b>");
if (metadata.getShortDescription() != null) {
text.append("<br>");
text.append(metadata.getShortDescription());
} else if (metadata.getLongDescription() != null) {
text.append("<br>").append(wrapTextToHTMLLines(metadata.getLongDescription(), parent));
} else {
// If there is neither a short nor a long description, display the CALDoc description instead.
CALDocComment caldoc = gem.getGemEntity().getFunctionalAgent().getCALDocComment();
if (caldoc != null) {
String caldocDesc = NavHtmlFactory.getHtmlForCALDocSummaryWithNoHyperlinks(caldoc, locale, true);
if (caldocDesc.length() > 0) {
text.append(caldocDesc);
} else {
text.append("<br>");
}
}
}
text.append("</body></html>");
return text.toString();
}
/**
* @param gemEntity the entity for which to get a tooltip
* @param namingPolicy the naming policy to use for displayed scoped entity names
* @param parent the component to get a tooltip for
* @return the HTML formatted tooltip text describing the given entity
*/
public static String getEntityToolTip(GemEntity gemEntity, ScopedEntityNamingPolicy namingPolicy, JComponent parent) {
Locale locale = GemCutter.getLocaleFromPreferences();
CALFeatureMetadata metadata = gemEntity.getMetadata(locale);
return getEntityToolTip(gemEntity.getFunctionalAgent(), namingPolicy, metadata, parent, locale);
}
/**
* @param scopedEntity the entity for which to get a tooltip
* @param namingPolicy the naming policy to use for displayed scoped entity names
* @param workspace the metamodule in which the entity exists.
* @param parent the component to get a tooltip for
* @return the HTML formatted tooltip text describing the given entity
*/
public static String getEntityToolTip(ScopedEntity scopedEntity, ScopedEntityNamingPolicy namingPolicy, CALWorkspace workspace, JComponent parent) {
Locale locale = GemCutter.getLocaleFromPreferences();
CALFeatureMetadata metadata = workspace.getMetadata(scopedEntity, locale);
return getEntityToolTip(scopedEntity, namingPolicy, metadata, parent, locale);
}
/**
* @param scopedEntity the entity for which to get a tooltip
* @param namingPolicy the naming policy to use for displayed scoped entity names
* @param metadata the metadata for the entity.
* @param parent the component to get a tooltip for
* @param locale the locale for the documentation generation.
* @return the HTML formatted tooltip text describing the given entity
*/
private static String getEntityToolTip(ScopedEntity scopedEntity, ScopedEntityNamingPolicy namingPolicy, CALFeatureMetadata metadata, JComponent parent, Locale locale) {
StringBuilder text = new StringBuilder();
// Start the tool tip with the name.
// Note: we want to show the fully qualified name in the title, so don't use the naming policy
// todo-jowong modify below if tooltips are to display minimally qualified module names
String name = metadata.getDisplayName() != null ? scopedEntity.getName().getModuleName() + "." + metadata.getDisplayName()
: scopedEntity.getName().getQualifiedName();
text.append("<html><body><b>");
text.append(name);
text.append("</b>");
// Add the description
if (metadata.getShortDescription() != null) {
text.append("<br>");
text.append(wrapTextToHTMLLines(metadata.getShortDescription(), parent));
text.append("<br>");
} else if (metadata.getLongDescription() != null) {
// wrap the long description to a maximum line length
text.append("<br>");
text.append(wrapTextToHTMLLines(metadata.getLongDescription(), parent));
text.append("<br>");
} else {
// If there is neither a short nor a long description, display the CALDoc description instead.
CALDocComment caldoc = scopedEntity.getCALDocComment();
if (caldoc != null) {
String caldocDesc = getHTMLForBoundedToolTipText(
NavHtmlFactory.getHtmlForCALDocSummaryWithNoHyperlinks(caldoc, locale, true), TOOLTIP_MAX_WIDTH, 0, true, parent);
if (caldocDesc.length() > 0) {
text.append(caldocDesc);
} else {
text.append("<br>");
}
} else {
text.append("<br>");
}
}
if (metadata instanceof FunctionalAgentMetadata) {
FunctionalAgent envEntity = (FunctionalAgent)scopedEntity;
PolymorphicVarContext polymorphicVarContext = PolymorphicVarContext.make();
TypeExpr[] typePieces = envEntity.getTypeExpr().getTypePieces();
CALDocComment caldoc = envEntity.getCALDocComment();
// Add the result type.
text.append("<br>");
text.append("<b>");
text.append(GemCutter.getResourceString("ToolTipHelper_Returns"));
text.append("</b>");
text.append("<br>");
String returnValueIndicator = "<i>" + GemCutter.getResourceString("ToolTipHelper_ReturnValueIndicator") + "</i>";
String returnType = typePieces[typePieces.length - 1].toString(polymorphicVarContext, namingPolicy);
String returnValueMetadata;
if (metadata instanceof FunctionMetadata) {
returnValueMetadata = ((FunctionMetadata)metadata).getReturnValueDescription();
} else if (metadata instanceof ClassMethodMetadata) {
returnValueMetadata = ((ClassMethodMetadata)metadata).getReturnValueDescription();
} else {
returnValueMetadata = null;
}
CALDocComment.TextBlock returnValueCALDoc;
if (caldoc != null) {
returnValueCALDoc = caldoc.getReturnBlock();
} else {
returnValueCALDoc = null;
}
buildTypeSignatureAndDescription(parent, text, returnValueIndicator, returnType, returnValueMetadata, returnValueCALDoc, true);
// Add the argument descriptions
NavAddressHelper.isMetadataValid(envEntity, (FunctionalAgentMetadata) metadata);
ArgumentMetadata[] arguments = ((FunctionalAgentMetadata) metadata).getArguments();
NavAddressHelper.adjustArgumentNames(envEntity, arguments);
if (arguments.length > 0) {
text.append("<br>");
text.append("<b>");
text.append(GemCutter.getResourceString("ToolTipHelper_Arguments"));
text.append("</b>");
// Process the tagged blocks in the CALDoc to extract the @arg blocks.
text.append("<br>");
for (int i = 0; i < arguments.length; i++) {
String displayName = arguments[i].getDisplayName();
String type = typePieces[i].toString(polymorphicVarContext, namingPolicy);
String descriptionFromMetadata = arguments[i].getShortDescription();
CALDocComment.TextBlock descriptionFromCALDoc;
if (caldoc != null && i < caldoc.getNArgBlocks()) {
descriptionFromCALDoc = caldoc.getNthArgBlock(i).getTextBlock();
} else {
descriptionFromCALDoc = null;
}
boolean shouldEmitLineBreakSuffix = i < arguments.length - 1;
buildTypeSignatureAndDescription(parent, text, displayName, type, descriptionFromMetadata, descriptionFromCALDoc, shouldEmitLineBreakSuffix);
}
}
}
text.append("</body></html>");
return text.toString();
}
/**
* Builds up a type signature and accompanying description in the given StringBuilder.
* @param parent the parent JComponent (used for measuring the displayed width of strings).
* @param buffer the StirngBuffer to be filled.
* @param displayName the name of the documented item.
* @param type the type of the documented item.
* @param descriptionFromMetadata the description from metadata. Can be null.
* @param descriptionFromCALDoc the description from CALDoc. Can be null.
* @param shouldEmitLineBreakSuffix whether a line break should be emitted at the end.
*/
private static void buildTypeSignatureAndDescription(JComponent parent, StringBuilder buffer, String displayName, String type, String descriptionFromMetadata, CALDocComment.TextBlock descriptionFromCALDoc, boolean shouldEmitLineBreakSuffix) {
String textOfArgName = displayName + " :: ";
int widthOfTextOfArgName = getDisplayedTextWidth(textOfArgName, parent);
int widthOfArgNameAndType = getDisplayedTextWidth(textOfArgName + type + " - ", parent);
buffer.append(textOfArgName);
String htmlForType = "<i>" + type + "</i>";
if (descriptionFromMetadata != null) {
String description = descriptionFromMetadata;
int maxWidthOfDescription = TOOLTIP_MAX_WIDTH - widthOfArgNameAndType;
boolean multilineDescription = getDisplayedTextWidth(description, parent) > maxWidthOfDescription;
buffer.append(getHTMLForBoundedToolTipText(htmlForType + (multilineDescription ? "" : " - "), TOOLTIP_MAX_WIDTH - widthOfTextOfArgName, 10, false, parent));
buffer.append(getHTMLForBoundedToolTipText(description, maxWidthOfDescription, 20, shouldEmitLineBreakSuffix, parent));
} else {
// If the argument has no short description but has a CALDoc description, display the CALDoc description.
if (descriptionFromCALDoc != null) {
String description = NavHtmlFactory.getHtmlForCALDocTextBlockWithNoHyperlinks(descriptionFromCALDoc, false);
int maxWidthOfDescription = TOOLTIP_MAX_WIDTH - widthOfArgNameAndType;
boolean multilineDescription = getDisplayedTextWidth(description, parent) > maxWidthOfDescription;
buffer.append(getHTMLForBoundedToolTipText(htmlForType + (multilineDescription ? "" : " - "), TOOLTIP_MAX_WIDTH - widthOfTextOfArgName, 10, false, parent));
buffer.append(getHTMLForBoundedToolTipText(description, maxWidthOfDescription, 20, shouldEmitLineBreakSuffix, parent));
} else {
buffer.append(getHTMLForBoundedToolTipText(htmlForType, TOOLTIP_MAX_WIDTH - widthOfTextOfArgName, 10, shouldEmitLineBreakSuffix, parent));
}
}
}
/**
* Return HTML for a properly word-wrapped piece of text.
* @param text the text to be converted into word-wrapped HTML.
* @param maxWidth the maximum width allowed on the first line before wrapping occurs.
* @param indentByPixels the number of pixels to indent if the text needs to be wrapped onto multiple lines.
* @param parent the parent component for the tooltip.
* @return HTML for the text, properly word-wrapped.
*/
private static String getHTMLForBoundedToolTipText(String text, int maxWidth, int indentByPixels, boolean shouldEmitLineBreakSuffixIfNotWrapped, JComponent parent) {
if (getDisplayedTextWidth(text, parent) > maxWidth) {
return "<table cellpadding='1' cellspacing='0' valign='top' width=" + TOOLTIP_MAX_WIDTH + "><tr><td width=" + indentByPixels + "></td><td>" + text + "</td></tr></table>";
} else {
return text + (shouldEmitLineBreakSuffixIfNotWrapped ? "<br>" : "");
}
}
/**
* @param part the PartConnectable to get a tooltip for
* @param typeStringProvider the type string provider from which to get type strings
* @param namingPolicy rules to use in determining the type string
* @param parent the component requesting the tooltip
* @return the HTML formatted tooltip for the PartConnectable
*/
public static String getPartToolTip(Gem.PartConnectable part, TypeStringProvider typeStringProvider, ScopedEntityNamingPolicy namingPolicy, JComponent parent) {
String argName = null;
String argDesc = null;
String argCALDocDesc = null;
String argType = null;
String argTarget = null;
if (part instanceof Gem.PartInput) {
// Show name and description for input parts.
Gem gem = part.getGem();
Gem.PartInput inputPart = (Gem.PartInput)part;
if (inputPart.isConnected() || inputPart.isBurnt()) {
// Don't show a description for connected or burnt inputs.
argDesc = null;
} else {
// First load the information from the design metadata.
ArgumentMetadata designMetadata = inputPart.getDesignMetadata();
argName = inputPart.getArgumentName().getCompositeName();
argDesc = designMetadata.getShortDescription();
if (argDesc == null && gem instanceof FunctionalAgentGem) {
// If there is no design description of the input,
// show the description of the gem argument instead.
FunctionalAgentGem faGem = (FunctionalAgentGem) gem;
FunctionalAgentMetadata gemMetadata = faGem.getGemEntity().getMetadata(GemCutter.getLocaleFromPreferences());
ArgumentMetadata[] gemArguments = gemMetadata.getArguments();
int argNum = inputPart.getInputNum();
argDesc = argNum < gemArguments.length ? gemArguments[argNum].getShortDescription() : null;
// If there is no description of the gem argument, display the CALDoc instead.
if (argDesc == null) {
CALDocComment caldoc = faGem.getGemEntity().getFunctionalAgent().getCALDocComment();
if (caldoc != null) {
if (argNum < caldoc.getNArgBlocks()) {
argCALDocDesc = NavHtmlFactory.getHtmlForCALDocTextBlockWithNoHyperlinks(caldoc.getNthArgBlock(argNum).getTextBlock(), true);
}
}
}
}
CollectorGem argTargetGem = GemGraph.getInputArgumentTarget(inputPart);
if (argTargetGem != null) {
argTarget = argTargetGem.getUnqualifiedName();
}
}
} else if (part instanceof Gem.PartOutput) {
Gem gem = part.getGem();
Gem.PartOutput outputPart = (Gem.PartOutput)part;
if (!outputPart.isConnected() && gem instanceof FunctionalAgentGem) {
FunctionalAgentGem faGem = (FunctionalAgentGem) gem;
FunctionalAgentMetadata gemMetadata = faGem.getGemEntity().getMetadata(GemCutter.getLocaleFromPreferences());
if (gemMetadata instanceof FunctionMetadata) {
argDesc = ((FunctionMetadata)gemMetadata).getReturnValueDescription();
} else if (gemMetadata instanceof ClassMethodMetadata) {
argDesc = ((ClassMethodMetadata)gemMetadata).getReturnValueDescription();
} else {
argDesc = null;
}
// If there is no description of the return value, display the CALDoc instead.
if (argDesc == null) {
CALDocComment caldoc = faGem.getGemEntity().getFunctionalAgent().getCALDocComment();
if (caldoc != null) {
CALDocComment.TextBlock returnBlock = caldoc.getReturnBlock();
if (returnBlock != null) {
argCALDocDesc = NavHtmlFactory.getHtmlForCALDocTextBlockWithNoHyperlinks(returnBlock, true);
}
}
}
}
}
// Determine the type of the part connectable
Gem rootGem = part.getGem().getRootGem();
if (part.isConnected()) {
argName = null;
argType = "<<i>" + GemCutter.getResourceString("ToolTipHelper_Connected") + "</i>>";
} else if ((part instanceof Gem.PartInput) && ((Gem.PartInput) part).isBurnt()) {
argType = "<<i>" + GemCutter.getResourceString("ToolTipHelper_Burnt") + "</i>>";
} else if ((rootGem != null && GemGraph.isAncestorOfBrokenGemForest(rootGem)) || part.getType() == null) {
argType = "<" + GemCutter.getResourceString("ToolTipHelper_UndefinedType") + ">";
} else {
argType = "<i>" + wrapTextToHTMLLines(typeStringProvider.getTypeString(part.getType(), namingPolicy), parent) + "</i>";
}
// Build the tooltip using the name, type and description
StringBuilder toolTip = new StringBuilder("<html><body>");
if (part instanceof Gem.PartInput) {
if (argName != null) {
toolTip.append("<b>" + argName + "</b> :: ");
}
toolTip.append(argType);
if (argTarget != null) {
toolTip.append("<br>" + GemCutterMessages.getString("ArgTargetToolTip", argTarget));
}
if (argDesc != null) {
toolTip.append("<br>" + argDesc);
}
if (argCALDocDesc != null) {
toolTip.append(argCALDocDesc);
}
} else if (part instanceof Gem.PartOutput) {
toolTip.append(argType);
if (argDesc != null) {
toolTip.append("<br>" + argDesc);
}
if (argCALDocDesc != null) {
toolTip.append(argCALDocDesc);
}
} else {
toolTip.append(argType);
}
toolTip.append("</body></html>");
return toolTip.toString();
}
/**
* Takes a string of text and splits it into lines of a maximum length. Lines longer than the maximum length
* will be wrapped at word boundaries. Words longer than the maximum line length will be truncated.
* An array of strings is returned where each element string is one line. The lines in the array will not
* have linebreaks at the end.
*
* NOTE: An assumption is made that the original text contains only text for display
* and that there are no html tags taking up space.
*
* @param originalText - the text that needs to be broken into lines that will fit into the tool tip.
* @param maxLineLength - the max width of the lines to create when breaking up the text.
* @param font - the font of the component that will display the tooltip.
* @param context - the font render context of the component that will display the tooltip.
* @return String[] - array of lines without line breaks.
*/
public static String[] splitTextIntoLines(String originalText, int maxLineLength, Font font, FontRenderContext context) {
List<String> result = new ArrayList<String> ();
/* We use split and not a StringTokenizer since we don't want the linebreaks
* but we do want empty lines between two adjacent line breaks.
*/
String lines[] = originalText.split("\n", -1);
for (final String line : lines) {
int lineLength = 0;
StringBuilder lineBuffer = new StringBuilder();
/* Here we use a StringTokenizer since we want each word and the spaces
* between the individual words.
*/
StringTokenizer words = new StringTokenizer (line, " ", true);
while (words.hasMoreTokens ()) {
String word = words.nextToken ();
int wordLength = (int) font.getStringBounds(word, context).getWidth();
if (wordLength > maxLineLength) {
// This word by itself is too long to fit on one line so we truncate the word.
AttributedString attrString = new AttributedString (word);
LineBreakMeasurer measurer = new LineBreakMeasurer(attrString.getIterator(), context);
int index = measurer.nextOffset(maxLineLength);
// Add the previous line if there is any
if (lineBuffer.length() > 0) {
result.add (lineBuffer.toString ());
lineBuffer = new StringBuilder ();
lineLength = 0;
}
// Truncate the word at index and add it as its own line
result.add (word.substring(0, index) + "...");
} else if (wordLength + lineLength > maxLineLength) {
// The current line would be too long with this word appended.
// So start a new line with this word.
result.add (lineBuffer.toString ());
lineBuffer = new StringBuilder (word);
lineLength = wordLength;
} else {
// This word fits on the current line
lineBuffer.append(word);
lineLength += wordLength;
}
}
// Add the last line
if (lineBuffer.length() > 0 || line.equals("")) {
result.add (lineBuffer.toString());
}
}
// Combine the list back into an array
String[] resultArray = new String[result.size()];
result.toArray(resultArray);
return resultArray;
}
/**
* Converts an arbitrary string into a properly formatted multiline tooltip. If lines are too wide they will
* be wrapped. If the total text is too long and exceeds the specified height it will be cut off and "more..."
* will be displayed on the last line.
*
* @param text the original text which should be displayed in the tooltip
* @param maxWidth the maximum width of the tooltip in pixels
* @param maxHeight the maximum height of the tooltip in pixels
* @param font the font that will be used for the tooltip
* @param context the context in which the tooltip will be rendered
* @return the HTML formatted tooltip text
*/
public static String stringToHtmlToolTip (String text, int maxWidth, int maxHeight, Font font, FontRenderContext context) {
// Get a properly formatted tooltip that is not too large
String[] toolTipLines = ToolTipHelpers.splitTextIntoLines(text, maxWidth, font, context);
// Combine the lines back into an HTML tooltip
int totalHeight = 0;
String toolTipText = "<html>";
for (int i = 0; i < toolTipLines.length; i++) {
totalHeight += font.getStringBounds(toolTipLines[i], context).getHeight();
if (totalHeight > maxHeight) {
break;
}
if (i > 0) {
toolTipText += "<br>";
}
toolTipText += toolTipLines[i];
}
// We want 20 lines max, so if there is more then add a ...
if (totalHeight > maxHeight) {
toolTipText += "<br><i>" + GemCutter.getResourceString("ToolTipHelper_More") + "</i>";
}
toolTipText += "</html>";
return toolTipText;
}
/**
* Takes a string of text and splits it into lines of a maximum length of TOOLTIP_MAX_WIDTH pixels.
* The lines will be separated by HTML <br> tags. Lines longer than the maximum length will
* be wrapped at word boundaries. Words longer than the maximum line length will be truncated.
*
* NOTE: An assumption is made that the original text contains only text for display
* and that there are no html tags taking up space.
*
* @param originalText - the text that needs to be broken into lines that will fit into the tool tip.
* @param component - the component whose font is used to calculate the line split locations
*/
public static String wrapTextToHTMLLines(String originalText, JComponent component) {
if (originalText == null) {
return "";
}
String text = "";
Graphics2D parentGraphics = (Graphics2D) component.getGraphics();
if (parentGraphics == null) {
// Cannot determine tooltip size because the parent component is not displayed
return originalText;
}
String[] lines = splitTextIntoLines(originalText, TOOLTIP_MAX_WIDTH, component.getFont(), parentGraphics.getFontRenderContext());
for (int i = 0; i < lines.length; i++) {
if (i > 0) {
text += "<br>";
}
text += lines[i];
}
return text;
}
/**
* Computes the width of the text if displayed as is in the component.
* @param text the text to check.
* @param component the component to check with.
* @return the width of the text if displayed as is in the component.
*/
public static int getDisplayedTextWidth(String text, JComponent component) {
if (text == null) {
return 0;
}
Graphics2D parentGraphics = (Graphics2D) component.getGraphics();
if (parentGraphics == null) {
// Cannot determine tooltip size because the parent component is not displayed
return 0;
}
return (int) component.getFont().getStringBounds(text, parentGraphics.getFontRenderContext()).getWidth();
}
}