File Tree View Sample(Part3) Drag and Drop

This is the 2nd day of the JavaFX Advent calendar.JavaFX Advent Calendar 2013 - Adventar
The 1st day is 曲線のアニメーション - JavaFX in the Box by Mr.@
The 3rd day is JavaFXでめくるエフェクト!! | るーつにゃんブログ by Mr.
@

I added the following one function to the previous sample application.File Tree View Sample(Part 2) - tomoTakaの日記

  1. Copying a file by a drag-and-drop operation on TreeView.

Thanks to this articleDrag-and-Drop Feature in JavaFX Applications | JavaFX 2 Tutorials and Documentation ,it is not difficult to implement this function.

I used JavaSE8 for this sample application.

  • Screen1(select the source file)

  • Screen2(When the mouse pointer hovers over a target that fits the given drag-and-drop gesture, the target changes its background color)

  • Screen3(The dialog shows up when the target has the item same as the sources)

  • Screen4(when the mouse pointer hovers over the sources parent directory, the background color does not change.)


  • code1(Starting the Drag-and-Drop Gesture on a Source)
  1. Define a file(isLead) as a source
  2. Pass the transfer mode only COPY, because MOVE event does not happen MAX OS X 10.7?(I will try this after get a new MAC)
  3. Put a file on a dragboard, since there is no method about Path.
        cell.setOnDragDetected(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if (item != null && item.isLeaf()) {
                Dragboard db = cell.startDragAndDrop(TransferMode.COPY);
                ClipboardContent content = new ClipboardContent();
                List<File> files = Arrays.asList(cell.getTreeItem().getValue().getPath().toFile());
                content.putFiles(files);
                db.setContent(content);
                event.consume();
            }
        });
  • code2(Handling a DRAG_OVER Event on a Target)
  1. Define a directory(!isLead) as a target
  2. Accept it only if it is not dragged from the same item nor the same parent(see Screen4)
  3. Allow only copying
        cell.setOnDragOver(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if ((item != null && !item.isLeaf()) &&
                    event.getGestureSource() != cell &&
                    event.getDragboard().hasFiles()) {
                Path targetPath = cell.getTreeItem().getValue().getPath();
                PathTreeCell sourceCell = (PathTreeCell) event.getGestureSource();
                final Path sourceParentPath = sourceCell.getTreeItem().getValue().getPath().getParent();
                if (sourceParentPath.compareTo(targetPath) != 0) {
                    event.acceptTransferModes(TransferMode.COPY);
                }
            }
            event.consume();
        });
  • code3(Providing Visual Feedback by a Gesture Target)
  1. Set the condition like the code2
  2. The target changes its backgound color here(see Screen2)
        cell.setOnDragEntered(event -> {
            TreeItem<PathItem> item = cell.getTreeItem();
            if ((item != null && !item.isLeaf()) &&
                    event.getGestureSource() != cell &&
                    event.getDragboard().hasFiles()) {
                Path targetPath = cell.getTreeItem().getValue().getPath();
                PathTreeCell sourceCell = (PathTreeCell) event.getGestureSource();
                final Path sourceParentPath = sourceCell.getTreeItem().getValue().getPath().getParent();
                if (sourceParentPath.compareTo(targetPath) != 0) {
                    cell.setStyle("-fx-background-color: powderblue;");
                }                
            }
            event.consume();
        });
  • code4(Handling a DRAG_EXIT Event on a Target)
  1. The target restore its original color here
        cell.setOnDragExited(event -> {
            cell.setStyle("-fx-background-color: white");
            event.consume();
        });
  • code5(Handling a DRAG_DROPPED Event on a Target)
  1. The dialog shows up when the target has the item same as the sources.(see Screen 3, code 7)
  2. If OK button is clicked, copying of files starts
  3. Add the source item to the target directory as a leaf, if the item does not exist on the target
        cell.setOnDragDropped(event -> {
            Dragboard db = event.getDragboard();
            boolean success = false;
            if (db.hasFiles()) {
                final Path source = db.getFiles().get(0).toPath();
                final Path target = Paths.get(
                        cell.getTreeItem().getValue().getPath().toAbsolutePath().toString(),
                        source.getFileName().toString());
                if (Files.exists(target, LinkOption.NOFOLLOW_LINKS)) {                    
                    Platform.runLater(() -> {
                        BooleanProperty replaceProp = new SimpleBooleanProperty();
                        // the dialog shows up here
                        CopyModalDialog dialog = new CopyModalDialog(stage, replaceProp);
                        replaceProp.addListener((ObservableValue<? extends Boolean> ov, Boolean oldValue, Boolean newValue) -> {
                            if (newValue) {
                                // if OK button is clicked, copying of files starts
                                FileCopyTask task = new FileCopyTask(source, target);
                                service.submit(task);
                            }
                        });
                    });
                } else {
                    // Copying of files starts
                    FileCopyTask task = new FileCopyTask(source, target);
                    service.submit(task);
                    task.setOnSucceeded(value -> {
                        Platform.runLater(() -> {
                            // add the source item to the target directory as a leaf
                            TreeItem<PathItem> item = PathTreeItem.createNode(new PathItem(target));
                            cell.getTreeItem().getChildren().add(item);
                        });
                    });
                }
                success = true;
            }
            event.setDropCompleted(success);
            event.consume();
        });
  • code6(FileCopyTask.java)
  1. This class extends the Task class for running in the background.
public class FileCopyTask extends Task<Void> {
    private Path source;
    private Path target;

    public FileCopyTask(Path source, Path target) {
        this.source = source;
        this.target = target;
    }    
    @Override
    protected Void call() throws Exception {
        Files.copy(this.source, this.target, StandardCopyOption.REPLACE_EXISTING);
        return null;
    }
}
  • code7(CopyModalDialog.java)
  1. This code shows you how to create the dialog
  2. replaceProp has the true value, if you click OK button
public class CopyModalDialog {    
    public CopyModalDialog(Stage owner, final BooleanProperty replaceProp) {
        final Stage dialog = new Stage(StageStyle.UTILITY);
        dialog.initOwner(owner);
        dialog.initModality(Modality.APPLICATION_MODAL);
        GridPane root = new GridPane();
        root.setPadding(new Insets(30));
        root.setHgap(5);
        root.setVgap(10);
        Label label = new Label("The item already exists in this location. Do you want to replace it?");
        Button okButton = new Button("OK");
        okButton.setOnAction(event -> {
            replaceProp.set(true);
            dialog.hide();
        });
        Button cancelButton = new Button("Cancel");
        cancelButton.setOnAction(event -> {
            replaceProp.set(false);
            dialog.hide();
        });
        root.add(label, 0, 0, 2, 1);
        root.addRow(1, okButton, cancelButton);
        dialog.setScene(new Scene(root));
        dialog.show();
    }
}

The whole code is heretomoTaka01/FileTreeViewSample: JavaFX File ... - GitHub
I learned JavaFX a lot from the articleJava技術最前線 - JavaFX 2ではじめる、GUI開発 第14回 非同期処理:ITpro written by Mr.@
keep coding...