Use JavaFX Clipboard API

Overview

Java has java.awt.datatransfer.Clipboard for manipulating the clipboard. Classes are available. The details are written by Mr. Hishidama, so please read that article.

Hishidama's Technical Memo Page-Clipboard

I'm the Clipboard API in JavaFX [javafx.scene.input.Clipboard](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/input/Clipboard I decided to look at .html).

Execution environment

Java SE 1.8.0_u131
OS Windows 10
IDE IntelliJ IDEA 2017.1.3

javafx.scene.input.Clipboard

Represents an operating system clipboard where data can be placed during operations such as cut, copy, and paste.

Well, it's a class that can do all sorts of clipboard related things.


Let's try! I will try to write the code.

Code that exits with an exception when executed


import javafx.scene.input.Clipboard;

import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        final Clipboard cb = Clipboard.getSystemClipboard();
        System.out.println(cb.getString());;
    }
}

Failure

As you can see from the package hierarchy, javafx.scene.input.Clipboard is a JavaFX class and is not available to regular threads.

Exception in thread "main" java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = main
	at com.sun.glass.ui.Application.checkEventThread(Application.java:443)
	at com.sun.glass.ui.ClipboardAssistance.<init>(ClipboardAssistance.java:40)
	at com.sun.javafx.tk.quantum.QuantumToolkit.getSystemClipboard(QuantumToolkit.java:1200)
	at javafx.scene.input.Clipboard.getSystemClipboardImpl(Clipboard.java:413)
	at javafx.scene.input.Clipboard.getSystemClipboard(Clipboard.java:178)
	at jp.toastkid.sandbox.Sandbox.main(Sandbox.java:10)

So, if you want to operate the clipboard in a normal application, use java.awt.datatransfer.Clipboard. ~~ End ~~

Code example that works

Since it can be used in any JavaFX application, make the class of the main method a subclass of javafx.application.Application.

Code that works fine


import javafx.application.Application;
import javafx.scene.input.Clipboard;
import javafx.stage.Stage;

public class MainFx extends Application {
    public static void main(String[] args) {
        Application.launch(SandboxFx.class);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Clipboard cb = Clipboard.getSystemClipboard();
        System.out.println(cb.getString());
    }
}

Running this code will output the string currently held in the clipboard.

final Clipboard cb = Clipboard.getSystemClipboard();
System.out.println(cb.getString());

About the method

getContentTypes() Gets the type of content that is being held.

getContentTypes()


System.out.println(cb.getContentTypes());

For text

[[text/uri-list], [JAVA_DATAFLAVOR:application/x-java-jvm-local-objectref; class=com.intellij.codeInsight.editorActions.FoldingData], [text/plain], [ms-stuff/oem-text], [text/rtf], [text/html], [ms-stuff/locale]]

For image files

[[FileName], [message/external-body;access-type=clipboard;index=1;size=28453;name="650contribution.png "], [AsyncFlag], [DataObjectAttributesRequiringElevation], [application/x-java-file-list, java.file-list], [FileNameW], [ms-stuff/preferred-drop-effect], [message/external-body;access-type=clipboard;index=2;size=220376;name="sample_images_1.jpg "], [text/uri-list], [message/external-body;access-type=clipboard;index=0;size=119242;name="sample_images_2.jpg "], [Shell Object Offsets], [Shell IDList Array], [DataObjectAttributes]]

For images on the internet

[[text/uri-list], [application/x-java-rawimage], [text/_moz_htmlinfo], [text/html;cf=49474], [application/x-java-file-list, java.file-list], [text/_moz_htmlcontext], [application/x-moz-nativeimage], [text/html], [application/x-moz-file-promise-url], [application/x-moz-file-promise-dest-filename], [cf17], [ms-stuff/preferred-drop-effect]]

hasXX() Determines the type of content you are holding.

hasXX()Code to try


System.out.println(cb.hasString());
System.out.println(cb.hasUrl());
System.out.println(cb.hasHtml());
System.out.println(cb.hasRtf());
System.out.println(cb.hasImage());
System.out.println(cb.hasFiles());

Execution result

T: true F: false

Method name Text Local image file Web image file URL on Browser
hasString() T F F T
hasUrl() T T T F
hasHtml() T F T F
hasRtf() T F F F
hasImage() F F T F
hasFiles() F T T F

setContent(Map<DataFormat, Object>) Not only can you retrieve the value, you can also plug it in. Use Map and DataFormat for merging. Dat ** a ** Format. Please note the name.

Single value

final Map<DataFormat, Object> content = new HashMap<>();
content.put(DataFormat.PLAIN_TEXT, "Orange");
cb.setContent(content);

If you press Ctrl + V on a text editor after running the application, the text "Orange" will be pasted.

Multiple values

But what if we have multiple values in the content?

final Map<DataFormat, Object> content = new HashMap<>();
content.put(DataFormat.URL, "https://www.yahoo.co.jp");
content.put(DataFormat.PLAIN_TEXT, "Toast");
cb.setContent(content);

If you press Ctrl + V on a text editor after running the application, the text "Toast" will be pasted. It seems that only one can be kept in the clipboard.

clear() This Clipboard API has a clear method.

Clipboard.clear()


System.out.println(clipboard.getString());
clipboard.clear();
System.out.println(clipboard.getString());

Execution result


holding text
null

What can you do with this API?

Since it is a JavaFX API, its use is inevitably combined with a GUI.

1. Show the value you currently have on the clipboard

Well, that's it.

2. Implementation of multi-clipboard in editor application

As the name implies, multi-clipboard is a function that can hold multiple values in the clipboard, and it seems that it is a standard function of UNIX text editors. What would happen if I talked to people about this function in a roundabout way? "Productive Programmer" had such a story.

You might end up being told by a man wearing sandals, "I've been using it for 20 years since high school," for about an hour.

3. Implementation of clipboard monitoring application

For example, if the image is retained, it will be automatically saved to a file, or if it is a URL, a WebView window will be displayed ...


Implementation of a simple clipboard monitoring application

So, I tried to implement a very simple one. The source code can be found in the following repository on GitHub.

https://github.com/toastkidjp/clipboard_observer_app/tree/qiita

screenshot

sample.png

It's a sloppy GUI application that displays a string or image held in the clipboard in a window and clicks on them to re-hold them in the clipboard.

Clipboard event monitoring

Since javafx.scene.input.Clipboard does not have a reactive API such as an event listener, I implemented it by using RxJava to check the contents at 1-second intervals.

Observable to monitor the clipboard


Observable.interval(1, TimeUnit.SECONDS)
          .observeOn(JavaFxScheduler.platform())
          .map(l -> getStringOrEmpty())
          .filter(str -> !str.isEmpty())
          .subscribe(stringConsumer, Throwable::printStackTrace);

RxJavaFX JavaFxScheduler

The RxJava Scheduler provided by RxJavaFX allows JavaFX application threads to execute processing in RxJava. I will. [Platform.runLater (Runnable)](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/application/Platform.html#runLater-] which is prepared as standard for Consumer etc. You can use java.lang.Runnable-), but if you have to return a value like map or filter, this JavaFxScheduler is useful.

Null non-allowable in RxJava2 map etc.

In RxJava2, returning null in map will result in a NullPointerException.

java.lang.NullPointerException: The mapper function returned a null value.
	at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39)
	at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:59)
	at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:200)
	at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
	at io.reactivex.rxjavafx.schedulers.JavaFxScheduler$JavaFxWorker$QueuedRunnable.run(JavaFxScheduler.java:87)
	at io.reactivex.rxjavafx.schedulers.JavaFxScheduler$JavaFxWorker.run(JavaFxScheduler.java:158)
	at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
	at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
	at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
	at java.lang.Thread.run(Thread.java:748)

To avoid this, we provide an Empty object.

private static final Image EMPTY_IMAGE = new WritableImage(1, 1);

Summary

I introduced JavaFX's Clipboard API and explained how to implement a simple application using it. If you are not creating a JavaFX application, not here java.awt.datatransfer.Clipboard. The JavaFX Clipboard API has a clear () method.

Recommended Posts

Use JavaFX Clipboard API
Use JavaFX / TextFormatter
How to use Chain API
Use Bulk API with RestHighLevelClient
Use Face API from Ruby
[Rails] Use cookies in API mode
[JavaFX] [Java8] How to use GridPane