/*
* *****************************************************************************
* * Copyright (c) 2006-2012 XMind Ltd. and others. This file is a part of XMind
* 3. XMind releases 3 and above are dual-licensed under the Eclipse Public
* License (EPL), which is available at
* http://www.eclipse.org/legal/epl-v10.html and the GNU Lesser General Public
* License (LGPL), which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details. Contributors: XMind Ltd. -
* initial API and implementation
*******************************************************************************/
package org.xmind.gef.draw2d;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.draw2d.AbstractBackground;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jface.util.Util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.internal.DPIUtil;
import org.eclipse.swt.widgets.Display;
import org.xmind.gef.GEF;
import org.xmind.gef.draw2d.geometry.PrecisionDimension;
import org.xmind.gef.draw2d.geometry.PrecisionInsets;
import org.xmind.gef.draw2d.geometry.PrecisionPoint;
import org.xmind.gef.draw2d.geometry.PrecisionRectangle;
import org.xmind.gef.draw2d.geometry.PrecisionRotator;
import org.xmind.gef.draw2d.graphics.GraphicsUtils;
import org.xmind.gef.draw2d.graphics.Path;
import org.xmind.gef.util.GEFUtils;
import com.ibm.icu.text.BreakIterator;
/**
* @author Frank Shaka
*/
@SuppressWarnings("restriction")
public class RotatableWrapLabel extends Figure implements ITextFigure,
IWrapFigure, IRotatableFigure, ITransparentableFigure {
private static final int FLAG_SINGLE_LINE = MAX_FLAG << 1;
private static final int FLAG_ABBREVIATED = MAX_FLAG << 2;
static {
MAX_FLAG = FLAG_ABBREVIATED;
}
private static final float PADDING = 1.5f;
private static final float RIGHT_MARGIN = 1.0f;
private static final float[] RECT = new float[4];
private static final PrecisionDimension D = new PrecisionDimension();
private static final PrecisionRectangle R = new PrecisionRectangle();
protected static final Dimension NO_TEXT_SIZE = new Dimension(1, 10);
public static final int NORMAL = 0;
public static final int ADVANCED = 1;
public static final String ELLIPSE = "..."; //$NON-NLS-1$
/*
* Infos:
*/
private String text = ""; //$NON-NLS-1$
private TextStyle style = null;
private int align = PositionConstants.LEFT;
private int textCase = GEF.MANUAL;
private int renderStyle = NORMAL;
private int lineSpacing = -1;
private int textAlpha = 0xff;
private int fillAlpha = 0xff;
private int prefWidth = -1;
/*
* Caches:
*/
private Dimension cachedPrefSize = null;
private String appliedText = null;
private PrecisionRectangle textArea = null;
private PrecisionDimension nonRotatedPrefSize = null;
private PrecisionInsets rotatedInsets = null;
private int cachedWidthHint = -1;
private static Map<String, Integer> textToLabelWidth = new HashMap<String, Integer>();
private PrecisionRotator rotator = new PrecisionRotator();
/**
*
*/
public RotatableWrapLabel() {
}
public RotatableWrapLabel(String text) {
setCachedPrefWidth(text);
setText(text);
}
public RotatableWrapLabel(int renderStyle) {
this.renderStyle = renderStyle;
}
public RotatableWrapLabel(String text, int renderStyle) {
setCachedPrefWidth(text);
setText(text);
this.renderStyle = renderStyle;
}
public int getPrefWidth() {
return prefWidth;
}
public void setPrefWidth(int prefWidth) {
if (prefWidth == this.prefWidth)
return;
this.prefWidth = prefWidth;
revalidate();
repaint();
}
public boolean isSingleLine() {
return getFlag(FLAG_SINGLE_LINE);
}
public void setSingleLine(boolean singleLine) {
if (singleLine == isSingleLine())
return;
setFlag(FLAG_SINGLE_LINE, singleLine);
revalidate();
repaint();
}
public boolean isAbbreviated() {
return getFlag(FLAG_ABBREVIATED);
}
public void setAbbreviated(boolean abbreviated) {
if (abbreviated == isAbbreviated())
return;
setFlag(FLAG_ABBREVIATED, abbreviated);
revalidate();
repaint();
}
public void setMainAlpha(int alpha) {
if (alpha == this.textAlpha)
return;
this.textAlpha = alpha;
repaint();
}
public int getMainAlpha() {
return textAlpha;
}
public void setSubAlpha(int alpha) {
if (alpha == this.fillAlpha)
return;
this.fillAlpha = alpha;
repaint();
}
public int getSubAlpha() {
return fillAlpha;
}
/**
* @see org.xmind.gef.draw2d.ITextFigure#getStyle()
*/
public TextStyle getStyle() {
return style;
}
public int getRenderStyle() {
return renderStyle;
}
/**
* @return the text of this label figure (never be null)
*/
public String getText() {
return text;
}
/**
* @see org.xmind.gef.draw2d.ITextFigure#getTextAlignment()
*/
public int getTextAlignment() {
return align;
}
/**
* @see org.xmind.gef.draw2d.ITextFigure#getTextCase()
*/
public int getTextCase() {
return textCase;
}
/**
* Gets the angle in degrees by which the label is rotated.
*
* @return the rotateAngle
*/
public double getRotationDegrees() {
return rotator.getAngle();
}
/**
* Sets the angle in degrees by which the label is rotated.
*
* @param degrees
* the rotateAngle to set
*/
public void setRotationDegrees(double degrees) {
double oldAngle = getRotationDegrees();
rotator.setAngle(degrees);
if (getBorder() instanceof IRotatable) {
((IRotatable) getBorder()).setRotationDegrees(degrees);
}
if (getLayoutManager() instanceof IRotatable) {
((IRotatable) getLayoutManager()).setRotationDegrees(degrees);
}
for (Object child : getChildren()) {
if (child instanceof IRotatable) {
((IRotatable) child).setRotationDegrees(degrees);
}
}
if (degrees != oldAngle) {
revalidate();
repaint();
}
}
// public Rotator getRotator() {
// return rotator.getRotator();
// }
//
// public void setRotator(Rotator rotator) {
// this.rotator.setRotator(rotator);
// setRotateAngle(rotator.getAngle());
// }
/**
* @see org.xmind.gef.draw2d.ITextFigure#setStyle(org.eclipse.swt.graphics.TextStyle)
*/
public void setStyle(TextStyle style) {
if (GEFUtils.equals(this.style, style))
return;
this.style = style;
if (style != null) {
super.setFont(style.font);
super.setForegroundColor(style.foreground);
} else {
super.setFont(null);
super.setForegroundColor(null);
}
repaint();
}
@Override
public void setFont(Font f) {
Font old = getLocalFont();
super.setFont(f);
if (getLocalFont() != old) {
if (style != null)
style.font = f;
}
}
@Override
public void setForegroundColor(Color fg) {
Color old = getLocalForegroundColor();
super.setForegroundColor(fg);
if (getLocalForegroundColor() != old) {
if (style != null)
style.foreground = fg;
}
}
public void setRenderStyle(int style) {
if (style == this.renderStyle || (style != NORMAL && style != ADVANCED))
return;
this.renderStyle = style;
revalidate();
repaint();
}
/**
* @see org.xmind.gef.draw2d.ITextFigure#setText(java.lang.String)
*/
public void setText(String text) {
// "text" will never be null.
if (text == null)
text = ""; //$NON-NLS-1$
String t = text.replaceAll("\\r\\n|\\r", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
if (this.text.equals(t))
return;
this.text = t;
revalidate();
repaint();
}
public void setTextAlignment(int align) {
if (this.align == align)
return;
this.align = align;
repaint();
}
public void setTextCase(int textCase) {
if (this.textCase == textCase)
return;
this.textCase = textCase;
revalidate();
repaint();
}
protected void receiveWidthCaches(int wHint) {
if (wHint != cachedWidthHint) {
flushCaches();
}
cachedWidthHint = wHint;
}
protected void flushCaches() {
cachedPrefSize = null;
appliedText = null;
textArea = null;
nonRotatedPrefSize = null;
}
/*
* (non-Javadoc)
* @see org.eclipse.draw2d.IFigure#getPreferredSize(int, int)
*/
@Override
public Dimension getPreferredSize(int wHint, int hHint) {
if (prefSize != null)
return prefSize;
if (prefWidth > 0) {
wHint = Math.max(0, prefWidth - getInsets().getWidth());
} else if (wHint > 0) {
wHint = Math.max(0, wHint - getInsets().getWidth());
}
receiveWidthCaches(wHint);
if (getText().length() == 0)
return NO_TEXT_SIZE;
if (cachedPrefSize == null) {
cachedPrefSize = calculateRotatedPreferredSize(wHint, hHint)
.toBiggerDraw2DDimension();
cachedPrefSize.union(getMinimumSize(wHint, hHint));
}
return cachedPrefSize;
}
protected PrecisionDimension calculateRotatedPreferredSize(int wHint,
int hHint) {
PrecisionDimension d = getNormalPreferredSize(wHint, hHint);
return rotator.td(d);
}
public PrecisionDimension getNormalPreferredSize(int wHint, int hHint) {
if (prefWidth > 0) {
wHint = Math.max(0, prefWidth - getInsets().getWidth());
} else if (wHint > 0) {
wHint = Math.max(0, wHint - getInsets().getWidth());
}
receiveWidthCaches(wHint);
if (nonRotatedPrefSize == null) {
nonRotatedPrefSize = calculateNormalPreferredSize(wHint);
}
return nonRotatedPrefSize;
}
/**
* @param hint
* @param hint2
* @return
*/
protected PrecisionDimension calculateNormalPreferredSize(int wHint) {
PrecisionDimension d = getTextArea(wHint).getSize();
Insets insets = getInsets();
d.expand(insets.getWidth(), insets.getHeight());
return d;
}
public String getAppliedText() {
return getAppliedText(cachedWidthHint);
}
protected String getAppliedText(int wHint) {
receiveWidthCaches(wHint);
if (appliedText == null) {
appliedText = calculateAppliedText(wHint);
}
return appliedText;
}
/**
* @param wHint
* @return
*/
protected String calculateAppliedText(double wHint) {
String theText = getText();
if (wHint < 0 || theText.length() == 0)
return theText;
Font f = getFont();
if (isSingleLine()) {
if (isAbbreviated())
return getAbbreviatedText(theText, f, wHint);
return theText;
}
String[] lines = forceSplit(theText);
StringBuilder accumlatedText = new StringBuilder(theText.length() + 10);
for (int lineIndex = 0; lineIndex < lines.length; lineIndex++) {
String line = lines[lineIndex];
if (line.length() == 0) {
accumlatedText.append('\n');
} else {
StringBuilder remainingLine = new StringBuilder(line);
int i = 0;
while (remainingLine.length() > 0) {
i = getLineWrapPosition(remainingLine.toString(), f, wHint);
if (i == 0)
break;
String substring = trim(remainingLine.substring(0, i));
if (isAbbreviated()) {
substring = getAbbreviatedText(substring, f, wHint);
}
accumlatedText.append(substring);
remainingLine.delete(0, i);
accumlatedText.append('\n');
}
}
}
if (accumlatedText.charAt(accumlatedText.length() - 1) == '\n')
accumlatedText.deleteCharAt(accumlatedText.length() - 1);
return accumlatedText.toString();
}
private static String[] forceSplit(String s) {
List<String> buffer = new ArrayList<String>(s.length());
int start = 0;
for (int end = 0; end < s.length(); end++) {
char c = s.charAt(end);
if (c == '\n') {
buffer.add(s.substring(start, end));
start = end + 1;
}
}
buffer.add(s.substring(start));
return buffer.toArray(new String[buffer.size()]);
}
private String getAbbreviatedText(String theText, Font f, double wHint) {
String result = theText;
int textLength = result.length();
if (wHint > 0 && textLength > 0) {
textToLabelWidth.put(text, (int) wHint);
int textWidth = getLooseTextSize(result, f).width;
if (textWidth > wHint) {
int tructionPosition = (int) ((double) result.length()
/ (double) (textWidth) * (int) wHint);
if (tructionPosition < textLength)
if (tructionPosition > ELLIPSE.length()) {
tructionPosition -= ELLIPSE.length();
}
return result.substring(0, tructionPosition) + ELLIPSE;
}
}
return result;
}
/**
* returns the position of last character within the supplied text that will
* fit within the supplied width.
*
* @param s
* a text string
* @param f
* font used to draw the text string
* @param w
* width in pixels.
*/
protected int getLineWrapPosition(String s, Font f, double w) {
// create an iterator for line breaking positions
BreakIterator iter = BreakIterator.getLineInstance();
iter.setText(s);
int start = iter.first();
int end = iter.next();
// if the first line segment does not fit in the width,
// determine the position within it where we need to cut
if (getSubTextSize(s, start, end, f).width > w) {
iter = BreakIterator.getWordInstance(); // BreakIterator.getCharacterInstance();
iter.setText(s);
start = iter.first();
// if the first word does not fit in the width,
// just return the full length of the very word
end = iter.next();
if (end == BreakIterator.DONE)
return iter.last();
if (getSubTextSize(s, start, end, f).width > w)
return end;
}
// keep iterating as long as width permits
do
end = iter.next();
while (end != BreakIterator.DONE
&& getSubTextSize(s, start, end, f).width <= w);
return (end == BreakIterator.DONE) ? iter.last() : iter.previous();
}
private Dimension getSubTextSize(String s, int start, int end, Font f) {
String t = trim(s.substring(start, end));
t = getShowText(t, getTextCase());
Dimension size = getLooseTextSize(t, f);
return size;
}
protected String getShowText(String t, int textCase) {
switch (textCase) {
case GEF.MANUAL:
return t;
case GEF.UPPERCASE:
return t.toUpperCase();
case GEF.LOWERCASE:
return t.toLowerCase();
case GEF.CAPITALIZE:
return capitalize(t);
}
return t;
}
private String capitalize(String str) {
StringBuffer stringbf = new StringBuffer();
// Matcher m = Pattern
// .compile("([^\\s])([^\\s]*)", Pattern.CASE_INSENSITIVE) //$NON-NLS-1$
// .matcher(str);
Matcher m = Pattern.compile("([a-z])([a-z]*)", Pattern.CASE_INSENSITIVE) //$NON-NLS-1$
.matcher(str);
while (m.find()) {
m.appendReplacement(stringbf,
m.group(1).toUpperCase() + m.group(2).toLowerCase());
}
return m.appendTail(stringbf).toString();
}
private static String trim(String s) {
return s.replaceAll("\\n", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
// protected PrecisionDimension getTightTextSize(String s, Font f) {
// if (s.length() == 0) {
// int height = GraphicsUtils.getAdvanced().getFontMetrics(f)
// .getHeight();
// return new PrecisionDimension(0, height);
// } else if (!isNormalRenderStyle()) {
// Path textShape = new Path(Display.getCurrent());
// textShape.addString(s, 0, 0, f);
// textShape.getBounds(_bounds);
// textShape.dispose();
// return new PrecisionDimension(_bounds[2], _bounds[3]);
// }
// return getLooseTextSize(s, f);
// }
protected Dimension getLooseTextSize(String s, Font f) {
if (s.length() == 0) {
int height = GraphicsUtils.getAdvanced().getFontMetrics(f)
.getHeight();
return new Dimension(0, height);
}
Dimension size = GraphicsUtils.getAdvanced().getTextSize(s, f);
if (!isNormalRenderStyle()) {
Path p = new Path(Display.getCurrent());
p.addString(s, 0, 0, f);
p.getBounds(RECT);
if (Util.isWindows()) {
float[] autoScaleDown = DPIUtil.autoScaleDown(RECT);
RECT[0] = autoScaleDown[0];
RECT[1] = autoScaleDown[1];
RECT[2] = autoScaleDown[2];
RECT[3] = autoScaleDown[3];
}
p.dispose();
size.width = Math.max(size.width, (int) Math.ceil(RECT[2]));
size.height = Math.max(size.height, (int) Math.ceil(RECT[3]));
}
return size;
}
// protected PrecisionDimension getPrecisionLooseTextSize(String s, Font f) {
// return new PrecisionDimension(getLooseTextSize(s, f));
// }
protected PrecisionRectangle getTextArea() {
return getTextArea(cachedWidthHint);
}
/**
* Returns the area of the label's text.
*
* @param wHint
* @return the area of this label's text
*/
protected PrecisionRectangle getTextArea(int wHint) {
receiveWidthCaches(wHint);
if (textArea == null) {
PrecisionDimension size = calculateTextSize(wHint);
textArea = new PrecisionRectangle();
float rightMargin = RIGHT_MARGIN;
int height = getFont().getFontData()[0].getHeight();
if (height > 30)
rightMargin = rightMargin + 5;
textArea.width = size.width + PADDING * 2 + rightMargin;
textArea.height = size.height + PADDING * 2;
textArea.x = -(textArea.width / 2);
textArea.y = -(textArea.height / 2);
}
return textArea;
}
/**
* Calculates and returns the size of the Label's text.
*
* @param wHint
* @return the size of the label's text, ignoring truncation
*/
protected PrecisionDimension calculateTextSize(int wHint) {
Font f = getFont();
String theText = getAppliedText(wHint);
String[] split = forceSplit(theText);
PrecisionDimension size = D.setSize(0, 0);
final int textCase = getTextCase();
for (String s : split) {
String t = getShowText(s, textCase);
Dimension d = getLooseTextSize(t, f);
if (size.height > 0)
size.height += getLineSpacing();
size.height += d.height;
size.width = Math.max(size.width, d.width);
}
//size.union(getTextExtents(theText, f));
return size;
}
public int getLineSpacing() {
if (lineSpacing < 0)
lineSpacing = calculateDefaultLineSpacing();
return lineSpacing;
}
public void setLineSpacing(int spacing) {
if (this.lineSpacing >= 0 && spacing == this.lineSpacing)
return;
this.lineSpacing = spacing;
revalidate();
repaint();
}
protected int calculateDefaultLineSpacing() {
Dimension s1 = getLooseTextSize("X\nX", getFont()); //$NON-NLS-1$
Dimension s = getLooseTextSize("X", getFont()); //$NON-NLS-1$
return (int) Math.max(0, s1.height - s.height * 2);
}
@Override
public Insets getInsets() {
if (isRotated())
return getRotatedInsets().toDraw2DInsets();
return super.getInsets();
}
protected PrecisionInsets getRotatedInsets() {
if (rotatedInsets == null)
rotatedInsets = calculateRotatedInsets();
return rotatedInsets;
}
protected PrecisionInsets calculateRotatedInsets() {
PrecisionInsets ins = new PrecisionInsets(super.getInsets());
return isRotated() ? rotator.t(ins) : ins;
}
/**
* @see org.eclipse.draw2d.Figure#invalidate()
*/
@Override
public void invalidate() {
flushCaches();
rotatedInsets = null;
super.invalidate();
}
protected boolean isRotated() {
return Math.abs(getRotationDegrees()) > 0.0000001;
}
@Override
protected void paintBorder(Graphics graphics) {
graphics.setAlpha(getSubAlpha());
super.paintBorder(graphics);
}
/**
* @see org.eclipse.draw2d.Figure#paintFigure(org.eclipse.draw2d.Graphics)
*/
@Override
protected void paintFigure(Graphics graphics) {
graphics.setAntialias(SWT.ON);
graphics.setTextAntialias(SWT.ON);
if (getBorder() instanceof AbstractBackground) {
int oldAlpha = graphics.getAlpha();
graphics.setAlpha(getSubAlpha());
((AbstractBackground) getBorder()).paintBackground(this, graphics,
NO_INSETS);
graphics.setAlpha(oldAlpha);
}
PrecisionPoint pCenter = calculateTextCenterLocation();
Point center = null;
boolean rotated = isRotated();
Rectangle clientArea = getClientArea();
double wHint = rotator
.r(D.setSize(clientArea.width, clientArea.height)).width;
PrecisionRectangle rect = new PrecisionRectangle(
getTextArea((int) wHint));
PrecisionDimension d = new PrecisionDimension(getSize());
Insets ins = getInsets();
double insWidth = ins.getWidth();
double insHeight = ins.getHeight();
if (rotated) {
d = rotator.r(d, -1, rect.height + super.getInsets().getHeight());
d.expand(-insWidth, -insHeight);
rect.x -= (d.width - rect.width) / 2;
rect.width = d.width;
center = pCenter.toRoundedDraw2DPoint();
rect.translate(new PrecisionPoint(center).getDifference(pCenter));
graphics.translate(center);
graphics.rotate((float) getRotationDegrees());
} else {
d.expand(-insWidth, -insHeight);
rect.x -= (d.width - rect.width) / 2;
rect.width = d.width;
rect.translate(pCenter);
}
paintTextArea(graphics, rect);
if (rotated && center != null) {
graphics.translate(center.negate());
graphics.rotate(-(float) getRotationDegrees());
}
}
/**
* @return
*/
private PrecisionPoint calculateTextCenterLocation() {
PrecisionRectangle rect = R.setBounds(getBounds());
return rect.crop(getRotatedInsets()).getCenter();
}
/**
* Paints the text area of this label using the given Graphics object.<br>
* <br>
* <b>IMPORTANT</b>: Subclasses should never use any method that might
* access clipping in the given Graphics, such as <hi>
* <li>{@link Graphics#getClip(Rectangle)},</li>
* <li>{@link Graphics#setClip(Rectangle)},</li>
* <li>{@link Graphics#setClip(org.eclipse.swt.graphics.Path)},</li>
* <li>{@link Graphics#clipRect(Rectangle)},</li>
* <li>{@link Graphics#translate(int, int)},</li>
* <li>{@link Graphics#translate(float, float)},</li>
* <li>{@link Graphics#translate(Point)},</li>
* <li>{@link Graphics#pushState()},</li>
* <li>{@link Graphics#restoreState()},</li> </hi><br>
* <br>
* for the given Graphics' coordinates may have been rotated and clipping is
* no longer preserved.<br>
* <br>
*
* @param graphics
* @param textArea
* @see Graphics#rotate(float)
*/
protected void paintTextArea(Graphics graphics,
PrecisionRectangle textArea) {
if (isOpaque() && getLocalBackgroundColor() != null) {
int oldAlpha = graphics.getAlpha();
graphics.setAlpha(getSubAlpha());
Path bg = new Path(Display.getCurrent());
bg.addRectangle(textArea);
graphics.fillPath(bg);
bg.dispose();
graphics.setAlpha(oldAlpha);
}
int oldAlpha = graphics.getAlpha();
graphics.setAlpha(getMainAlpha());
paintText(graphics, getAppliedText((int) Math.ceil(textArea.width)),
textArea, getFont());
graphics.setAlpha(oldAlpha);
}
/**
* <b>IMPORTANT</b>: Subclasses should never use any method that might
* access clipping in the given Graphics, such as <hi>
* <li>{@link Graphics#getClip(Rectangle)},</li>
* <li>{@link Graphics#setClip(Rectangle)},</li>
* <li>{@link Graphics#setClip(org.eclipse.swt.graphics.Path)},</li>
* <li>{@link Graphics#clipRect(Rectangle)},</li>
* <li>{@link Graphics#translate(int, int)},</li>
* <li>{@link Graphics#translate(float, float)},</li>
* <li>{@link Graphics#translate(Point)},</li>
* <li>{@link Graphics#pushState()},</li>
* <li>{@link Graphics#restoreState()},</li> </hi><br>
* <br>
* for the given Graphics' coordinates may have been rotated and clipping is
* no longer preserved.<br>
* <br>
*
* @param graphics
* @param text
* @param textArea
*/
protected void paintText(Graphics graphics, String text,
PrecisionRectangle textArea, Font f) {
String[] tokens = forceSplit(text);
float textWidth = (float) textArea.width - PADDING * 2;
float y = (float) textArea.y + PADDING;
float vSpacing = getLineSpacing();
final int wrapAlignment = getTextAlignment();
final int textCase = getTextCase();
boolean isUnderlined = isTextUnderlined();
boolean isStrikedThrough = isTextStrikedThrough();
//graphics.drawRectangle(textArea.toDraw2DRectangle().resize(-1, -1));
for (String token : tokens) {
float x = (float) textArea.x + PADDING;
String t = getShowText(token, textCase);
Dimension tokenSize = getLooseTextSize(t, f);
float tokenWidth = tokenSize.width;
float tokenHeight = tokenSize.height;
float tokenHeightHalf = tokenHeight / 2;
switch (wrapAlignment) {
case PositionConstants.CENTER:
x += (textWidth - tokenWidth) / 2;
break;
case PositionConstants.RIGHT:
x += textWidth - tokenWidth - RIGHT_MARGIN;
break;
}
paintText(graphics, t, x, y, tokenWidth, tokenHeight, f);
y += tokenHeight;
if (isUnderlined) {
Path underline = new Path(Display.getCurrent());
underline.moveTo(x, y - 1);
underline.lineTo(x + tokenWidth, y - 1);
graphics.drawPath(underline);
underline.dispose();
}
if (isStrikedThrough) {
Path strikeOutLine = new Path(Display.getCurrent());
strikeOutLine.moveTo(x, y - tokenHeightHalf + 1);
strikeOutLine.lineTo(x + tokenWidth, y - tokenHeightHalf + 1);
graphics.drawPath(strikeOutLine);
strikeOutLine.dispose();
}
y += vSpacing;
}
}
protected void paintText(Graphics graphics, String token, float x, float y,
float width, float height, Font f) {
if (isNormalRenderStyle()) {
graphics.translate(x, y);
graphics.drawText(token, 0, 0);
graphics.translate(-x, -y);
return;
}
Path shape = new Path(Display.getCurrent());
shape.addString(token, 0, 0, f);
shape.getBounds(RECT);
if (Util.isWindows()) {
float[] autoScaleDown = DPIUtil.autoScaleDown(RECT);
RECT[0] = autoScaleDown[0];
RECT[1] = autoScaleDown[1];
RECT[2] = autoScaleDown[2];
RECT[3] = autoScaleDown[3];
}
float dx = (width - RECT[2]) / 2 - RECT[0];
float dy = (height - RECT[3]) / 2 - RECT[1];
if (Math.abs(dx) > 0.0000000001 || Math.abs(dy) > 0.0000000001) {
shape.dispose();
shape = new Path(Display.getCurrent());
shape.addString(token, x + dx, y + dy, f);
}
drawTextShape(graphics, shape);
shape.dispose();
}
protected void drawTextShape(Graphics graphics, Path shape) {
graphics.setLineStyle(SWT.LINE_SOLID);
graphics.setLineWidth(1);
graphics.setFillRule(SWT.FILL_WINDING);
graphics.setBackgroundColor(graphics.getForegroundColor());
graphics.fillPath(shape);
}
protected boolean isNormalRenderStyle() {
return renderStyle == NORMAL;
}
/**
* @return
*/
private boolean isTextStrikedThrough() {
return getStyle() != null && getStyle().strikeout;
}
/**
* @return
*/
private boolean isTextUnderlined() {
return getStyle() != null && getStyle().underline;
}
public String toString() {
return "RotatableWrapLabl (" + getText() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
private void setCachedPrefWidth(String text) {
Integer cached = textToLabelWidth.get(text);
if (cached != null)
this.prefWidth = cached;
}
}