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