diff dwt/custom/CLabel.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children f565d3a95c0a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/custom/CLabel.d	Sat Aug 09 17:00:02 2008 +0200
@@ -0,0 +1,1040 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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
+ *     
+ * Port to the D Programming language:
+ *     Jacob Carlborg <jacob.carlborg@gmail.com>
+ *******************************************************************************/
+module dwt.custom.CLabel;
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.accessibility.ACC;
+import dwt.accessibility.Accessible;
+import dwt.accessibility.AccessibleAdapter;
+import dwt.accessibility.AccessibleControlAdapter;
+import dwt.accessibility.AccessibleControlEvent;
+import dwt.accessibility.AccessibleEvent;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.PaintEvent;
+import dwt.events.PaintListener;
+import dwt.events.TraverseEvent;
+import dwt.events.TraverseListener;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.graphics.TextLayout;
+import dwt.widgets.Canvas;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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>
+ */
+public class CLabel : Canvas
+{
+
+    /** Gap between icon and text */
+    private static const int GAP = 5;
+    /** Left and right margins */
+    private static const int INDENT = 3;
+    /** a String inserted in the middle of text that has been shortened */
+    private static const String ELLIPSIS = "..."; //$NON-NLS-1$ // could use the ellipsis glyph on some platforms "\u2026"
+    /** the alignnment. Either CENTER, RIGHT, LEFT. Default is LEFT*/
+    private int alignn = DWT.LEFT;
+    private int hIndent = INDENT;
+    private int vIndent = INDENT;
+    /** 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[] gradientColors;
+    private int[] gradientPercents;
+    private bool gradientVertical;
+    private Color background;
+
+    private static int
+            DRAW_FLAGS = DWT.DRAW_MNEMONIC | DWT.DRAW_TAB | DWT.DRAW_TRANSPARENT | DWT.DRAW_DELIMITER;
+
+    /**
+     * 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>DWT</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>DWT</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 DWTException <ul>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+     * </ul>
+     *
+     * @see DWT#LEFT
+     * @see DWT#RIGHT
+     * @see DWT#CENTER
+     * @see DWT#SHADOW_IN
+     * @see DWT#SHADOW_OUT
+     * @see DWT#SHADOW_NONE
+     * @see #getStyle()
+     */
+    public this (Composite parent, int style)
+    {
+        super(parent, checkStyle(style));
+        if ((style & (DWT.CENTER | DWT.RIGHT)) is 0)
+            style |= DWT.LEFT;
+        if ((style & DWT.CENTER) !is 0)
+            alignn = DWT.CENTER;
+        if ((style & DWT.RIGHT) !is 0)
+            alignn = DWT.RIGHT;
+        if ((style & DWT.LEFT) !is 0)
+            alignn = DWT.LEFT;
+
+        addPaintListener(new class PaintListener
+        {
+            public void paintControl (PaintEvent event)
+            {
+                onPaint(event);
+            }
+        });
+
+        addDisposeListener(new class DisposeListener
+        {
+            public void widgetDisposed (DisposeEvent event)
+            {
+                onDispose(event);
+            }
+        });
+
+        addTraverseListener(new class TraverseListener
+        {
+            public void keyTraversed (TraverseEvent event)
+            {
+                if (event.detail is DWT.TRAVERSE_MNEMONIC)
+                {
+                    onMnemonic(event);
+                }
+            }
+        });
+
+        initAccessible();
+
+    }
+
+    /**
+     * Check the style bits to ensure that no invalid styles are applied.
+     */
+    private static int checkStyle (int style)
+    {
+        if ((style & DWT.BORDER) !is 0)
+            style |= DWT.SHADOW_IN;
+        int
+                mask = DWT.SHADOW_IN | DWT.SHADOW_OUT | DWT.SHADOW_NONE | DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
+        style = style & mask;
+        return style |= DWT.NO_FOCUS | DWT.DOUBLE_BUFFERED;
+    }
+
+    //protected void checkSubclass () {
+    //  String name = getClass().getName ();
+    //  String validName = CLabel.class.getName();
+    //  if (!validName.opEquals(name)) {
+    //      DWT.error (DWT.ERROR_INVALID_SUBCLASS);
+    //  }
+    //}
+
+    public Point computeSize (int wHint, int hHint, bool changed)
+    {
+        checkWidget();
+        Point e = getTotalSize(image, text);
+        if (wHint is DWT.DEFAULT)
+        {
+            e.x += 2 * hIndent;
+        }
+        else
+        {
+            e.x = wHint;
+        }
+        if (hHint is DWT.DEFAULT)
+        {
+            e.y += 2 * vIndent;
+        }
+        else
+        {
+            e.y = hHint;
+        }
+        return e;
+    }
+
+    /**
+     * Draw a rectangle in the given colors.
+     */
+    private void drawBevelRect (GC gc, int x, int y, int w, int h,
+            Color topleft, Color bottomright)
+    {
+        gc.setForeground(bottomright);
+        gc.drawLine(x + w, y, x + w, y + h);
+        gc.drawLine(x, y + h, x + w, y + h);
+
+        gc.setForeground(topleft);
+        gc.drawLine(x, y, x + w - 1, y);
+        gc.drawLine(x, y, x, y + h - 1);
+    }
+
+    /*
+     * Return the lowercase of the first non-'&' character following
+     * an '&' character in the given String. If there are no '&'
+     * characters in the given String, return '\0'.
+     */
+    char _findMnemonic (String str)
+    {
+        if (str is null)
+            return '\0';
+        int index = 0;
+        int length = str.length();
+        do
+        {
+            while (index < length && str.charAt(index) !is '&')
+                index++;
+            if (++index >= length)
+                return '\0';
+            if (str.charAt(index) !is '&')
+                return CharacterToLower(str.charAt(index));
+            index++;
+        } while (index < length);
+        return '\0';
+    }
+
+    /**
+     * Returns the alignnment.
+     * The alignnment style (LEFT, CENTER or RIGHT) is returned.
+     * 
+     * @return DWT.LEFT, DWT.RIGHT or DWT.CENTER
+     */
+    public int getAlignment ()
+    {
+        //checkWidget();
+        return alignn;
+    }
+
+    /**
+     * 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);
+
+        if (image !is null)
+        {
+            Rectangle r = image.getBounds();
+            size.x += r.width;
+            size.y += r.height;
+        }
+
+        GC gc = new GC(this);
+        if (text !is 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 !is 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 (alignn)
+        {
+            case DWT.RIGHT:
+                style |= DWT.RIGHT;
+            break;
+            case DWT.CENTER:
+                style |= DWT.CENTER;
+            break;
+            case DWT.LEFT:
+                style |= DWT.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;
+    }
+
+    private void initAccessible ()
+    {
+        Accessible accessible = getAccessible();
+        accessible.addAccessibleListener(new class 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(this.text);
+                if (mnemonic !is '\0')
+                {
+                    e.result = "Alt+" + mnemonic; //$NON-NLS-1$
+            }
+        }
+    }   );
+
+        accessible.addAccessibleControlListener(new class
+                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 (DisposeEvent event)
+    {
+        gradientColors = null;
+        gradientPercents = null;
+        backgroundImage = null;
+        text = null;
+        image = null;
+        appToolTipText = null;
+    }
+
+    void onMnemonic (TraverseEvent event)
+    {
+        char mnemonic = _findMnemonic(text);
+        if (mnemonic is '\0')
+            return;
+        if (CharacterToLower(event.character) !is mnemonic)
+            return;
+        Composite control = this.getParent();
+        while (control !is null)
+        {
+            Control[] children = control.getChildren();
+            int index = 0;
+            while (index < children.length)
+            {
+                if (children[index] is this)
+                    break;
+                index++;
+            }
+            index++;
+            if (index < children.length)
+            {
+                if (children[index].setFocus())
+                {
+                    event.doit = true;
+                    event.detail = DWT.TRAVERSE_NONE;
+                }
+            }
+            control = control.getParent();
+        }
+    }
+
+    void onPaint (PaintEvent event)
+    {
+        Rectangle rect = getClientArea();
+        if (rect.width is 0 || rect.height is 0)
+            return;
+
+        bool shortenText = false;
+        String t = text;
+        Image img = image;
+        int availableWidth = Math.max(0, rect.width - 2 * hIndent);
+        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 is 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 is null)
+            {
+                super.setToolTipText(text);
+            }
+        }
+        else
+        {
+            super.setToolTipText(appToolTipText);
+        }
+
+        // determine horizontal position
+        int x = rect.x + hIndent;
+        if (alignn is DWT.CENTER)
+        {
+            x = (rect.width - extent.x) / 2;
+        }
+        if (alignn is DWT.RIGHT)
+        {
+            x = rect.width - hIndent - extent.x;
+        }
+
+        // draw a background image behind the text
+        try
+        {
+            if (backgroundImage !is 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 !is null)
+            {
+                // draw a gradient behind the text
+                const Color oldBackground = gc.getBackground();
+                if (gradientColors.length is 1)
+                {
+                    if (gradientColors[0] !is null)
+                        gc.setBackground(gradientColors[0]);
+                    gc.fillRectangle(0, 0, rect.width, rect.height);
+                }
+                else
+                {
+                    const Color oldForeground = gc.getForeground();
+                    Color lastColor = gradientColors[0];
+                    if (lastColor is null)
+                        lastColor = oldBackground;
+                    int pos = 0;
+                    for (int i = 0; i < gradientPercents.length; ++i)
+                    {
+                        gc.setForeground(lastColor);
+                        lastColor = gradientColors[i + 1];
+                        if (lastColor is null)
+                            lastColor = oldBackground;
+                        gc.setBackground(lastColor);
+                        if (gradientVertical)
+                        {
+                            const int
+                                    gradientHeight = (gradientPercents[i] * rect.height / 100) - pos;
+                            gc.fillGradientRectangle(0, pos, rect.width,
+                                    gradientHeight, true);
+                            pos += gradientHeight;
+                        }
+                        else
+                        {
+                            const 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 !is null || (getStyle() & DWT.DOUBLE_BUFFERED) is 0)
+                {
+                    gc.setBackground(getBackground());
+                    gc.fillRectangle(rect);
+                }
+            }
+        }
+        catch (DWTException e)
+        {
+            if ((getStyle() & DWT.DOUBLE_BUFFERED) is 0)
+            {
+                gc.setBackground(getBackground());
+                gc.fillRectangle(rect);
+            }
+        }
+
+        // draw border
+        int style = getStyle();
+        if ((style & DWT.SHADOW_IN) !is 0 || (style & DWT.SHADOW_OUT) !is 0)
+        {
+            paintBorder(gc, rect);
+        }
+
+        // draw the image
+        if (img !is null)
+        {
+            Rectangle imageRect = img.getBounds();
+            gc.drawImage(img, 0, 0, imageRect.width, imageRect.height, x,
+                    (rect.height - imageRect.height) / 2, imageRect.width,
+                    imageRect.height);
+            x += imageRect.width + GAP;
+            extent.x -= imageRect.width + GAP;
+        }
+        // draw the text
+        if (lines !is null)
+        {
+            int lineHeight = gc.getFontMetrics().getHeight();
+            int textHeight = lines.length * lineHeight;
+            int lineY = Math.max(vIndent,
+                    rect.y + (rect.height - textHeight) / 2);
+            gc.setForeground(getForeground());
+            for (int i = 0; i < lines.length; i++)
+            {
+                int lineX = x;
+                if (lines.length > 1)
+                {
+                    if (alignn is DWT.CENTER)
+                    {
+                        int lineWidth = gc.textExtent(lines[i], DRAW_FLAGS).x;
+                        lineX = x + Math.max(0, (extent.x - lineWidth) / 2);
+                    }
+                    if (alignn is DWT.RIGHT)
+                    {
+                        int lineWidth = gc.textExtent(lines[i], DRAW_FLAGS).x;
+                        lineX = Math.max(x,
+                                rect.x + rect.width - hIndent - 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 & DWT.SHADOW_IN) !is 0)
+        {
+            c1 = disp.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
+            c2 = disp.getSystemColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+        }
+        if ((style & DWT.SHADOW_OUT) !is 0)
+        {
+            c1 = disp.getSystemColor(DWT.COLOR_WIDGET_LIGHT_SHADOW);
+            c2 = disp.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
+        }
+
+        if (c1 !is null && c2 !is null)
+        {
+            gc.setLineWidth(1);
+            drawBevelRect(gc, r.x, r.y, r.width - 1, r.height - 1, c1, c2);
+        }
+    }
+
+    /**
+     * Set the alignnment of the CLabel.
+     * Use the values LEFT, CENTER and RIGHT to alignn image and text within the available space.
+     * 
+     * @param alignn the alignnment style of LEFT, RIGHT or CENTER
+     * 
+     * @exception DWTException <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 alignn is not one of DWT.LEFT, DWT.RIGHT or DWT.CENTER</li>
+     * </ul>
+     */
+    public void setAlignment (int alignn)
+    {
+        checkWidget();
+        if (alignn !is DWT.LEFT && alignn !is DWT.RIGHT && alignn !is DWT.CENTER)
+        {
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        }
+        if (this.alignn !is alignn)
+        {
+            this.alignn = alignn;
+            redraw();
+        }
+    }
+
+    public void setBackground (Color color)
+    {
+        super.setBackground(color);
+        // Are these settings the same as before?
+        if (backgroundImage is null && gradientColors is null && gradientPercents is null)
+        {
+            if (color is null)
+            {
+                if (background is null)
+                    return;
+            }
+            else
+            {
+                if (color.opEquals(background))
+                    return;
+            }
+        }
+        background = color;
+        backgroundImage = null;
+        gradientColors = null;
+        gradientPercents = null;
+        redraw();
+    }
+
+    /**
+     * 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(DWT.COLOR_DARK_BLUE), 
+     *                                 display.getSystemColor(DWT.COLOR_BLUE),
+     *                                 display.getSystemColor(DWT.COLOR_WHITE), 
+     *                                 display.getSystemColor(DWT.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 DWTException <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>
+     */
+    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(DWT.COLOR_DARK_BLUE), 
+     *                                 display.getSystemColor(DWT.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 DWTException <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 3.0
+     */
+    public void setBackground (Color[] colors, int[] percents, bool vertical)
+    {
+        checkWidget();
+        if (colors !is null)
+        {
+            if (percents is null || percents.length !is colors.length - 1)
+            {
+                DWT.error(DWT.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 i = 0; i < percents.length; i++)
+            {
+                if (percents[i] < 0 || percents[i] > 100)
+                {
+                    DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+                }
+                if (i > 0 && percents[i] < percents[i - 1])
+                {
+                    DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+                }
+            }
+        }
+
+        // Are these settings the same as before?
+        const Color background = getBackground();
+        if (backgroundImage is null)
+        {
+            if ((gradientColors !is null) && (colors !is null) && (gradientColors.length is colors.length))
+            {
+                bool same = false;
+                for (int i = 0; i < gradientColors.length; i++)
+                {
+                    same = (gradientColors[i] is colors[i]) || ((gradientColors[i] is null) && (colors[i] is background)) || ((gradientColors[i] is background) && (colors[i] is null));
+                    if (!same)
+                        break;
+                }
+                if (same)
+                {
+                    for (int i = 0; i < gradientPercents.length; i++)
+                    {
+                        same = gradientPercents[i] is percents[i];
+                        if (!same)
+                            break;
+                    }
+                }
+                if (same && this.gradientVertical is vertical)
+                    return;
+            }
+        }
+        else
+        {
+            backgroundImage = null;
+        }
+        // Store the new settings
+        if (colors is null)
+        {
+            gradientColors = null;
+            gradientPercents = null;
+            gradientVertical = false;
+        }
+        else
+        {
+            gradientColors = new Color[colors.length];
+            for (int i = 0; i < colors.length; ++i)
+                gradientColors[i] = (colors[i] !is null) ? colors[i] : background;
+            gradientPercents = new int[percents.length];
+            for (int i = 0; i < percents.length; ++i)
+                gradientPercents[i] = percents[i];
+            gradientVertical = vertical;
+        }
+        // Refresh with the new settings
+        redraw();
+    }
+
+    /**
+     * Set the image to be drawn in the background of the label.
+     * 
+     * @param image the image to be drawn in the background
+     * 
+     * @exception DWTException <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 is backgroundImage)
+            return;
+        if (image !is null)
+        {
+            gradientColors = null;
+            gradientPercents = null;
+        }
+        backgroundImage = image;
+        redraw();
+
+    }
+
+    public void setFont (Font font)
+    {
+        super.setFont(font);
+        redraw();
+    }
+
+    /**
+     * 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 DWTException <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 !is this.image)
+        {
+            this.image = image;
+            redraw();
+        }
+    }
+
+    /**
+     * 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 DWTException <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 is null)
+            text = ""; //$NON-NLS-1$
+        if (!text.opEquals(this.text))
+        {
+            this.text = text;
+            redraw();
+        }
+    }
+
+    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
+     */
+    protected String shortenText (GC gc, String t, int width)
+    {
+        if (t is 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 is 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, DWT.MOVEMENT_CLUSTER);
+        if (nextOffset !is offset)
+            return layout.getPreviousOffset(nextOffset, DWT.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 is -1)
+            {
+                lines[lines.length - 1] = text.substring(start);
+            }
+            else
+            {
+                bool crlf = (pos > 0) && (text.charAt(pos - 1) is '\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 !is -1);
+        return lines;
+    }
+}