2

I need to set clipboard contents, with a java application, such that there are two MIME types available to the user. This will allow the user to paste the version of the text they want to.

I found an example that applies to JavaScript, I'm hoping that there is an equivalent for Java.

JavaScript Example:

const blob = new Blob([
    '<a href="https://stackoverflow.com/">goto stack</a>'
], { type: "text/html" });
const blobPlain = new Blob(['goto stack'], { type: "text/plain" });

navigator.clipboard.write([
    new ClipboardItem({
        [blob.type]: blob,
        [blobPlain.type]: blobPlain
    },),
])

This is essentially what I have in my Java application:

import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Clipboard;

public class tester{

 public static void main(String[] args){

     // from string to clipboard
    StringSelection selection = new StringSelection("hi");
    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    clipboard.setContents(selection, selection);
 }
}

I know how to set the contents, with either a "text/plain" string or a "text/html" string. The above Java example will place a plain-text string in the clipboard, it can easily be modified to put HTML formatted text in the clipboard instead - but only one or the other, not both.

If I use the following code, I can get the clipboard contents as a transferable object and then set the clipboard to that same transferable object.

@Override
    public void run() {
        try {
                Transferable trans = systemClipboard.getContents(this);
                setThisOwner(trans);
                ClipboardMonitor.sleep(sleepInterval);
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
        }
    

    private void setThisOwner(Transferable clipContents) {
        try {
            systemClipboard.setContents(clipContents, this);
        } catch (Exception ex) {
            System.out.println(ex.toString());
        }
    }

The above isn't useful so far as my question here is concerned, but the code appears to indicate to me that there should be a way to do what I want to. If I could, for example, create a custom "Transferable" and place that in "setContents()"?

Is there a way I can place both plain-text and HTML strings in the clipboard?

RHSkinner
  • 37
  • 7
  • You need to supply multiple `DataFlovor`s, each describing the style of content they support, for [example](https://stackoverflow.com/questions/24966974/copy-jtable-row-with-its-grid-lines-into-excel-word-documents/24978019#24978019); [example](https://stackoverflow.com/questions/30518705/copy-jtextarea-as-text-html-dataflavor/30519137#30519137) – MadProgrammer Mar 20 '22 at 20:39
  • Thank you @MadProgrammer - I have implemented something similar to the first example already, the second example does appear to cover exactly the functionality I'm looking for. I'm going to try understand it and implement it, if it works I'll indicate that here and mark this as answered – RHSkinner Mar 20 '22 at 20:50

1 Answers1

2

So, after a little bit of playing around (and reading the updated JavaDocs). The basic answer is.

  1. You need to supply both a "plain text" and "html text" value
  2. You need to supply multiple data flavours, each representing the type of data you're willing to export
  3. You need to return the appropriate data from getTransferData based on the flavour (and the flavours desired export method)

Please Note

This example exports two different strings, one is plain text and one is HTML, this is deliberate, so as it's easier to see when different text is used by different consumers. You could, obviously, use the html formatted text for both if you wished.

enter image description here

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.util.ArrayList;
import java.util.List;

public final class Main {
    public static void main(String[] args) {

        String plainText = "Hello World";
        String htmlText = "<html><body><h1>This is a test</h1></body></html>";

        HtmlSelection htmlSelection = new HtmlSelection(htmlText, plainText);
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.setContents(htmlSelection, null);
    }

    public static class HtmlSelection implements Transferable {

        private static List<DataFlavor> htmlFlavors = new ArrayList<>(3);

        static {
                htmlFlavors.add(DataFlavor.stringFlavor);
                htmlFlavors.add(DataFlavor.allHtmlFlavor);
        }

        private String html;
        private String plainText;

        public HtmlSelection(String html, String plainText) {
            this.html = html;
            this.plainText = plainText;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return (DataFlavor[]) htmlFlavors.toArray(new DataFlavor[htmlFlavors.size()]);
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return htmlFlavors.contains(flavor);
        }

        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {

            String toBeExported = plainText;
            if (flavor == DataFlavor.stringFlavor) {
                toBeExported = plainText;
            } else if (flavor == DataFlavor.allHtmlFlavor) {
                toBeExported = html;
            }

            if (String.class.equals(flavor.getRepresentationClass())) {
                return toBeExported;
            }
            throw new UnsupportedFlavorException(flavor);
        }
    }
}

You should also beware that some applications may prefer a different method of export (ie via a Reader or InputStream), this is demonstrated in...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366