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);
}
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.
package gui;
import java.awt.HeadlessException;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Observable;
/**
* @author Matthias Hinz
*/
class ClipboardTextListener extends Observable implements Runnable {
Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
private volatile boolean running = true;
public void terminate() {
running = false;
}
public void run() {
System.out.println("Listening to clipboard...");
// the first output will be when a non-empty text is detected
String recentContent = "";
// continuously perform read from clipboard
while (running) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
// request what kind of data-flavor is supported
List<DataFlavor> flavors = Arrays.asList(sysClip.getAvailableDataFlavors());
// this implementation only supports string-flavor
if (flavors.contains(DataFlavor.stringFlavor)) {
String data = (String) sysClip.getData(DataFlavor.stringFlavor);
if (!data.equals(recentContent)) {
recentContent = data;
// Do whatever you want to do when a clipboard change was detected, e.g.:
System.out.println("New clipboard text detected: " + data);
setChanged();
notifyObservers(data);
}
}
} catch (HeadlessException e1) {
e1.printStackTrace();
} catch (UnsupportedFlavorException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
public static void main(String[] args) {
ClipboardTextListener b = new ClipboardTextListener();
Thread thread = new Thread(b);
thread.start();
}
}
You can call Clipboard.addFlavorListener to listen for clipboard updates from the OS:
Toolkit.getDefaultToolkit().getSystemClipboard().addFlavorListener(new FlavorListener() {
@Override
public void flavorsChanged(FlavorEvent e) {
System.out.println("ClipBoard UPDATED: " + e.getSource() + " " + e.toString());
}
});
Some Side Notes:
JFrame.pack
to set the frame size.KeyListeners
for mapping KeyEvents
in Swing.I use this. The whole class.
public class ClipBoardListener extends Thread implements ClipboardOwner{
Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
@Override
public void run() {
Transferable trans = sysClip.getContents(this);
TakeOwnership(trans);
}
@Override
public void lostOwnership(Clipboard c, Transferable t) {
try {
ClipBoardListener.sleep(250); //waiting e.g for loading huge elements like word's etc.
} catch(Exception e) {
System.out.println("Exception: " + e);
}
Transferable contents = sysClip.getContents(this);
try {
process_clipboard(contents, c);
} catch (Exception ex) {
Logger.getLogger(ClipBoardListener.class.getName()).log(Level.SEVERE, null, ex);
}
TakeOwnership(contents);
}
void TakeOwnership(Transferable t) {
sysClip.setContents(t, this);
}
public void process_clipboard(Transferable t, Clipboard c) { //your implementation
String tempText;
Transferable trans = t;
try {
if (trans != null?trans.isDataFlavorSupported(DataFlavor.stringFlavor):false) {
tempText = (String) trans.getTransferData(DataFlavor.stringFlavor);
System.out.println(tempText);
}
} catch (Exception e) {
}
}
}
When other program takes ownership of the clipboard it waits 250 ms and takes back clipboard's ownership with updated content.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With