| package org.eclipse.ui.internal.forms.widgets; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.SWTException; |
| import org.eclipse.swt.events.PaintEvent; |
| import org.eclipse.swt.events.PaintListener; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.ImageData; |
| import org.eclipse.swt.graphics.ImageLoader; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Canvas; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| |
| public class BusyIndicator extends Canvas { |
| private static final int MARGIN = 2; |
| |
| private ImageData[] progressData; |
| |
| protected ImageLoader loader; |
| |
| protected Image image; |
| |
| protected Image animationImage; |
| |
| protected Thread busyThread; |
| |
| protected boolean stop; |
| |
| /** |
| * BusyWidget constructor comment. |
| * |
| * @param parent |
| * org.eclipse.swt.widgets.Composite |
| * @param style |
| * int |
| */ |
| public BusyIndicator(Composite parent, int style) { |
| super(parent, style); |
| |
| loadProgressImage(); |
| |
| addPaintListener(new PaintListener() { |
| public void paintControl(PaintEvent event) { |
| onPaint(event); |
| } |
| }); |
| } |
| |
| private void loadProgressImage() { |
| InputStream is = BusyIndicator.class |
| .getResourceAsStream("progress.gif"); //$NON-NLS-1$ |
| if (is != null) { |
| loader = new ImageLoader(); |
| try { |
| progressData = loader.load(is); |
| } catch (IllegalArgumentException e) { |
| } |
| try { |
| is.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| |
| public Point computeSize(int wHint, int hHint, boolean changed) { |
| Point size = new Point(0, 0); |
| if (image != null) { |
| Rectangle ibounds = image.getBounds(); |
| size.x = ibounds.width; |
| size.y = ibounds.height; |
| } |
| if (loader != null) { |
| size.x = Math.max(size.x, loader.logicalScreenWidth); |
| size.y = Math.max(size.y, loader.logicalScreenHeight); |
| } |
| size.x += MARGIN + MARGIN; |
| size.y += MARGIN + MARGIN; |
| return size; |
| } |
| |
| /** |
| * Creates a thread to animate the image. |
| */ |
| protected synchronized void createBusyThread() { |
| if (busyThread != null) |
| return; |
| |
| stop = false; |
| busyThread = new Thread() { |
| private Image timage; |
| |
| public void run() { |
| try { |
| /* |
| * Create an off-screen image to draw on, and fill it with |
| * the shell background. |
| */ |
| Image offScreenImage = new Image(getDisplay(), |
| loader.logicalScreenWidth, |
| loader.logicalScreenHeight); |
| final GC offScreenImageGC = new GC(offScreenImage); |
| getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| drawBackground(offScreenImageGC, 0, 0, loader.logicalScreenWidth, loader.logicalScreenHeight); |
| } |
| }); |
| try { |
| /* |
| * Create the first image and draw it on the off-screen |
| * image. |
| */ |
| int imageDataIndex = 0; |
| ImageData imageData = progressData[imageDataIndex]; |
| if (timage != null && !timage.isDisposed()) |
| timage.dispose(); |
| timage = new Image(getDisplay(), imageData); |
| 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. |
| */ |
| int repeatCount = loader.repeatCount; |
| while (loader.repeatCount == 0 || repeatCount > 0) { |
| if (stop || isDisposed()) |
| break; |
| switch (imageData.disposalMethod) { |
| case SWT.DM_FILL_BACKGROUND: |
| /* |
| * Fill with the background color before |
| * drawing. |
| */ |
| offScreenImageGC.fillRectangle(imageData.x, |
| imageData.y, imageData.width, |
| imageData.height); |
| break; |
| case SWT.DM_FILL_PREVIOUS: |
| /* Restore the previous image before drawing. */ |
| offScreenImageGC.drawImage(timage, 0, 0, |
| imageData.width, imageData.height, |
| imageData.x, imageData.y, |
| imageData.width, imageData.height); |
| break; |
| } |
| |
| imageDataIndex = (imageDataIndex + 1) |
| % progressData.length; |
| imageData = progressData[imageDataIndex]; |
| timage.dispose(); |
| timage = new Image(getDisplay(), imageData); |
| 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; |
| getDisplay().syncExec(new Runnable() { |
| public void run() { |
| if (!isDisposed()) |
| redraw(); |
| } |
| }); |
| /* |
| * Sleep for the specified delay time (adding |
| * commonly-used slow-down fudge factors). |
| */ |
| try { |
| int ms = imageData.delayTime * 10; |
| if (ms < 20) |
| ms += 30; |
| if (ms < 30) |
| ms += 10; |
| Thread.sleep(ms); |
| } catch (InterruptedException e) { |
| } |
| |
| /* |
| * If we have just drawn the last image, decrement |
| * the repeat count and start again. |
| */ |
| if (imageDataIndex == progressData.length - 1) |
| repeatCount--; |
| } |
| } catch (SWTException ex) { |
| //System.out |
| // .println("There was an error animating the GIF"); |
| } finally { |
| if (offScreenImage != null |
| && !offScreenImage.isDisposed()) |
| offScreenImage.dispose(); |
| if (offScreenImageGC != null |
| && !offScreenImageGC.isDisposed()) |
| offScreenImageGC.dispose(); |
| if (timage != null && !timage.isDisposed()) |
| timage.dispose(); |
| } |
| if (busyThread == null) |
| Display.getDefault().syncExec(new Runnable() { |
| public void run() { |
| animationImage = null; |
| redraw(); |
| } |
| }); |
| } catch (Exception e) { |
| // Trace.trace(Trace.WARNING, "Busy error", e); |
| // //$NON-NLS-1$ |
| } |
| } |
| }; |
| busyThread.setPriority(Thread.NORM_PRIORITY + 2); |
| busyThread.setDaemon(true); |
| busyThread.start(); |
| } |
| |
| public void dispose() { |
| stop = 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 boolean |
| */ |
| public boolean isBusy() { |
| return (busyThread != null); |
| } |
| |
| /* |
| * Process the paint event |
| */ |
| protected void onPaint(PaintEvent event) { |
| if (animationImage!=null && animationImage.isDisposed()) { |
| animationImage=null; |
| } |
| Rectangle rect = getClientArea(); |
| if (rect.width == 0 || rect.height == 0) |
| return; |
| |
| GC gc = event.gc; |
| Image activeImage = animationImage != null ? animationImage : image; |
| if (activeImage != 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 |
| * boolean |
| */ |
| public synchronized void setBusy(boolean busy) { |
| if (busy) { |
| if (busyThread == null) |
| createBusyThread(); |
| } else { |
| if (busyThread != null) { |
| stop = true; |
| busyThread = null; |
| } |
| } |
| } |
| |
| /** |
| * Set the image. The value <code>null</code> clears it. |
| */ |
| public void setImage(Image image) { |
| if (image != this.image && !isDisposed()) { |
| this.image = image; |
| redraw(); |
| } |
| } |
| } |