blob: 1ef91365fec6b8444be841c37a859b488af264e7 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2009 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
* Mariot Chauvin <mariot.chauvin@obeo.fr> - bug 272658
****************************************************************************/
package org.eclipse.gmf.runtime.draw2d.ui.figures;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.Image;
import com.ibm.icu.text.BreakIterator;
import com.ibm.icu.util.StringTokenizer;
/**
* An extended label that has the following extra features:
*
* 1- It is capable of showing selection and focus feedback (primary or
* secondary) 2- It is capable of optionally underlining the label's text 3- It
* is capable of wrapping the label's text at a given width with a given
* alignment 4- It is capable of supporting multiple label icons (temporary
* feature)
*
* This class was originally deriving off Draw2d's <code>Label</code> class
* but with the introduction of the auto-wrapping feature, a copy had to be made
* overriding was not straightforward. Hopefully, this extended version can be
* pushed to opensource
*
* <p>
* Code taken from Eclipse reference bugzilla #98820
*
* @author melaasar, crevells
* @deprecated This class has been deprecated and may be removed in the future.
* Use <code>WrappingLabel</code> instead. This class now extends
* from <code>WrappingLabel</code> so the behavior should be the
* same. If client code is calling one of the methods on
* <code>WrapLabel</code> that no longer exists, evaluate whether
* this is necessary or not.
*/
public class WrapLabel
extends WrappingLabel {
/** the label's text used in painting after applying required styles */
private String subStringText;
/**
* Construct an empty Label.
*
* @since 2.0
*/
public WrapLabel() {
super();
}
/**
* Construct a Label with passed String as its text.
*
* @param s
* the label text
* @since 2.0
*/
public WrapLabel(String s) {
super(s);
// Compensate for the fact that the original wraplabel never called the
// default constructor to set the default layout values.
setTextWrapAlignment(CENTER);
}
/**
* Construct a Label with passed Image as its icon.
*
* @param i
* the label image
* @since 2.0
*/
public WrapLabel(Image i) {
super(i);
// Compensate for the fact that the original wraplabel never called the
// default constructor to set the default layout values.
setTextWrapAlignment(CENTER);
}
/**
* Construct a Label with passed String as text and passed Image as its
* icon.
*
* @param s
* the label text
* @param i
* the label image
* @since 2.0
*/
public WrapLabel(String s, Image i) {
super(s, i);
// Compensate for the fact that the original wraplabel never called the
// default constructor to set the default layout values.
setTextWrapAlignment(CENTER);
}
/**
* Calculates the size of the Label's text size. The text size calculated
* takes into consideration if the Label's text is currently truncated. If
* text size without considering current truncation is desired, use
* {@link #calculateTextSize(int, int)}.
*
* @return the size of the label's text, taking into account truncation
* @since 2.0
* @deprecated If this behavior is required then a request can be made (with
* justification) for the WrappingLabel.
*/
protected Dimension calculateSubStringTextSize() {
Font f = getFont();
return getTextExtents(getSubStringText(), f, getFigureMapMode().DPtoLP(FigureUtilities.getFontMetrics(f).getHeight()));
}
/**
* Calculates and returns the size of the Label's text. Note that this
* Dimension is calculated using the Label's full text, regardless of
* whether or not its text is currently truncated. If text size considering
* current truncation is desired, use {@link #calculateSubStringTextSize()}.
*
* @param wHint a width hint
* @param hHint a height hint
* @return the size of the label's text, ignoring truncation
* @since 2.0
* @deprecated If this behavior is required then a request can be made (with
* justification) for the WrappingLabel.
*/
protected Dimension calculateTextSize(int wHint, int hHint) {
return getTextFlow().getPreferredSize(wHint, hHint);
}
/**
* Returns the bounds of the Label's icon.
*
* @return the icon's bounds
* @since 2.0
* @deprecated The icon location can be retrieved with
* {@link #getIconLocation()} and the icon(s) size can be
* retrieved with {@link #getTotalIconSize()}.
*/
public Rectangle getIconBounds() {
if (hasIcons()) {
final Point safeIconLocation = getIconLocation() != null ? getIconLocation() : new Point(0,0);
return new Rectangle(getBounds().getLocation().translate(
safeIconLocation), getTotalIconSize());
}
return new Rectangle(0, 0, 0, 0);
}
/**
* Calculates the amount of the Label's current text will fit in the Label,
* including an elipsis "..." if truncation is required.
*
* @return the substring
* @since 2.0
* @deprecated If this behavior is required then a request can be made (with
* justification) for the WrappingLabel.
*/
public String getSubStringText() {
if (subStringText != null)
return subStringText;
String theText = getText();
int textLen = theText.length();
if (textLen == 0) {
return subStringText = "";//$NON-NLS-1$;;
}
Dimension size = getSize();
Dimension shrink = getPreferredSize(size.width, size.height)
.getDifference(size);
Dimension effectiveSize = getTextSize().getExpanded(-shrink.width,
-shrink.height);
if (effectiveSize.height == 0) {
return subStringText = "";//$NON-NLS-1$;
}
Font f = getFont();
FontMetrics metrics = FigureUtilities.getFontMetrics(f);
IMapMode mm = getFigureMapMode();
int fontHeight = mm.DPtoLP(metrics.getHeight());
int charAverageWidth = mm.DPtoLP(metrics.getAverageCharWidth());
int maxLines = (int) (effectiveSize.height / (double) fontHeight);
if (maxLines == 0) {
return subStringText = "";//$NON-NLS-1$
}
StringBuffer accumlatedText = new StringBuffer();
StringBuffer remainingText = new StringBuffer(theText);
int effectiveSizeWidth = effectiveSize.width;
int widthHint = Math.max(effectiveSizeWidth
- getTruncationStringSize().width, 0);
int i = 0, j = 0;
while (remainingText.length() > 0 && j++ < maxLines) {
i = getLineWrapPosition(remainingText.toString(), f,
effectiveSizeWidth, fontHeight);
if (accumlatedText.length() > 0)
accumlatedText.append('\n');
if (i == 0 || (remainingText.length() > i && j == maxLines)) {
i = getLargestSubstringConfinedTo(remainingText.toString(), f,
widthHint, fontHeight, charAverageWidth);
accumlatedText.append(remainingText.substring(0, i));
accumlatedText.append(getEllipse());
} else
accumlatedText.append(remainingText.substring(0, i));
remainingText.delete(0, i);
}
return subStringText = accumlatedText.toString();
}
/**
* Returns the size of the Label's current text. If the text is currently
* truncated, the truncated text with its ellipsis is used to calculate the
* size.
*
* @return the size of this label's text, taking into account truncation
* @since 2.0
* @deprecated If this behavior is required then a request can be made (with
* justification) for the WrappingLabel.
*/
protected Dimension getSubStringTextSize() {
return calculateSubStringTextSize();
}
/**
* Returns the location of the label's text relative to the label.
*
* @return the text location
* @since 2.0
* @deprecated Use <code>getTextBounds().getLocation()</code> instead.
*/
protected Point getTextLocation() {
return getTextBounds().getLocation();
}
/**
* Returns the size of the label's complete text. Note that the text used to
* make this calculation is the label's full text, regardless of whether the
* label's text is currently being truncated and is displaying an ellipsis.
* If the size considering current truncation is desired, call
* {@link #getSubStringTextSize()}.
*
* @param wHint
* a width hint
* @param hHint
* a height hint
* @return the size of this label's complete text
* @since 2.0
* @deprecated If this behavior is required then a request can be made (with
* justification) for the WrappingLabel.
*/
protected Dimension getTextSize(int wHint, int hHint) {
return getTextFlow().getPreferredSize(wHint, hHint);
}
/**
* Gets the text size given the current size as a width hint
*/
private final Dimension getTextSize() {
Rectangle r = getBounds();
return getTextSize(r.width, r.height);
}
/**
* @see IFigure#invalidate()
*/
public void invalidate() {
subStringText = null;
super.invalidate();
}
/**
* Returns <code>true</code> if the label's text is currently truncated
* and is displaying an ellipsis, <code>false</code> otherwise.
*
* @return <code>true</code> if the label's text is truncated
* @since 2.0
* @deprecated If this behavior is required then a request can be made (with
* justification) for the WrappingLabel.
*/
public boolean isTextTruncated() {
return !getSubStringTextSize().equals(getTextSize());
}
/**
* Return the ellipse string.
*
* @return the <code>String</code> that represents the fact that the text
* has been truncated and that more text is available but hidden.
* Usually this is represented by "...".
* @deprecated Renamed to {@link #getTruncationString()}
*/
protected String getEllipse() {
return ELLIPSIS;
}
protected String getTruncationString() {
if (getEllipse() != null) {
return getEllipse();
}
return ELLIPSIS;
}
/**
* @return whether the label text wrap is on
* @deprecated Use {@link #isTextWrapOn()} instead. This method was renamed
* because it never indicated if the text was actually wrapped,
* but whether text wrapping was turned on in the label.
*/
public boolean isTextWrapped() {
return isTextWrapOn();
}
/**
* Sets the wrapping width of the label text. This is only valid if text
* wrapping is turned on
*
* @param i
* The label text wrapping width
* @deprecated this method was empty and never called
*/
public void setTextWrapWidth(int i) {
// do nothing
}
/**
* Sets the wrapping width of the label text. This is only valid if text
* wrapping is turned on
*
* @param i
* The label text wrapping width
* @deprecated Call {@link #setTextJustification(int)} and
* {@link #setAlignment(int)} instead. This method was somewhat
* controlling text justification and label alignment, but they
* are really two independent settings. Previously,
* setTextWrapAlignment(CENTER) would not only center-justifies
* the text, but also put the label in the center. Now, you need
* to call {@link #setTextJustification(int)} to justify the
* text (this only affects text when it is wrapped) and
* {@link #setAlignment(int)} to position the text correctly in
* the label. If you want the text in the center of the label
* than call <code>setAlignment(PositionConstants.CENTER)</code>.
* Look at the implementation of this method to see how your
* code needs to be migrated.
*/
public void setTextWrapAlignment(int i) {
setTextJustification(i);
// The old WrapLabel's Text Wrap Alignment (i.e. justification) and
// Label Alignment did not work properly. They worked together
// previously so we need to compensate for this here.
switch (i) {
case LEFT:
setAlignment(TOP | LEFT);
break;
case CENTER:
setAlignment(TOP);
break;
case RIGHT:
setAlignment(TOP | RIGHT);
break;
default:
break;
}
}
/**
* @deprecated This never worked properly anyways. Call
* {@link #setAlignment(int)} instead to position the icon and
* text within the label.
*/
public void setLabelAlignment(int alignment) {
// setLabelAlignment() never worked properly instead the label alignment
// seemed to be based on the text justification. Therefore, if it was
// set it will be ignored.
}
/**
* @return the label text wrapping width
* @deprecated Renamed to {@link #getTextJustification()}
*/
public int getTextWrapAlignment() {
return getTextJustification();
}
/**
* 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 pixles.
* @param fontHeight
* int <b>mapped already to logical units</b>.
*/
private int getLineWrapPosition(String s, Font f, int w, int fontHeight) {
if (getTextExtents(s, f, fontHeight).width <= w) {
return s.length();
}
// 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 (getTextExtents(s.substring(start, end), f, fontHeight).width > w) {
iter = BreakIterator.getCharacterInstance();
iter.setText(s);
start = iter.first();
}
// keep iterating as long as width permits
do
end = iter.next();
while (end != BreakIterator.DONE
&& getTextExtents(s.substring(start, end), f, fontHeight).width <= w);
return (end == BreakIterator.DONE) ? iter.last()
: iter.previous();
}
/**
* Returns the largest substring of <i>s </i> in Font <i>f </i> that can be
* confined to the number of pixels in <i>availableWidth <i>.
*
* @param s
* the original string
* @param f
* the font
* @param w
* the available width
* @param fontHeight
* int <b>mapped already to logical units</b>.
* @param charAverageWidth
* int <b>mapped already to logical units</b>.
* @return the largest substring that fits in the given width
* @since 2.0
*/
private int getLargestSubstringConfinedTo(String s, Font f, int w,
int fontHeight, int charAverageWidth) {
float avg = charAverageWidth;
int min = 0;
int max = s.length() + 1;
// The size of the current guess
int guess = 0, guessSize = 0;
while ((max - min) > 1) {
// Pick a new guess size
// New guess is the last guess plus the missing width in pixels
// divided by the average character size in pixels
guess = guess + (int) ((w - guessSize) / avg);
if (guess >= max)
guess = max - 1;
if (guess <= min)
guess = min + 1;
// Measure the current guess
guessSize = getTextExtents(s.substring(0, guess), f, fontHeight).width;
if (guessSize < w)
// We did not use the available width
min = guess;
else
// We exceeded the available width
max = guess;
}
return min;
}
/**
* Gets the tex extent scaled to the mapping mode
*/
private Dimension getTextExtents(String s, Font f, int fontHeight) {
if (s.length() == 0) {
return getMapModeConstants().dimension_nDPtoLP_0;
} else {
// height should be set using the font height and the number of
// lines in the string
Dimension d = FigureUtilities.getTextExtents(s, f);
IMapMode mapMode = getFigureMapMode();
d.width = mapMode.DPtoLP(d.width);
d.height = fontHeight * new StringTokenizer(s, "\n").countTokens();//$NON-NLS-1$
return d;
}
}
}