75
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2006, 2007 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Stefan Mucke - fix for Bug 156456
|
|
11 * Port to the D programming language:
|
|
12 * Frank Benoit <benoit@tionex.de>
|
|
13 *******************************************************************************/
|
|
14 module dwtx.ui.internal.forms.widgets.BusyIndicator;
|
|
15
|
|
16 import dwtx.ui.internal.forms.widgets.FormUtil;
|
|
17
|
|
18 import dwt.DWT;
|
|
19 import dwt.events.PaintEvent;
|
|
20 import dwt.events.PaintListener;
|
|
21 import dwt.graphics.GC;
|
|
22 import dwt.graphics.Image;
|
|
23 import dwt.graphics.ImageData;
|
|
24 import dwt.graphics.Point;
|
|
25 import dwt.graphics.Rectangle;
|
|
26 import dwt.widgets.Canvas;
|
|
27 import dwt.widgets.Composite;
|
|
28 import dwt.widgets.Display;
|
|
29 import dwtx.jface.resource.ImageDescriptor;
|
|
30
|
|
31 import dwt.dwthelper.utils;
|
|
32 import dwt.dwthelper.Runnable;
|
|
33
|
|
34 import tango.util.Convert;
|
|
35 import tango.core.Thread;
|
|
36
|
|
37 public final class BusyIndicator : Canvas {
|
|
38
|
|
39 class BusyThread : Thread {
|
|
40 Rectangle bounds;
|
|
41 Display display;
|
|
42 GC offScreenImageGC;
|
|
43 Image offScreenImage;
|
|
44 Image timage;
|
|
45 bool stop;
|
|
46
|
|
47 private this(Rectangle bounds, Display display, GC offScreenImageGC, Image offScreenImage) {
|
|
48 this.bounds = bounds;
|
|
49 this.display = display;
|
|
50 this.offScreenImageGC = offScreenImageGC;
|
|
51 this.offScreenImage = offScreenImage;
|
|
52 }
|
|
53
|
|
54 public void run() {
|
|
55 try {
|
|
56 /*
|
|
57 * Create an off-screen image to draw on, and fill it with
|
|
58 * the shell background.
|
|
59 */
|
|
60 FormUtil.setAntialias(offScreenImageGC, DWT.ON);
|
|
61 display.syncExec(dgRunnable( {
|
|
62 if (!isDisposed())
|
|
63 drawBackground(offScreenImageGC, 0, 0,
|
|
64 bounds.width,
|
|
65 bounds.height);
|
|
66 }));
|
|
67 if (isDisposed())
|
|
68 return;
|
|
69
|
|
70 /*
|
|
71 * Create the first image and draw it on the off-screen
|
|
72 * image.
|
|
73 */
|
|
74 int imageDataIndex = 0;
|
|
75 ImageData imageData;
|
|
76 synchronized (this.outer) {
|
|
77 timage = getImage(imageDataIndex);
|
|
78 imageData = timage.getImageData();
|
|
79 offScreenImageGC.drawImage(timage, 0, 0,
|
|
80 imageData.width, imageData.height, imageData.x,
|
|
81 imageData.y, imageData.width, imageData.height);
|
|
82 }
|
|
83
|
|
84 /*
|
|
85 * Now loop through the images, creating and drawing
|
|
86 * each one on the off-screen image before drawing it on
|
|
87 * the shell.
|
|
88 */
|
|
89 while (!stop && !isDisposed() && timage !is null) {
|
|
90
|
|
91 /*
|
|
92 * Fill with the background color before
|
|
93 * drawing.
|
|
94 */
|
|
95 display.syncExec(dgRunnable( (ImageData fimageData){
|
|
96 if (!isDisposed()) {
|
|
97 drawBackground(offScreenImageGC, fimageData.x,
|
|
98 fimageData.y, fimageData.width,
|
|
99 fimageData.height);
|
|
100 }
|
|
101 }, imageData ));
|
|
102
|
|
103 synchronized (this.outer) {
|
|
104 imageDataIndex = (imageDataIndex + 1) % IMAGE_COUNT;
|
|
105 timage = getImage(imageDataIndex);
|
|
106 imageData = timage.getImageData();
|
|
107 offScreenImageGC.drawImage(timage, 0, 0,
|
|
108 imageData.width, imageData.height,
|
|
109 imageData.x, imageData.y, imageData.width,
|
|
110 imageData.height);
|
|
111 }
|
|
112
|
|
113 /* Draw the off-screen image to the shell. */
|
|
114 animationImage = offScreenImage;
|
|
115 display.syncExec(dgRunnable( {
|
|
116 if (!isDisposed())
|
|
117 redraw();
|
|
118 }));
|
|
119 /*
|
|
120 * Sleep for the specified delay time
|
|
121 */
|
|
122 try {
|
|
123 Thread.sleep(MILLISECONDS_OF_DELAY/1000.0);
|
|
124 } catch (InterruptedException e) {
|
|
125 e.printStackTrace();
|
|
126 }
|
|
127
|
|
128
|
|
129 }
|
|
130 } catch (Exception e) {
|
|
131 } finally {
|
|
132 display.syncExec(dgRunnable( {
|
|
133 if (offScreenImage !is null
|
|
134 && !offScreenImage.isDisposed())
|
|
135 offScreenImage.dispose();
|
|
136 if (offScreenImageGC !is null
|
|
137 && !offScreenImageGC.isDisposed())
|
|
138 offScreenImageGC.dispose();
|
|
139 }));
|
|
140 clearImages();
|
|
141 }
|
|
142 if (busyThread is null)
|
|
143 display.syncExec(dgRunnable( {
|
|
144 animationImage = null;
|
|
145 if (!isDisposed())
|
|
146 redraw();
|
|
147 }));
|
|
148 }
|
|
149
|
|
150 public void setStop(bool stop) {
|
|
151 this.stop = stop;
|
|
152 }
|
|
153 }
|
|
154
|
|
155 private static const int MARGIN = 0;
|
|
156 private static const int IMAGE_COUNT = 8;
|
|
157 private static const int MILLISECONDS_OF_DELAY = 180;
|
|
158 private Image[] imageCache;
|
|
159 protected Image image;
|
|
160
|
|
161 protected Image animationImage;
|
|
162
|
|
163 protected BusyThread busyThread;
|
|
164
|
|
165 /**
|
|
166 * BusyWidget constructor comment.
|
|
167 *
|
|
168 * @param parent
|
|
169 * dwt.widgets.Composite
|
|
170 * @param style
|
|
171 * int
|
|
172 */
|
|
173 public this(Composite parent, int style) {
|
|
174 super(parent, style);
|
|
175
|
|
176 addPaintListener(new class PaintListener {
|
|
177 public void paintControl(PaintEvent event) {
|
|
178 onPaint(event);
|
|
179 }
|
|
180 });
|
|
181 }
|
|
182
|
|
183 public Point computeSize(int wHint, int hHint, bool changed) {
|
|
184 Point size = new Point(0, 0);
|
|
185 if (image !is null) {
|
|
186 Rectangle ibounds = image.getBounds();
|
|
187 size.x = ibounds.width;
|
|
188 size.y = ibounds.height;
|
|
189 }
|
|
190 if (isBusy()) {
|
|
191 Rectangle bounds = getImage(0).getBounds();
|
|
192 size.x = Math.max(size.x, bounds.width);
|
|
193 size.y = Math.max(size.y, bounds.height);
|
|
194 }
|
|
195 size.x += MARGIN + MARGIN;
|
|
196 size.y += MARGIN + MARGIN;
|
|
197 return size;
|
|
198 }
|
|
199
|
|
200 /* (non-Javadoc)
|
|
201 * @see dwt.widgets.Control#forceFocus()
|
|
202 */
|
|
203 public bool forceFocus() {
|
|
204 return false;
|
|
205 }
|
|
206
|
|
207 /**
|
|
208 * Creates a thread to animate the image.
|
|
209 */
|
|
210 protected synchronized void createBusyThread() {
|
|
211 if (busyThread !is null)
|
|
212 return;
|
|
213
|
|
214 Rectangle bounds = getImage(0).getBounds();
|
|
215 Display display = getDisplay();
|
|
216 Image offScreenImage = new Image(display, bounds.width, bounds.height);
|
|
217 GC offScreenImageGC = new GC(offScreenImage);
|
|
218 busyThread = new BusyThread(bounds, display, offScreenImageGC, offScreenImage);
|
|
219 busyThread.setPriority(Thread.NORM_PRIORITY + 2);
|
|
220 busyThread.setDaemon(true);
|
|
221 busyThread.start();
|
|
222 }
|
|
223
|
|
224 public void dispose() {
|
|
225 if (busyThread !is null) {
|
|
226 busyThread.setStop(true);
|
|
227 busyThread = null;
|
|
228 }
|
|
229 super.dispose();
|
|
230 }
|
|
231
|
|
232 /**
|
|
233 * Return the image or <code>null</code>.
|
|
234 */
|
|
235 public Image getImage() {
|
|
236 return image;
|
|
237 }
|
|
238
|
|
239 /**
|
|
240 * Returns true if it is currently busy.
|
|
241 *
|
|
242 * @return bool
|
|
243 */
|
|
244 public bool isBusy() {
|
|
245 return (busyThread !is null);
|
|
246 }
|
|
247
|
|
248 /*
|
|
249 * Process the paint event
|
|
250 */
|
|
251 protected void onPaint(PaintEvent event) {
|
|
252 if (animationImage !is null && animationImage.isDisposed()) {
|
|
253 animationImage = null;
|
|
254 }
|
|
255 Rectangle rect = getClientArea();
|
|
256 if (rect.width is 0 || rect.height is 0)
|
|
257 return;
|
|
258
|
|
259 GC gc = event.gc;
|
|
260 Image activeImage = animationImage !is null ? animationImage : image;
|
|
261 if (activeImage !is null) {
|
|
262 Rectangle ibounds = activeImage.getBounds();
|
|
263 gc.drawImage(activeImage, rect.width / 2 - ibounds.width / 2,
|
|
264 rect.height / 2 - ibounds.height / 2);
|
|
265 }
|
|
266 }
|
|
267
|
|
268 /**
|
|
269 * Sets the indicators busy count up (true) or down (false) one.
|
|
270 *
|
|
271 * @param busy
|
|
272 * bool
|
|
273 */
|
|
274 public synchronized void setBusy(bool busy) {
|
|
275 if (busy) {
|
|
276 if (busyThread is null)
|
|
277 createBusyThread();
|
|
278 } else {
|
|
279 if (busyThread !is null) {
|
|
280 busyThread.setStop(true);
|
|
281 busyThread = null;
|
|
282 }
|
|
283 }
|
|
284 }
|
|
285
|
|
286 /**
|
|
287 * Set the image. The value <code>null</code> clears it.
|
|
288 */
|
|
289 public void setImage(Image image) {
|
|
290 if (image !is this.image && !isDisposed()) {
|
|
291 this.image = image;
|
|
292 redraw();
|
|
293 }
|
|
294 }
|
|
295
|
|
296
|
|
297 private ImageDescriptor createImageDescriptor(String relativePath) {
|
|
298 //DWT_TODO
|
|
299 // Bundle bundle = Platform.getBundle("dwtx.ui.forms"); //$NON-NLS-1$
|
|
300 // URL url = FileLocator.find(bundle, new Path(relativePath),null);
|
|
301 // if (url is null) return null;
|
|
302 // try {
|
|
303 // url = FileLocator.resolve(url);
|
|
304 // return ImageDescriptor.createFromURL(url);
|
|
305 // } catch (IOException e) {
|
|
306 // return null;
|
|
307 // }
|
|
308 return null;
|
|
309 }
|
|
310
|
|
311 private synchronized Image getImage(int index) {
|
|
312 if (imageCache is null) {
|
|
313 imageCache = new Image[IMAGE_COUNT];
|
|
314 }
|
|
315 if (imageCache[index] is null){
|
|
316 ImageDescriptor descriptor = createImageDescriptor("$nl$/icons/progress/ani/" ~ to!(String)(index + 1) ~ ".png"); //$NON-NLS-1$ //$NON-NLS-2$
|
|
317 imageCache[index] = descriptor.createImage();
|
|
318 }
|
|
319 return imageCache[index];
|
|
320 }
|
|
321
|
|
322 private synchronized void clearImages() {
|
|
323 if (busyThread !is null)
|
|
324 return;
|
|
325 if (imageCache !is null) {
|
|
326 for (int index = 0; index < IMAGE_COUNT; index++) {
|
|
327 if (imageCache[index] !is null && !imageCache[index].isDisposed()) {
|
|
328 imageCache[index].dispose();
|
|
329 imageCache[index] = null;
|
|
330 }
|
|
331 }
|
|
332 }
|
|
333 }
|
|
334
|
|
335 }
|