/*******************************************************************************
* Copyright (c) 2000, 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
*******************************************************************************/
package org.eclipse.swt.custom;
import org.eclipse.rwt.graphics.Graphics;
import org.eclipse.rwt.internal.theme.IThemeAdapter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.custom.clabelkit.CLabelThemeAdapter;
import org.eclipse.swt.internal.graphics.TextSizeDetermination;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
/**
* A Label which supports aligned text and/or an image and different border styles.
* <p>
* If there is not enough space a CLabel uses the following strategy to fit the
* information into the available space:
* <pre>
* ignores the indent in left align mode
* ignores the image and the gap
* shortens the text by replacing the center portion of the label with an ellipsis
* shortens the text by removing the center portion of the label
* </pre>
* <p>
* <dl>
* <dt><b>Styles:</b>
* <dd>LEFT, RIGHT, CENTER, SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd>
* <dt><b>Events:</b>
* <dd></dd>
* </dl>
*
* </p><p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
* @since 1.0
*/
public class CLabel extends Canvas {
/** a string inserted in the middle of text that has been shortened */
// private static final String ELLIPSIS = "..."; //$NON-NLS-1$ // could use the ellipsis glyph on some platforms "\u2026"
/** the alignment. Either CENTER, RIGHT, LEFT. Default is LEFT */
private int align = SWT.LEFT;
private int leftMargin;
private int topMargin;
private int rightMargin;
private int bottomMargin;
/** the current text */
private String text;
/** the current icon */
private Image image;
// The tooltip is used for two purposes - the application can set
// a tooltip or the tooltip can be used to display the full text when the
// the text has been truncated due to the label being too short.
// The appToolTip stores the tooltip set by the application.
// Control.tooltiptext
// contains whatever tooltip is currently being displayed.
private String appToolTipText;
private Image backgroundImage;
private Color background;
/**
* Constructs a new instance of this class given its parent
* and a style value describing its behavior and appearance.
* <p>
* The style value is either one of the style constants defined in
* class <code>SWT</code> which is applicable to instances of this
* class, or must be built by <em>bitwise OR</em>'ing together
* (that is, using the <code>int</code> "|" operator) two or more
* of those <code>SWT</code> style constants. The class description
* lists the style constants that are applicable to the class.
* Style bits are also inherited from superclasses.
* </p>
*
* @param parent a widget which will be the parent of the new instance (cannot be null)
* @param style the style of widget to construct
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* </ul>
*
* @see SWT#LEFT
* @see SWT#RIGHT
* @see SWT#CENTER
* @see SWT#SHADOW_IN
* @see SWT#SHADOW_OUT
* @see SWT#SHADOW_NONE
* @see #getStyle()
*/
public CLabel( Composite parent, int style ) {
super( parent, checkStyle( style ) );
int result = style;
if ( (style & (SWT.CENTER | SWT.RIGHT)) == 0 )
result |= SWT.LEFT;
if ( (result & SWT.CENTER) != 0 )
align = SWT.CENTER;
if ( (result & SWT.RIGHT) != 0 )
align = SWT.RIGHT;
if ( (result & SWT.LEFT) != 0 )
align = SWT.LEFT;
addDisposeListener( new DisposeListener() {
public void widgetDisposed( DisposeEvent event ) {
onDispose( event );
}
} );
initMargins();
}
/**
* Check the style bits to ensure that no invalid styles are applied.
*/
private static int checkStyle( int style ) {
int result = style;
if ( (style & SWT.BORDER) != 0 )
result |= SWT.SHADOW_IN;
int mask = SWT.SHADOW_IN | SWT.SHADOW_OUT | SWT.SHADOW_NONE | SWT.LEFT_TO_RIGHT /*| SWT.RIGHT_TO_LEFT*/;
result = style & mask;
return result |= SWT.NO_FOCUS;
}
public Point computeSize( int wHint, int hHint, boolean changed ) {
checkWidget();
CLabelThemeAdapter themeAdapter
= ( CLabelThemeAdapter )getAdapter( IThemeAdapter.class );
int borderWidth = themeAdapter.getBorderWidth( this );
Point e = getTotalSize( image, text );
if ( wHint == SWT.DEFAULT ) {
e.x += leftMargin + rightMargin;
e.x += 2 * borderWidth;
} else {
e.x = wHint;
}
if ( hHint == SWT.DEFAULT ) {
e.y += topMargin + bottomMargin;
e.y += 2 * borderWidth;
} else {
e.y = hHint;
}
return e;
}
/**
* Returns the alignment.
* The alignment style (LEFT, CENTER or RIGHT) is returned.
*
* @return SWT.LEFT, SWT.RIGHT or SWT.CENTER
*/
public int getAlignment() {
checkWidget();
return align;
}
/**
* Return the CLabel's image or <code>null</code>.
*
* @return the image of the label or null
*/
public Image getImage() {
checkWidget();
return image;
}
/**
* Compute the minimum size.
*/
private Point getTotalSize( Image image, String text ) {
Point size = new Point( 0, 0 );
CLabelThemeAdapter themeAdapter
= ( CLabelThemeAdapter )getAdapter( IThemeAdapter.class );
int spacing = themeAdapter.getSpacing( this );
if ( image != null ) {
Rectangle r = image.getBounds();
size.x += r.width;
size.y += r.height;
}
if ( text != null && text.length() > 0 ) {
Point e = TextSizeDetermination.textExtent( getFont(), text, 0 );
size.x += e.x;
size.y = Math.max( size.y, e.y );
if ( image != null )
size.x += spacing;
} else {
int charHeight = Graphics.getCharHeight( getFont() );
size.y = Math.max( size.y, charHeight );
}
return size;
}
public int getStyle() {
int style = super.getStyle();
switch (align) {
case SWT.RIGHT:
style |= SWT.RIGHT;
break;
case SWT.CENTER:
style |= SWT.CENTER;
break;
case SWT.LEFT:
style |= SWT.LEFT;
break;
}
return style;
}
/**
* Return the Label's text.
*
* @return the text of the label or null
*/
public String getText() {
checkWidget();
return text;
}
public String getToolTipText() {
checkWidget();
return appToolTipText;
}
void onDispose( DisposeEvent event ) {
backgroundImage = null;
text = null;
image = null;
appToolTipText = null;
}
/**
* Set the alignment of the CLabel.
* Use the values LEFT, CENTER and RIGHT to align image and text within the available space.
*
* @param align the alignment style of LEFT, RIGHT or CENTER
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* <li>ERROR_INVALID_ARGUMENT - if the value of align is not one of SWT.LEFT, SWT.RIGHT or SWT.CENTER</li>
* </ul>
*/
public void setAlignment( int align ) {
checkWidget();
if ( align != SWT.LEFT && align != SWT.RIGHT && align != SWT.CENTER ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
if ( this.align != align ) {
this.align = align;
}
}
public void setBackground( Color color ) {
super.setBackground( color );
// Are these settings the same as before?
if ( backgroundImage == null ) {
if ( color == null ) {
if ( background == null )
return;
} else {
if ( color.equals( background ) )
return;
}
}
background = color;
backgroundImage = null;
}
/**
* Set the image to be drawn in the background of the label.
*
* @param image the image to be drawn in the background
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
// TODO: [bm] review
public void setBackground( Image image ) {
checkWidget();
if ( image == backgroundImage )
return;
backgroundImage = image;
}
public void setFont( Font font ) {
super.setFont( font );
}
/**
* Set the label's Image.
* The value <code>null</code> clears it.
*
* @param image the image to be displayed in the label or null
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setImage( Image image ) {
checkWidget();
if ( image != this.image ) {
this.image = image;
}
}
/**
* Set the label's text.
* The value <code>null</code> clears it.
*
* @param text the text to be displayed in the label or null
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setText( String text ) {
checkWidget();
if ( text == null )
text = ""; //$NON-NLS-1$
if ( !text.equals( this.text ) ) {
this.text = text;
}
}
public void setToolTipText( String string ) {
super.setToolTipText( string );
appToolTipText = super.getToolTipText();
}
/**
* Shorten the given text <code>t</code> so that its length doesn't exceed
* the given width. The default implementation replaces characters in the
* center of the original string with an ellipsis ("...").
* Override if you need a different strategy.
*
* @param gc the gc to use for text measurement
* @param t the text to shorten
* @param width the width to shorten the text to, in pixels
* @return the shortened text
*/
// TODO: [bm] review
//protected String shortenText(Object gc, String t, int width) {
// if (t == null) return null;
// int w = FontSizeEstimation.stringExtent( ELLIPSIS, getFont() ).x;
// if (width<=w) return t;
// int l = t.length();
// int max = l/2;
// int min = 0;
// int mid = (max+min)/2 - 1;
// if (mid <= 0) return t;
// while (min < mid && mid < max) {
// String s1 = t.substring(0, mid);
// String s2 = t.substring(l-mid, l);
// int l1 = FontSizeEstimation.stringExtent( s1, getFont() ).x;
// int l2 = FontSizeEstimation.stringExtent( s2, getFont() ).x;
// if (l1+w+l2 > width) {
// max = mid;
// mid = (max+min)/2;
// } else if (l1+w+l2 < width) {
// min = mid;
// mid = (max+min)/2;
// } else {
// min = max;
// }
// }
// if (mid == 0) return t;
// return t.substring(0, mid)+ELLIPSIS+t.substring(l-mid, l);
//}
//
//private String[] splitString(String text) {
// String[] lines = new String[1];
// int start = 0, pos;
// do {
// pos = text.indexOf('\n', start);
// if (pos == -1) {
// lines[lines.length - 1] = text.substring(start);
// } else {
// boolean crlf = (pos > 0) && (text.charAt(pos - 1) == '\r');
// lines[lines.length - 1] = text.substring(start, pos - (crlf ? 1 : 0));
// start = pos + 1;
// String[] newLines = new String[lines.length+1];
// System.arraycopy(lines, 0, newLines, 0, lines.length);
// lines = newLines;
// }
// } while (pos != -1);
// return lines;
//}
/**
* Set the label's margins, in pixels.
*
* @param leftMargin the left margin.
* @param topMargin the top margin.
* @param rightMargin the right margin.
* @param bottomMargin the bottom margin.
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 1.3
*/
public void setMargins( final int leftMargin,
final int topMargin,
final int rightMargin,
final int bottomMargin )
{
checkWidget();
this.leftMargin = Math.max( 0, leftMargin );
this.topMargin = Math.max( 0, topMargin );
this.rightMargin = Math.max( 0, rightMargin );
this.bottomMargin = Math.max( 0, bottomMargin );
}
/**
* Set the label's horizontal left margin, in pixels.
*
* @param leftMargin the left margin of the label, which must be equal to or greater than zero
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 1.3
*/
public void setLeftMargin( final int leftMargin ) {
checkWidget();
if( leftMargin >= 0 ) {
this.leftMargin = leftMargin;
}
}
/**
* Return the CLabel's left margin.
*
* @return the left margin of the label
*
* @since 1.3
*/
public int getLeftMargin() {
//checkWidget(); // [if] Commented in SWT
return leftMargin;
}
/**
* Set the label's top margin, in pixels.
*
* @param topMargin the top margin of the label, which must be equal to or greater than zero
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 1.3
*/
public void setTopMargin( final int topMargin ) {
checkWidget();
if( topMargin >= 0 ) {
this.topMargin = topMargin;
}
}
/**
* Return the CLabel's top margin.
*
* @return the top margin of the label
*
* @since 1.3
*/
public int getTopMargin() {
//checkWidget(); // [if] Commented in SWT
return topMargin;
}
/**
* Set the label's right margin, in pixels.
*
* @param rightMargin the right margin of the label, which must be equal to or greater than zero
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 1.3
*/
public void setRightMargin( final int rightMargin ) {
checkWidget();
if( rightMargin >= 0 ) {
this.rightMargin = rightMargin;
}
}
/**
* Return the CLabel's right margin.
*
* @return the right margin of the label
*
* @since 1.3
*/
public int getRightMargin() {
//checkWidget(); // [if] Commented in SWT
return rightMargin;
}
/**
* Set the label's bottom margin, in pixels.
*
* @param bottomMargin the bottom margin of the label, which must be equal to or greater than zero
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 1.3
*/
public void setBottomMargin( final int bottomMargin ) {
checkWidget();
if( bottomMargin >= 0 ) {
this.bottomMargin = bottomMargin;
}
}
/**
* Return the CLabel's bottom margin.
*
* @return the bottom margin of the label
*
* @since 1.3
*/
public int getBottomMargin() {
//checkWidget(); // [if] Commented in SWT
return bottomMargin;
}
private void initMargins() {
CLabelThemeAdapter themeAdapter
= ( CLabelThemeAdapter )getAdapter( IThemeAdapter.class );
Rectangle padding = themeAdapter.getPadding( this );
leftMargin = padding.x;
topMargin = padding.y;
rightMargin = padding.width - padding.x;
bottomMargin = padding.height - padding.y;
}
}