1 /*******************************************************************************
2  * Copyright (c) 2000, 2016 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.examples.graphics;
15 
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.MissingResourceException;
23 import java.util.ResourceBundle;
24 
25 import org.eclipse.swt.SWT;
26 import org.eclipse.swt.SWTException;
27 import org.eclipse.swt.graphics.Color;
28 import org.eclipse.swt.graphics.Device;
29 import org.eclipse.swt.graphics.GC;
30 import org.eclipse.swt.graphics.Image;
31 import org.eclipse.swt.graphics.Path;
32 import org.eclipse.swt.graphics.Pattern;
33 import org.eclipse.swt.graphics.Point;
34 import org.eclipse.swt.graphics.Rectangle;
35 import org.eclipse.swt.layout.FormAttachment;
36 import org.eclipse.swt.layout.FormData;
37 import org.eclipse.swt.layout.FormLayout;
38 import org.eclipse.swt.layout.RowLayout;
39 import org.eclipse.swt.widgets.Canvas;
40 import org.eclipse.swt.widgets.Composite;
41 import org.eclipse.swt.widgets.Control;
42 import org.eclipse.swt.widgets.Display;
43 import org.eclipse.swt.widgets.Group;
44 import org.eclipse.swt.widgets.Menu;
45 import org.eclipse.swt.widgets.MessageBox;
46 import org.eclipse.swt.widgets.Sash;
47 import org.eclipse.swt.widgets.Shell;
48 import org.eclipse.swt.widgets.Text;
49 import org.eclipse.swt.widgets.ToolBar;
50 import org.eclipse.swt.widgets.ToolItem;
51 import org.eclipse.swt.widgets.Tree;
52 import org.eclipse.swt.widgets.TreeItem;
53 
54 /**
55  * This class is the main class of the graphics application. Various "tabs" are
56  * created and made visible by this class.
57  */
58 public class GraphicsExample {
59 
60 	Composite parent;
61 	GraphicsTab[] tabs;				// tabs to be found in the application
62 	GraphicsTab tab;				// the current tab
63 	GraphicsBackground background;	// used to store information about the background
64 
65 	ToolBar toolBar;				// toolbar that contains backItem and dbItem
66 	Tree tabList;					// tree structure of tabs
67 	Text tabDesc;					// multi-line text widget that displays a tab description
68 	Sash hSash, vSash;
69 	Canvas canvas;
70 	Composite tabControlPanel;
71 	ToolItem backItem, dbItem;		// background, double buffer items
72 	Menu backMenu;					// background menu item
73 
74 	List<Image> resources;			// stores resources that will be disposed
75 	List<GraphicsTab> tabs_in_order;		// stores GraphicsTabs in the order that they appear in the tree
76 	boolean animate = true;			// whether animation should happen
77 
78 	static boolean advanceGraphics, advanceGraphicsInit;
79 
80 	static final int MARGIN = 5;
81 	static final int SASH_SPACING = 1;
82 	static final int TIMER = 30;
83 	static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle("examples_graphics"); //$NON-NLS-1$
84 
85 /*
86  * Default constructor is needed so that example launcher can create an instance.
87  */
GraphicsExample()88 public GraphicsExample() {
89 	super();
90 }
91 
GraphicsExample(final Composite parent)92 public GraphicsExample(final Composite parent) {
93 	this.parent = parent;
94 	resources = new ArrayList<>();
95 	createControls(parent);
96 	setTab(tab);
97 	startAnimationTimer();
98 }
99 
checkAdvancedGraphics()100 boolean checkAdvancedGraphics() {
101 	if (advanceGraphicsInit) return advanceGraphics;
102 	advanceGraphicsInit = true;
103 	Display display = parent.getDisplay();
104 	try {
105 		Path path = new Path(display);
106 		path.dispose();
107 	} catch (SWTException e) {
108 		Shell shell = display.getActiveShell(), newShell = null;
109 		if (shell == null) shell = newShell = new Shell(display);
110 		MessageBox dialog = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
111 		dialog.setText(RESOURCE_BUNDLE.getString("Warning")); //$NON-NLS-1$
112 		dialog.setMessage(RESOURCE_BUNDLE.getString("LibNotFound")); //$NON-NLS-1$
113 		dialog.open();
114 		if (newShell != null) newShell.dispose();
115 		return false;
116 	}
117 	return advanceGraphics = true;
118 }
119 
createControls(final Composite parent)120 void createControls(final Composite parent) {
121 	tabs = createTabs();
122 	createToolBar(parent);
123 	createTabList(parent);
124 	hSash = new Sash(parent, SWT.HORIZONTAL);
125 	createTabDesc(parent);
126 	vSash = new Sash(parent, SWT.VERTICAL);
127 	createCanvas(parent);
128 	createControlPanel(parent);
129 
130 	FormData data;
131 	FormLayout layout = new FormLayout();
132 	parent.setLayout(layout);
133 
134 	data = new FormData();
135 	data.left = new FormAttachment(0, MARGIN);
136 	data.top = new FormAttachment(0, MARGIN);
137 	data.right = new FormAttachment(100, -MARGIN);
138 	toolBar.setLayoutData(data);
139 
140 	data = new FormData();
141 	data.left = new FormAttachment(0, MARGIN);
142 	data.top = new FormAttachment(toolBar, MARGIN);
143 	data.right = new FormAttachment(vSash, -SASH_SPACING);
144 	data.bottom = new FormAttachment(hSash, -SASH_SPACING);
145 	tabList.setLayoutData(data);
146 
147 	data = new FormData();
148 	data.left = new FormAttachment(0, MARGIN);
149 	int offset = parent.getBounds().height - tabDesc.computeSize(SWT.DEFAULT, tabDesc.getLineHeight() * 10).y;
150 	data.top = new FormAttachment(null, offset);
151 	data.right = new FormAttachment(vSash, -SASH_SPACING);
152 	hSash.setLayoutData(data);
153 
154 	data = new FormData();
155 	data.left = new FormAttachment(0, MARGIN);
156 	data.top = new FormAttachment(hSash, SASH_SPACING);
157 	data.right = new FormAttachment(vSash, -SASH_SPACING);
158 	data.bottom = new FormAttachment(100, -MARGIN);
159 	tabDesc.setLayoutData(data);
160 
161 	data = new FormData();
162 	data.left = new FormAttachment(null, tabList.computeSize(SWT.DEFAULT, SWT.DEFAULT).x + 50);
163 	data.top = new FormAttachment(toolBar, MARGIN);
164 	data.bottom = new FormAttachment(100, -MARGIN);
165 	vSash.setLayoutData(data);
166 
167 	data = new FormData();
168 	data.left = new FormAttachment(vSash, SASH_SPACING);
169 	data.top = new FormAttachment(toolBar, MARGIN);
170 	data.right = new FormAttachment(100, -MARGIN);
171 	data.bottom = new FormAttachment(tabControlPanel);
172 	canvas.setLayoutData(data);
173 
174 	data = new FormData();
175 	data.left = new FormAttachment(vSash, SASH_SPACING);
176 	data.right = new FormAttachment(100, -MARGIN);
177 	data.bottom = new FormAttachment(100, -MARGIN);
178 	tabControlPanel.setLayoutData(data);
179 
180 	vSash.addListener(SWT.Selection, event -> {
181 		Rectangle rect = hSash.getParent().getClientArea();
182 		event.x = Math.min (Math.max (event.x, 60), rect.width - 60);
183 		if (event.detail != SWT.DRAG) {
184 			FormData data1 = (FormData)vSash.getLayoutData();
185 			data1.left.offset = event.x;
186 			vSash.requestLayout();
187 			animate = true;
188 		} else {
189 			animate = false;
190 		}
191 	});
192 	hSash.addListener (SWT.Selection, event -> {
193 		Rectangle rect = vSash.getParent().getClientArea();
194 		event.y = Math.min (Math.max (event.y, tabList.getLocation().y + 60), rect.height - 60);
195 		if (event.detail != SWT.DRAG) {
196 			FormData data1 = (FormData)hSash.getLayoutData();
197 			data1.top.offset = event.y;
198 			hSash.requestLayout();
199 		}
200 	});
201 }
202 
createCanvas(Composite parent)203 void createCanvas(Composite parent) {
204 	int style = SWT.NO_BACKGROUND;
205 	if (dbItem.getSelection()) style |= SWT.DOUBLE_BUFFERED;
206 	canvas = new Canvas(parent, style);
207 	canvas.addListener(SWT.Paint, event -> {
208 		GC gc = event.gc;
209 		Rectangle rect = canvas.getClientArea();
210 		Device device = gc.getDevice();
211 		Pattern pattern = null;
212 		if (background.getBgColor1() != null) {
213 			if (background.getBgColor2() != null) { // gradient
214 				pattern = new Pattern(device, 0, 0, rect.width,
215 						rect.height,
216 						background.getBgColor1(),
217 						background.getBgColor2());
218 				gc.setBackgroundPattern(pattern);
219 			} else {	// solid color
220 				gc.setBackground(background.getBgColor1());
221 			}
222 		} else if (background.getBgImage() != null) {		// image
223 			pattern = new Pattern(device, background.getBgImage());
224 			gc.setBackgroundPattern(pattern);
225 		}
226 		gc.fillRectangle(rect);
227 		GraphicsTab tab = getTab();
228 		if (tab != null) tab.paint(gc, rect.width, rect.height);
229 		if (pattern != null) pattern.dispose();
230 	});
231 }
232 
recreateCanvas()233 void recreateCanvas() {
234 	if (dbItem.getSelection() == ((canvas.getStyle() & SWT.DOUBLE_BUFFERED) != 0)) return;
235 	Object data = canvas.getLayoutData();
236 	if (canvas != null) canvas.dispose();
237 	createCanvas(parent);
238 	canvas.setLayoutData(data);
239 	parent.layout(true, true);
240 }
241 
242 /**
243  * Creates the control panel
244  * @param parent
245  */
createControlPanel(Composite parent)246 void createControlPanel(Composite parent) {
247 	Group group;
248 	tabControlPanel = group = new Group(parent, SWT.NONE);
249 	group.setText(getResourceString("Settings")); //$NON-NLS-1$
250 	tabControlPanel.setLayout(new RowLayout());
251 }
252 
createToolBar(final Composite parent)253 void createToolBar(final Composite parent) {
254 	final Display display = parent.getDisplay();
255 
256 	toolBar = new ToolBar(parent, SWT.FLAT);
257 
258 	ToolItem  back = new ToolItem(toolBar, SWT.PUSH);
259 	back.setText(getResourceString("Back")); //$NON-NLS-1$
260 	back.setImage(loadImage(display, "back.gif")); //$NON-NLS-1$
261 
262 	back.addListener(SWT.Selection, event -> {
263 		int index = tabs_in_order.indexOf(tab) - 1;
264 		if (index < 0)
265 			index = tabs_in_order.size() - 1;
266 		setTab(tabs_in_order.get(index));
267 	});
268 
269 	ToolItem  next = new ToolItem(toolBar, SWT.PUSH);
270 	next.setText(getResourceString("Next")); //$NON-NLS-1$
271 	next.setImage(loadImage(display, "next.gif")); //$NON-NLS-1$
272 	next.addListener(SWT.Selection, event -> {
273 		int index = (tabs_in_order.indexOf(tab) + 1)%tabs_in_order.size();
274 		setTab(tabs_in_order.get(index));
275 	});
276 
277 	ColorMenu colorMenu = new ColorMenu();
278 
279 	// setup items to be contained in the background menu
280 	colorMenu.setColorItems(true);
281 	colorMenu.setPatternItems(checkAdvancedGraphics());
282 	colorMenu.setGradientItems(checkAdvancedGraphics());
283 
284 	// create the background menu
285 	backMenu = colorMenu.createMenu(parent, gb -> {
286 		background = gb;
287 		backItem.setImage(gb.getThumbNail());
288 		if (canvas != null) canvas.redraw();
289 	});
290 
291 	// initialize the background to the first item in the menu
292 	background = (GraphicsBackground)backMenu.getItem(0).getData();
293 
294 	// background tool item
295 	backItem = new ToolItem(toolBar, SWT.PUSH);
296 	backItem.setText(getResourceString("Background")); //$NON-NLS-1$
297 	backItem.setImage(background.getThumbNail());
298 	backItem.addListener(SWT.Selection, event -> {
299 		if (event.widget == backItem) {
300 			final ToolItem toolItem = (ToolItem) event.widget;
301 			final ToolBar  toolBar = toolItem.getParent();
302 			Rectangle toolItemBounds = toolItem.getBounds();
303 			Point point = toolBar.toDisplay(new Point(toolItemBounds.x, toolItemBounds.y));
304 			backMenu.setLocation(point.x, point.y + toolItemBounds.height);
305 			backMenu.setVisible(true);
306 		}
307 	});
308 
309 	// double buffer tool item
310 	dbItem = new ToolItem(toolBar, SWT.CHECK);
311 	dbItem.setText(getResourceString("DoubleBuffer")); //$NON-NLS-1$
312 	dbItem.setImage(loadImage(display, "db.gif")); //$NON-NLS-1$
313 	dbItem.addListener(SWT.Selection, event -> setDoubleBuffered(dbItem.getSelection()));
314 }
315 
316 /**
317  * Creates and returns a thumbnail image.
318  *
319  * @param device
320  * 			a device
321  * @param name
322  * 			filename of the image
323  */
createThumbnail(Device device, String name)324 static Image createThumbnail(Device device, String name) {
325 	Image image = new Image(device, name);
326 	Rectangle src = image.getBounds();
327 	Image result = null;
328 	if (src.width != 16 || src.height != 16) {
329 		result = new Image(device, 16, 16);
330 		GC gc = new GC(result);
331 		Rectangle dest = result.getBounds();
332 		gc.drawImage(image, src.x, src.y, src.width, src.height, dest.x, dest.y, dest.width, dest.height);
333 		gc.dispose();
334 	}
335 	if (result != null) {
336 		image.dispose();
337 		return result;
338 	}
339 	return image;
340 }
341 
342 /**
343  * Creates an image based on a gradient pattern made up of two colors.
344  *
345  * @param device - The Device
346  * @param color1 - The first color used to create the image
347  * @param color2 - The second color used to create the image
348  *
349  * */
createImage(Device device, Color color1, Color color2, int width, int height)350 static Image createImage(Device device, Color color1, Color color2, int width, int height) {
351 	Image image = new Image(device, width, height);
352 	GC gc = new GC(image);
353 	Rectangle rect = image.getBounds();
354 	Pattern pattern = new Pattern(device, rect.x, rect.y, rect.width - 1,
355 				rect.height - 1, color1, color2);
356 	gc.setBackgroundPattern(pattern);
357 	gc.fillRectangle(rect);
358 	gc.drawRectangle(rect.x, rect.y, rect.width - 1, rect.height - 1);
359 	gc.dispose();
360 	pattern.dispose();
361 	return image;
362 }
363 
364 /**
365  * Creates an image based on the color provided and returns it.
366  *
367  * @param device - The Device
368  * @param color - The color used to create the image
369  *
370  * */
createImage(Device device, Color color)371 static Image createImage(Device device, Color color) {
372 	Image image = new Image(device, 16, 16);
373 	GC gc = new GC(image);
374 	gc.setBackground(color);
375 	Rectangle rect = image.getBounds();
376 	gc.fillRectangle(rect);
377 	if (color.equals(device.getSystemColor(SWT.COLOR_BLACK))) {
378 		gc.setForeground(device.getSystemColor(SWT.COLOR_WHITE));
379 	}
380 	gc.drawRectangle(rect.x, rect.y, rect.width - 1, rect.height - 1);
381 	gc.dispose();
382 	return image;
383 }
384 
createTabList(Composite parent)385 void createTabList(Composite parent) {
386 	tabList = new Tree(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
387 	Arrays.sort(tabs, (tab0, tab1) -> tab0.getText().compareTo(tab1.getText()));
388 	HashSet<String> set = new HashSet<>();
389 	for (GraphicsTab tab : tabs) {
390 		set.add(tab.getCategory());
391 	}
392 	String[] categories = new String[set.size()];
393 	set.toArray(categories);
394 	Arrays.sort(categories);
395 	for (String text : categories) {
396 		TreeItem item = new TreeItem(tabList, SWT.NONE);
397 		item.setText(text);
398 	}
399 	tabs_in_order = new ArrayList<>();
400 	TreeItem[] items = tabList.getItems();
401 	for (TreeItem item : items) {
402 		for (GraphicsTab tab : tabs) {
403 			if (item.getText().equals(tab.getCategory())) {
404 				TreeItem item1 = new TreeItem(item, SWT.NONE);
405 				item1.setText(tab.getText());
406 				item1.setData(tab);
407 				tabs_in_order.add(tab);
408 			}
409 		}
410 	}
411 	tabList.addListener(SWT.Selection, event -> {
412 		TreeItem item = (TreeItem)event.item;
413 		if (item != null) {
414 			GraphicsTab gt = (GraphicsTab)item.getData();
415 			if (gt == tab) return;
416 			setTab((GraphicsTab)item.getData());
417 		}
418 	});
419 }
420 
421 /**
422  * Creates the multi-line text widget that will contain the tab description.
423  * */
createTabDesc(Composite parent)424 void createTabDesc(Composite parent) {
425 	tabDesc = new Text(parent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.WRAP | SWT.BORDER);
426 	tabDesc.setEditable(false);
427 	tabDesc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
428 }
429 
430 /**
431  * Initializes the GraphicsTab instances that will be contained in GraphicsExample.
432  * */
createTabs()433 GraphicsTab[] createTabs() {
434 	return new GraphicsTab[] {
435 		new LineTab(this),
436 		new StarPolyTab(this),
437 		tab = new IntroTab(this),
438 		new BlackHoleTab(this),
439 		new AlphaTab(this),
440 		new BallTab(this),
441 		new CountDownTab(this),
442 		new CurvesSWTTab(this),
443 		new CurvesTab(this),
444 		new CustomFontTab(this),
445 		new FontBounceTab(this),
446 		new GradientTab(this),
447 		new ImageTransformTab(this),
448 		new ShapesTab(this),
449 		new MazeTab(this),
450 		new RGBTab(this),
451 		new SpiralTab(this),
452 		new CardsTab(this),
453 		new LineCapTab(this),
454 		new InterpolationTab(this),
455 		new PathClippingTab(this),
456 		new PathClippingAnimTab(this),
457 		new LineStyleTab(this),
458 		new LineJoinTab(this),
459 		new RegionClippingTab(this),
460 		new CustomAlphaTab(this),
461 		new TextAntialiasTab(this),
462 		new GraphicAntialiasTab(this),
463 		new ImageFlipTab(this),
464 		new ImageScaleTab(this),
465 		new PathTab(this),
466 	};
467 }
468 
469 /**
470  * Disposes all resources created by the receiver.
471  */
dispose()472 public void dispose() {
473 	if (tabs != null) {
474 		for (GraphicsTab tab : tabs) {
475 			tab.dispose();
476 		}
477 	}
478 	tabs = null;
479 	if (resources != null) {
480 		for (Image image : resources) {
481 			if (image != null) {
482 				image.dispose();
483 			}
484 		}
485 	}
486 	resources = null;
487 
488 	if (backMenu != null) {
489 		backMenu.dispose();
490 		backMenu = null;
491 	}
492 }
493 
findItemByData(TreeItem[] items, Object data)494 TreeItem findItemByData(TreeItem[] items, Object data) {
495 	for (TreeItem item : items) {
496 		if (item.getData() == data) return item;
497 		item = findItemByData(item.getItems(), data);
498 		if (item != null) return item;
499 	}
500 	return null;
501 }
502 
503 /**
504  * Gets the current tab.
505  */
getTab()506 public GraphicsTab getTab() {
507 	return tab;
508 }
509 
510 /**
511  * Gets a string from the resource bundle.
512  * We don't want to crash because of a missing String.
513  * Returns the key if not found.
514  */
getResourceString(String key)515 static String getResourceString(String key) {
516 	try {
517 		return RESOURCE_BUNDLE.getString(key);
518 	} catch (MissingResourceException e) {
519 		return key;
520 	} catch (NullPointerException e) {
521 		return "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
522 	}
523 }
524 
loadImage(Device device, Class<GraphicsExample> clazz, String string)525 static Image loadImage (Device device, Class<GraphicsExample> clazz, String string) {
526 	InputStream stream = clazz.getResourceAsStream (string);
527 	if (stream == null) return null;
528 	Image image = null;
529 	try {
530 		image = new Image (device, stream);
531 	} catch (SWTException ex) {
532 	} finally {
533 		try {
534 			stream.close ();
535 		} catch (IOException ex) {}
536 	}
537 	return image;
538 }
539 
loadImage(Device device, String name)540 Image loadImage(Device device, String name) {
541 	Image image = loadImage(device, GraphicsExample.class, name);
542 	if (image != null) resources.add(image);
543 	return image;
544 }
545 
open(final Display display)546 public Shell open(final Display display) {
547 	Shell shell = new Shell(display);
548 	shell.setText(getResourceString("GraphicsExample")); //$NON-NLS-1$
549 	final GraphicsExample example = new GraphicsExample(shell);
550 	shell.addListener(SWT.Close, event -> example.dispose());
551 	shell.open();
552 	return shell;
553 }
554 
555 /**
556  * Redraws the current tab.
557  */
redraw()558 public void redraw() {
559 	canvas.redraw();
560 }
561 
562 /**
563  * Sets wheter the canvas is double buffered or not.
564  */
setDoubleBuffered(boolean doubleBuffered)565 public void setDoubleBuffered(boolean doubleBuffered) {
566 	dbItem.setSelection(doubleBuffered);
567 	recreateCanvas();
568 }
569 
570 /**
571  * Grabs input focus.
572  */
setFocus()573 public void setFocus() {
574 	tabList.setFocus();
575 }
576 
577 /**
578  * Sets the current tab.
579  */
setTab(GraphicsTab tab)580 public void setTab(GraphicsTab tab) {
581 	Control[] children = tabControlPanel.getChildren();
582 	for (Control control: children) {
583 		control.dispose();
584 	}
585 	if (this.tab != null) this.tab.dispose();
586 	this.tab = tab;
587 	if (tab != null) {
588 		setDoubleBuffered(tab.getDoubleBuffered());
589 		tab.createControlPanel(tabControlPanel);
590 		tabDesc.setText(tab.getDescription());
591 	} else {
592 		tabDesc.setText("");
593 	}
594 	FormData data = (FormData)tabControlPanel.getLayoutData();
595 	children = tabControlPanel.getChildren();
596 	if (children.length != 0) {
597 		data.top = null;
598 	} else {
599 		data.top = new FormAttachment(100, -MARGIN);
600 	}
601 	parent.layout(true, true);
602 	if (tab != null) {
603 		TreeItem[] selection = tabList.getSelection();
604 		if (selection.length == 0 || selection[0].getData() != tab) {
605 			TreeItem item = findItemByData(tabList.getItems(), tab);
606 			if (item != null) tabList.setSelection(new TreeItem[]{item});
607 		}
608 	}
609 	canvas.redraw();
610 }
611 
612 /**
613  * Starts the animation if the animate flag is set.
614  */
startAnimationTimer()615 void startAnimationTimer() {
616 	final Display display = parent.getDisplay();
617 	display.timerExec(TIMER, new Runnable() {
618 		@Override
619 		public void run() {
620 			if (canvas.isDisposed()) return;
621 			int timeout = TIMER;
622 			GraphicsTab tab = getTab();
623 			if (tab instanceof AnimatedGraphicsTab) {
624 				AnimatedGraphicsTab animTab = (AnimatedGraphicsTab) tab;
625 				if (animate && animTab.getAnimation()) {
626 					Rectangle rect = canvas.getClientArea();
627 					animTab.next(rect.width, rect.height);
628 					canvas.redraw();
629 					canvas.update();
630 				}
631 				timeout =  animTab.getAnimationTime();
632 			}
633 			display.timerExec(timeout, this);
634 		}
635 	});
636 }
637 
main(String[] args)638 public static void main(String[] args) {
639 	Display display = new Display();
640 	Shell shell = new GraphicsExample().open(display);
641 	while (shell != null && !shell.isDisposed()) {
642 		if (!display.readAndDispatch())
643 			display.sleep();
644 	}
645 	display.dispose();
646 }
647 }
648