-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathSwtDemo.java
403 lines (368 loc) · 17.1 KB
/
SwtDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/****************************************************************************
**
** This demo file is part of yFiles for JavaFX 3.6.
**
** Copyright (c) 2000-2023 by yWorks GmbH, Vor dem Kreuzberg 28,
** 72070 Tuebingen, Germany. All rights reserved.
**
** yFiles demo files exhibit yFiles for JavaFX functionalities. Any redistribution
** of demo files in source code or binary form, with or without
** modification, is not permitted.
**
** Owners of a valid software license for a yFiles for JavaFX version that this
** demo is shipped with are allowed to use the demo source code as basis
** for their own yFiles for JavaFX powered applications. Use of such programs is
** governed by the rights and conditions as set out in the yFiles for JavaFX
** license agreement.
**
** THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
** NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***************************************************************************/
package integration.swt;
import com.yworks.yfiles.graph.GraphItemTypes;
import com.yworks.yfiles.graph.IModelItem;
import com.yworks.yfiles.graph.INode;
import com.yworks.yfiles.graph.styles.GroupNodeStyle;
import com.yworks.yfiles.view.GraphControl;
import com.yworks.yfiles.view.input.GraphEditorInputMode;
import com.yworks.yfiles.view.input.ICommand;
import com.yworks.yfiles.view.input.PopulateItemContextMenuEventArgs;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.embed.swt.FXCanvas;
import javafx.embed.swt.SWTFXUtils;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.input.TransferMode;
import javafx.scene.paint.Color;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import toolkit.DemoStyles;
import toolkit.Themes;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* This demo shows how to integrate yFiles for JavaFX in a Standard Widget Toolkit (SWT) application.
* <ul>
* <li> A toolbar that provides SWT buttons to change the zoom level of the GraphControl that is a JavaFX control
* as well as SWT buttons for undo/redo functionality. </li>
* <li> A right click on a node shown in the GraphControl opens a SWT context menu and allows the user to delete the
* clicked node from the GraphControl. </li>
* <li> On the left side a SWT palette offers nodes with different styles that can be dragged into the GraphControl.
* </li>
* </ul>
* <p>
* In JavaFX, the code that creates and manipulates JavaFX classes runs in the JavaFX User thread. In SWT, code that
* creates and manipulates SWT widgets runs in the event loop thread. When JavaFX is embedded in SWT, these two threads
* are the same. This means that there are no restrictions when calling methods defined in one toolkit from the other.
* </p>
* <p>
* To simplify matters the drag data is provided in text format: we convert an enum constant to text when dragging
* starts and convert the text back to the enum constant when the node is dropped. Have a look at
* <a href="http://git.eclipse.org/c/platform/eclipse.platform.swt.git/tree/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet319.java">snippet 319</a>
* from the Eclipse resource web page that shows how to implement drag and drop with a custom data transfer type from
* SWT to AWT/Swing.
* </p>
* <p>
* Note that you need to add a <code>swt.jar</code> to the classpath/libraries/dependencies to run this demo. The
* <code>swt.jar</code> must be the same bit version as the JVM on which the demo runs.The <code>swt.jar</code> can be
* downloaded form the <a href="http://www.eclipse.org/swt/" target="_blank">SWT homepage</a>. You can use the Ant build
* script located in the current folder that downloads the <code>swt.jar</code> on demand and run this demo.
* </p>
* <p>
* Note that you need to add <code>javafx-swt.jar</code> to the classpath/libraries/dependencies to run this demo with
* Java 9.
* </p>
* <p>
* Note that the IDE IntelliJ IDEA marks the usages of {@link FXCanvas} as incorrect. This seems to be a problem of the
* IDE. The demo can be compiled and run anyway.
* </p>
*/
public class SwtDemo {
private static final int PALETTE_WIDTH = 180;
private static final int PALETTE_HEIGHT = 35;
private GraphControl graphControl;
// handles drop events from a SWT component
private SwtNodeDropInputMode dropMode;
/**
* Initializes and shows the user interface.
*/
private void initialize() {
// initialize the SWT part of the user interface
Display display = new Display();
Shell shell = new Shell(display);
initializeSwt(shell);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
/**
* Initializes the SWT part of the user interface.
* @param shell the SWT window
*/
private void initializeSwt(Shell shell) {
// create a window with title and icon
shell.setText("SWT Integration Demo - yFiles for JavaFX");
shell.setImage(loadImage(shell.getDisplay(), "logo_128.png"));
shell.setLayout(new GridLayout(3, false));
shell.setSize(1400, 850);
// create a (still empty) toolbar on the top
// the toolbar is populated later on when the application's GraphControl
// instance is available
ToolBar toolBar = new ToolBar(shell, SWT.HORIZONTAL);
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.horizontalSpan = 3;
toolBar.setLayoutData(gridData);
// create a palette on the left side
Table palette = new Table(shell, SWT.SINGLE);
gridData = new GridData(GridData.FILL_VERTICAL);
gridData.widthHint = PALETTE_WIDTH;
palette.setLayoutData(gridData);
// initialize the Java FX part of the user interface
// create a component to embed JavaFX content into SWT applications
FXCanvas fxCanvas = new FXCanvas(shell, SWT.NONE);
initializeFX(fxCanvas);
gridData = new GridData(GridData.FILL_BOTH);
fxCanvas.setLayoutData(gridData);
// the toolbar can be populated with buttons for undo/redo functionality and
// changing the GraphControl's zoom level as soon as the application's
// GraphControl instance has ben created
populateToolBar(toolBar);
populatePalette(palette);
// create a Text control showing the help text on the right side
Text helpPane = createHelpPane(shell);
gridData = new GridData(GridData.FILL_VERTICAL);
gridData.widthHint = 300;
helpPane.setLayoutData(gridData);
}
/**
* Populates the palette containing the nodes which may be dragged into the GraphComponent.
* @param palette the table to populate with nodes
*/
private Table populatePalette(Table palette) {
for (NodeTemplate template : NodeTemplate.values()) {
// create a table row for each node template
TableItem item = new TableItem(palette, SWT.CENTER);
// show an image and a description of the node template in the palette
ImageData imageData = SWTFXUtils.fromFXImage(template.createImage(), null);
if (imageData != null) {
item.setImage(new Image(palette.getDisplay(), imageData));
}
item.setText(template.description());
// set the node template as data of the table row
// we use this later to generate a drag data
item.setData(template.name());
}
// specify the size of the table cells (seems to be necessary for SWT 4.4 on Linux)
palette.addListener(SWT.MeasureItem, event -> {
event.width = Math.max(PALETTE_WIDTH, event.width);
event.height = Math.max(PALETTE_HEIGHT, event.height);
});
// enable the palette as a drag source
DragSource dragSource = new DragSource(palette, DND.DROP_COPY);
// provide drag data in text format
Transfer[] transfers = new Transfer[]{TextTransfer.getInstance()};
dragSource.setTransfer(transfers);
dragSource.addDragListener(new DragSourceAdapter() {
@Override
public void dragStart(DragSourceEvent event) {
// SWT does not allow us to get the drag data while dragging over. However,
// since we need the data for the preview, we store it in a separate variable.
// set the name of the enum constant of the selected template as drag data
dropMode.setDragData(palette.getSelection()[0].getData());
// since our GraphControl has its own preview we prevent the image of
// the template to be shown as preview by setting the event image to a
// blank image
event.image = new Image(palette.getDisplay(), 1, 1);
}
@Override
public void dragSetData(DragSourceEvent event) {
// set the name of the enum constant of the selected template as drag data
event.data = palette.getSelection()[0].getData();
}
@Override
public void dragFinished(DragSourceEvent event) {
event.data = null;
dropMode.setDragData(null);
}
});
return palette;
}
/**
* Populates the toolbar with buttons for undo/redo functionality and changing
* the given GraphControl's zoom level.
* @param toolBar the toolbar to add the buttons to
*/
private void populateToolBar(ToolBar toolBar) {
addButton(toolBar, "plus2-16.png", ICommand.INCREASE_ZOOM, null, graphControl);
addButton(toolBar, "minus2-16.png", ICommand.DECREASE_ZOOM, null, graphControl);
addButton(toolBar, "fit2-16.png", ICommand.FIT_GRAPH_BOUNDS, null, graphControl);
new ToolItem(toolBar, SWT.SEPARATOR);
addButton(toolBar, "undo-16.png", ICommand.UNDO, null, graphControl);
addButton(toolBar, "redo-16.png", ICommand.REDO, null, graphControl);
}
/**
* Adds a {@link ToolItem} wired up with the given {@link com.yworks.yfiles.view.input.ICommand} to the given toolbar.
* @param toolBar the toolbar to add the button to
* @param icon the icon to show on the button
* @param command the command to execute when the button is hit
* @param parameter the parameter for the execution of the command
* @param target the target to execute the command on
*/
private static void addButton(ToolBar toolBar, String icon, ICommand command, Object parameter, Control target) {
ToolItem button = new ToolItem(toolBar, SWT.PUSH);
button.setImage(loadImage(toolBar.getDisplay(), icon));
button.setToolTipText(command.toString());
// execute the command when the button is selected
button.addListener(SWT.Selection, event -> command.execute(parameter, target));
// enable/disable depending on the command's state
command.addCanExecuteChangedListener((source, args) -> {
// avoid accessing SWT control if they are disposed (when exiting the application)
if (!button.isDisposed()) {
button.setEnabled(command.canExecute(parameter, target));
}
});
}
/**
* Initializes the JavaFX part of the user interface.
* @param fxCanvas the canvas to add the scene to
*/
private void initializeFX(FXCanvas fxCanvas) {
graphControl = new GraphControl();
Scene scene = new Scene(graphControl);
fxCanvas.setScene(scene);
// Initialize the input mode and enable context menu for nodes.
GraphEditorInputMode inputMode = new GraphEditorInputMode();
inputMode.setContextMenuInputMode(new SwtContextMenuInputMode(fxCanvas));
inputMode.setContextMenuItems(GraphItemTypes.NODE);
inputMode.addPopulateItemContextMenuListener(this::populateNodeContextMenu);
// activate drag and drop from the palette
dropMode = new SwtNodeDropInputMode();
// we identify the group nodes during a drag by the type of its style
dropMode.setIsGroupNodePredicate(node -> node.getStyle() instanceof GroupNodeStyle);
dropMode.setTransferMode(TransferMode.COPY);
// Enables preview during drag operations.
// Keep in mind that, prior to JDK 8u40, due to an issue in the DnD handling in FXCanvas (RT-37906),
// using the preview feature of the yFiles library may cause problems when dropping a dragged node
// outside the GraphControl.
// To prevent this, the native SWT preview, which shows a simple image, can also be used at this point.
// To do this, just disable the option here instead (setShowPreview(false);) and adjust the
// TableHandler.dragStart method.
dropMode.setPreviewEnabled(true);
dropMode.setEnabled(true);
inputMode.setNodeDropInputMode(dropMode);
// enable grouping and undo support
inputMode.setGroupingOperationsAllowed(true);
graphControl.setInputMode(inputMode);
// load a sample graph and configure the default node style after the GraphControl has got its size
graphControl.heightProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
if (newValue.doubleValue() > 0) {
graphControl.heightProperty().removeListener(this);
initializeGraph();
}
}
});
}
/**
* Populates the context menu for nodes.
*/
private void populateNodeContextMenu(Object source, PopulateItemContextMenuEventArgs<IModelItem> args) {
if (args.getItem() instanceof INode) {
INode node = (INode) args.getItem();
// The return type of the following method is Object to be able to support context menus of different Java GUI
// toolkits. By default, this is an instance of JavaFX ContextMenu, but our SwtContextMenuInputMode specifies
// a SWT Menu to be used as context menu control.
Menu contextMenu = (Menu) args.getMenu();
// add menu item to delete the clicked node on SWT event thread
addDeleteNodeMenuItem(contextMenu, node);
args.setHandled(true);
}
}
/**
* Adds a {@link org.eclipse.swt.widgets.MenuItem} to the given {@link org.eclipse.swt.widgets.Menu context menu}
* that enables the user to delete the given node.
* @param contextMenu the context menu to add the menu item
* @param node the node to delete with the context menu
*/
private void addDeleteNodeMenuItem(Menu contextMenu, INode node) {
MenuItem deleteItem = new MenuItem(contextMenu, SWT.PUSH);
deleteItem.setText("Delete node");
deleteItem.setImage(loadImage(contextMenu.getDisplay(), "delete3-16.png"));
deleteItem.addListener(SWT.Selection, e -> graphControl.getGraph().remove(node));
}
/**
* Creates the pane that shows a help text about the demo.
* @param shell the SWT window
*/
private static Text createHelpPane(Shell shell) {
Text text = new Text(shell, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY);
try {
URI file = SwtDemo.class.getResource("resources/help.html").toURI();
String help = new String(Files.readAllBytes(Paths.get(file)));
text.setText(help);
} catch (Exception e) {
text.setText("Could not resolve help text. Please ensure that your build process or IDE adds the " +
"help.html file to the class path.");
}
return text;
}
/**
* Enables grouping and undo support, loads a sample graph and initializes the default node style.
*/
private void initializeGraph() {
// set the default node styles
DemoStyles.initDemoStyles(graphControl.getGraph(), Themes.PALETTE_LIGHTBLUE);
// load the sample graph
try {
String filename = getClass().getResource("resources/example.graphml").toExternalForm();
graphControl.importFromGraphML(filename);
} catch (IOException e) {
e.printStackTrace();
}
// enable undo support
graphControl.getGraph().setUndoEngineEnabled(true);
}
/**
* Loads an {@link org.eclipse.swt.graphics.Image} of the given resource file.
*/
private static Image loadImage(Device device, String fileName) {
return new Image(device, SwtDemo.class.getResourceAsStream("/resources/" + fileName));
}
public static void main(String[] args) {
(new SwtDemo()).initialize();
}
}