1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation. Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javafx.draganddrop;
  26 
  27 import java.io.File;
  28 import java.io.Serializable;
  29 import java.util.ArrayList;
  30 import java.util.Collections;
  31 import java.util.EnumSet;
  32 import java.util.HashMap;
  33 import java.util.HashSet;
  34 import java.util.LinkedList;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.Set;
  38 import java.util.concurrent.Callable;
  39 import java.util.concurrent.ConcurrentHashMap;
  40 import javafx.beans.value.ChangeListener;
  41 import javafx.beans.value.ObservableValue;
  42 import javafx.event.ActionEvent;
  43 import javafx.event.EventHandler;
  44 import javafx.factory.ControlsFactory;
  45 import javafx.factory.NodeFactory;
  46 import javafx.factory.Panes;
  47 import javafx.factory.Shapes;
  48 import javafx.geometry.Orientation;
  49 import javafx.scene.Node;
  50 import javafx.scene.Parent;
  51 import javafx.scene.Scene;
  52 import javafx.scene.control.Button;
  53 import javafx.scene.control.CheckBox;
  54 import javafx.scene.control.ChoiceBox;
  55 import javafx.scene.control.Label;
  56 import javafx.scene.control.Separator;
  57 import javafx.scene.control.TextField;
  58 import javafx.scene.image.Image;
  59 import javafx.scene.image.ImageView;
  60 import javafx.scene.input.Clipboard;
  61 import javafx.scene.input.ClipboardContent;
  62 import javafx.scene.input.DataFormat;
  63 import javafx.scene.input.DragEvent;
  64 import javafx.scene.input.Dragboard;
  65 import javafx.scene.input.MouseEvent;
  66 import javafx.scene.input.TransferMode;
  67 import javafx.scene.layout.HBox;
  68 import javafx.scene.layout.Pane;
  69 import javafx.scene.layout.StackPane;
  70 import javafx.scene.layout.VBox;
  71 import javafx.scene.text.Text;
  72 import javafx.stage.Stage;
  73 import javafx.util.Pair;
  74 import static org.junit.Assert.*;
  75 import test.javaclient.shared.InteroperabilityApp;
  76 import static test.javaclient.shared.TestUtil.isEmbedded;
  77 import test.javaclient.shared.Utils;
  78 
  79 public class DragDropWithControls extends InteroperabilityApp {
  80 
  81     final public static String PARAMETER_ONLY_SOURCE_STAGE = "onlySource";
  82     final public static String PARAMETER_ONLY_TARGET_STAGE = "onlyTarget";
  83     final static String TITLE_TARGET_STAGE = "Target";
  84     final static String TITLE_SOURCE_STAGE = "Source";
  85     final static String ID_NODE_CHOOSER = "nodeChooser";
  86     final static String ID_DRAG_SOURCE = "from";
  87     final static String ID_DRAG_TARGET = "to";
  88     final static String ID_TO_CLIPBOARD_BUTTON = "toClipboardButton";
  89     final static String ID_FROM_CLIPBOARD_BUTTON = "fromClipboardButton";
  90     final static String ID_PLAIN_TEXT = "PLAIN_TEXT";
  91     final static String ID_HTML = "HTML";
  92     final static String ID_IMAGE = "IMAGE";
  93     final static String ID_RTF = "RTF";
  94     final static String ID_URL = "URL";
  95     final static String ID_FILES = "Files";
  96     final static String ID_CUSTOM_BYTES = "Custom (bytes)";
  97     final static String ID_CUSTOM_STRING = "Custom (string)";
  98     final static String ID_CUSTOM_CLASS = "Custom (class)";
  99     final static String ID_RECEIVED_IMAGE = "ReceivedImage";
 100     final static String ID_SRC_IMAGE = "SrcImage";
 101     public final static DataFormat DF_CUSTOM_BYTES = new DataFormat("dndwithcontrols.custom.bytes");
 102     public final static DataFormat DF_CUSTOM_STRING = new DataFormat("dndwithcontrols.custom.string");
 103     public final static DataFormat DF_CUSTOM_CLASS = new DataFormat("dndwithcontrols.custom.class");
 104     final static String CONTENT_PLAIN_TEXT = "Hello!!!";
 105     final static String CONTENT_URL = "http://www.oracle.com";
 106     private static Image CONTENT_IMAGE;
 107     final static String CONTENT_HTML =
 108             "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
 109             + "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
 110             + "<head></head>"
 111             + "<body><i><b>Hello!</b></i></body>"
 112             + "</html>";
 113     final static String CONTENT_RTF = "{\\rtf1 This is some {\\b bold} text.\\par}";
 114     final static String CONTENT_CUSTOM_STRING = "Hello Custom String!";
 115     final static List<File> CONTENT_FILES = new LinkedList<File>();
 116     private Scene leftScene;
 117     private Scene rightScene;
 118 
 119     static {
 120         CONTENT_FILES.add(new File("./content/index.html"));
 121         System.setProperty("prism.lcdtext", "false");
 122     }
 123     final static byte[] CONTENT_CUSTOM_BYTES =
 124             new byte[]{1, 2, 3, 4};
 125     final static SerializableClass CONTENT_CUSTOM_CLASS = new SerializableClass(new SerializableClass(100, 100.9), 10, 10.9);
 126     final static Map<DataFormat, Object> receivedContent = new ConcurrentHashMap<DataFormat, Object>();
 127     public final static Map<DataFormat, Pair<String, Object>> dataFormatToCheckBoxID = new HashMap<DataFormat, Pair<String, Object>>(10);
 128 
 129     static {
 130 
 131         dataFormatToCheckBoxID.put(DataFormat.PLAIN_TEXT, new Pair<String, Object>(ID_PLAIN_TEXT, CONTENT_PLAIN_TEXT));
 132         dataFormatToCheckBoxID.put(DataFormat.HTML, new Pair<String, Object>(ID_HTML, CONTENT_HTML));
 133         dataFormatToCheckBoxID.put(DataFormat.RTF, new Pair<String, Object>(ID_RTF, CONTENT_RTF));
 134         dataFormatToCheckBoxID.put(DataFormat.URL, new Pair<String, Object>(ID_URL, CONTENT_URL));
 135         dataFormatToCheckBoxID.put(DataFormat.FILES, new Pair<String, Object>(ID_FILES, CONTENT_FILES));
 136         dataFormatToCheckBoxID.put(DF_CUSTOM_BYTES, new Pair<String, Object>(ID_CUSTOM_BYTES, CONTENT_CUSTOM_BYTES));
 137         dataFormatToCheckBoxID.put(DF_CUSTOM_STRING, new Pair<String, Object>(ID_CUSTOM_STRING, CONTENT_CUSTOM_STRING));
 138         dataFormatToCheckBoxID.put(DF_CUSTOM_CLASS, new Pair<String, Object>(ID_CUSTOM_CLASS, CONTENT_CUSTOM_CLASS));
 139         InteroperabilityApp.isEmbeddedFullScreenMode = false;
 140     }
 141 
 142     @Override
 143     protected Scene getScene() {
 144         if (null == CONTENT_IMAGE) {
 145             CONTENT_IMAGE = new Image(DragDropWithControls.class.getResource("JavaFX.png").toExternalForm());
 146             dataFormatToCheckBoxID.put(DataFormat.IMAGE, new Pair<String, Object>(ID_IMAGE, CONTENT_IMAGE));
 147         }
 148 
 149         Parameters params = getParameters();
 150         parameters = params == null ? Collections.<String>emptyList() : params.getRaw();
 151 
 152         if (parameters.size() > 1 && parameters.get(1).equals(PARAMETER_ONLY_SOURCE_STAGE)) {
 153             leftScene = prepareSourceStage(stage);
 154             return leftScene;
 155         } else if (parameters.size() > 1 && parameters.get(1).equals(PARAMETER_ONLY_TARGET_STAGE)) {
 156             rightScene = prepareTargetStage(secondaryStage);
 157             return rightScene;
 158         } else {
 159             leftScene = prepareSourceStage(stage);
 160             secondaryStage = new Stage();
 161             rightScene = prepareTargetStage(secondaryStage);
 162             return leftScene;
 163         }
 164     }
 165 
 166     @Override
 167     protected String getFirstStageName() {
 168         return TITLE_SOURCE_STAGE;
 169     }
 170 
 171     @Override
 172     protected StageInfo getSecondaryScene() {
 173         return new StageInfo(new Callable<Scene>() {
 174             public Scene call() throws Exception {
 175                 return rightScene;
 176             }
 177         }, isEmbedded() ? 510 : 670 + (Utils.isLinux() ? 70 : 0), 30, TITLE_TARGET_STAGE);
 178     }
 179 
 180     @Override
 181     protected boolean hasSecondaryScene() {
 182         return leftScene != null && rightScene != null;
 183     }
 184 
 185     private static class SerializableClass implements Serializable {
 186 
 187         public SerializableClass() {
 188         }
 189 
 190         public SerializableClass(SerializableClass clazz, int i, double d) {
 191             this(i, d);
 192             this.clazz = clazz;
 193         }
 194 
 195         public SerializableClass(int i, double d) {
 196             this.i = i;
 197             this.d = d;
 198         }
 199         SerializableClass clazz;
 200         int i;
 201         double d;
 202 
 203         @Override
 204         public String toString() {
 205             return "Class{" + " clazz=" + clazz + ", i=" + i + ", d=" + d + "}";
 206         }
 207 
 208         @Override
 209         public boolean equals(Object obj) {
 210             if (obj == null) {
 211                 return false;
 212             }
 213             if (getClass() != obj.getClass()) {
 214                 return false;
 215             }
 216             final SerializableClass other = (SerializableClass) obj;
 217             if (this.clazz != other.clazz && (this.clazz == null || !this.clazz.equals(other.clazz))) {
 218                 return false;
 219             }
 220             if (this.i != other.i) {
 221                 return false;
 222             }
 223             if (Double.doubleToLongBits(this.d) != Double.doubleToLongBits(other.d)) {
 224                 return false;
 225             }
 226             return true;
 227         }
 228 
 229         @Override
 230         public int hashCode() {
 231             int hash = 5;
 232             hash = 17 * hash + (this.clazz != null ? this.clazz.hashCode() : 0);
 233             hash = 17 * hash + this.i;
 234             hash = 17 * hash + (int) (Double.doubleToLongBits(this.d) ^ (Double.doubleToLongBits(this.d) >>> 32));
 235             return hash;
 236         }
 237     }
 238     Pane sourceControlPane = new StackPane() {
 239         {
 240             setStyle("-fx-border-color:green;-fx-border-width: 2px;");
 241         }
 242     };
 243     Pane targetControlPane = new StackPane() {
 244         {
 245             setStyle("-fx-border-color:red;-fx-border-width: 2px;");
 246         }
 247     };
 248     Pane transferedContentPane = new VBox();
 249     public Set<TransferMode> sourceModes = EnumSet.noneOf(TransferMode.class);
 250     public Set<TransferMode> targetModes = EnumSet.noneOf(TransferMode.class);
 251     Set<DataFormat> sourceFormats = new HashSet<DataFormat>();
 252     Set<DataFormat> targetFormats = new HashSet<DataFormat>();
 253     static List<DragEvents> eventList = new ArrayList<DragEvents>();
 254     Text log = new Text();
 255     CheckBox useCustomViewCB;
 256     List<String> messages = new LinkedList<String>();
 257     List<String> parameters;
 258 
 259     private Scene prepareTargetStage(Stage stageTarget) {
 260         final Scene localScene = new Scene(createRightPane(), isEmbedded() ? 420 : 630, 700);
 261         localScene.getRoot().setId(TITLE_TARGET_STAGE);
 262 
 263         if (stageTarget != null) {
 264             stageTarget.setTitle(TITLE_TARGET_STAGE);
 265             stageTarget.setScene(localScene);
 266             stageTarget.setX(680);
 267             stageTarget.setY(30);
 268         }
 269         return localScene;
 270     }
 271 
 272     private Scene prepareSourceStage(final Stage stageSource) {
 273         final Scene localScene = new Scene(createLeftPane(), isEmbedded() ? 420 : 580, 700);
 274         localScene.getRoot().setId(TITLE_SOURCE_STAGE);
 275 
 276         if (stageSource != null) {
 277             stageSource.setTitle(TITLE_SOURCE_STAGE);
 278             stageSource.setScene(localScene);
 279             stageSource.setX(0);
 280             stageSource.setY(30);
 281         }
 282 
 283         return localScene;
 284     }
 285 
 286     private Parent createLeftPane() {
 287         VBox lbox = new VBox(10);
 288         lbox.getChildren().addAll(sourceControlPane, new Separator(),
 289                 new Text("Source control type:"),
 290                 createControlCombo(sourceControlPane, true),
 291                 new Text("Source transfer modes:"),
 292                 createTMSelect(sourceModes));
 293 
 294         VBox rbox = new VBox(10);
 295         Button b = new Button("Put to clipboard");
 296         b.setId(ID_TO_CLIPBOARD_BUTTON);
 297         b.setOnAction(new EventHandler<ActionEvent>() {
 298             public void handle(ActionEvent t) {
 299                 Clipboard.getSystemClipboard().setContent(prepareClipboardContent());
 300             }
 301         });
 302         rbox.getChildren().addAll(new Text("Data formats:"),
 303                 createFormatSelect(sourceFormats),
 304                 b);
 305 
 306         HBox hbox = new HBox(10);
 307         hbox.getChildren().addAll(lbox, new Separator(Orientation.VERTICAL), rbox);
 308 
 309         final Text fileHdr = new Text("Files to drag (1):");
 310         final Text fileNames = new Text();
 311         showFileNames(fileNames);
 312         final TextField tb = new TextField("Put full path here");
 313         final Button add = new Button("Add");
 314         add.setOnAction(new EventHandler<ActionEvent>() {
 315             @Override
 316             public void handle(ActionEvent event) {
 317                 File f = new File(tb.getText());
 318                 if (f.exists()) {
 319                     CONTENT_FILES.add(f);
 320                     tb.setText("");
 321                     fileHdr.setText("Files to drag (" + CONTENT_FILES.size() + ")");
 322                     showFileNames(fileNames);
 323                     log("Added file " + f.getPath());
 324                 } else {
 325                     log("File doesn't exist: " + f.getPath());
 326                 }
 327             }
 328         });
 329         final Button clear = new Button("Clear");
 330         clear.setOnAction(new EventHandler<ActionEvent>() {
 331             @Override
 332             public void handle(ActionEvent event) {
 333                 CONTENT_FILES.clear();
 334                 fileHdr.setText("Files to drag (0)");
 335                 log("File list cleared");
 336             }
 337         });
 338 
 339         HBox btns = new HBox();
 340         btns.getChildren().addAll(add, clear);
 341 
 342         useCustomViewCB = new CheckBox("Use custom drag view.");
 343 
 344         VBox box = new VBox(10);
 345         ImageView i = new ImageView();
 346         i.setImage(CONTENT_IMAGE);
 347         i.setId(ID_SRC_IMAGE);
 348         box.getChildren().addAll(hbox, new Separator(), fileHdr,
 349                 fileNames, tb, btns,
 350                 useCustomViewCB, new Text("Image: "),
 351                 i,
 352                 log);
 353         if (parameters.size() > 0) {
 354             box.setStyle("-fx-background-color: " + parameters.get(0) + ";");
 355         }
 356         return box;
 357     }
 358 
 359     private void showFileNames(Text text) {
 360         StringBuilder sb = new StringBuilder();
 361         for (int i = 0; i < CONTENT_FILES.size(); i++) {
 362             File file = CONTENT_FILES.get(i);
 363             sb.append(i + 1).append(": ").append(file.toURI()).append("\n");
 364         }
 365         text.setText(sb.toString());
 366     }
 367 
 368     private Parent createRightPane() {
 369         HBox hbox = new HBox(10);
 370 
 371         VBox lbox = new VBox(10);
 372         lbox.getChildren().addAll(targetControlPane, new Separator(), new Text("Target control type:"),
 373                 createControlCombo(targetControlPane, false), new Text("Target transfer modes:"), createTMSelect(targetModes));
 374 
 375         VBox rbox = new VBox(10);
 376         Button b = new Button("paste from clipboard");
 377         b.setId(ID_FROM_CLIPBOARD_BUTTON);
 378         b.setOnAction(new EventHandler<ActionEvent>() {
 379             public void handle(ActionEvent t) {
 380                 getDataFromClipboard(Clipboard.getSystemClipboard());
 381             }
 382         });
 383         rbox.getChildren().addAll(new Text("Data formats:"), createFormatSelect(targetFormats), b);
 384 
 385         VBox content = new VBox(10);
 386         content.getChildren().addAll(new Text("Transfered content:"), transferedContentPane);
 387 
 388         hbox.getChildren().addAll(lbox, new Separator(Orientation.VERTICAL), rbox,
 389                 new Separator(Orientation.VERTICAL), content);
 390         if (parameters.size() > 0) {
 391             hbox.setStyle("-fx-background-color: " + parameters.get(0) + ";");
 392         }
 393         return hbox;
 394     }
 395 
 396     private Node createControlCombo(final Pane sourceControlPane, final boolean source) {
 397         ChoiceBox<NodeFactory> cb = new ChoiceBox<NodeFactory>();
 398         cb.setId(ID_NODE_CHOOSER);
 399         cb.getItems().addAll(ControlsFactory.filteredValues());
 400         cb.getItems().addAll(Shapes.values());
 401         cb.getItems().addAll(Panes.values());
 402 
 403         cb.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<NodeFactory>() {
 404             @Override
 405             public void changed(ObservableValue<? extends NodeFactory> ov, NodeFactory t, NodeFactory t1) {
 406                 Node ctrl = null;
 407                 ctrl = t1.createNode();
 408                 if (ctrl instanceof ImageView) {
 409                     ctrl.setPickOnBounds(true);
 410                 }
 411                 if (source) {
 412                     ctrl.setId(ID_DRAG_SOURCE);
 413                 } else {
 414                     ctrl.setId(ID_DRAG_TARGET);
 415                 }
 416                 eventList.clear();
 417                 sourceControlPane.getChildren().clear();
 418                 sourceControlPane.getChildren().add(ctrl);
 419 
 420                 final Node control = ctrl;
 421 
 422                 if (source) {
 423                     System.out.println("Source control : " + control);
 424                     control.setOnDragDetected(new EventHandler<MouseEvent>() {
 425                         @Override
 426                         public void handle(MouseEvent event) {
 427                             eventList.add(DragEvents.DRAG_DETECTED);
 428                             System.out.println("DragDetected");
 429                             Dragboard db = control.startDragAndDrop(sourceModes.toArray(new TransferMode[sourceModes.size()]));
 430                             if (db == null) {
 431                                 log("Cannot start drag and drop.");
 432                                 return;
 433                             }
 434                             System.out.println("db " + db);
 435                             final ClipboardContent prepareClipboardContent = prepareClipboardContent();
 436                             System.out.println("prepareClipboardContent " + prepareClipboardContent);
 437                             db.setContent(prepareClipboardContent);
 438 
 439                             if (useCustomViewCB.isSelected()) {
 440                                 final int const1 = 100;
 441                                 final int const2 = 100;
 442                                 db.setDragView(CONTENT_IMAGE, const1, const2);
 443 
 444                                 //Assertions here normally prevent DnD, and don't crash the app.
 445                                 assertEquals("OffsetX equals", db.getDragViewOffsetX(), const1, 0);
 446                                 assertEquals("OffsetY equals", db.getDragViewOffsetY(), const2, 0);
 447                                 assertEquals("Image correct get", db.getDragView(), CONTENT_IMAGE);
 448                             }
 449 
 450                             event.consume();
 451                         }
 452                     });
 453 
 454                     control.setOnDragDone(new EventHandler<DragEvent>() {
 455                         @Override
 456                         public void handle(DragEvent event) {
 457                             System.out.println("DragDone");
 458                             if (event.getTransferMode() == null) {
 459                                 eventList.add(DragEvents.EMPTY_DRAG_DONE);
 460                             }
 461 
 462                             eventList.add(DragEvents.DRAG_DONE);
 463                             log("Transfer done: " + event.getTransferMode());
 464                             log("");
 465                         }
 466                     });
 467                 } else {
 468                     System.out.println("Target Control : " + control);
 469                     control.setOnDragEntered(new EventHandler<DragEvent>() {
 470                         public void handle(DragEvent t) {
 471                             System.out.println("DragEntered");
 472                             eventList.add(DragEvents.DRAG_ENTER);
 473                         }
 474                     });
 475                     control.setOnDragOver(new EventHandler<DragEvent>() {
 476                         @Override
 477                         public void handle(DragEvent event) {
 478                             System.out.println("DragOver");
 479                             eventList.add(DragEvents.DRAG_OVER);
 480                             Dragboard db = event.getDragboard();
 481                             for (DataFormat df : targetFormats) {
 482                                 if (db.hasContent(df)) {
 483                                     event.acceptTransferModes(
 484                                             targetModes.toArray(new TransferMode[targetModes.size()]));
 485                                     return;
 486                                 }
 487                             }
 488                         }
 489                     });
 490 
 491                     control.setOnDragDropped(new EventHandler<DragEvent>() {
 492                         @Override
 493                         public void handle(DragEvent event) {
 494                             System.out.println("DragDropped");
 495                             eventList.add(DragEvents.DRAG_DROPPED);
 496                             Dragboard db = event.getDragboard();
 497                             boolean gotData = getDataFromClipboard(db);
 498                             event.setDropCompleted(gotData);
 499                         }
 500                     });
 501                 }
 502             }
 503         });
 504 
 505         cb.getSelectionModel().select(0);
 506         return cb;
 507     }
 508 
 509     private ClipboardContent prepareClipboardContent() {
 510         ClipboardContent content = new ClipboardContent();
 511         if (sourceFormats.contains(DataFormat.PLAIN_TEXT)) {
 512             log("Source is putting string on dragboard");
 513             content.putString(CONTENT_PLAIN_TEXT);
 514         }
 515         if (sourceFormats.contains(DataFormat.URL)) {
 516             log("Source is putting URL on dragboard");
 517             content.putUrl(CONTENT_URL);
 518         }
 519         if (sourceFormats.contains(DataFormat.IMAGE)) {
 520             log("Source is putting image on dragboard");
 521             content.putImage(CONTENT_IMAGE);
 522         }
 523         if (sourceFormats.contains(DataFormat.HTML)) {
 524             log("Source is putting HTML on dragboard");
 525             content.putHtml(CONTENT_HTML);
 526         }
 527         if (sourceFormats.contains(DataFormat.RTF)) {
 528             log("Source is putting RTF on dragboard");
 529             content.putRtf(CONTENT_RTF);
 530         }
 531         if (sourceFormats.contains(DF_CUSTOM_BYTES)) {
 532             log("Source is putting custom four bytes on dragboard");
 533             content.put(DF_CUSTOM_BYTES, CONTENT_CUSTOM_BYTES);
 534         }
 535         if (sourceFormats.contains(DF_CUSTOM_STRING)) {
 536             log("Source is putting custom four bytes on dragboard");
 537             content.put(DF_CUSTOM_STRING, CONTENT_CUSTOM_STRING);
 538         }
 539         if (sourceFormats.contains(DF_CUSTOM_CLASS)) {
 540             log("Source is putting custom class on dragboard");
 541             content.put(DF_CUSTOM_CLASS, CONTENT_CUSTOM_CLASS);
 542         }
 543         if (sourceFormats.contains(DataFormat.FILES)) {
 544             log("Source is putting two files on dragboard");
 545             content.putFiles(CONTENT_FILES);
 546         }
 547         return content;
 548     }
 549 
 550     private boolean getDataFromClipboard(Clipboard cb) {
 551         boolean gotData = false;
 552         receivedContent.clear();
 553         transferedContentPane.getChildren().clear();
 554         if (targetFormats.contains(DataFormat.PLAIN_TEXT) && cb.hasString()) {
 555             receivedContent.put(DataFormat.PLAIN_TEXT, cb.getString());
 556             transferedContentPane.getChildren().addAll(new WrappedLabel("String: " + cb.getString()));
 557             log("Dropped string: " + cb.getString());
 558             gotData = true;
 559         }
 560         if (targetFormats.contains(DataFormat.HTML) && cb.hasHtml()) {
 561             receivedContent.put(DataFormat.HTML, cb.getHtml());
 562             transferedContentPane.getChildren().addAll(new WrappedLabel("Html: " + cb.getHtml()));
 563             log("Dropped HTML: " + cb.getHtml());
 564             gotData = true;
 565         }
 566         if (targetFormats.contains(DataFormat.RTF) && cb.hasRtf()) {
 567             receivedContent.put(DataFormat.RTF, cb.getRtf());
 568             transferedContentPane.getChildren().addAll(new WrappedLabel("Rtf: " + cb.getRtf()));
 569             log("Dropped RTF: " + cb.getRtf());
 570             gotData = true;
 571         }
 572         if (targetFormats.contains(DataFormat.URL) && cb.hasUrl()) {
 573             receivedContent.put(DataFormat.URL, cb.getUrl());
 574             transferedContentPane.getChildren().addAll(new WrappedLabel("Url: " + cb.getUrl()));
 575             log("Dropped URL: " + cb.getUrl());
 576             gotData = true;
 577         }
 578         if (targetFormats.contains(DataFormat.IMAGE) && cb.hasImage()) {
 579             receivedContent.put(DataFormat.IMAGE, cb.getImage());
 580             ImageView i = new ImageView(cb.getImage());
 581             i.setId(ID_RECEIVED_IMAGE);
 582             transferedContentPane.getChildren().addAll(new Text("Image: "), i);
 583             log("Dropped image: " + cb.getImage());
 584             gotData = true;
 585         }
 586         if (targetFormats.contains(DataFormat.FILES) && cb.hasFiles()) {
 587             log("Dropped files:");
 588             receivedContent.put(DataFormat.FILES, cb.getFiles());
 589             transferedContentPane.getChildren().addAll(new WrappedLabel("Files: "));
 590             for (File f : cb.getFiles()) {
 591                 transferedContentPane.getChildren().addAll(new Label("File: " + f) {
 592                     {
 593                         setWrapText(true);
 594                     }
 595                 });
 596                 log("   " + f.getPath());
 597             }
 598             transferedContentPane.getChildren().addAll(new Separator());
 599             gotData = true;
 600         }
 601         if (targetFormats.contains(DF_CUSTOM_BYTES) && cb.hasContent(DF_CUSTOM_BYTES)) {
 602             byte[] b = (byte[]) cb.getContent(DF_CUSTOM_BYTES);
 603             receivedContent.put(DF_CUSTOM_BYTES, b);
 604             transferedContentPane.getChildren().addAll(new WrappedLabel("bytes: " + b[0] + ", " + b[1] + ", " + b[2] + ", " + b[3]));
 605             log("Dropped custom bytes: " + b[0] + ", " + b[1] + ", " + b[2] + ", " + b[3]);
 606             gotData = true;
 607         }
 608         if (targetFormats.contains(DF_CUSTOM_STRING) && cb.hasContent(DF_CUSTOM_STRING)) {
 609             receivedContent.put(DF_CUSTOM_STRING, cb.getContent(DF_CUSTOM_STRING));
 610             String s = (String) cb.getContent(DF_CUSTOM_STRING);
 611             transferedContentPane.getChildren().addAll(new WrappedLabel("customString: " + s));
 612             log("Dropped custom string: " + s);
 613             gotData = true;
 614         }
 615         if (targetFormats.contains(DF_CUSTOM_CLASS) && cb.hasContent(DF_CUSTOM_CLASS)) {
 616             receivedContent.put(DF_CUSTOM_CLASS, cb.getContent(DF_CUSTOM_CLASS));
 617             String s = cb.getContent(DF_CUSTOM_CLASS).toString();
 618             transferedContentPane.getChildren().addAll(new WrappedLabel("customClass: " + s));
 619             log("Dropped custom class: " + s);
 620             gotData = true;
 621         }
 622 
 623         return gotData;
 624     }
 625 
 626     private Node createTMSelect(final Set<TransferMode> tms) {
 627         VBox box = new VBox();
 628 
 629         for (final TransferMode tm : TransferMode.values()) {
 630             CheckBox cb = new CheckBox(tm.toString());
 631             cb.selectedProperty().addListener(new ChangeListener<Boolean>() {
 632                 @Override
 633                 public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
 634                     if (t1) {
 635                         tms.add(tm);
 636                     } else {
 637                         tms.remove(tm);
 638                     }
 639                 }
 640             });
 641             if (tm == TransferMode.COPY) {
 642                 cb.selectedProperty().set(true);
 643             }
 644             box.getChildren().add(cb);
 645         }
 646 
 647         return box;
 648     }
 649 
 650     private Node createFormatSelect(final Set<DataFormat> dataFormats) {
 651         VBox box = new VBox();
 652 
 653 
 654 
 655         for (final Map.Entry<DataFormat, Pair<String, Object>> df : dataFormatToCheckBoxID.entrySet()) {
 656             CheckBox cb = new CheckBox(df.getValue().getKey());
 657             cb.selectedProperty().addListener(new ChangeListener<Boolean>() {
 658                 @Override
 659                 public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
 660                     if (t1) {
 661                         dataFormats.add(df.getKey());
 662                     } else {
 663                         dataFormats.remove(df.getKey());
 664                     }
 665                 }
 666             });
 667             box.getChildren().add(cb);
 668         }
 669 
 670         ((CheckBox) box.getChildren().get(0)).selectedProperty().set(true);
 671 
 672         return box;
 673     }
 674 
 675     private void log(String text) {
 676         System.out.println(text);
 677 
 678         messages.add(text);
 679         if (messages.size() > 15) {
 680             messages.remove(0);
 681         }
 682 
 683         StringBuilder sb = new StringBuilder();
 684         for (String msg : messages) {
 685             sb.append(msg).append("\n");
 686         }
 687         log.setText(sb.toString());
 688     }
 689 
 690     public static void main(String[] args) {
 691         Utils.launch(DragDropWithControls.class, args);
 692     }
 693 
 694     private static class WrappedLabel extends VBox {
 695 
 696         public WrappedLabel(String string) {
 697             getChildren().add(new Label(string) {
 698                 {
 699                     setWrapText(true);
 700                 }
 701             });
 702             getChildren().add(new Separator());
 703         }
 704     }
 705 }