org.eclipse.swt.custom.CLabel.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.swt.custom.CLabel.java

Source

/*******************************************************************************
 * Copyright (c) 2007, 2015 Innoopract Informationssysteme GmbH 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:
 *    Innoopract Informationssysteme GmbH - initial API and implementation
 *    EclipseSource - ongoing development
 ******************************************************************************/
package org.eclipse.swt.custom;

import static org.eclipse.rap.rwt.internal.textsize.TextSizeUtil.textExtent;
import static org.eclipse.swt.internal.widgets.MarkupUtil.isMarkupEnabledFor;
import static org.eclipse.swt.internal.widgets.MarkupValidator.isValidationDisabledFor;

import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA;
import org.eclipse.rap.rwt.internal.textsize.TextSizeUtil;
import org.eclipse.rap.rwt.internal.theme.ThemeAdapter;
import org.eclipse.rap.rwt.theme.BoxDimensions;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.custom.clabelkit.CLabelLCA;
import org.eclipse.swt.internal.custom.clabelkit.CLabelThemeAdapter;
import org.eclipse.swt.internal.widgets.IWidgetGraphicsAdapter;
import org.eclipse.swt.internal.widgets.MarkupValidator;
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 {

    private class LabelDisposeListener implements DisposeListener {
        @Override
        public void widgetDisposed(DisposeEvent event) {
            onDispose();
        }
    }

    /** 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 LabelDisposeListener());
        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;
    }

    @Override
    public Point computeSize(int wHint, int hHint, boolean changed) {
        checkWidget();
        BoxDimensions border = getCLabelThemeAdapter().getBorder(this);
        Point e = getTotalSize(image, text);
        if (wHint == SWT.DEFAULT) {
            e.x += leftMargin + rightMargin;
            e.x += border.left + border.right;
        } else {
            e.x = wHint;
        }
        if (hHint == SWT.DEFAULT) {
            e.y += topMargin + bottomMargin;
            e.y += border.top + border.bottom;
        } 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);
        int spacing = getCLabelThemeAdapter().getSpacing(this);
        if (image != null) {
            Rectangle imageBounds = image.getBounds();
            size.x += imageBounds.width;
            size.y += imageBounds.height;
        }
        if (text != null && text.length() > 0) {
            Point extent = textExtent(getFont(), text, SWT.DEFAULT, isMarkupEnabledFor(this));
            size.x += extent.x;
            size.y = Math.max(size.y, extent.y);
            if (image != null) {
                size.x += spacing;
            }
        } else {
            int charHeight = TextSizeUtil.getCharHeight(getFont());
            size.y = Math.max(size.y, charHeight);
        }
        return size;
    }

    @Override
    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;
    }

    @Override
    public String getToolTipText() {
        checkWidget();
        return appToolTipText;
    }

    private void onDispose() {
        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;
        }
    }

    @Override
    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;
        setBackgroundGradient(null, null, false);
    }

    /**
     * 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>
     */
    public void setBackground(Image image) {
        checkWidget();
        if (image != backgroundImage) {
            backgroundImage = image;
            if (image != null) {
                setBackgroundGradient(null, null, false);
            }
        }
    }

    /**
     * Specify a gradient of colours to be drawn in the background of the CLabel.
     * <p>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:</p>
     * <pre>
     *  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});
     * </pre>
     *
     * @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 <code>null</code>
     *               clears the background gradient; the value <code>null</code> 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 <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 values of colors and percents are not consistent</li>
     * </ul>
     *
     * @since 1.4
     */
    public void setBackground(Color[] colors, int[] percents) {
        setBackground(colors, percents, false);
    }

    /**
     * Specify a gradient of colours to be drawn in the background of the CLabel.
     * <p>For example, to draw a gradient that varies from dark blue to white in the vertical,
     * direction use the following call
     * to setBackground:</p>
     * <pre>
     *  clabel.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
     *                               display.getSystemColor(SWT.COLOR_WHITE)},
     *                     new int[] {100}, true);
     * </pre>
     *
     * @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 <code>null</code>
     *               clears the background gradient; the value <code>null</code> 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 <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 values of colors and percents are not consistent</li>
     * </ul>
     *
     * @since 1.4
     */
    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);
            }
            for (int i = 0; i < percents.length; i++) {
                if (percents[i] < 0 || percents[i] > 100) {
                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
                }
                if (i > 0 && percents[i] < percents[i - 1]) {
                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
                }
            }
        }
        if (colors == null) {
            setBackgroundGradient(null, null, false);
        } else {
            Color[] gradientColors = new Color[colors.length];
            for (int i = 0; i < colors.length; ++i) {
                gradientColors[i] = colors[i] != null ? colors[i] : background;
            }
            int[] gradientPercents = new int[gradientColors.length];
            gradientPercents[0] = 0;
            for (int i = 1; i < gradientPercents.length; i++) {
                gradientPercents[i] = percents[i - 1];
            }
            setBackgroundGradient(gradientColors, gradientPercents, vertical);
        }
        backgroundImage = null;
    }

    private void setBackgroundGradient(Color[] colors, int[] percents, boolean vertical) {
        IWidgetGraphicsAdapter adapter = getAdapter(IWidgetGraphicsAdapter.class);
        adapter.setBackgroundGradient(colors, percents, vertical);
    }

    @Override
    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) {
            this.text = "";
        } else if (!text.equals(this.text)) {
            if (isMarkupEnabledFor(this) && !isValidationDisabledFor(this)) {
                MarkupValidator.getInstance().validate(text);
            }
            this.text = text;
        }
    }

    @Override
    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(int leftMargin, int topMargin, int rightMargin, 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(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(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(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(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;
    }

    @Override
    public void setData(String key, Object value) {
        if (!RWT.MARKUP_ENABLED.equals(key) || !isMarkupEnabledFor(this)) {
            super.setData(key, value);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getAdapter(Class<T> adapter) {
        if (adapter == WidgetLCA.class) {
            return (T) CLabelLCA.INSTANCE;
        }
        return super.getAdapter(adapter);
    }

    private void initMargins() {
        BoxDimensions padding = getCLabelThemeAdapter().getPadding(this);
        leftMargin = padding.left;
        topMargin = padding.top;
        rightMargin = padding.right;
        bottomMargin = padding.bottom;
    }

    private CLabelThemeAdapter getCLabelThemeAdapter() {
        return (CLabelThemeAdapter) getAdapter(ThemeAdapter.class);
    }
}