blob: 7def6450ff2cbf5ca21c500dc617f3312ecb39ba [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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.draw2d;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
/**
* A figure that can display text and/or an image.
*/
public class Label extends Figure implements PositionConstants {
private static String ELLIPSIS = "..."; //$NON-NLS-1$
private Image icon;
private String text = "";//$NON-NLS-1$
private String subStringText;
private Dimension textSize;
private Dimension subStringTextSize;
private Dimension iconSize = new Dimension(0, 0);
private Point iconLocation;
private Point textLocation;
private int textAlignment = CENTER;
private int iconAlignment = CENTER;
private int labelAlignment = CENTER;
private int textPlacement = EAST;
private int iconTextGap = 3;
/**
* Construct an empty Label.
*
* @since 2.0
*/
public Label() {
}
/**
* Construct a Label with passed String as its text.
*
* @param s
* the label text
* @since 2.0
*/
public Label(String s) {
setText(s);
}
/**
* Construct a Label with passed Image as its icon.
*
* @param i
* the label image
* @since 2.0
*/
public Label(Image i) {
setIcon(i);
}
/**
* 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 Label(String s, Image i) {
setText(s);
setIcon(i);
}
private void alignOnHeight(Point loc, Dimension size, int alignment) {
Insets insets = getInsets();
switch (alignment) {
case TOP:
loc.y = insets.top;
break;
case BOTTOM:
loc.y = bounds.height - size.height - insets.bottom;
break;
default:
loc.y = (bounds.height - size.height) / 2;
}
}
private void alignOnWidth(Point loc, Dimension size, int alignment) {
Insets insets = getInsets();
switch (alignment) {
case LEFT:
loc.x = insets.left;
break;
case RIGHT:
loc.x = bounds.width - size.width - insets.right;
break;
default:
loc.x = (bounds.width - size.width) / 2;
}
}
private void calculateAlignment() {
switch (textPlacement) {
case EAST:
case WEST:
alignOnHeight(textLocation, getTextSize(), textAlignment);
alignOnHeight(iconLocation, getIconSize(), iconAlignment);
break;
case NORTH:
case SOUTH:
alignOnWidth(textLocation, getSubStringTextSize(), textAlignment);
alignOnWidth(iconLocation, getIconSize(), iconAlignment);
break;
}
}
/**
* Calculates the size of the Label using the passed Dimension as the size
* of the Label's text.
*
* @param txtSize
* the precalculated size of the label's text
* @return the label's size
* @since 2.0
*/
protected Dimension calculateLabelSize(Dimension txtSize) {
int gap = getIconTextGap();
if (getIcon() == null || getText().equals("")) //$NON-NLS-1$
gap = 0;
Dimension d = new Dimension(0, 0);
if (textPlacement == WEST || textPlacement == EAST) {
d.width = getIconSize().width + gap + txtSize.width;
d.height = Math.max(getIconSize().height, txtSize.height);
} else {
d.width = Math.max(getIconSize().width, txtSize.width);
d.height = getIconSize().height + gap + txtSize.height;
}
return d;
}
private void calculateLocations() {
textLocation = new Point();
iconLocation = new Point();
calculatePlacement();
calculateAlignment();
Dimension offset = getSize().getDifference(getPreferredSize());
offset.width += getTextSize().width - getSubStringTextSize().width;
switch (labelAlignment) {
case CENTER:
offset.scale(0.5f);
break;
case LEFT:
offset.scale(0.0f);
break;
case RIGHT:
offset.scale(1.0f);
break;
case TOP:
offset.height = 0;
offset.scale(0.5f);
break;
case BOTTOM:
offset.height = offset.height * 2;
offset.scale(0.5f);
break;
default:
offset.scale(0.5f);
break;
}
switch (textPlacement) {
case EAST:
case WEST:
offset.height = 0;
break;
case NORTH:
case SOUTH:
offset.width = 0;
break;
}
textLocation.translate(offset);
iconLocation.translate(offset);
}
private void calculatePlacement() {
int gap = getIconTextGap();
if (icon == null || text.equals("")) //$NON-NLS-1$
gap = 0;
Insets insets = getInsets();
switch (textPlacement) {
case EAST:
iconLocation.x = insets.left;
textLocation.x = getIconSize().width + gap + insets.left;
break;
case WEST:
textLocation.x = insets.left;
iconLocation.x = getSubStringTextSize().width + gap + insets.left;
break;
case NORTH:
textLocation.y = insets.top;
iconLocation.y = getTextSize().height + gap + insets.top;
break;
case SOUTH:
textLocation.y = getIconSize().height + gap + insets.top;
iconLocation.y = insets.top;
}
}
/**
* 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()}.
*
* @return the size of the label's text, taking into account truncation
* @since 2.0
*/
protected Dimension calculateSubStringTextSize() {
return getTextUtilities().getTextExtents(getSubStringText(), getFont());
}
/**
* 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()}.
*
* @return the size of the label's text, ignoring truncation
* @since 2.0
*/
protected Dimension calculateTextSize() {
return getTextUtilities().getTextExtents(getText(), getFont());
}
private void clearLocations() {
iconLocation = textLocation = null;
}
/**
* Returns the Label's icon.
*
* @return the label icon
* @since 2.0
*/
public Image getIcon() {
return icon;
}
/**
* Returns the current alignment of the Label's icon. The default is
* {@link PositionConstants#CENTER}.
*
* @return the icon alignment
* @since 2.0
*/
public int getIconAlignment() {
return iconAlignment;
}
/**
* Returns the bounds of the Label's icon.
*
* @return the icon's bounds
* @since 2.0
*/
public Rectangle getIconBounds() {
Rectangle bounds = getBounds();
return new Rectangle(bounds.getLocation().translate(getIconLocation()),
getIconSize());
}
/**
* Returns the location of the Label's icon relative to the Label.
*
* @return the icon's location
* @since 2.0
*/
protected Point getIconLocation() {
if (iconLocation == null)
calculateLocations();
return iconLocation;
}
/**
* Returns the gap in pixels between the Label's icon and its text.
*
* @return the gap
* @since 2.0
*/
public int getIconTextGap() {
return iconTextGap;
}
/**
* @see IFigure#getMinimumSize(int, int)
*/
public Dimension getMinimumSize(int w, int h) {
if (minSize != null)
return minSize;
minSize = new Dimension();
if (getLayoutManager() != null)
minSize.setSize(getLayoutManager().getMinimumSize(this, w, h));
Dimension labelSize = calculateLabelSize(getTextUtilities()
.getTextExtents(getTruncationString(), getFont())
.intersect(
getTextUtilities().getTextExtents(getText(), getFont())));
Insets insets = getInsets();
labelSize.expand(insets.getWidth(), insets.getHeight());
minSize.union(labelSize);
return minSize;
}
/**
* @see IFigure#getPreferredSize(int, int)
*/
public Dimension getPreferredSize(int wHint, int hHint) {
if (prefSize == null) {
prefSize = calculateLabelSize(getTextSize());
Insets insets = getInsets();
prefSize.expand(insets.getWidth(), insets.getHeight());
if (getLayoutManager() != null)
prefSize.union(getLayoutManager().getPreferredSize(this, wHint,
hHint));
}
if (wHint >= 0 && wHint < prefSize.width) {
Dimension minSize = getMinimumSize(wHint, hHint);
Dimension result = prefSize.getCopy();
result.width = Math.min(result.width, wHint);
result.width = Math.max(minSize.width, result.width);
return result;
}
return prefSize;
}
/**
* 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
*/
public String getSubStringText() {
if (subStringText != null)
return subStringText;
subStringText = text;
int widthShrink = getPreferredSize().width - getSize().width;
if (widthShrink <= 0)
return subStringText;
Dimension effectiveSize = getTextSize().getExpanded(-widthShrink, 0);
Font currentFont = getFont();
int dotsWidth = getTextUtilities().getTextExtents(
getTruncationString(), currentFont).width;
if (effectiveSize.width < dotsWidth)
effectiveSize.width = dotsWidth;
int subStringLength = getTextUtilities().getLargestSubstringConfinedTo(
text, currentFont, effectiveSize.width - dotsWidth);
subStringText = new String(text.substring(0, subStringLength)
+ getTruncationString());
return subStringText;
}
/**
* 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
*/
protected Dimension getSubStringTextSize() {
if (subStringTextSize == null)
subStringTextSize = calculateSubStringTextSize();
return subStringTextSize;
}
/**
* Returns the text of the label. Note that this is the complete text of the
* label, regardless of whether it is currently being truncated. Call
* {@link #getSubStringText()} to return the label's current text contents
* with truncation considered.
*
* @return the complete text of this label
* @since 2.0
*/
public String getText() {
return text;
}
/**
* Returns the current alignment of the Label's text. The default text
* alignment is {@link PositionConstants#CENTER}.
*
* @return the text alignment
*/
public int getTextAlignment() {
return textAlignment;
}
/**
* Returns the bounds of the label's text. Note that the bounds are
* calculated using the label's complete text regardless of whether the
* label's text is currently truncated.
*
* @return the bounds of this label's complete text
* @since 2.0
*/
public Rectangle getTextBounds() {
Rectangle bounds = getBounds();
return new Rectangle(bounds.getLocation().translate(getTextLocation()),
textSize);
}
/**
* Returns the location of the label's text relative to the label.
*
* @return the text location
* @since 2.0
*/
protected Point getTextLocation() {
if (textLocation != null)
return textLocation;
calculateLocations();
return textLocation;
}
/**
* Returns the current placement of the label's text relative to its icon.
* The default text placement is {@link PositionConstants#EAST}.
*
* @return the text placement
* @since 2.0
*/
public int getTextPlacement() {
return textPlacement;
}
/**
* 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()}.
*
* @return the size of this label's complete text
* @since 2.0
*/
protected Dimension getTextSize() {
if (textSize == null)
textSize = calculateTextSize();
return textSize;
}
/**
* @see IFigure#invalidate()
*/
public void invalidate() {
prefSize = null;
minSize = null;
clearLocations();
textSize = null;
subStringTextSize = null;
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
*/
public boolean isTextTruncated() {
return !getSubStringText().equals(getText());
}
/**
* @see Figure#paintFigure(Graphics)
*/
protected void paintFigure(Graphics graphics) {
if (isOpaque())
super.paintFigure(graphics);
Rectangle bounds = getBounds();
graphics.translate(bounds.x, bounds.y);
if (icon != null)
graphics.drawImage(icon, getIconLocation());
if (!isEnabled()) {
graphics.translate(1, 1);
graphics.setForegroundColor(ColorConstants.buttonLightest);
graphics.drawText(getSubStringText(), getTextLocation());
graphics.translate(-1, -1);
graphics.setForegroundColor(ColorConstants.buttonDarker);
}
graphics.drawText(getSubStringText(), getTextLocation());
graphics.translate(-bounds.x, -bounds.y);
}
/**
* Sets the label's icon to the passed image.
*
* @param image
* the new label image
* @since 2.0
*/
public void setIcon(Image image) {
if (icon == image)
return;
icon = image;
// Call repaint, in case the image dimensions are the same.
repaint();
if (icon == null)
setIconDimension(new Dimension());
else
setIconDimension(new Dimension(image));
}
/**
* This method sets the alignment of the icon within the bounds of the
* label. If the label is larger than the icon, then the icon will be
* aligned according to this alignment. Valid values are:
* <UL>
* <LI><EM>{@link PositionConstants#CENTER}</EM>
* <LI>{@link PositionConstants#TOP}
* <LI>{@link PositionConstants#BOTTOM}
* <LI>{@link PositionConstants#LEFT}
* <LI>{@link PositionConstants#RIGHT}
* </UL>
*
* @param align
* the icon alignment
* @since 2.0
*/
public void setIconAlignment(int align) {
if (iconAlignment == align)
return;
iconAlignment = align;
clearLocations();
repaint();
}
/**
* Sets the label's icon size to the passed Dimension.
*
* @param d
* the new icon size
* @deprecated the icon is automatically displayed at 1:1
* @since 2.0
*/
public void setIconDimension(Dimension d) {
if (d.equals(getIconSize()))
return;
iconSize = d;
revalidate();
}
/**
* Sets the gap in pixels between the label's icon and text to the passed
* value. The default is 4.
*
* @param gap
* the gap
* @since 2.0
*/
public void setIconTextGap(int gap) {
if (iconTextGap == gap)
return;
iconTextGap = gap;
repaint();
revalidate();
}
/**
* Sets the alignment of the label (icon and text) within the figure. If
* this figure's bounds are larger than the size needed to display the
* label, the label will be aligned accordingly. Valid values are:
* <UL>
* <LI><EM>{@link PositionConstants#CENTER}</EM>
* <LI>{@link PositionConstants#TOP}
* <LI>{@link PositionConstants#BOTTOM}
* <LI>{@link PositionConstants#LEFT}
* <LI>{@link PositionConstants#RIGHT}
* </UL>
*
* @param align
* label alignment
*/
public void setLabelAlignment(int align) {
if (labelAlignment == align)
return;
labelAlignment = align;
clearLocations();
repaint();
}
/**
* Sets the label's text.
*
* @param s
* the new label text
* @since 2.0
*/
public void setText(String s) {
// "text" will never be null.
if (s == null)
s = "";//$NON-NLS-1$
if (text.equals(s))
return;
text = s;
revalidate();
repaint();
}
/**
* Sets the alignment of the text relative to the icon within the label. The
* text alignment must be orthogonal to the text placement. For example, if
* the placement is EAST, then the text can be aligned using TOP, CENTER, or
* BOTTOM. Valid values are:
* <UL>
* <LI><EM>{@link PositionConstants#CENTER}</EM>
* <LI>{@link PositionConstants#TOP}
* <LI>{@link PositionConstants#BOTTOM}
* <LI>{@link PositionConstants#LEFT}
* <LI>{@link PositionConstants#RIGHT}
* </UL>
*
* @see #setLabelAlignment(int)
* @param align
* the text alignment
* @since 2.0
*/
public void setTextAlignment(int align) {
if (textAlignment == align)
return;
textAlignment = align;
clearLocations();
repaint();
}
/**
* Sets the placement of the text relative to the icon within the label.
* Valid values are:
* <UL>
* <LI><EM>{@link PositionConstants#EAST}</EM>
* <LI>{@link PositionConstants#NORTH}
* <LI>{@link PositionConstants#SOUTH}
* <LI>{@link PositionConstants#WEST}
* </UL>
*
* @param where
* the text placement
* @since 2.0
*/
public void setTextPlacement(int where) {
if (textPlacement == where)
return;
textPlacement = where;
revalidate();
repaint();
}
/**
* Gets the <code>TextUtilities</code> instance to be used in measurement
* calculations.
*
* @return a <code>TextUtilities</code> instance
* @since 3.4
*/
public TextUtilities getTextUtilities() {
return TextUtilities.INSTANCE();
}
/**
* Gets the string that will be appended to the text when the label is
* truncated. By default, this returns an ellipsis.
*
* @return the string to append to the text when truncated
* @since 3.4
*/
protected String getTruncationString() {
return ELLIPSIS;
}
/**
* Gets the icon size
*
* @return the icon size
* @since 3.4
*/
protected Dimension getIconSize() {
return iconSize;
}
/**
* Returns the alignment of the entire label (icon and text). The default
* label alignment is {@link PositionConstants#CENTER}.
*
* @return the label alignment
* @since 3.5
*/
public int getLabelAlignment() {
return labelAlignment;
}
}