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