/******************************************************************************* * Copyright (c) 2000, 2012 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.swt.*; import org.eclipse.swt.widgets.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.events.*; import org.eclipse.swt.accessibility.*; /** * A Label which supports aligned text and/or an image and different border styles. *
* If there is not enough space a CLabel uses the following strategy to fit the * information into the available space: *
* 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 **
*
* This class may be subclassed for the purpose of overriding the default string
* shortening algorithm that is implemented in method shortenText()
.
*
* The style value is either one of the style constants defined in
* class SWT
which is applicable to instances of this
* class, or must be built by bitwise OR'ing together
* (that is, using the int
"|" operator) two or more
* of those SWT
style constants. The class description
* lists the style constants that are applicable to the class.
* Style bits are also inherited from superclasses.
*
null
.
*
* @return the image of the label or null
*/
public Image getImage() {
//checkWidget();
return image;
}
/**
* Return the CLabel's left margin.
*
* @return the left margin of the label
*
* @since 3.6
*/
public int getLeftMargin() {
//checkWidget();
return leftMargin;
}
/**
* Return the CLabel's right margin.
*
* @return the right margin of the label
*
* @since 3.6
*/
public int getRightMargin() {
//checkWidget();
return rightMargin;
}
/**
* Compute the minimum size.
*/
private Point getTotalSize(Image image, String text) {
Point size = new Point(0, 0);
if (image != null) {
Rectangle r = image.getBounds();
size.x += r.width;
size.y += r.height;
}
GC gc = new GC(this);
if (text != null && text.length() > 0) {
Point e = gc.textExtent(text, DRAW_FLAGS);
size.x += e.x;
size.y = Math.max(size.y, e.y);
if (image != null) size.x += GAP;
} else {
size.y = Math.max(size.y, gc.getFontMetrics().getHeight());
}
gc.dispose();
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;
}
/**
* Return the CLabel's top margin.
*
* @return the top margin of the label
*
* @since 3.6
*/
public int getTopMargin() {
//checkWidget();
return topMargin;
}
private void initAccessible() {
Accessible accessible = getAccessible();
accessible.addAccessibleListener(new AccessibleAdapter() {
public void getName(AccessibleEvent e) {
e.result = getText();
}
public void getHelp(AccessibleEvent e) {
e.result = getToolTipText();
}
public void getKeyboardShortcut(AccessibleEvent e) {
char mnemonic = _findMnemonic(CLabel.this.text);
if (mnemonic != '\0') {
e.result = "Alt+"+mnemonic; //$NON-NLS-1$
}
}
});
accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
public void getChildAtPoint(AccessibleControlEvent e) {
e.childID = ACC.CHILDID_SELF;
}
public void getLocation(AccessibleControlEvent e) {
Rectangle rect = getDisplay().map(getParent(), null, getBounds());
e.x = rect.x;
e.y = rect.y;
e.width = rect.width;
e.height = rect.height;
}
public void getChildCount(AccessibleControlEvent e) {
e.detail = 0;
}
public void getRole(AccessibleControlEvent e) {
e.detail = ACC.ROLE_LABEL;
}
public void getState(AccessibleControlEvent e) {
e.detail = ACC.STATE_READONLY;
}
});
}
void onDispose(Event event) {
/* make this handler run after other dispose listeners */
if (ignoreDispose) {
ignoreDispose = false;
return;
}
ignoreDispose = true;
notifyListeners (event.type, event);
event.type = SWT.NONE;
gradientColors = null;
gradientPercents = null;
backgroundImage = null;
text = null;
image = null;
appToolTipText = null;
}
void onMnemonic(TraverseEvent event) {
char mnemonic = _findMnemonic(text);
if (mnemonic == '\0') return;
if (Character.toLowerCase(event.character) != mnemonic) return;
Composite control = this.getParent();
while (control != null) {
Control [] children = control.getChildren();
int index = 0;
while (index < children.length) {
if (children [index] == this) break;
index++;
}
index++;
if (index < children.length) {
if (children [index].setFocus ()) {
event.doit = true;
event.detail = SWT.TRAVERSE_NONE;
}
}
control = control.getParent();
}
}
void onPaint(PaintEvent event) {
Rectangle rect = getClientArea();
if (rect.width == 0 || rect.height == 0) return;
boolean shortenText = false;
String t = text;
Image img = image;
int availableWidth = Math.max(0, rect.width - (leftMargin + rightMargin));
Point extent = getTotalSize(img, t);
if (extent.x > availableWidth) {
img = null;
extent = getTotalSize(img, t);
if (extent.x > availableWidth) {
shortenText = true;
}
}
GC gc = event.gc;
String[] lines = text == null ? null : splitString(text);
// shorten the text
if (shortenText) {
extent.x = 0;
for(int i = 0; i < lines.length; i++) {
Point e = gc.textExtent(lines[i], DRAW_FLAGS);
if (e.x > availableWidth) {
lines[i] = shortenText(gc, lines[i], availableWidth);
extent.x = Math.max(extent.x, getTotalSize(null, lines[i]).x);
} else {
extent.x = Math.max(extent.x, e.x);
}
}
if (appToolTipText == null) {
super.setToolTipText(text);
}
} else {
super.setToolTipText(appToolTipText);
}
// determine horizontal position
int x = rect.x + leftMargin;
if (align == SWT.CENTER) {
x = (rect.width - extent.x)/2;
}
if (align == SWT.RIGHT) {
x = rect.width - rightMargin - extent.x;
}
// draw a background image behind the text
try {
if (backgroundImage != null) {
// draw a background image behind the text
Rectangle imageRect = backgroundImage.getBounds();
// tile image to fill space
gc.setBackground(getBackground());
gc.fillRectangle(rect);
int xPos = 0;
while (xPos < rect.width) {
int yPos = 0;
while (yPos < rect.height) {
gc.drawImage(backgroundImage, xPos, yPos);
yPos += imageRect.height;
}
xPos += imageRect.width;
}
} else if (gradientColors != null) {
// draw a gradient behind the text
final Color oldBackground = gc.getBackground();
if (gradientColors.length == 1) {
if (gradientColors[0] != null) gc.setBackground(gradientColors[0]);
gc.fillRectangle(0, 0, rect.width, rect.height);
} else {
final Color oldForeground = gc.getForeground();
Color lastColor = gradientColors[0];
if (lastColor == null) lastColor = oldBackground;
int pos = 0;
for (int i = 0; i < gradientPercents.length; ++i) {
gc.setForeground(lastColor);
lastColor = gradientColors[i + 1];
if (lastColor == null) lastColor = oldBackground;
gc.setBackground(lastColor);
if (gradientVertical) {
final int gradientHeight = (gradientPercents[i] * rect.height / 100) - pos;
gc.fillGradientRectangle(0, pos, rect.width, gradientHeight, true);
pos += gradientHeight;
} else {
final int gradientWidth = (gradientPercents[i] * rect.width / 100) - pos;
gc.fillGradientRectangle(pos, 0, gradientWidth, rect.height, false);
pos += gradientWidth;
}
}
if (gradientVertical && pos < rect.height) {
gc.setBackground(getBackground());
gc.fillRectangle(0, pos, rect.width, rect.height - pos);
}
if (!gradientVertical && pos < rect.width) {
gc.setBackground(getBackground());
gc.fillRectangle(pos, 0, rect.width - pos, rect.height);
}
gc.setForeground(oldForeground);
}
gc.setBackground(oldBackground);
} else {
if (background != null || (getStyle() & SWT.DOUBLE_BUFFERED) == 0) {
gc.setBackground(getBackground());
gc.fillRectangle(rect);
}
}
} catch (SWTException e) {
if ((getStyle() & SWT.DOUBLE_BUFFERED) == 0) {
gc.setBackground(getBackground());
gc.fillRectangle(rect);
}
}
// draw border
int style = getStyle();
if ((style & SWT.SHADOW_IN) != 0 || (style & SWT.SHADOW_OUT) != 0) {
paintBorder(gc, rect);
}
/*
* Compute text height and image height. If image height is more than
* the text height, draw image starting from top margin. Else draw text
* starting from top margin.
*/
Rectangle imageRect = null;
int lineHeight = 0, textHeight = 0, imageHeight = 0;
if (img != null) {
imageRect = img.getBounds();
imageHeight = imageRect.height;
}
if (lines != null) {
lineHeight = gc.getFontMetrics().getHeight();
textHeight = lines.length * lineHeight;
}
int imageY = 0, midPoint = 0, lineY = 0;
if (imageHeight > textHeight ) {
if (topMargin == DEFAULT_MARGIN && bottomMargin == DEFAULT_MARGIN) imageY = rect.y + (rect.height - imageHeight) / 2;
else imageY = topMargin;
midPoint = imageY + imageHeight/2;
lineY = midPoint - textHeight / 2;
}
else {
if (topMargin == DEFAULT_MARGIN && bottomMargin == DEFAULT_MARGIN) lineY = rect.y + (rect.height - textHeight) / 2;
else lineY = topMargin;
midPoint = lineY + textHeight/2;
imageY = midPoint - imageHeight / 2;
}
// draw the image
if (img != null) {
gc.drawImage(img, 0, 0, imageRect.width, imageHeight,
x, imageY, imageRect.width, imageHeight);
x += imageRect.width + GAP;
extent.x -= imageRect.width + GAP;
}
// draw the text
if (lines != null) {
gc.setForeground(getForeground());
for (int i = 0; i < lines.length; i++) {
int lineX = x;
if (lines.length > 1) {
if (align == SWT.CENTER) {
int lineWidth = gc.textExtent(lines[i], DRAW_FLAGS).x;
lineX = x + Math.max(0, (extent.x - lineWidth) / 2);
}
if (align == SWT.RIGHT) {
int lineWidth = gc.textExtent(lines[i], DRAW_FLAGS).x;
lineX = Math.max(x, rect.x + rect.width - rightMargin - lineWidth);
}
}
gc.drawText(lines[i], lineX, lineY, DRAW_FLAGS);
lineY += lineHeight;
}
}
}
/**
* Paint the Label's border.
*/
private void paintBorder(GC gc, Rectangle r) {
Display disp= getDisplay();
Color c1 = null;
Color c2 = null;
int style = getStyle();
if ((style & SWT.SHADOW_IN) != 0) {
c1 = disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
c2 = disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
}
if ((style & SWT.SHADOW_OUT) != 0) {
c1 = disp.getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
c2 = disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
}
if (c1 != null && c2 != null) {
gc.setLineWidth(1);
drawBevelRect(gc, r.x, r.y, r.width-1, r.height-1, c1, c2);
}
}
/**
* Set the horizontal 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 For example, to draw a gradient that varies from dark blue to blue and then to * white and stays white for the right half of the label, use the following call * to setBackground:
** clabel.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE), * display.getSystemColor(SWT.COLOR_BLUE), * display.getSystemColor(SWT.COLOR_WHITE), * display.getSystemColor(SWT.COLOR_WHITE)}, * new int[] {25, 50, 100}); ** * @param colors an array of Color that specifies the colors to appear in the gradient * in order of appearance from left to right; The value
null
* clears the background gradient; the value null
can be used
* inside the array of Color to specify the background color.
* @param percents an array of integers between 0 and 100 specifying the percent of the width
* of the widget at which the color should change; the size of the percents
* array must be one less than the size of the colors array.
*
* @exception SWTException For example, to draw a gradient that varies from dark blue to white in the vertical, * direction use the following call * to setBackground:
** clabel.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE), * display.getSystemColor(SWT.COLOR_WHITE)}, * new int[] {100}, true); ** * @param colors an array of Color that specifies the colors to appear in the gradient * in order of appearance from left/top to right/bottom; The value
null
* clears the background gradient; the value null
can be used
* inside the array of Color to specify the background color.
* @param percents an array of integers between 0 and 100 specifying the percent of the width/height
* of the widget at which the color should change; the size of the percents
* array must be one less than the size of the colors array.
* @param vertical indicate the direction of the gradient. True is vertical and false is horizontal.
*
* @exception SWTException null
clears it.
*
* @param image the image to be displayed in the label or null
*
* @exception SWTException null
clears it.
* * Mnemonics are indicated by an '&' that causes the next * character to be the mnemonic. When the user presses a * key sequence that matches the mnemonic, focus is assigned * to the control that follows the label. On most platforms, * the mnemonic appears underlined but may be emphasised in a * platform specific manner. The mnemonic indicator character * '&' can be escaped by doubling it in the string, causing * a single '&' to be displayed. *
* * @param text the text to be displayed in the label or null * * @exception SWTExceptiont
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
*/
protected String shortenText(GC gc, String t, int width) {
if (t == null) return null;
int w = gc.textExtent(ELLIPSIS, DRAW_FLAGS).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;
TextLayout layout = new TextLayout (getDisplay());
layout.setText(t);
mid = validateOffset(layout, mid);
while (min < mid && mid < max) {
String s1 = t.substring(0, mid);
String s2 = t.substring(validateOffset(layout, l-mid), l);
int l1 = gc.textExtent(s1, DRAW_FLAGS).x;
int l2 = gc.textExtent(s2, DRAW_FLAGS).x;
if (l1+w+l2 > width) {
max = mid;
mid = validateOffset(layout, (max+min)/2);
} else if (l1+w+l2 < width) {
min = mid;
mid = validateOffset(layout, (max+min)/2);
} else {
min = max;
}
}
String result = mid == 0 ? t : t.substring(0, mid) + ELLIPSIS + t.substring(validateOffset(layout, l-mid), l);
layout.dispose();
return result;
}
int validateOffset(TextLayout layout, int offset) {
int nextOffset = layout.getNextOffset(offset, SWT.MOVEMENT_CLUSTER);
if (nextOffset != offset) return layout.getPreviousOffset(nextOffset, SWT.MOVEMENT_CLUSTER);
return offset;
}
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;
}
}