简体   繁体   中英

JavaFX/ScalaFX & Clipboard: Cannot copy files?

Copying files does not work:

  def toClipboard(selectedLinesOnly: Boolean = false): Unit = {
    val clipboard = Clipboard.systemClipboard
    val content = new ClipboardContent
    val items: Iterable[FileRecord] = selectedLinesOnly match {
      case true => tableView.selectionModel.value.selectedItems.toSeq
      case false => tableView.items.value
    }
    val files = items.map(_.file)
    println(s"COPY $files")
    content.putFiles(files.toSeq)
    clipboard.content = content
  }

Output: [info] COPY [SFX][/tmp/test/a.txt, /tmp/test/b.txt]

No files to paste.

  def toClipboard(selectedLinesOnly: Boolean = false): Unit = {
    val clipboard = Clipboard.systemClipboard
    val content = new ClipboardContent
    val items: Iterable[FileRecord] = selectedLinesOnly match {
      case true => tableView.selectionModel.value.selectedItems.toSeq
      case false => tableView.items.value
    }
    val files = items.map(_.file.getPath)
    println(s"COPY $files")
    content.putFilesByPath(files.toSeq)
    clipboard.content = content
  }

Output: [info] COPY [SFX][/tmp/test/a.txt, /tmp/test/b.txt]

No files to paste.

  def toClipboard(selectedLinesOnly: Boolean = false): Unit = {
    val clipboard = Clipboard.systemClipboard
    val content = new ClipboardContent
    val items: Iterable[FileRecord] = selectedLinesOnly match {
      case true => tableView.selectionModel.value.selectedItems.toSeq
      case false => tableView.items.value
    }
    val files = items.map("file://" + _.file.getPath)
    println(s"COPY $files")
    content.putFilesByPath(files.toSeq)
    clipboard.content = content
  }

Output: [info] COPY [SFX][file:///tmp/test/a.txt, file:///tmp/test/b.txt]

No files to paste.

But copying the paths to the string clipboard is possible:

  def toClipboard(selectedLinesOnly: Boolean = false): Unit = {
    val clipboard = Clipboard.systemClipboard
    val content = new ClipboardContent
    val items: Iterable[FileRecord] = selectedLinesOnly match {
      case true => tableView.selectionModel.value.selectedItems.toSeq
      case false => tableView.items.value
    }
    val files = items.map(_.file.getPath)
    println(s"COPY $files")
    content.putString(files.mkString(" "))
    clipboard.content = content
  }

Now this is in my clipboard: "/tmp/test/a.txt /tmp/test/b.txt"

But I need it in the form of files, not a string.

How can I make copying files work in my application?

I am working with OpenJFX 8 on Ubuntu.

ClipBoard doesn't have the functionality like FileUtils.copyDirectoryToDirectory and FileUtils.moveDirectoryToDirectory (aka copy or cut). Clipboard can give only path or general data. This features can be possible using Drag and Drop by Dragboard .

Dragboard of JavaFX:

During the drag-and-drop gesture, various types of data can be transferred such as text, images, URLs, files, bytes, and strings .

The javafx.scene.input.DragEvent class is the basic class used to implement the drag-and-drop gesture. For more information on particular methods and other classes in the javafx.scene.input package, see the API documentation.

For more, you can go through this tutorial: Drag-and-Drop Feature in JavaFX Applications

Dragboard JavaFX code sample: HelloDragAndDrop.java

package hellodraganddrop;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;

/**
 * Demonstrates a drag-and-drop feature.
 */
public class HelloDragAndDrop extends Application {

    @Override public void start(Stage stage) {
        stage.setTitle("Hello Drag And Drop");

        Group root = new Group();
        Scene scene = new Scene(root, 400, 200);
        scene.setFill(Color.LIGHTGREEN);

        final Text source = new Text(50, 100, "DRAG ME");
        source.setScaleX(2.0);
        source.setScaleY(2.0);

        final Text target = new Text(250, 100, "DROP HERE");
        target.setScaleX(2.0);
        target.setScaleY(2.0);

        source.setOnDragDetected(new EventHandler <MouseEvent>() {
            public void handle(MouseEvent event) {
                /* drag was detected, start drag-and-drop gesture*/
                System.out.println("onDragDetected");

                /* allow any transfer mode */
                Dragboard db = source.startDragAndDrop(TransferMode.ANY);

                /* put a string on dragboard */
                ClipboardContent content = new ClipboardContent();
                content.putString(source.getText());
                db.setContent(content);

                event.consume();
            }
        });

        target.setOnDragOver(new EventHandler <DragEvent>() {
            public void handle(DragEvent event) {
                /* data is dragged over the target */
                System.out.println("onDragOver");

                /* accept it only if it is  not dragged from the same node 
                 * and if it has a string data */
                if (event.getGestureSource() != target &&
                        event.getDragboard().hasString()) {
                    /* allow for both copying and moving, whatever user chooses */
                    event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
                }

                event.consume();
            }
        });

        target.setOnDragEntered(new EventHandler <DragEvent>() {
            public void handle(DragEvent event) {
                /* the drag-and-drop gesture entered the target */
                System.out.println("onDragEntered");
                /* show to the user that it is an actual gesture target */
                if (event.getGestureSource() != target &&
                        event.getDragboard().hasString()) {
                    target.setFill(Color.GREEN);
                }

                event.consume();
            }
        });

        target.setOnDragExited(new EventHandler <DragEvent>() {
            public void handle(DragEvent event) {
                /* mouse moved away, remove the graphical cues */
                target.setFill(Color.BLACK);

                event.consume();
            }
        });

        target.setOnDragDropped(new EventHandler <DragEvent>() {
            public void handle(DragEvent event) {
                /* data dropped */
                System.out.println("onDragDropped");
                /* if there is a string data on dragboard, read it and use it */
                Dragboard db = event.getDragboard();
                boolean success = false;
                if (db.hasString()) {
                    target.setText(db.getString());
                    success = true;
                }
                /* let the source know whether the string was successfully 
                 * transferred and used */
                event.setDropCompleted(success);

                event.consume();
            }
        });

        source.setOnDragDone(new EventHandler <DragEvent>() {
            public void handle(DragEvent event) {
                /* the drag-and-drop gesture ended */
                System.out.println("onDragDone");
                /* if the data was successfully moved, clear it */
                if (event.getTransferMode() == TransferMode.MOVE) {
                    source.setText("");
                }

                event.consume();
            }
        });

        root.getChildren().add(source);
        root.getChildren().add(target);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

Actually Java have some ways to copy file: 4 Ways to Copy File in Java

Resource Link:

  1. How do I properly handle file copy/cut & paste in javafx?

My solution which I tested successfully with Ubuntu:

FilesTransferable.scala

package myapp.clipboard

import java.awt.datatransfer._

case class FilesTransferable(files: Iterable[String]) extends Transferable {
  val clipboardString: String = "copy\n" + files.map(path => s"file://$path").mkString("\n")
  val dataFlavor = new DataFlavor("x-special/gnome-copied-files")
  def getTransferDataFlavors(): Array[DataFlavor] = {
    Seq(dataFlavor).toArray
  }
  def isDataFlavorSupported(flavor: DataFlavor): Boolean = {
    dataFlavor.equals(flavor)
  }
  @throws(classOf[UnsupportedFlavorException])
  @throws(classOf[java.io.IOException])
  def getTransferData(flavor: DataFlavor): Object = {
    if(flavor.getRepresentationClass() == classOf[java.io.InputStream]) {
      new java.io.ByteArrayInputStream(clipboardString.getBytes())
    } else {
      return null;
    }
  }
}

Clipboard.scala

package myapp.clipboard

import java.awt.Toolkit
import java.awt.datatransfer._

object Clipboard {
  def toClipboard(
    transferable: Transferable,
    lostOwnershipCallback: (Clipboard, Transferable) => Unit =
      { (clipboard: Clipboard, contents: Transferable) => Unit }
  ): Unit = {
    Toolkit.getDefaultToolkit.getSystemClipboard.setContents(
      transferable,
      new ClipboardOwner {
        def lostOwnership(clipboard: Clipboard, contents: Transferable): Unit = {
          lostOwnershipCallback(clipboard, contents)
        }
      }
    )
  }
}

Quite a lot code for such a basic functionality (and still not OS independent).

Usage Example:

val transferable = FilesTransferable(filePaths)
myapp.clipboard.Clipboard.toClipboard(transferable, { (cb, contents) =>
  println(s"lost clipboard ownership (clipboard: $cb)")
})

If it is true that there still do not exist any solutions which are part of Java-AWT/Swing/JavaFX/Scala-Swing/ScalaFX (actually, I still can't believe it), my plan is to build an easy-to-use clipboard library which supports at least OS X, Ubuntu and Windows.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM