view dwtx/ui/internal/forms/widgets/BusyIndicator.d @ 167:862b05e0334a

Add a wrapper for Thread
author Frank Benoit <benoit@tionex.de>
date Tue, 09 Sep 2008 15:59:16 +0200
parents 7ffeace6c47f
children
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2006, 2007 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
 *     Stefan Mucke - fix for Bug 156456
 * Port to the D programming language:
 *     Frank Benoit <benoit@tionex.de>
 *******************************************************************************/
module dwtx.ui.internal.forms.widgets.BusyIndicator;

import dwtx.ui.internal.forms.widgets.FormUtil;

import dwt.DWT;
import dwt.events.PaintEvent;
import dwt.events.PaintListener;
import dwt.graphics.GC;
import dwt.graphics.Image;
import dwt.graphics.ImageData;
import dwt.graphics.Point;
import dwt.graphics.Rectangle;
import dwt.widgets.Canvas;
import dwt.widgets.Composite;
import dwt.widgets.Display;
// import dwtx.core.runtime.FileLocator;
// import dwtx.core.runtime.Path;
// import dwtx.core.runtime.Platform;
import dwtx.jface.resource.ImageDescriptor;

import dwt.dwthelper.utils;
import dwt.dwthelper.Runnable;

import tango.util.Convert;
import dwtx.dwtxhelper.JThread;

public final class BusyIndicator : Canvas {

    alias Canvas.computeSize computeSize;

    class BusyThread : JThread {
        Rectangle bounds;
        Display display;
        GC offScreenImageGC;
        Image offScreenImage;
        Image timage;
        bool stop;

        private this(Rectangle bounds, Display display, GC offScreenImageGC, Image offScreenImage) {
            this.bounds = bounds;
            this.display = display;
            this.offScreenImageGC = offScreenImageGC;
            this.offScreenImage = offScreenImage;
        }

        public override void run() {
            try {
                /*
                 * Create an off-screen image to draw on, and fill it with
                 * the shell background.
                 */
                FormUtil.setAntialias(offScreenImageGC, DWT.ON);
                display.syncExec(dgRunnable( {
                        if (!isDisposed())
                            drawBackground(offScreenImageGC, 0, 0,
                                    bounds.width,
                                    bounds.height);
                }));
                if (isDisposed())
                    return;

                /*
                 * Create the first image and draw it on the off-screen
                 * image.
                 */
                int imageDataIndex = 0;
                ImageData imageData;
                synchronized (this.outer) {
                    timage = getImage(imageDataIndex);
                    imageData = timage.getImageData();
                    offScreenImageGC.drawImage(timage, 0, 0,
                            imageData.width, imageData.height, imageData.x,
                            imageData.y, imageData.width, imageData.height);
                }

                /*
                 * Now loop through the images, creating and drawing
                 * each one on the off-screen image before drawing it on
                 * the shell.
                 */
                while (!stop && !isDisposed() && timage !is null) {

                    /*
                     * Fill with the background color before
                     * drawing.
                     */
                    display.syncExec(dgRunnable( (ImageData fimageData){
                        if (!isDisposed()) {
                            drawBackground(offScreenImageGC, fimageData.x,
                                    fimageData.y, fimageData.width,
                                    fimageData.height);
                        }
                    }, imageData ));

                    synchronized (this.outer) {
                        imageDataIndex = (imageDataIndex + 1) % IMAGE_COUNT;
                        timage = getImage(imageDataIndex);
                        imageData = timage.getImageData();
                        offScreenImageGC.drawImage(timage, 0, 0,
                                imageData.width, imageData.height,
                                imageData.x, imageData.y, imageData.width,
                                imageData.height);
                    }

                    /* Draw the off-screen image to the shell. */
                    animationImage = offScreenImage;
                    display.syncExec(dgRunnable( {
                        if (!isDisposed())
                            redraw();
                    }));
                    /*
                     * Sleep for the specified delay time
                     */
                    try {
                        JThread.sleep(MILLISECONDS_OF_DELAY);
                    } catch (InterruptedException e) {
                        ExceptionPrintStackTrace(e);
                    }


                }
            } catch (Exception e) {
            } finally {
                display.syncExec(dgRunnable( {
                    if (offScreenImage !is null
                            && !offScreenImage.isDisposed())
                        offScreenImage.dispose();
                    if (offScreenImageGC !is null
                            && !offScreenImageGC.isDisposed())
                        offScreenImageGC.dispose();
                }));
                clearImages();
            }
            if (busyThread is null)
                display.syncExec(dgRunnable( {
                    animationImage = null;
                    if (!isDisposed())
                        redraw();
                }));
        }

        public void setStop(bool stop) {
            this.stop = stop;
        }
    }

    private static const int MARGIN = 0;
    private static const int IMAGE_COUNT = 8;
    private static const int MILLISECONDS_OF_DELAY = 180;
    private Image[] imageCache;
    protected Image image;

    protected Image animationImage;

    protected BusyThread busyThread;

    /**
     * BusyWidget constructor comment.
     *
     * @param parent
     *            dwt.widgets.Composite
     * @param style
     *            int
     */
    public this(Composite parent, int style) {
        super(parent, style);

        addPaintListener(new class PaintListener {
            public void paintControl(PaintEvent event) {
                onPaint(event);
            }
        });
    }

    public Point computeSize(int wHint, int hHint, bool changed) {
        Point size = new Point(0, 0);
        if (image !is null) {
            Rectangle ibounds = image.getBounds();
            size.x = ibounds.width;
            size.y = ibounds.height;
        }
        if (isBusy()) {
            Rectangle bounds = getImage(0).getBounds();
            size.x = Math.max(size.x, bounds.width);
            size.y = Math.max(size.y, bounds.height);
        }
        size.x += MARGIN + MARGIN;
        size.y += MARGIN + MARGIN;
        return size;
    }

    /* (non-Javadoc)
     * @see dwt.widgets.Control#forceFocus()
     */
    public bool forceFocus() {
        return false;
    }

    /**
     * Creates a thread to animate the image.
     */
    protected synchronized void createBusyThread() {
        if (busyThread !is null)
            return;

        Rectangle bounds = getImage(0).getBounds();
        Display display = getDisplay();
        Image offScreenImage = new Image(display, bounds.width, bounds.height);
        GC offScreenImageGC = new GC(offScreenImage);
        busyThread = new BusyThread(bounds, display, offScreenImageGC, offScreenImage);
        busyThread.setPriority(JThread.NORM_PRIORITY + 2);
        busyThread.setDaemon(true);
        busyThread.start();
    }

    public void dispose() {
        if (busyThread !is null) {
            busyThread.setStop(true);
            busyThread = null;
        }
        super.dispose();
    }

    /**
     * Return the image or <code>null</code>.
     */
    public Image getImage() {
        return image;
    }

    /**
     * Returns true if it is currently busy.
     *
     * @return bool
     */
    public bool isBusy() {
        return (busyThread !is null);
    }

    /*
     * Process the paint event
     */
    protected void onPaint(PaintEvent event) {
        if (animationImage !is null && animationImage.isDisposed()) {
            animationImage = null;
        }
        Rectangle rect = getClientArea();
        if (rect.width is 0 || rect.height is 0)
            return;

        GC gc = event.gc;
        Image activeImage = animationImage !is null ? animationImage : image;
        if (activeImage !is null) {
            Rectangle ibounds = activeImage.getBounds();
            gc.drawImage(activeImage, rect.width / 2 - ibounds.width / 2,
                    rect.height / 2 - ibounds.height / 2);
        }
    }

    /**
     * Sets the indicators busy count up (true) or down (false) one.
     *
     * @param busy
     *            bool
     */
    public synchronized void setBusy(bool busy) {
        if (busy) {
            if (busyThread is null)
                createBusyThread();
        } else {
            if (busyThread !is null) {
                busyThread.setStop(true);
                busyThread = null;
            }
        }
    }

    /**
     * Set the image. The value <code>null</code> clears it.
     */
    public void setImage(Image image) {
        if (image !is this.image && !isDisposed()) {
            this.image = image;
            redraw();
        }
    }


    private ImageDescriptor createImageDescriptor(String relativePath) {
//         Bundle bundle = Platform.getBundle("dwtx.ui.forms"); //$NON-NLS-1$
//         URL url = FileLocator.find(bundle, new Path(relativePath),null);
//         if (url is null) return null;
//         try {
//             url = FileLocator.resolve(url);
//             return ImageDescriptor.createFromURL(url);
//         } catch (IOException e) {
//             return null;
//         }
        return null;
    }

    private synchronized Image getImage(int index) {
        if (imageCache is null) {
            imageCache = new Image[IMAGE_COUNT];
        }
        if (imageCache[index] is null){
            ImageDescriptor descriptor = createImageDescriptor("$nl$/icons/progress/ani/" ~ to!(String)(index + 1) ~ ".png"); //$NON-NLS-1$ //$NON-NLS-2$
            imageCache[index] = descriptor.createImage();
        }
        return imageCache[index];
    }

    private synchronized void clearImages() {
        if (busyThread !is null)
            return;
        if (imageCache !is null) {
            for (int index = 0; index < IMAGE_COUNT; index++) {
                if (imageCache[index] !is null && !imageCache[index].isDisposed()) {
                    imageCache[index].dispose();
                    imageCache[index] = null;
                }
            }
        }
    }

}