blob: d08c354645653134f87f30f8e868fe2ad4046354 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2007, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.draw2d.ui.text;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.text.TextFlow;
import org.eclipse.draw2d.text.TextFragmentBox;
import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
/**
* A <code>TextFlow</code> with the following additional capabilities: <br>
* <UL>
* <LI>text can be underlined or have a strike-through
* <LI>truncated with ... when the full text doesn't fit vertically
* </UL>
*
* @since 2.1
* @author satif, crevells
*
*/
public class TextFlowEx
extends TextFlow {
// reserve 1 bit
private static int FLAG_UNDERLINED = Figure.MAX_FLAG << 1;
private static int FLAG_STRIKEDTHROUGH = Figure.MAX_FLAG << 2;
/**
* The highest reserved flag used by this class.
*
* @see Figure#MAX_FLAG
*/
protected static final int MAX_FLAG = FLAG_STRIKEDTHROUGH;
private String truncationString = "..."; //$NON-NLS-1$
/** the FlowUtilities instance that is dependent on the mapmode */
private FlowUtilitiesEx flowUtilities;
/** the TextUtilities instance that is dependent on the mapmode */
private TextUtilitiesEx textUtilities;
private int nDPtoLP_1 = -1;
/**
* Constructs a new TextFlow with the empty String.
*/
public TextFlowEx() {
super();
}
/**
* Constructs a new TextFlow with the specified String.
*
* @param text
* the string
*/
public TextFlowEx(String text) {
super(text);
}
/**
* Gets the truncation string. The default is an ellipsis "...".
*
* @return the truncation string
*/
protected String getTruncationString() {
return truncationString;
}
/**
* Sets the truncation string. The default is an ellipsis "...".
*
* @param truncationString
* the new truncation string
*/
public void setTruncationString(String truncationString) {
this.truncationString = truncationString;
}
/**
* @return whether the label text is striked-through
*/
public boolean isTextStrikedThrough() {
return (flags & FLAG_STRIKEDTHROUGH) != 0;
}
/**
* Sets whether the label text should be striked-through
*
* @param strikeThrough
* Whether the label text should be striked-through
*/
public void setTextStrikeThrough(boolean strikeThrough) {
if (isTextStrikedThrough() == strikeThrough)
return;
setFlag(FLAG_STRIKEDTHROUGH, strikeThrough);
repaint();
}
/**
* @return whether the label text is underlined
*/
public boolean isTextUnderlined() {
return (flags & FLAG_UNDERLINED) != 0;
}
/**
* Sets whether the label text should be underlined
*
* @param underline
* Whether the label text should be underlined
*/
public void setTextUnderline(boolean underline) {
if (isTextUnderlined() == underline)
return;
setFlag(FLAG_UNDERLINED, underline);
repaint();
}
protected void paintFigure(Graphics g) {
TextFragmentBox frag;
g.getClip(Rectangle.SINGLETON);
int yStart = Rectangle.SINGLETON.y;
int yEnd = Rectangle.SINGLETON.bottom();
Rectangle maxBounds = getVisibleBounds();
for (int i = 0; i < getFragments().size(); i++) {
frag = (TextFragmentBox) getFragments().get(i);
if (frag.offset == -1)
continue;
// Loop until first visible fragment
if (yStart > getVisibleBottom(frag) + 1)// The + 1
// is for
// disabled
// text
continue;
// Break loop at first non-visible fragment
if (yEnd < getVisibleTop(frag))
break;
String draw = getBidiSubstring(frag, i);
// ///////////////////////////////////////////
// If the next fragment will not be completely visible, then
// truncate this fragment.
boolean truncate = frag.isTruncated();
if (i + 1 < getFragments().size()
&& maxBounds.bottom() < getVisibleBottom((TextFragmentBox) getFragments()
.get(i + 1))) {
draw = truncateText(draw);
truncate = true;
// increment the counter so no further fragments will be
// processed
i = getFragments().size();
}
if (truncate)
draw += getTruncationString();
// ///////////////////////////////////////////
if (!isEnabled()) {
Color cachedfgColor = g.getForegroundColor();
g.setForegroundColor(ColorConstants.buttonLightest);
paintText(g, draw, frag.getX() + getDPtoLP1(), frag
.getBaseline()
- frag.getAscent() + getDPtoLP1(), frag.getBidiLevel());
g.setForegroundColor(ColorConstants.buttonDarker);
paintText(g, draw, frag.getX(), frag.getBaseline()
- frag.getAscent(), frag.getBidiLevel());
g.setForegroundColor(cachedfgColor);
} else {
paintText(g, draw, frag.getX(), frag.getBaseline()
- frag.getAscent(), frag.getBidiLevel());
}
drawTextAdornments(g, frag);
}
}
private int getDPtoLP1() {
if (nDPtoLP_1 == -1) {
nDPtoLP_1 = MapModeUtil.getMapMode(this).DPtoLP(1);
}
return nDPtoLP_1;
}
private void drawTextAdornments(Graphics g, TextFragmentBox fragment) {
int baseline = fragment.getBaseline();
if (isTextUnderlined()) {
baseline += getDPtoLP1();
g.drawLine(fragment.getX(), baseline, fragment.getWidth()
+ fragment.getX(), baseline);
}
if (isTextStrikedThrough()) {
int y = fragment.getBaseline()
- fragment.getAscent()
+ ((fragment.getAscent() + fragment.getDescent() + getDPtoLP1()) / 2);
g.drawLine(fragment.getX(), y, fragment.getWidth()
+ fragment.getX(), y);
}
}
/**
* Gets the y-value representing the top of the visible text.
*
* @param fragment
* the text fragment
* @return the top value
*/
private int getVisibleTop(TextFragmentBox fragment) {
return fragment.getBaseline() - fragment.getAscent();
}
/**
* Gets the y-value representing the bottom of the visible text.
*
* @param fragment
* the text fragment
* @return the bottom value
*/
private int getVisibleBottom(TextFragmentBox fragment) {
return fragment.getBaseline() + fragment.getDescent();
}
/**
* Adds an ellipsis to the text passed in if this will fit in the width of
* this figure, otherwise first truncates the text as required and then adds
* the ellipsis.
*
* @param text
* the full text
* @return a new string with the text ending in an ellipsis
*/
protected String truncateText(String text) {
int maxWidth = getVisibleBounds().width;
Font currentFont = getFont();
int ellipsisWidth = getTextUtilities().getTextExtents(
getTruncationString(), currentFont).width;
if (maxWidth < ellipsisWidth) {
maxWidth = ellipsisWidth;
}
int subStringLength = getTextUtilities().getLargestSubstringConfinedTo(
text, currentFont, maxWidth - ellipsisWidth);
return new String(text.substring(0, subStringLength));
}
public FlowUtilitiesEx getFlowUtilities() {
if (flowUtilities == null) {
flowUtilities = new FlowUtilitiesEx(MapModeUtil.getMapMode(this));
}
return flowUtilities;
}
public TextUtilitiesEx getTextUtilities() {
if (textUtilities == null) {
textUtilities = new TextUtilitiesEx(MapModeUtil.getMapMode(this));
}
return textUtilities;
}
/**
* Gets the area that will be visible to know where to truncate.
*
* @return the visible bounds
*/
private Rectangle getVisibleBounds() {
// Not the best idea to depend on the parent, but it will have to do for
// now.
return getParent().getClientArea();
}
}