diff --git a/CHANGELOG.md b/CHANGELOG.md index 59545f15c4e..30f70513fe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We added an extra option in the 'Find Unlinked Files' dialog view to ignore unnecessary files like Thumbs.db, DS_Store, etc. [koppor#373](https://github.com/koppor/jabref/issues/373) - JabRef now writes log files. Linux: `$home/.cache/jabref/logs/version`, Windows: `%APPDATA%\..\Local\harawata\jabref\version\logs`, Mac: `Users/.../Library/Logs/jabref/version` - We added an importer for Citavi backup files, support ".ctv5bak" and ".ctv6bak" file formats. [#8322](https://github.com/JabRef/jabref/issues/8322) +- We added a feature to drag selected entries and drop them to other opened inactive library tabs [koppor521](https://github.com/koppor/jabref/issues/521). ### Changed diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java index 7c39965740c..440dc84dec6 100644 --- a/src/main/java/org/jabref/gui/JabRefFrame.java +++ b/src/main/java/org/jabref/gui/JabRefFrame.java @@ -225,6 +225,54 @@ private void initDragAndDrop() { } else { tabbedPane.getTabs().remove(dndIndicator); } + // Accept drag entries from MainTable + if (event.getDragboard().hasContent(DragAndDropDataFormats.ENTRIES)) { + event.acceptTransferModes(TransferMode.COPY); + event.consume(); + } + }); + + 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())) { + tabDragEvent.acceptTransferModes(TransferMode.ANY); + if (!tabbedPane.getTabs().contains(dndIndicator)) { + tabbedPane.getTabs().add(dndIndicator); + } + event.consume(); + } else { + tabbedPane.getTabs().remove(dndIndicator); + } + + if (tabDragEvent.getDragboard().hasContent(DragAndDropDataFormats.ENTRIES)) { + tabDragEvent.acceptTransferModes(TransferMode.COPY); + tabDragEvent.consume(); + } + }); + tab.setOnDragExited(event1 -> tabbedPane.getTabs().remove(dndIndicator)); + tab.setOnDragDropped(tabDragEvent -> { + if (DragAndDropHelper.hasBibFiles(tabDragEvent.getDragboard())) { + tabbedPane.getTabs().remove(dndIndicator); + List bibFiles = DragAndDropHelper.getBibFiles(tabDragEvent.getDragboard()); + OpenDatabaseAction openDatabaseAction = this.getOpenDatabaseAction(); + openDatabaseAction.openFiles(bibFiles, true); + tabDragEvent.setDropCompleted(true); + tabDragEvent.consume(); + } else { + for (Tab libraryTab : tabbedPane.getTabs()) { + if (libraryTab.getId().equals(tab.getId()) && + !tabbedPane.getSelectionModel().getSelectedItem().equals(libraryTab)) { + ((LibraryTab) libraryTab).dropEntry(stateManager.getLocalDragboard().getBibEntries()); + } + } + tabDragEvent.consume(); + } + }); + }); + event.consume(); }); this.getScene().setOnDragExited(event -> tabbedPane.getTabs().remove(dndIndicator)); diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index f2448a3bdbe..f037135f6d9 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Random; import javafx.animation.PauseTransition; import javafx.application.Platform; @@ -157,6 +158,10 @@ public LibraryTab(JabRefFrame frame, this.entryEditor = new EntryEditor(this, externalFileTypes); + // set LibraryTab ID for drag'n'drop + // ID content doesn't matter, we only need different tabs to have different ID + this.setId(Long.valueOf(new Random().nextLong()).toString()); + Platform.runLater(() -> { EasyBind.subscribe(changedProperty, this::updateTabTitle); stateManager.getOpenDatabases().addListener((ListChangeListener) c -> @@ -764,6 +769,10 @@ public void paste() { mainTable.paste(); } + public void dropEntry(List entriesToAdd) { + mainTable.dropEntry(entriesToAdd); + } + public void cut() { mainTable.cut(); } diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index 15964b0771e..dc2a8dd8077 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -345,6 +345,11 @@ private List handleNonBibteXStringData() { } } return entries; + + public void dropEntry(List entriesToAdd) { + for (BibEntry entry : entriesToAdd) { + importHandler.importEntryWithDuplicateCheck(database, (BibEntry) entry.clone()); + } } private void handleOnDragOver(TableRow row, BibEntryTableViewModel item, DragEvent event) { @@ -383,8 +388,9 @@ private void handleOnDragDetected(TableRow row, BibEntry // The following is necesary to initiate the drag and drop in javafx, although we don't need the contents // It doesn't work without + // Drag'n'drop to other tabs use COPY TransferMode, drop to group sidepane use MOVE ClipboardContent content = new ClipboardContent(); - Dragboard dragboard = startDragAndDrop(TransferMode.MOVE); + Dragboard dragboard = startDragAndDrop(TransferMode.COPY_OR_MOVE); content.put(DragAndDropDataFormats.ENTRIES, ""); dragboard.setContent(content); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java b/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java index e2f6f2951a1..d8f30866596 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/CiteSeerTest.java @@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @FetcherTest +@Disabled("CiteSeer is down as of 2022-07-18") class CiteSeerTest { private CiteSeer fetcher = new CiteSeer(); diff --git a/src/test/java/org/jabref/logic/importer/fetcher/JstorFetcherTest.java b/src/test/java/org/jabref/logic/importer/fetcher/JstorFetcherTest.java index e9c3e0963d8..1f1b0a521f7 100644 --- a/src/test/java/org/jabref/logic/importer/fetcher/JstorFetcherTest.java +++ b/src/test/java/org/jabref/logic/importer/fetcher/JstorFetcherTest.java @@ -1,12 +1,12 @@ package org.jabref.logic.importer.fetcher; -import java.io.IOException; import java.net.URL; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.List; import java.util.Optional; -import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.ImportFormatPreferences; import org.jabref.logic.importer.SearchBasedFetcher; import org.jabref.model.entry.BibEntry; @@ -40,8 +40,7 @@ public class JstorFetcherTest implements SearchBasedFetcherCapabilityTest { .withField(StandardField.PAGES, "63--73") .withField(StandardField.VOLUME, "20") .withField(StandardField.URL, "http://www.jstor.org/stable/90002164") - .withField(StandardField.YEAR, "2017") - .withField(StandardField.URLDATE, "2022-07-16"); + .withField(StandardField.YEAR, "2017"); private final BibEntry doiEntry = new BibEntry(StandardEntryType.Article) .withCitationKey("10.1086/501484") @@ -64,13 +63,19 @@ void searchByTitle() throws Exception { } @Test - void searchById() throws FetcherException { + void searchById() throws Exception { + bibEntry.setField(StandardField.URLDATE, LocalDate.now().format(DateTimeFormatter.ISO_DATE)); assertEquals(Optional.of(bibEntry), fetcher.performSearchById("90002164")); + } + + @Test + void searchByUrlUsingId() throws Exception { + doiEntry.setField(StandardField.URLDATE, LocalDate.now().format(DateTimeFormatter.ISO_DATE)); assertEquals(Optional.of(doiEntry), fetcher.performSearchById("https://www.jstor.org/stable/10.1086/501484?seq=1")); } @Test - void fetchPDF() throws IOException { + void fetchPDF() throws Exception { Optional url = fetcher.findFullText(bibEntry); assertEquals(Optional.of(new URL("https://www.jstor.org/stable/pdf/90002164.pdf")), url); }