diff --git a/src/main/java/org/jabref/gui/DragAndDropHelper.java b/src/main/java/org/jabref/gui/DragAndDropHelper.java index cbc16c3c573..92e229a5c7f 100644 --- a/src/main/java/org/jabref/gui/DragAndDropHelper.java +++ b/src/main/java/org/jabref/gui/DragAndDropHelper.java @@ -23,4 +23,16 @@ public static List getBibFiles(Dragboard dragboard) { return dragboard.getFiles().stream().map(File::toPath).filter(FileUtil::isBibFile).collect(Collectors.toList()); } } + + public static boolean hasGroups(Dragboard dragboard) { + return !getGroups(dragboard).isEmpty(); + } + + public static List getGroups(Dragboard dragboard) { + if (!dragboard.hasContent(DragAndDropDataFormats.GROUP)) { + return Collections.emptyList(); + } else { + return (List) dragboard.getContent(DragAndDropDataFormats.GROUP); + } + } } diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 19da0ef09be..5abadc9423e 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -38,6 +38,7 @@ import javafx.scene.control.ToolBar; import javafx.scene.control.Tooltip; import javafx.scene.control.skin.TabPaneSkin; +import javafx.scene.input.Dragboard; import javafx.scene.input.KeyEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.BorderPane; @@ -137,6 +138,7 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.field.SpecialField; import org.jabref.model.entry.types.StandardEntryType; +import org.jabref.model.groups.GroupTreeNode; import org.jabref.preferences.PreferencesService; import org.jabref.preferences.TelemetryPreferences; @@ -232,9 +234,9 @@ private void initDragAndDrop() { this.getScene().setOnDragEntered(event -> { // It is necessary to setOnDragOver for newly opened tabs // drag'n'drop on tabs covered dnd on tabbedPane, so dnd on tabs should contain all dnds on tabbedPane - tabbedPane.lookupAll(".tab").forEach(tab -> { - tab.setOnDragOver(tabDragEvent -> { - if (DragAndDropHelper.hasBibFiles(tabDragEvent.getDragboard())) { + tabbedPane.lookupAll(".tab").forEach(destinationTabNode -> { + destinationTabNode.setOnDragOver(tabDragEvent -> { + if (DragAndDropHelper.hasBibFiles(tabDragEvent.getDragboard()) || DragAndDropHelper.hasGroups(tabDragEvent.getDragboard())) { tabDragEvent.acceptTransferModes(TransferMode.ANY); if (!tabbedPane.getTabs().contains(dndIndicator)) { tabbedPane.getTabs().add(dndIndicator); @@ -249,8 +251,11 @@ private void initDragAndDrop() { tabDragEvent.consume(); } }); - tab.setOnDragExited(event1 -> tabbedPane.getTabs().remove(dndIndicator)); - tab.setOnDragDropped(tabDragEvent -> { + destinationTabNode.setOnDragExited(event1 -> tabbedPane.getTabs().remove(dndIndicator)); + destinationTabNode.setOnDragDropped(tabDragEvent -> { + + Dragboard dragboard = tabDragEvent.getDragboard(); + if (DragAndDropHelper.hasBibFiles(tabDragEvent.getDragboard())) { tabbedPane.getTabs().remove(dndIndicator); List bibFiles = DragAndDropHelper.getBibFiles(tabDragEvent.getDragboard()); @@ -260,9 +265,32 @@ private void initDragAndDrop() { tabDragEvent.consume(); } else { for (Tab libraryTab : tabbedPane.getTabs()) { - if (libraryTab.getId().equals(tab.getId()) && + if (libraryTab.getId().equals(destinationTabNode.getId()) && !tabbedPane.getSelectionModel().getSelectedItem().equals(libraryTab)) { - ((LibraryTab) libraryTab).dropEntry(stateManager.getLocalDragboard().getBibEntries()); + LibraryTab destinationLibraryTab = (LibraryTab) libraryTab; + if (DragAndDropHelper.hasGroups(tabDragEvent.getDragboard())) { + List groupPathToSources = DragAndDropHelper.getGroups(tabDragEvent.getDragboard()); + + copyRootNode(destinationLibraryTab); + + GroupTreeNode destinationLibraryGroupRoot = destinationLibraryTab + .getBibDatabaseContext() + .getMetaData() + .getGroups().get(); + + for (String pathToSource : groupPathToSources) { + GroupTreeNode groupTreeNodeToCopy = getCurrentLibraryTab() + .getBibDatabaseContext() + .getMetaData() + .getGroups() + .get() + .getChildByPath(pathToSource) + .get(); + copyGroupTreeNode((LibraryTab) libraryTab, destinationLibraryGroupRoot, groupTreeNodeToCopy); + } + return; + } + destinationLibraryTab.dropEntry(stateManager.getLocalDragboard().getBibEntries()); } } tabDragEvent.consume(); @@ -273,7 +301,6 @@ private void initDragAndDrop() { }); this.getScene().setOnDragExited(event -> tabbedPane.getTabs().remove(dndIndicator)); - this.getScene().setOnDragDropped(event -> { tabbedPane.getTabs().remove(dndIndicator); List bibFiles = DragAndDropHelper.getBibFiles(event.getDragboard()); @@ -1315,6 +1342,42 @@ public DialogService getDialogService() { return dialogService; } + private void copyGroupTreeNode(LibraryTab destinationLibraryTab, GroupTreeNode parent, GroupTreeNode groupTreeNodeToCopy) { + List allEntries = getCurrentLibraryTab() + .getBibDatabaseContext() + .getEntries(); + // add groupTreeNodeToCopy to the parent-- in the first run that will the source/main GroupTreeNode + GroupTreeNode copiedNode = parent.addSubgroup(groupTreeNodeToCopy.copyNode().getGroup()); + // add all entries of a groupTreeNode to the new library. + destinationLibraryTab.dropEntry(groupTreeNodeToCopy.getEntriesInGroup(allEntries)); + // List of all children of groupTreeNodeToCopy + List children = groupTreeNodeToCopy.getChildren(); + + if (!children.isEmpty()) { + // use recursion to add all subgroups of the original groupTreeNodeToCopy + for (GroupTreeNode child : children) { + copyGroupTreeNode(destinationLibraryTab, copiedNode, child); + } + } + } + + private void copyRootNode(LibraryTab destinationLibraryTab) { + if (!destinationLibraryTab.getBibDatabaseContext().getMetaData().getGroups().isEmpty()) { + return; + } + // a root (all entries) GroupTreeNode + GroupTreeNode currentLibraryGroupRoot = getCurrentLibraryTab().getBibDatabaseContext() + .getMetaData() + .getGroups() + .get() + .copyNode(); + + // add currentLibraryGroupRoot to the Library if it does not have a root. + destinationLibraryTab.getBibDatabaseContext() + .getMetaData() + .setGroups(currentLibraryGroupRoot); + } + /** * The action concerned with closing the window. */