diff --git a/src/main/java/org/jabref/Globals.java b/src/main/java/org/jabref/Globals.java index 71466ee47c7..12531280708 100644 --- a/src/main/java/org/jabref/Globals.java +++ b/src/main/java/org/jabref/Globals.java @@ -56,13 +56,11 @@ public class Globals { /** * Manager for the state of the GUI. */ - - public static ClipBoardManager clipboardManager = new ClipBoardManager(); - public static StateManager stateManager = new StateManager(); public static ExporterFactory exportFactory; public static CountingUndoManager undoManager = new CountingUndoManager(); public static BibEntryTypesManager entryTypesManager = new BibEntryTypesManager(); + public static ClipBoardManager clipboardManager = new ClipBoardManager(); // Key binding preferences private static KeyBindingRepository keyBindingRepository; private static DefaultFileUpdateMonitor fileUpdateMonitor; diff --git a/src/main/java/org/jabref/gui/ClipBoardManager.java b/src/main/java/org/jabref/gui/ClipBoardManager.java index 0411f8afd4e..64bc6617533 100644 --- a/src/main/java/org/jabref/gui/ClipBoardManager.java +++ b/src/main/java/org/jabref/gui/ClipBoardManager.java @@ -1,5 +1,10 @@ package org.jabref.gui; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -7,9 +12,11 @@ import java.util.List; import java.util.Optional; +import javafx.scene.control.TextInputControl; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.input.DataFormat; +import javafx.scene.input.MouseButton; import org.jabref.Globals; import org.jabref.logic.bibtex.BibEntryWriter; @@ -30,36 +37,53 @@ import org.slf4j.LoggerFactory; public class ClipBoardManager { + public static final DataFormat XML = new DataFormat("application/xml"); private static final Logger LOGGER = LoggerFactory.getLogger(ClipBoardManager.class); - private final Clipboard clipboard; - private final ImportFormatReader importFormatReader; + private static Clipboard clipboard; + private static java.awt.datatransfer.Clipboard primary; + private static ImportFormatReader importFormatReader; - public ClipBoardManager() { - this(Clipboard.getSystemClipboard(), Globals.IMPORT_FORMAT_READER); + public ClipBoardManager(Clipboard clipboard, java.awt.datatransfer.Clipboard primary, ImportFormatReader importFormatReader) { + ClipBoardManager.clipboard = clipboard; + ClipBoardManager.primary = primary; + ClipBoardManager.importFormatReader = importFormatReader; } - public ClipBoardManager(Clipboard clipboard, ImportFormatReader importFormatReader) { - this.clipboard = clipboard; - this.importFormatReader = importFormatReader; + public ClipBoardManager() { + this(Clipboard.getSystemClipboard(), Toolkit.getDefaultToolkit().getSystemSelection(), Globals.IMPORT_FORMAT_READER); } /** - * Puts content onto the clipboard. + * Add X11 clipboard support to a text input control. + * It is necessary to call this method in every input where you want to use it: + * {@code ClipBoardManager.addX11Support(TextInputControl input);}. + * + * @param input the TextInputControl (e.g., TextField, TextArea, and children) where adding this functionality. + * @see Short summary for X11 clipboards + * @see Longer text over clipboards */ - public void setContent(ClipboardContent content) { - clipboard.setContent(content); + public static void addX11Support(TextInputControl input) { + input.selectedTextProperty().addListener((observable, oldValue, newValue) -> { + if (!newValue.isEmpty()) { + primary.setContents(new StringSelection(newValue), null); + } + }); + input.setOnMouseClicked(event -> { + if (event.getButton() == MouseButton.MIDDLE) { + input.insertText(input.getCaretPosition(), getContentsPrimary()); + } + }); } /** - * Get the String residing on the clipboard. + * Get the String residing on the system clipboard. * - * @return any text found on the Clipboard; if none found, return an - * empty String. + * @return any text found on the Clipboard; if none found, return an empty String. */ - public String getContents() { + public static String getContents() { String result = clipboard.getString(); if (result == null) { return ""; @@ -67,16 +91,54 @@ public String getContents() { return result; } + /** + * Get the String residing on the primary clipboard. + * + * @return any text found on the primary Clipboard; if none found, try with the system clipboard. + */ + public static String getContentsPrimary() { + Transferable contents = primary.getContents(null); + if (contents != null && contents.isDataFlavorSupported(DataFlavor.stringFlavor)) { + try { + return (String) contents.getTransferData(DataFlavor.stringFlavor); + } catch (UnsupportedFlavorException | IOException e) { + LOGGER.warn(e.getMessage()); + } + } + return getContents(); + } + + /** + * Puts content onto the system clipboard. + * + * @param content the ClipboardContent to set as current value of the system clipboard. + */ + public void setContent(ClipboardContent content) { + clipboard.setContent(content); + setPrimaryClipboardContent(content); + } + + /** + * Puts content onto the primary clipboard. + * + * @param content the ClipboardContent to set as current value of the primary clipboard. + */ + public void setPrimaryClipboardContent(ClipboardContent content) { + primary.setContents(new StringSelection(content.getString()), null); + } + public void setHtmlContent(String html) { final ClipboardContent content = new ClipboardContent(); content.putHtml(html); clipboard.setContent(content); + setPrimaryClipboardContent(content); } public void setContent(String string) { final ClipboardContent content = new ClipboardContent(); content.putString(string); clipboard.setContent(content); + setPrimaryClipboardContent(content); } public void setContent(List entries) throws IOException { @@ -86,6 +148,7 @@ public void setContent(List entries) throws IOException { content.put(DragAndDropDataFormats.ENTRIES, serializedEntries); content.putString(serializedEntries); clipboard.setContent(content); + setPrimaryClipboardContent(content); } public List extractData() { diff --git a/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java b/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java index 65a55854974..13a7f7f6ac3 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java +++ b/src/main/java/org/jabref/gui/fieldeditors/EditorTextArea.java @@ -10,6 +10,8 @@ import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; +import org.jabref.gui.ClipBoardManager; + public class EditorTextArea extends javafx.scene.control.TextArea implements Initializable, ContextMenuAddable { private final ContextMenu contextMenu = new ContextMenu(); @@ -29,6 +31,8 @@ public EditorTextArea(final String text) { // Hide horizontal scrollbar and always wrap text setWrapText(true); + + ClipBoardManager.addX11Support(this); } @Override diff --git a/src/main/java/org/jabref/gui/fieldeditors/EditorTextField.java b/src/main/java/org/jabref/gui/fieldeditors/EditorTextField.java index a10583e2774..215083980a8 100644 --- a/src/main/java/org/jabref/gui/fieldeditors/EditorTextField.java +++ b/src/main/java/org/jabref/gui/fieldeditors/EditorTextField.java @@ -11,6 +11,8 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import org.jabref.gui.ClipBoardManager; + public class EditorTextField extends javafx.scene.control.TextField implements Initializable, ContextMenuAddable { private final ContextMenu contextMenu = new ContextMenu(); @@ -25,6 +27,8 @@ public EditorTextField(final String text) { // Always fill out all the available space setPrefHeight(Double.POSITIVE_INFINITY); HBox.setHgrow(this, Priority.ALWAYS); + + ClipBoardManager.addX11Support(this); } @Override diff --git a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java index 6938411b4ad..9ce56f914b7 100644 --- a/src/main/java/org/jabref/gui/search/GlobalSearchBar.java +++ b/src/main/java/org/jabref/gui/search/GlobalSearchBar.java @@ -34,6 +34,7 @@ import org.jabref.Globals; import org.jabref.gui.BasePanel; +import org.jabref.gui.ClipBoardManager; import org.jabref.gui.JabRefFrame; import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.AppendPersonNamesStrategy; @@ -111,6 +112,8 @@ public GlobalSearchBar(JabRefFrame frame, StateManager stateManager) { } }); + ClipBoardManager.addX11Support(searchField); + regularExp = IconTheme.JabRefIcons.REG_EX.asToggleButton(); regularExp.setSelected(searchPreferences.isRegularExpression()); regularExp.setTooltip(new Tooltip(Localization.lang("regular expression")));