/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.ui.common.widget;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
/**
* @since 8.0
*/
public class Label extends CLabel {
// ===========================================================================================================================
// Constants
private static final int DFLT_GAP = 5;
private static final int DFLT_MARGIN = 3;
private static final int DRAW_FLAGS = SWT.DRAW_MNEMONIC | SWT.DRAW_TAB | SWT.DRAW_TRANSPARENT | SWT.DRAW_DELIMITER;
// ===========================================================================================================================
// Variables
private boolean skippedSuperclassListener;
private int gap = DFLT_GAP;
private int leftMargin = DFLT_MARGIN;
private int rightMargin = DFLT_MARGIN;
private int topMargin = DFLT_MARGIN;
private int bottomMargin = DFLT_MARGIN;
private Image backgroundImage;
private Color[] gradientColors;
private int[] gradientPercents;
private boolean gradientVertical;
// ===========================================================================================================================
// Constructors
public Label(Composite parent,
int style) {
super(parent, style);
}
// ===========================================================================================================================
// Methods
/**
* @see org.eclipse.swt.widgets.Control#addPaintListener(org.eclipse.swt.events.PaintListener)
* @since 5.0
*/
@Override
public void addPaintListener(PaintListener listener) {
if (skippedSuperclassListener) {
super.addPaintListener(listener);
} else {
skippedSuperclassListener = !skippedSuperclassListener;
super.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent event) {
paint(event);
}
});
}
}
@Override
public Point computeSize(int wHint,
int hHint,
boolean changed) {
checkWidget();
Point size = getTotalSize(getImage(), getText());
if (wHint == SWT.DEFAULT) {
size.x += this.leftMargin + this.rightMargin;
} else {
size.x = wHint;
}
if (hHint == SWT.DEFAULT) {
size.y += this.topMargin + this.bottomMargin;
} else {
size.y = hHint;
}
return size;
}
/**
* @see org.eclipse.swt.widgets.Widget#dispose()
* @since 5.0
*/
@Override
public void dispose() {
super.dispose();
this.gradientColors = null;
this.gradientPercents = null;
this.backgroundImage = null;
}
/**
* @return The number of pixels in this label's bottom margin. Default is {@value #DFLT_MARGIN}.
* @see #getLeftMargin()
* @see #getRightMargin()
* @see #getTopMargin()
* @see #setLeftMargin(int)
* @see #setRightMargin(int)
* @see #setTopMargin(int)
* @see #setBottomMargin(int)
* @see #setHorizontalMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
@Override
public int getBottomMargin() {
return this.bottomMargin;
}
/**
* @return The number of pixels between this label's image and text, assuming both are not <code>null</code>. Default is
* {@value #DFLT_GAP}.
* @since 5.0
*/
public int getGap() {
return this.gap;
}
/**
* @return The number of pixels in this label's left margin. Default is {@value #DFLT_MARGIN}.
* @see #getRightMargin()
* @see #getTopMargin()
* @see #getBottomMargin()
* @see #setLeftMargin(int)
* @see #setRightMargin(int)
* @see #setTopMargin(int)
* @see #setBottomMargin(int)
* @see #setHorizontalMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
@Override
public int getLeftMargin() {
return this.leftMargin;
}
/**
* @return The number of pixels in this label's right margin. Default is {@value #DFLT_MARGIN}.
* @see #getLeftMargin()
* @see #getTopMargin()
* @see #getBottomMargin()
* @see #setLeftMargin(int)
* @see #setRightMargin(int)
* @see #setTopMargin(int)
* @see #setBottomMargin(int)
* @see #setHorizontalMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
@Override
public int getRightMargin() {
return this.rightMargin;
}
/**
* @return The number of pixels in this label's top margin. Default is {@value #DFLT_MARGIN}.
* @see #getLeftMargin()
* @see #getRightMargin()
* @see #getBottomMargin()
* @see #setLeftMargin(int)
* @see #setRightMargin(int)
* @see #setTopMargin(int)
* @see #setBottomMargin(int)
* @see #setHorizontalMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
@Override
public int getTopMargin() {
return this.topMargin;
}
private Point getTotalSize(Image image,
String text) {
Point size = new Point(0, 0);
if (image != null) {
Rectangle bounds = image.getBounds();
size.x += bounds.width;
size.y += bounds.height;
}
GC gc = new GC(this);
if (text != null && text.length() > 0) {
Point extent = gc.textExtent(text, DRAW_FLAGS);
size.x += extent.x;
size.y = Math.max(size.y, extent.y);
if (image != null) {
size.x += this.gap;
}
} else {
size.y = Math.max(size.y, gc.getFontMetrics().getHeight());
}
gc.dispose();
return size;
}
void paint(PaintEvent event) {
Rectangle bounds = getClientArea();
if (bounds.width == 0 || bounds.height == 0) {
return;
}
// Check if text needs to be shortened (using ellipses)
boolean shortenText = false;
String text = getText();
Image img = getImage();
int availableWidth = Math.max(0, bounds.width - this.leftMargin - this.rightMargin);
Point size = getTotalSize(img, text);
if (size.x > availableWidth) {
shortenText = true;
}
// Split text into multiple lines if it contains line-feeds.
String[] lines = (text == null ? null : splitString(text));
// Shorten text if necessary
GC gc = event.gc;
if (shortenText) {
size.x = 0;
if (lines != null) {
for (int ndx = 0; ndx < lines.length; ndx++) {
Point extent = gc.textExtent(lines[ndx], DRAW_FLAGS);
if (extent.x > availableWidth) {
lines[ndx] = shortenText(gc, lines[ndx], availableWidth);
size.x = Math.max(size.x, getTotalSize(null, lines[ndx]).x);
} else {
size.x = Math.max(size.x, extent.x);
}
}
if (getToolTipText() == null) {
super.setToolTipText(text);
}
}
}
// Determine horizontal position
int x = bounds.x + this.leftMargin;
int align = getAlignment();
if (align == SWT.CENTER) {
x = (bounds.width - size.x) / 2;
} else if (align == SWT.RIGHT) {
x = bounds.width - this.rightMargin - size.x;
}
// Draw a background image behind the text
Color bkgd = getBackground();
Color frgd = getForeground();
try {
if (this.backgroundImage != null) {
// Draw a background image behind the text, tiling to fill space
Rectangle imgBounds = this.backgroundImage.getBounds();
gc.setBackground(getBackground());
gc.fillRectangle(bounds);
for (int imgX = 0; imgX < bounds.width; imgX += imgBounds.width) {
for (int imgY = 0; imgY < bounds.height; imgY += imgBounds.height) {
gc.drawImage(this.backgroundImage, imgX, imgY);
}
}
} else if (this.gradientColors != null && this.gradientColors.length > 0) {
// Draw a gradient behind the text
if (this.gradientColors.length == 1) {
if (this.gradientColors[0] != null) {
gc.setBackground(this.gradientColors[0]);
}
gc.fillRectangle(0, 0, bounds.width, bounds.height);
} else {
Color lastColor = this.gradientColors[0];
int pos = 0;
for (int ndx = 0; ndx < this.gradientPercents.length; ++ndx) {
gc.setForeground(lastColor);
lastColor = this.gradientColors[ndx + 1];
gc.setBackground(lastColor);
if (this.gradientVertical) {
int hgt = (this.gradientPercents[ndx] * bounds.height / 100) - pos;
gc.fillGradientRectangle(0, pos, bounds.width, hgt, true);
pos += hgt;
} else {
int wth = (this.gradientPercents[ndx] * bounds.width / 100) - pos;
gc.fillGradientRectangle(pos, 0, wth, bounds.height, false);
pos += wth;
}
}
if (this.gradientVertical && pos < bounds.height) {
gc.setBackground(bkgd);
gc.fillRectangle(0, pos, bounds.width, bounds.height - pos);
}
if (!this.gradientVertical && pos < bounds.width) {
gc.setBackground(bkgd);
gc.fillRectangle(pos, 0, bounds.width - pos, bounds.height);
}
}
} else {
if ((getStyle() & SWT.NO_BACKGROUND) != 0) {
gc.setBackground(bkgd);
gc.fillRectangle(bounds);
}
}
} catch (SWTException err) {
if ((getStyle() & SWT.NO_BACKGROUND) != 0) {
gc.setBackground(bkgd);
gc.fillRectangle(bounds);
}
}
// Draw the image
if (img != null) {
Rectangle imgBounds = img.getBounds();
gc.drawImage(img,
0,
0,
imgBounds.width,
imgBounds.height,
x,
(bounds.height - imgBounds.height) / 2,
imgBounds.width,
imgBounds.height);
x += imgBounds.width + this.gap;
size.x -= imgBounds.width + this.gap;
}
// Draw the text
if (lines != null) {
int lineHgt = gc.getFontMetrics().getHeight();
int textHgt = lines.length * lineHgt;
int lineY = Math.max(this.topMargin, bounds.y + (bounds.height - textHgt) / 2);
gc.setForeground(frgd);
for (int ndx = 0; ndx < lines.length; ndx++) {
int lineX = x;
if (lines.length > 1) {
if (align == SWT.CENTER) {
int lineWth = gc.textExtent(lines[ndx], DRAW_FLAGS).x;
lineX = x + Math.max(0, (size.x - lineWth) / 2);
}
if (align == SWT.RIGHT) {
int lineWth = gc.textExtent(lines[ndx], DRAW_FLAGS).x;
lineX = Math.max(x, bounds.x + bounds.width - this.rightMargin - lineWth);
}
}
gc.drawText(lines[ndx], lineX, lineY, DRAW_FLAGS);
lineY += lineHgt;
}
}
}
/**
* @see org.eclipse.swt.custom.CLabel#setBackground(org.eclipse.swt.graphics.Color)
* @since 5.0
*/
@Override
public void setBackground(Color color) {
// Return if color unchanged
if (color != null
&& this.backgroundImage == null
&& this.gradientColors == null
&& this.gradientPercents == null
&& color.equals(getBackground())) {
return;
}
super.setBackground(color);
this.backgroundImage = null;
this.gradientColors = null;
this.gradientPercents = null;
redraw();
}
/**
* @see org.eclipse.swt.custom.CLabel#setBackground(org.eclipse.swt.graphics.Color[], int[], boolean)
* @since 5.0
*/
@Override
public void setBackground(Color[] colors,
int[] percents,
boolean vertical) {
checkWidget();
if (colors != null) {
if (percents == null || percents.length != colors.length - 1) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (getDisplay().getDepth() < 15) {
// Don't use gradients on low color displays
colors = new Color[] {
colors[colors.length - 1]
};
percents = new int[] {};
}
for (int ndx = 0; ndx < percents.length; ndx++) {
if (percents[ndx] < 0 || percents[ndx] > 100) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (ndx > 0 && percents[ndx] < percents[ndx - 1]) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
}
}
Color bkgd = getBackground();
if (this.backgroundImage == null) {
// Return if gradient unchanged
if ((this.gradientColors != null) && (colors != null) && (this.gradientColors.length == colors.length)) {
boolean same = false;
for (int
ndx = 0; ndx < this.gradientColors.length; ndx++) {
same = (this.gradientColors[ndx] == colors[ndx])
|| ((this.gradientColors[ndx] == null) && (colors[ndx] == bkgd))
|| ((this.gradientColors[ndx] == bkgd) && (colors[ndx] == null));
if (!same) {
break;
}
}
if (same) {
for (int ndx = 0; ndx < this.gradientPercents.length; ndx++) {
same = this.gradientPercents[ndx] == percents[ndx];
if (!same) {
break;
}
}
}
if (same && this.gradientVertical == vertical) {
return;
}
}
} else {
this.backgroundImage = null;
}
// Store the new gradient
if (colors == null) {
this.gradientColors = null;
this.gradientPercents = null;
this.gradientVertical = false;
} else {
this.gradientColors = new Color[colors.length];
for (int ndx = 0; ndx < colors.length; ++ndx) {
this.gradientColors[ndx] = (colors[ndx] != null) ? colors[ndx] : bkgd;
}
this.gradientPercents = new int[percents.length];
for (int ndx = 0; ndx < percents.length; ++ndx) {
this.gradientPercents[ndx] = percents[ndx];
}
this.gradientVertical = vertical;
}
// Refresh with the new gradient
redraw();
}
/**
* @see org.eclipse.swt.custom.CLabel#setBackground(org.eclipse.swt.graphics.Image)
* @since 5.0
*/
@Override
public void setBackground(Image image) {
checkWidget();
if (image == this.backgroundImage) {
return;
}
if (image != null) {
this.gradientColors = null;
this.gradientPercents = null;
}
this.backgroundImage = image;
redraw();
}
/**
* @param margin
* The number of pixels in this label's bottom margin.
* @see #getLeftMargin()
* @see #getRightMargin()
* @see #getTopMargin()
* @see #getBottomMargin()
* @see #setLeftMargin(int)
* @see #setRightMargin(int)
* @see #setTopMargin(int)
* @see #setHorizontalMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
@Override
public void setBottomMargin(int margin) {
this.bottomMargin = Math.max(0, margin);
}
/**
* @param gap
* The number of pixels between this label's image and text, assuming both are not <code>null</code>.
* @since 5.0
*/
public void setGap(int gap) {
this.gap = Math.max(0, gap);
}
/**
* @param margin
* The number of pixels in this label's left and right margins.
* @see #getLeftMargin()
* @see #getRightMargin()
* @see #getTopMargin()
* @see #getBottomMargin()
* @see #setLeftMargin(int)
* @see #setRightMargin(int)
* @see #setTopMargin(int)
* @see #setBottomMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
public void setHorizontalMargin(int margin) {
this.leftMargin = this.rightMargin = Math.max(0, margin);
}
/**
* @param margin
* The number of pixels in this label's left margin.
* @see #getLeftMargin()
* @see #getRightMargin()
* @see #getTopMargin()
* @see #getBottomMargin()
* @see #setRightMargin(int)
* @see #setTopMargin(int)
* @see #setBottomMargin(int)
* @see #setHorizontalMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
@Override
public void setLeftMargin(int margin) {
this.leftMargin = Math.max(0, margin);
}
/**
* @param margin
* The number of pixels in this label's right margin.
* @see #getLeftMargin()
* @see #getRightMargin()
* @see #getTopMargin()
* @see #getBottomMargin()
* @see #setLeftMargin(int)
* @see #setTopMargin(int)
* @see #setBottomMargin(int)
* @see #setHorizontalMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
@Override
public void setRightMargin(int margin) {
this.rightMargin = Math.max(0, margin);
}
/**
* @param margin
* The number of pixels in this label's top margin.
* @see #getLeftMargin()
* @see #getRightMargin()
* @see #getTopMargin()
* @see #getBottomMargin()
* @see #setLeftMargin(int)
* @see #setRightMargin(int)
* @see #setBottomMargin(int)
* @see #setHorizontalMargin(int)
* @see #setVerticalMargin(int)
* @since 5.0
*/
@Override
public void setTopMargin(int margin) {
this.topMargin = Math.max(0, margin);
}
/**
* @param margin
* The number of pixels in this label's top and bottom margins.
* @see #getLeftMargin()
* @see #getRightMargin()
* @see #getTopMargin()
* @see #getBottomMargin()
* @see #setLeftMargin(int)
* @see #setRightMargin(int)
* @see #setTopMargin(int)
* @see #setBottomMargin(int)
* @see #setHorizontalMargin(int)
* @since 5.0
*/
public void setVerticalMargin(int margin) {
this.topMargin = this.bottomMargin = Math.max(0, margin);
}
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;
}
}