001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.forms.widgets;
011:
012: import java.io.IOException;
013: import java.net.URL;
014:
015: import org.eclipse.core.runtime.FileLocator;
016: import org.eclipse.core.runtime.Path;
017: import org.eclipse.core.runtime.Platform;
018: import org.eclipse.jface.resource.ImageDescriptor;
019: import org.eclipse.swt.SWT;
020: import org.eclipse.swt.events.PaintEvent;
021: import org.eclipse.swt.events.PaintListener;
022: import org.eclipse.swt.graphics.GC;
023: import org.eclipse.swt.graphics.Image;
024: import org.eclipse.swt.graphics.ImageData;
025: import org.eclipse.swt.graphics.Point;
026: import org.eclipse.swt.graphics.Rectangle;
027: import org.eclipse.swt.widgets.Canvas;
028: import org.eclipse.swt.widgets.Composite;
029: import org.eclipse.swt.widgets.Display;
030: import org.osgi.framework.Bundle;
031:
032: public final class BusyIndicator extends Canvas {
033: private static final int MARGIN = 0;
034: private static final int IMAGE_COUNT = 8;
035: private static final int MILLISECONDS_OF_DELAY = 180;
036: private Image[] imageCache;
037: protected Image image;
038:
039: protected Image animationImage;
040:
041: protected Thread busyThread;
042:
043: protected boolean stop;
044:
045: /**
046: * BusyWidget constructor comment.
047: *
048: * @param parent
049: * org.eclipse.swt.widgets.Composite
050: * @param style
051: * int
052: */
053: public BusyIndicator(Composite parent, int style) {
054: super (parent, style);
055:
056: addPaintListener(new PaintListener() {
057: public void paintControl(PaintEvent event) {
058: onPaint(event);
059: }
060: });
061: }
062:
063: public Point computeSize(int wHint, int hHint, boolean changed) {
064: Point size = new Point(0, 0);
065: if (image != null) {
066: Rectangle ibounds = image.getBounds();
067: size.x = ibounds.width;
068: size.y = ibounds.height;
069: }
070: if (isBusy()) {
071: Rectangle bounds = getImage(0).getBounds();
072: size.x = Math.max(size.x, bounds.width);
073: size.y = Math.max(size.y, bounds.height);
074: }
075: size.x += MARGIN + MARGIN;
076: size.y += MARGIN + MARGIN;
077: return size;
078: }
079:
080: /**
081: * Creates a thread to animate the image.
082: */
083: protected synchronized void createBusyThread() {
084: if (busyThread != null)
085: return;
086:
087: stop = false;
088: final Rectangle bounds = getImage(0).getBounds();
089: final Display display = getDisplay();
090: final Image offScreenImage = new Image(display, bounds.width,
091: bounds.height);
092: final GC offScreenImageGC = new GC(offScreenImage);
093: busyThread = new Thread() {
094: private Image timage;
095:
096: public void run() {
097: try {
098: /*
099: * Create an off-screen image to draw on, and fill it with
100: * the shell background.
101: */
102: FormUtil.setAntialias(offScreenImageGC, SWT.ON);
103: display.syncExec(new Runnable() {
104: public void run() {
105: if (!isDisposed())
106: drawBackground(offScreenImageGC, 0, 0,
107: bounds.width, bounds.height);
108: }
109: });
110: if (isDisposed())
111: return;
112:
113: /*
114: * Create the first image and draw it on the off-screen
115: * image.
116: */
117: int imageDataIndex = 0;
118: timage = getImage(imageDataIndex);
119: ImageData imageData = timage.getImageData();
120: offScreenImageGC.drawImage(timage, 0, 0,
121: imageData.width, imageData.height,
122: imageData.x, imageData.y, imageData.width,
123: imageData.height);
124:
125: /*
126: * Now loop through the images, creating and drawing
127: * each one on the off-screen image before drawing it on
128: * the shell.
129: */
130: while (!stop && !isDisposed() && timage != null) {
131:
132: /*
133: * Fill with the background color before
134: * drawing.
135: */
136: final ImageData fimageData = imageData;
137: display.syncExec(new Runnable() {
138: public void run() {
139: if (!isDisposed()) {
140: drawBackground(offScreenImageGC,
141: fimageData.x, fimageData.y,
142: fimageData.width,
143: fimageData.height);
144: }
145: }
146: });
147:
148: imageDataIndex = (imageDataIndex + 1)
149: % IMAGE_COUNT;
150: timage = getImage(imageDataIndex);
151: imageData = timage.getImageData();
152: offScreenImageGC.drawImage(timage, 0, 0,
153: imageData.width, imageData.height,
154: imageData.x, imageData.y,
155: imageData.width, imageData.height);
156:
157: /* Draw the off-screen image to the shell. */
158: animationImage = offScreenImage;
159: display.syncExec(new Runnable() {
160: public void run() {
161: if (!isDisposed())
162: redraw();
163: }
164: });
165: /*
166: * Sleep for the specified delay time
167: */
168: try {
169: Thread.sleep(MILLISECONDS_OF_DELAY);
170: } catch (InterruptedException e) {
171: e.printStackTrace();
172: }
173:
174: }
175: } catch (Exception e) {
176: } finally {
177: display.syncExec(new Runnable() {
178: public void run() {
179: if (offScreenImage != null
180: && !offScreenImage.isDisposed())
181: offScreenImage.dispose();
182: if (offScreenImageGC != null
183: && !offScreenImageGC.isDisposed())
184: offScreenImageGC.dispose();
185: }
186: });
187: clearImages();
188: }
189: if (busyThread == null)
190: display.syncExec(new Runnable() {
191: public void run() {
192: animationImage = null;
193: if (!isDisposed())
194: redraw();
195: }
196: });
197: }
198: };
199: busyThread.setPriority(Thread.NORM_PRIORITY + 2);
200: busyThread.setDaemon(true);
201: busyThread.start();
202: }
203:
204: public void dispose() {
205: stop = true;
206: busyThread = null;
207: super .dispose();
208: }
209:
210: /**
211: * Return the image or <code>null</code>.
212: */
213: public Image getImage() {
214: return image;
215: }
216:
217: /**
218: * Returns true if it is currently busy.
219: *
220: * @return boolean
221: */
222: public boolean isBusy() {
223: return (busyThread != null);
224: }
225:
226: /*
227: * Process the paint event
228: */
229: protected void onPaint(PaintEvent event) {
230: if (animationImage != null && animationImage.isDisposed()) {
231: animationImage = null;
232: }
233: Rectangle rect = getClientArea();
234: if (rect.width == 0 || rect.height == 0)
235: return;
236:
237: GC gc = event.gc;
238: Image activeImage = animationImage != null ? animationImage
239: : image;
240: if (activeImage != null) {
241: Rectangle ibounds = activeImage.getBounds();
242: gc.drawImage(activeImage, rect.width / 2 - ibounds.width
243: / 2, rect.height / 2 - ibounds.height / 2);
244: }
245: }
246:
247: /**
248: * Sets the indicators busy count up (true) or down (false) one.
249: *
250: * @param busy
251: * boolean
252: */
253: public synchronized void setBusy(boolean busy) {
254: if (busy) {
255: if (busyThread == null)
256: createBusyThread();
257: } else {
258: if (busyThread != null) {
259: stop = true;
260: busyThread = null;
261: }
262: }
263: }
264:
265: /**
266: * Set the image. The value <code>null</code> clears it.
267: */
268: public void setImage(Image image) {
269: if (image != this .image && !isDisposed()) {
270: this .image = image;
271: redraw();
272: }
273: }
274:
275: private ImageDescriptor createImageDescriptor(String relativePath) {
276: Bundle bundle = Platform.getBundle("org.eclipse.ui.forms"); //$NON-NLS-1$
277: URL url = FileLocator
278: .find(bundle, new Path(relativePath), null);
279: if (url == null)
280: return null;
281: try {
282: url = FileLocator.resolve(url);
283: return ImageDescriptor.createFromURL(url);
284: } catch (IOException e) {
285: return null;
286: }
287: }
288:
289: private Image getImage(int index) {
290: if (imageCache == null) {
291: imageCache = new Image[IMAGE_COUNT];
292: }
293: if (imageCache[index] == null) {
294: ImageDescriptor descriptor = createImageDescriptor("$nl$/icons/progress/ani/" + (index + 1) + ".png"); //$NON-NLS-1$ //$NON-NLS-2$
295: imageCache[index] = descriptor.createImage();
296: }
297: return imageCache[index];
298: }
299:
300: private void clearImages() {
301: if (imageCache != null) {
302: for (int index = 0; index < IMAGE_COUNT; index++) {
303: if (imageCache[index] != null
304: && !imageCache[index].isDisposed()) {
305: imageCache[index].dispose();
306: imageCache[index] = null;
307: }
308: }
309: }
310: }
311:
312: }
|