JavaFx Drag and Drop a file INTO a program - java

Hey there community I was wondering if is possible to create a program that allows for the user to Drag a file from anywhere on there hard drive (the desktop, documents folder, videos folder) and drop it into the window of the program.
I am creating a media player and I want to be able to play a video by dragging and dropping a MP4 into the window. Do I need to store the file in a variable, or just the location of the file into a variable. Also, it is important I keep support for cross platform.
I am using JavaFx with java 7 update 79 jdk.
Thanks in advance.

Here is a simple drag and drop example that just sets the file name and location. Drag files to it and it shows their name and location. Once you know that it should be a completely separate matter to actually play the file. It is primarily taken from Oracle's documentation: https://docs.oracle.com/javafx/2/drag_drop/jfxpub-drag_drop.htm
A minimal implementation needs two EventHandler s set OnDragOver and OnDragDropped.
public class DragAndDropTest extends Application {
#Override
public void start(Stage primaryStage) {
Label label = new Label("Drag a file to me.");
Label dropped = new Label("");
VBox dragTarget = new VBox();
dragTarget.getChildren().addAll(label,dropped);
dragTarget.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
if (event.getGestureSource() != dragTarget
&& event.getDragboard().hasFiles()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
event.consume();
}
});
dragTarget.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasFiles()) {
dropped.setText(db.getFiles().toString());
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
}
});
StackPane root = new StackPane();
root.getChildren().add(dragTarget);
Scene scene = new Scene(root, 500, 250);
primaryStage.setTitle("Drag Test");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

When working with Drag and Drop events, you could try the following:
Obtain a Dragboard-object of the DragEvent and work with the method getFiles:
private void handleDragDropped(DragEvent event){
Dragboard db = event.getDragboard();
File file = db.getFiles().get(0);
}

I solved this by adding two event handlers. One for DragDropped event and the other for DragOver event.
e.g:
#FXML
void handleFileOverEvent(DragEvent event)
{
Dragboard db = event.getDragboard();
if (db.hasFiles())
{
event.acceptTransferModes(TransferMode.COPY);
}
else
{
event.consume();
}
}
#FXML
void handleFileDroppedEvent(DragEvent event)
{
Dragboard db = event.getDragboard();
File file = db.getFiles().get(0);
handleSelectedFile(file);
}
Else it did not work for me, dragging the file over my pane, didn't trigger anything.

Related

Dragging with invisible Dragboard in JavaFX 8

I borrowed code from here.
Here is the code:
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 {
public static void main(String[] args) {
Application.launch(args);
}
#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();
}
}
When drag detected it creates bended paper on the scene, and "DROP HERE" text catches it.
Is it possible to make it invisible or transparent? I tried any type of TransferMode but it is still existing.
The only solution came to my mind was just adding transparent png to DragBoard, but when drag detected on "DROP HERE" text some kind of plus icon is appearing below the cursor.
Are there any proper ways of implementing this feature?

JavaFX KeyEvents during Drag & Drop operation

I need to know whether a certain key is down while performing a drag & drop operation.
So I tried to use setOnKeyPressed / setOnKeyReleased of a Scene with a combination of HashMap, but I have a problem with this approach:
Imagine a scenario that one drags & drops a TableView item to somewhere while holding Control down. Now if I display a dialog at the end of the drop, while still holding Control down, the setOnKeyReleased is never called with this approach... as the Dialog is the one receiving the key released event.
How could I fix this?
Hope I understand your question here is a possible solution(work with any key):
public class Main extends Application {
SimpleBooleanProperty isKeyPress = new SimpleBooleanProperty(false);
#Override
public void start(Stage primaryStage) throws Exception{
Parent window = new VBox();
((VBox) window).getChildren().add(new Label("example of small window:"));
primaryStage.setTitle("example");
Scene scene=new Scene(window);
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println("Press");
isKeyPress.set(true);
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Information Dialog");
alert.setHeaderText(null);
alert.setContentText("I have a great message for you!");
Scene alertScene = alert.getDialogPane().getScene();
alertScene.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println("Released on dialog");
isKeyPress.set(false);
}
});
alert.showAndWait();
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println("Released");
isKeyPress.set(false);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
output exmple:
Press
Released on dialog
From your comment the goal is to change the behavior of the drag and drop depending on whether or not Ctrl is down. When it is do a copy operation, otherwise do a move operation. You do not need to deal with KeyEvents to implement this behavior. Instead, you would determine whether to copy or move in the onDragDetected handler. The onDragDetected handler uses a MouseEvent which has methods for querying the status of modifier keys—such as isControlDown(). Using this, we can specify what transfer modes are allowed based on the modifier keys.
Node node = ...;
node.setOnDragDetected(event -> {
Dragboard board;
if (event.isControlDown()) {
board = node.startDragAndDrop(TransferMode.COPY);
} else {
board = node.startDragAndDrop(TransferMode.MOVE);
}
// add contents to Dragboard
});
Note it may be more cross-platform to use isShortcutDown().

JavaFX - Block user from changing stages without using MODAL

I have an application that looks like the following:
When a user clicks on the deck of cards, it opens up a new Stage.
This stage can be closed in one of two ways:
Right click the stage.
Click outside of the stage (it has a evenhandler for when it loses focus).
However, sometimes I NEED the user to select one or more cards from the deck using this window. I do not want to allow him to close the window until he has selected at least one card. This means I had to use MODAL to stop him from being able to access the stage underneath (My Applicaiton). The problem with MODAL is now he can never leave the window like he could before by clicking outside the stage, even when I want him to be able to. He is now only able to leave through right clicking. I could add a button but I'd really rather not.
I hope I explained my problem well enough. What would you guys recommend I do? Is there a way I could somehow block the user from going back to the previous stage without MODAL? I'm also not able to change Modality after the Stage has been shown, so that won't work.
Thanks!
The idea is to use the onCloseRequestProperty property of your pop-up Stage.
Called when there is an external request to close this Window. The
installed event handler can prevent window closing by consuming the
received event.
With this property you can interrupt the closing of the Stage if a condition (in your case at lest one card is selected) is not met by calling consume on the WindowEvent.
Note: As the documentation states: it is only valid if the request is external, so if you call the close method of the Stage, the attached listener will be not executed. As a solution rather than calling this method you can fire the WindowEvent.WINDOW_CLOSE_REQUEST event manually.
Example:
public class PopUpApp extends Application {
Stage popupStage;
Stage primaryStage;
#Override
public void start(Stage stage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
primaryStage = stage;
initPopUpStage();
// When the Pop-Up stage is showing, do not handle any action on the
// main GUI
root.disableProperty().bind(popupStage.showingProperty());
Button b = new Button("Open deck");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// Add some ToggleButtons to simulate the cards
VBox vbox = new VBox();
vbox.setAlignment(Pos.CENTER);
List<ToggleButton> toggles = new ArrayList<ToggleButton>();
for (int i = 0; i < 4; i++) {
ToggleButton tb = new ToggleButton("Card " + i + 1);
toggles.add(tb);
}
vbox.getChildren().addAll(toggles);
Scene sc = new Scene(vbox, 300, 300);
popupStage.setScene(sc);
// On close request check for the condition
popupStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
Boolean readytoClose = false;
for (ToggleButton toggle : toggles) {
if (toggle.isSelected()) {
readytoClose = true;
break;
}
}
// Consume the event a show a dialog
if (!readytoClose) {
event.consume();
Alert alert = new Alert(AlertType.INFORMATION,
"At least one card has be to be selected!");
alert.showAndWait();
}
}
});
popupStage.show();
}
});
root.setCenter(b);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
private void initPopUpStage() {
popupStage = new Stage();
popupStage.initOwner(primaryStage);
popupStage.initStyle(StageStyle.UNDECORATED);
// On focus loss, close the window
popupStage.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// Rather than popupStage.close(); fire the event manually
if (!newValue)
popupStage.fireEvent(new WindowEvent(popupStage, WindowEvent.WINDOW_CLOSE_REQUEST));
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Update:
To make the main Stage unavailable I have added this line:
root.disableProperty().bind(popupStage.showingProperty());
This will disable the root BorderPane while the pop-up stage is showing. As soon as the pop-up window closed, the main window is enabled again.

JavaFX: Setting Drag Done

Currently I am developing a program which transfers a list item into another list. It works well with setOnDragExited,if I drag it to another component with the setOnDragExited, it will be dropped. (Which I think the program is rightbecause that is the function of setOnDragExited. But I need the program to only drop the list item when I release the button).
So I have the source which is fieldList and the target which is mainTable
Here is the code for the source, which I have no problem with.
fieldList.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
/* drag was detected, start a drag-and-drop gesture*/
/* allow any transfer mode */
Dragboard db =fieldList.startDragAndDrop(TransferMode.ANY);
/* Put a string on a dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(fieldList.getSelectionModel().getSelectedItem());
db.setContent(content);
event.consume();
}
});
And here is the code which doesn't work with setOnDragDropped but works with setOnDragExited
mainTable.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()) {
ObservableList<String> ls = FXCollections.observableArrayList();
try {
columnLS.add(Resources.getgBCon().getActualName(tableList.getSelectionModel().getSelectedItem())+"."+db.getString());
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
success = true;
String base= "";
if(!columnLS.isEmpty()){
for(int i = 0; i<= columnLS.size()-1 ;i++){
if(i == 0){
base = columnLS.get(i);
}else{
base = base + ", " + columnLS.get(i);
}
}
}
columnQuery = base;
me.refreshSQLQuery(columnQuery,getTableQuery(),filterQuery,groupQuery,sortQuery);
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(true);
event.consume();
}
});
So far I didn't find anything that relates to my problem, on those that are remotely related I found, I have tried but to no avail.
Thank you Stack Overflow.
I have added setOnDragEntered. It seems it still didn't work. Is there a difference between setOnDrag and setOnMouseDrag?
mainTable.setOnDragEntered(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
System.out.println("onDragEntered");
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
});
I have also tried TransferMode.ANY but to no avail.
Solution:
Used mainTable.setOnDragOver instead of mainTable.setOnDragEntered

JavaFx Drag and Drop between 2 Swing JFXPanels not fully working?

I have a Swing application, and inside my JFrame there are 2 JFXPanels, containing 2 JavaFx Nodes, a TableView and a WebView. The table contains a list of urls (think of it like a bookmarks list). I want the user to drag a url from the table on the left to the webview on the right. I set up drag-and-drop events according to the JavaFx tutorial and answers to previous drag-and-drop questions here. However, the 2 JFXPanels seem to not be able to communicate with each other.
I can drag URLs from the table to outside of my application (e.g. my web browser) and that works OK.
I can also drop URLs from the outside (e.g. copied from by web browser's address bar) to the web view and it loads them correctly too.
But when I drag URLs from the table, and try to drop them on the WebView, I always get an empty dragboard on the target side (I tried using both the String and the Url data formats, but both come back as null, also the event.getGestureSource() comes back as null), eventhough the source fills it correctly (watched it step-by-step with the debugger).
Could this be a side-effect of the fact that this is between 2 JFXPanels, and there is Swing in between which handles drag-and-drop using a different API? By the way, I do not have any other drag-and-drop code in the remaining Swing part of the application, so not mixing the two event models.
Thanks in advance for any pointers, tips or ideas.
Sample code snippets of my event handlers below:
(using JavaFx2.2, Jdk 1.7.0_40, on Windows 7 64 bit machine)
/* on the source side, the TableView */
// initiate drag
table.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
/* Put the selected file or url on the dragboard */
MyObject tableItem = table.getSelectionModel().getSelectedItem();
String item = tableItem.getUrlString();
if (item != null)
{
Dragboard db = table.startDragAndDrop(TransferMode.COPY);
ClipboardContent content = new ClipboardContent();
content.putString(item);
content.putUrl(item);
db.setContent(content);
}
event.consume();
}
});
table.setOnDragDone(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event)
{
event.consume();
}
});
/* on the target side, the WebView */
webView.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
if (event.getGestureSource() != webView && (db.hasUrl() || db.hasString())) {
event.acceptTransferModes(TransferMode.ANY);
} else {
event.consume();
}
}
});
// handle drop
webView.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
boolean success = false;
String urlDropped = db.getUrl();
if (urlDropped == null)
urlDropped = db.getString();
if (urlDropped != null)
{
webView.getEngine().load(urlDropped);
}
event.setDropCompleted(success);
event.consume();
}
});

Categories