package org.eclipse.nebula.widgets.ganttchart.utils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.nebula.widgets.ganttchart.ColorCache;
import org.eclipse.nebula.widgets.ganttchart.Utils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
/**
* Helper class to draw texts with markups within the GanttChart.
* Currently used for rendering tooltips and section details.
* Following markups are supported:
* <ul>
* <li>\\ce - render the following text in black</li>
* <li>\\c[0-9]{9} - render the following text in the specified rbg color (Note that there need to be nine digits)</li>
* <li>\\s[0-9]{1,3} - render the following text with the specified font size</li>
* <li>\\i - render the following text italic</li>
* <li>\\b - render the following text bold</li>
* <li>\\x - normalize, which means to reset the previous assigned markups and render with the default font and color</li>
* </ul>
*/
@SuppressWarnings("nls")
public class TextPainterHelper {
/**
* Will draw the given text to the given GC. Before drawing the containing
* markups will be interpreted to apply color and font settings.
* @param gc The GC to draw the text to
* @param text The text to draw
* @param x The x coordinate where the text should be drawed to the GC
* @param y The y coordinate where the text should be drawed to the GC
* @return The end point of the drawed text.
*/
public static Point drawText(final GC gc, final String text, final int x, final int y) {
Pattern pattern = Pattern.compile("(\\\\(ce|c[0-9]{9}|s[0-9]{1,3}|[xbi]))*[^\\\\]*");
try {
final Font old = gc.getFont();
final int oldSize = (int) old.getFontData()[0].height;
int curX = x;
boolean bold = false;
boolean italic = false;
int size = oldSize;
Color fg = ColorCache.getBlack();
int maxWidth = 0;
int maxHeight = 0;
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
String token = matcher.group();
if (isNormalize(token)) {
bold = false;
italic = false;
size = oldSize;
fg = ColorCache.getBlack();
}
else {
final int newSize = getSize(token);
if (newSize != size && newSize != -1) {
size = newSize;
}
final boolean newBold = isBold(token);
if (bold && !newBold) {
bold = true;
}
else {
bold = newBold;
}
final boolean newItalic = isItalic(token);
if (italic && !newItalic) {
italic = true;
}
else {
italic = newItalic;
}
final Color newColor = getColor(token);
if (newColor != null && !newColor.equals(fg)) {
fg = newColor;
}
}
if (fg != null) {
gc.setForeground(fg);
}
token = cleanUp(token);
int style = SWT.NORMAL;
if (bold) {
style |= SWT.BOLD;
}
if (italic) {
style |= SWT.ITALIC;
}
Font used = Utils.applyFontData(old, style, size);
gc.setFont(used);
if (token.length() != 0) {
gc.drawText(token, curX, y, true);
final int extX = gc.textExtent(token).x;
final int extY = gc.textExtent(token).y;
curX += extX;
maxWidth = Math.max(maxWidth, curX);
maxHeight = Math.max(maxHeight, extY);
}
used.dispose();
}
gc.setFont(old);
return new Point(maxWidth - x, maxHeight);
}
catch (Exception err) {
SWT.error(SWT.ERROR_UNSPECIFIED, err);
}
return null;
}
/**
* Removes all markups out of the given string. Needed to render
* after all markups have been resolved.
* @param string The string whose markups should be removed.
* @return The given string without any further markups.
*/
public static String cleanUp(final String string) {
String str = string;
str = str.replaceAll("\\\\ce", "");
str = str.replaceAll("\\\\c[0-9]{9}", "");
str = str.replaceAll("\\\\s[0-9]{1,3}", "");
str = str.replaceAll("\\\\x", "");
str = str.replaceAll("\\\\b", "");
str = str.replaceAll("\\\\i", "");
return str;
}
/**
* Checks for the existence of the normalize markup.
* The normalize markup can be used to reset all former assigned
* markups for the following text to render.
* The normalize markup is \\x
* @param str The string to check for the normalize markup
* @return <code>true</code> if the given string contains the
* normalize markup
*/
public static boolean isNormalize(final String str) {
return str.indexOf("\\x") > -1;
}
/**
* Checks for the existence of the bold markup.
* The bold markup is \\b
* @param str The string to check for the bold markup
* @return <code>true</code> if the given string contains the
* bold markup
*/
public static boolean isBold(final String str) {
return str.indexOf("\\b") > -1;
}
/**
* Checks for the existence of the italic markup.
* The italic markup is \\i
* @param str The string to check for the italic markup
* @return <code>true</code> if the given string contains the
* italic markup
*/
public static boolean isItalic(final String str) {
return str.indexOf("\\i") > -1;
}
/**
* Gets the font size out of a size markup in the given string.
* A size markup is specified \\s[0-9]{1,3}
* @param str The string containing the size markup
* @return The font size to use for rendering
*/
public static int getSize(final String str) {
Pattern pattern = Pattern.compile("\\\\s[0-9]{1,3}");
Matcher matcher = pattern.matcher(str);
if (matcher.find()) {
String sizeString = matcher.group();
sizeString = sizeString.substring(2);
try {
return Integer.parseInt(sizeString);
} catch (Exception badParse) {
SWT.error(SWT.ERROR_UNSPECIFIED, badParse);
}
}
return -1;
}
/**
* Gets the color for a color markup in the given string.
* A color markup is either \\ce for black or \\c[0-9]{9}
* to specify a custom color by rgb code
* @param str The string containing a color markup
* @return The Color that is specified by the color markup
*/
public static Color getColor(final String str) {
final int start = str.indexOf("\\c");
if (start == -1) {
return null;
}
if (str.indexOf("\\ce") != -1) {
return ColorCache.getBlack();
}
try {
final int red = Integer.parseInt(str.substring(start + 2, start + 5));
final int green = Integer.parseInt(str.substring(start + 5, start + 8));
final int blue = Integer.parseInt(str.substring(start + 8, start + 11));
return ColorCache.getColor(red, green, blue);
} catch (Exception err) {
SWT.error(SWT.ERROR_UNSPECIFIED, err);
}
return ColorCache.getBlack();
}
}