diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eec5f81d27..f98cb2c822c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We improved [cleanup](https://docs.jabref.org/finding-sorting-and-cleaning-entries/cleanupentries) of `arXiv` IDs in distributed in the fields `note`, `version`, `institution`, and `eid` fields. [#11306](https://github.com/JabRef/jabref/issues/11306) - We added a switch not to store the linked file URL, because it caused troubles at other apps. [#11735](https://github.com/JabRef/jabref/pull/11735) - When starting a new SLR, the selected catalogs now persist within and across JabRef sessions. [koppor#614](https://github.com/koppor/jabref/issues/614) +- We added support for drag'n'drop on an entry in the maintable to an external application to get the entry preview dropped. [#11846](https://github.com/JabRef/jabref/pull/11846) - We added a different background color to the search bar to indicate when the search syntax is wrong. [#11658](https://github.com/JabRef/jabref/pull/11658) - We added a setting which always adds the literal "Cited on pages" text before each JStyle citation. [#11691](https://github.com/JabRef/jabref/pull/11732) diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index 4719e2e8220..e8939f9aec8 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -1,6 +1,7 @@ package org.jabref.gui.maintable; import java.io.File; +import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.Objects; @@ -39,12 +40,14 @@ import org.jabref.gui.maintable.columns.LibraryColumn; import org.jabref.gui.maintable.columns.MainTableColumn; import org.jabref.gui.preferences.GuiPreferences; +import org.jabref.gui.preview.ClipboardContentGenerator; import org.jabref.gui.search.MatchCategory; import org.jabref.gui.util.ControlHelper; import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.UiTaskExecutor; import org.jabref.gui.util.ViewModelTableRowFactory; import org.jabref.logic.FilePreferences; +import org.jabref.logic.citationstyle.CitationStyleOutputFormat; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.util.TaskExecutor; import org.jabref.model.database.BibDatabaseContext; @@ -75,6 +78,8 @@ public class MainTable extends TableView { private final UndoManager undoManager; private final FilePreferences filePreferences; private final ImportHandler importHandler; + private final ClipboardContentGenerator clipboardContentGenerator; + private long lastKeyPressTime; private String columnSearchTerm; @@ -100,6 +105,7 @@ public MainTable(MainTableDataModel model, this.undoManager = libraryTab.getUndoManager(); this.filePreferences = preferences.getFilePreferences(); this.importHandler = importHandler; + this.clipboardContentGenerator = new ClipboardContentGenerator(preferences.getPreviewPreferences(), preferences.getLayoutFormatterPreferences(), Injector.instantiateModelOrService(JournalAbbreviationRepository.class)); MainTablePreferences mainTablePreferences = preferences.getMainTablePreferences(); @@ -392,12 +398,18 @@ private void handleOnDragDetected(TableRow row, BibEntry List entries = getSelectionModel().getSelectedItems().stream().map(BibEntryTableViewModel::getEntry).collect(Collectors.toList()); - // The following is necessary to initiate the drag and drop in JavaFX, - // although we don't need the contents, it does not work without + ClipboardContent content; + try { + content = clipboardContentGenerator.generate(entries, CitationStyleOutputFormat.HTML, database); + } catch (IOException e) { + LOGGER.warn("Could not generate clipboard content. Falling back to empty clipboard", e); + content = new ClipboardContent(); + } + // Required to be able to drop the entries inside JabRef + content.put(DragAndDropDataFormats.ENTRIES, ""); + // Drag'n'drop to other tabs use COPY TransferMode, drop to group sidepane use MOVE - ClipboardContent content = new ClipboardContent(); Dragboard dragboard = startDragAndDrop(TransferMode.COPY_OR_MOVE); - content.put(DragAndDropDataFormats.ENTRIES, ""); dragboard.setContent(content); if (!entries.isEmpty()) { diff --git a/src/main/java/org/jabref/gui/preview/ClipboardContentGenerator.java b/src/main/java/org/jabref/gui/preview/ClipboardContentGenerator.java new file mode 100644 index 00000000000..a47915c206c --- /dev/null +++ b/src/main/java/org/jabref/gui/preview/ClipboardContentGenerator.java @@ -0,0 +1,136 @@ +package org.jabref.gui.preview; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import javafx.scene.input.ClipboardContent; + +import org.jabref.logic.citationstyle.CitationStyleGenerator; +import org.jabref.logic.citationstyle.CitationStyleOutputFormat; +import org.jabref.logic.citationstyle.CitationStylePreviewLayout; +import org.jabref.logic.journals.JournalAbbreviationRepository; +import org.jabref.logic.layout.Layout; +import org.jabref.logic.layout.LayoutFormatterPreferences; +import org.jabref.logic.layout.LayoutHelper; +import org.jabref.logic.layout.TextBasedPreviewLayout; +import org.jabref.logic.os.OS; +import org.jabref.logic.preview.PreviewLayout; +import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.BibEntryTypesManager; + +import com.airhacks.afterburner.injection.Injector; +import com.google.common.annotations.VisibleForTesting; + +public class ClipboardContentGenerator { + + private PreviewPreferences previewPreferences; + private final LayoutFormatterPreferences layoutFormatterPreferences; + private final JournalAbbreviationRepository abbreviationRepository; + + public ClipboardContentGenerator(PreviewPreferences previewPreferences, + LayoutFormatterPreferences layoutFormatterPreferences, + JournalAbbreviationRepository abbreviationRepository) { + this.previewPreferences = previewPreferences; + this.layoutFormatterPreferences = layoutFormatterPreferences; + this.abbreviationRepository = abbreviationRepository; + } + + public ClipboardContent generate(List selectedEntries, CitationStyleOutputFormat outputFormat, BibDatabaseContext bibDatabaseContext) throws IOException { + List citations = generateCitations(selectedEntries, outputFormat, bibDatabaseContext); + PreviewLayout previewLayout = previewPreferences.getSelectedPreviewLayout(); + + // if it is not a citation style take care of the preview + if (!(previewLayout instanceof CitationStylePreviewLayout)) { + return processPreview(citations); + } else { + // if it's generated by a citation style take care of each output format + ClipboardContent content; + return switch (outputFormat) { + case HTML -> processHtml(citations); + case TEXT -> processText(citations); + }; + } + } + + private List generateCitations(List selectedEntries, CitationStyleOutputFormat outputFormat, BibDatabaseContext bibDatabaseContext) throws IOException { + // This worker stored the style as filename. The CSLAdapter and the CitationStyleCache store the source of the + // style. Therefore, we extract the style source from the file. + String styleSource = null; + PreviewLayout previewLayout = previewPreferences.getSelectedPreviewLayout(); + + if (previewLayout instanceof CitationStylePreviewLayout citationStyleLayout) { + styleSource = citationStyleLayout.getText(); + } + + if (styleSource != null) { + return CitationStyleGenerator.generateBibliographies( + selectedEntries, + styleSource, + outputFormat, + bibDatabaseContext, + Injector.instantiateModelOrService(BibEntryTypesManager.class)); + } else { + return generateTextBasedPreviewLayoutCitations(selectedEntries, bibDatabaseContext); + } + } + + /** + * Generates a plain text string out of the preview (based on {@link org.jabref.logic.layout.TextBasedPreviewLayout} or {@link org.jabref.logic.bst.BstPreviewLayout}) + * and copies it additionally to the html to the clipboard (WYSIWYG Editors use the HTML, plain text editors the text) + */ + @VisibleForTesting + static ClipboardContent processPreview(List citations) { + ClipboardContent content = new ClipboardContent(); + content.putHtml(String.join(CitationStyleOutputFormat.HTML.getLineSeparator(), citations)); + content.putString(String.join(CitationStyleOutputFormat.HTML.getLineSeparator(), citations)); + return content; + } + + /** + * Joins every citation with a newline and returns it. + */ + @VisibleForTesting + static ClipboardContent processText(List citations) { + ClipboardContent content = new ClipboardContent(); + content.putString(String.join(CitationStyleOutputFormat.TEXT.getLineSeparator(), citations)); + return content; + } + + /** + * Inserts each citation into a HTML body and copies it to the clipboard. + * The given preview is based on {@link org.jabref.logic.citationstyle.CitationStylePreviewLayout}. + */ + @VisibleForTesting + static ClipboardContent processHtml(List citations) { + String result = "" + OS.NEWLINE + + "" + OS.NEWLINE + + " " + OS.NEWLINE + + " " + OS.NEWLINE + + " " + OS.NEWLINE + + " " + OS.NEWLINE + OS.NEWLINE; + + result += String.join(CitationStyleOutputFormat.HTML.getLineSeparator(), citations); + result += OS.NEWLINE + + " " + OS.NEWLINE + + "" + OS.NEWLINE; + + ClipboardContent content = new ClipboardContent(); + content.putString(result); + content.putHtml(result); + return content; + } + + private List generateTextBasedPreviewLayoutCitations(List selectedEntries, BibDatabaseContext bibDatabaseContext) throws IOException { + TextBasedPreviewLayout customPreviewLayout = previewPreferences.getCustomPreviewLayout(); + StringReader customLayoutReader = new StringReader(customPreviewLayout.getText().replace("__NEWLINE__", "\n")); + Layout layout = new LayoutHelper(customLayoutReader, layoutFormatterPreferences, abbreviationRepository).getLayoutFromText(); + List citations = new ArrayList<>(selectedEntries.size()); + for (BibEntry entry : selectedEntries) { + citations.add(layout.doLayout(entry, bibDatabaseContext.getDatabase())); + } + return citations; + } +} diff --git a/src/main/java/org/jabref/gui/preview/CopyCitationAction.java b/src/main/java/org/jabref/gui/preview/CopyCitationAction.java index 5d89498fe01..e8364ca1a3f 100644 --- a/src/main/java/org/jabref/gui/preview/CopyCitationAction.java +++ b/src/main/java/org/jabref/gui/preview/CopyCitationAction.java @@ -1,9 +1,6 @@ package org.jabref.gui.preview; import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import javafx.scene.input.ClipboardContent; @@ -14,22 +11,13 @@ import org.jabref.gui.actions.ActionHelper; import org.jabref.gui.actions.SimpleCommand; import org.jabref.gui.preferences.GuiPreferences; -import org.jabref.logic.citationstyle.CitationStyleGenerator; import org.jabref.logic.citationstyle.CitationStyleOutputFormat; -import org.jabref.logic.citationstyle.CitationStylePreviewLayout; import org.jabref.logic.journals.JournalAbbreviationRepository; import org.jabref.logic.l10n.Localization; -import org.jabref.logic.layout.Layout; -import org.jabref.logic.layout.LayoutHelper; -import org.jabref.logic.layout.TextBasedPreviewLayout; -import org.jabref.logic.os.OS; -import org.jabref.logic.preview.PreviewLayout; import org.jabref.logic.util.BackgroundTask; import org.jabref.logic.util.TaskExecutor; import org.jabref.model.entry.BibEntry; -import org.jabref.model.entry.BibEntryTypesManager; -import com.airhacks.afterburner.injection.Injector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,8 +35,7 @@ public class CopyCitationAction extends SimpleCommand { private final DialogService dialogService; private final ClipBoardManager clipBoardManager; private final TaskExecutor taskExecutor; - private final GuiPreferences preferences; - private final JournalAbbreviationRepository abbreviationRepository; + private final ClipboardContentGenerator clipboardContentGenerator; public CopyCitationAction(CitationStyleOutputFormat outputFormat, DialogService dialogService, @@ -63,8 +50,7 @@ public CopyCitationAction(CitationStyleOutputFormat outputFormat, this.selectedEntries = stateManager.getSelectedEntries(); this.clipBoardManager = clipBoardManager; this.taskExecutor = taskExecutor; - this.preferences = preferences; - this.abbreviationRepository = abbreviationRepository; + this.clipboardContentGenerator = new ClipboardContentGenerator(preferences.getPreviewPreferences(), preferences.getLayoutFormatterPreferences(), abbreviationRepository); this.executable.bind(ActionHelper.needsEntriesSelected(stateManager)); } @@ -77,106 +63,12 @@ public void execute() { .executeWith(taskExecutor); } - private List generateCitations() throws IOException { - // This worker stored the style as filename. The CSLAdapter and the CitationStyleCache store the source of the - // style. Therefore, we extract the style source from the file. - String styleSource = null; - PreviewLayout previewLayout = preferences.getPreviewPreferences().getSelectedPreviewLayout(); - - if (previewLayout instanceof CitationStylePreviewLayout citationStyleLayout) { - styleSource = citationStyleLayout.getText(); - } - - if (styleSource != null) { - return CitationStyleGenerator.generateBibliographies( - selectedEntries, - styleSource, - outputFormat, - stateManager.getActiveDatabase().get(), - Injector.instantiateModelOrService(BibEntryTypesManager.class)); - } else { - return generateTextBasedPreviewLayoutCitations(); - } - } - - private List generateTextBasedPreviewLayoutCitations() throws IOException { - if (stateManager.getActiveDatabase().isEmpty()) { - return Collections.emptyList(); - } - - TextBasedPreviewLayout customPreviewLayout = preferences.getPreviewPreferences().getCustomPreviewLayout(); - StringReader customLayoutReader = new StringReader(customPreviewLayout.getText().replace("__NEWLINE__", "\n")); - Layout layout = new LayoutHelper(customLayoutReader, preferences.getLayoutFormatterPreferences(), abbreviationRepository) - .getLayoutFromText(); - - List citations = new ArrayList<>(selectedEntries.size()); - for (BibEntry entry : selectedEntries) { - citations.add(layout.doLayout(entry, stateManager.getActiveDatabase().get().getDatabase())); - } - return citations; - } - - /** - * Generates a plain text string out of the preview and copies it additionally to the html to the clipboard (WYSIWYG Editors use the HTML, plain text editors the text) - */ - protected static ClipboardContent processPreview(List citations) { - ClipboardContent content = new ClipboardContent(); - content.putHtml(String.join(CitationStyleOutputFormat.HTML.getLineSeparator(), citations)); - content.putString(String.join(CitationStyleOutputFormat.HTML.getLineSeparator(), citations)); - return content; + private ClipboardContent generateCitations() throws IOException { + return clipboardContentGenerator.generate(selectedEntries, outputFormat, stateManager.getActiveDatabase().get()); } - /** - * Joins every citation with a newline and returns it. - */ - protected static ClipboardContent processText(List citations) { - ClipboardContent content = new ClipboardContent(); - content.putString(String.join(CitationStyleOutputFormat.TEXT.getLineSeparator(), citations)); - return content; - } - - /** - * Inserts each citation into a HTML body and copies it to the clipboard - */ - protected static ClipboardContent processHtml(List citations) { - String result = "" + OS.NEWLINE + - "" + OS.NEWLINE + - " " + OS.NEWLINE + - " " + OS.NEWLINE + - " " + OS.NEWLINE + - " " + OS.NEWLINE + OS.NEWLINE; - - result += String.join(CitationStyleOutputFormat.HTML.getLineSeparator(), citations); - result += OS.NEWLINE + - " " + OS.NEWLINE + - "" + OS.NEWLINE; - - ClipboardContent content = new ClipboardContent(); - content.putString(result); - content.putHtml(result); - return content; - } - - private void setClipBoardContent(List citations) { - PreviewLayout previewLayout = preferences.getPreviewPreferences().getSelectedPreviewLayout(); - - // if it's not a citation style take care of the preview - if (!(previewLayout instanceof CitationStylePreviewLayout)) { - clipBoardManager.setContent(processPreview(citations)); - } else { - // if it's generated by a citation style take care of each output format - ClipboardContent content; - switch (outputFormat) { - case HTML -> content = processHtml(citations); - case TEXT -> content = processText(citations); - default -> { - LOGGER.warn("unknown output format: '{}', processing it via the default.", outputFormat); - content = processText(citations); - } - } - clipBoardManager.setContent(content); - } - + private void setClipBoardContent(ClipboardContent clipBoardContent) { + clipBoardManager.setContent(clipBoardContent); dialogService.notify(Localization.lang("Copied %0 citations.", String.valueOf(selectedEntries.size()))); } } diff --git a/src/test/java/org/jabref/gui/preview/CopyCitationActionTest.java b/src/test/java/org/jabref/gui/preview/ClipboardContentGeneratorTest.java similarity index 93% rename from src/test/java/org/jabref/gui/preview/CopyCitationActionTest.java rename to src/test/java/org/jabref/gui/preview/ClipboardContentGeneratorTest.java index 8a6b3b1f178..fefbe501147 100644 --- a/src/test/java/org/jabref/gui/preview/CopyCitationActionTest.java +++ b/src/test/java/org/jabref/gui/preview/ClipboardContentGeneratorTest.java @@ -1,6 +1,7 @@ package org.jabref.gui.preview; import java.util.Arrays; +import java.util.List; import javafx.scene.input.ClipboardContent; @@ -10,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -class CopyCitationActionTest { +class ClipboardContentGeneratorTest { @Test void processPreviewText() throws Exception { @@ -38,7 +39,7 @@ void processPreviewText() throws Exception { OS.NEWLINE + "Abstract: This entry describes a test scenario which may be useful in JabRef. By providing a test entry it is possible to see how certain things will look in this graphical BIB-file mananger. "; - ClipboardContent clipboardContent = CopyCitationAction.processPreview(Arrays.asList(citation, citation)); + ClipboardContent clipboardContent = ClipboardContentGenerator.processPreview(List.of(citation, citation)); String actual = clipboardContent.getString(); assertEquals(expected, actual); @@ -91,7 +92,7 @@ void processPreviewHtml() throws Exception { "" + OS.NEWLINE + "

"; - ClipboardContent clipboardContent = CopyCitationAction.processPreview(Arrays.asList(citation, citation)); + ClipboardContent clipboardContent = ClipboardContentGenerator.processPreview(Arrays.asList(citation, citation)); String actual = clipboardContent.getString(); assertEquals(expected, actual); } @@ -102,7 +103,7 @@ void processText() throws Exception { "[1]B. Smith, B. Jones, and J. Williams, “Title of the test entry,” BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016." + OS.NEWLINE; String citation = "[1]B. Smith, B. Jones, and J. Williams, “Title of the test entry,” BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016." + OS.NEWLINE; - ClipboardContent textTransferable = CopyCitationAction.processText(Arrays.asList(citation, citation)); + ClipboardContent textTransferable = ClipboardContentGenerator.processText(Arrays.asList(citation, citation)); String actual = textTransferable.getString(); assertEquals(expected, actual); @@ -132,7 +133,7 @@ void processHtmlAsHtml() throws Exception { String citation = "
" + OS.NEWLINE + "
[1]
B. Smith, B. Jones, and J. Williams, “Title of the test entry,” BibTeX Journal, vol. 34, no. 3, pp. 45–67, Jul. 2016.
" + OS.NEWLINE + "
" + OS.NEWLINE; - ClipboardContent htmlTransferable = CopyCitationAction.processHtml(Arrays.asList(citation, citation)); + ClipboardContent htmlTransferable = ClipboardContentGenerator.processHtml(Arrays.asList(citation, citation)); Object actual = htmlTransferable.getHtml(); assertEquals(expected, actual);