I'm trying to make a little desktop app that should show the contents of the clipboard (if it is a string). I have done a constructor that does that and it works well, now I just want to make a call to a similar method whenever a text is copied into the clipboard in the OS. I'm quite new to this so any help would be appreciated! Something tells me I should use interrupts in some way...
package pasty;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public class PastyFrame implements KeyListener {
String currentClipboardString;
JLabel clipboardLabel = new JLabel();
public PastyFrame() {
JFrame frame = new JFrame();
frame.setVisible(true);
try {
currentClipboardString = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor);
} catch (UnsupportedFlavorException | IOException ex) {
Logger.getLogger(PastyFrame.class.getName()).log(Level.SEVERE, null, ex);
currentClipboardString = "";
}
if (currentClipboardString.isEmpty()) {
currentClipboardString = "The clipboard is empty";
}
frame.setSize(400, 100);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLayout(new FlowLayout());
clipboardLabel.setText(currentClipboardString);
frame.add(clipboardLabel);
}
You can call Clipboard.addFlavorListener to listen for clipboard updates from the OS:
Some Side Notes:
JFrame.pack
to set the frame size.KeyListeners
for mappingKeyEvents
in Swing.I came up with another solution to this problem: The following listener continuously reads the clipboard content using a loop. If a text is detected, it will be compared to the previous content of the clipboard, which is cached. When the clipboard contains a new text that was not cached previously, it may perform some action like 'notify observers', as in this example, which may prompt a GUI to update itself.
In this example, content changes, where the clipboard contains something other than a string, are ignored.
Other than just detecting content type changes (i.e. by using the FlavorListerner), this solution detects changes by continuous string comparison. By just read-accessing the clipboard, I'd expect this code to cause less interference with other applications than e.g. by taking ownership of the clipboard.
Suggestions are welcome.
Reimeus recommended using Clipboard.AddFlavorListener. I'd like to expand on his answer slightly. The way I've been doing this in my program is like this:
This remedies the problem of the listener only being fired when a new app copies contents to the clipboard, by essentially making the program itself become the app that copies the text to the clipboard.
The caveat of this being the listener will get called twice for each copy event, but there are ways to deal with that, of course. At least, in this situation, the clipboard event will be called properly every time something is copied to the clipboard.
I use this. The whole class.
When other program takes ownership of the clipboard it waits 250 ms and takes back clipboard's ownership with updated content.
Below is an SSCCE... you can run it and select text and go Ctrl-C, multiple times ... the text selected gets printed out.
As you can see it is a little more involved than Reimius' answer. You do in fact have to clear the clipboard (which is tricky!) for the flavour listener to respond each time you copy some new text.
Furthermore you probably need to suppress output caused by the "flavour change" when you clear the clipboard... though there may be a cleverer solution than mine.
I'm not completely sure if it is correct and optimal approach, but at least it worked for me just fine. Below is the sample of the clipboard handler which implements clipboard owner interface and reads clipboard buffer each time it loses the ownership. Then it gets the ownership back to be able to lose it next time when new entry comes to the clipboard to read it again. It also allows to set clipboard contents pragmatically (in following example providing new stdin line).