Method call when changing clipboard contents

I am trying to create a small desktop application that should display the contents of the clipboard (if it is a string). I made a constructor that does this and it works well, now I just want to make a call to a similar method whenever text is copied to the clipboard in the OS. I am completely new to this, so any help would be appreciated! Something tells me that I must somehow use interrupts ...

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); } 
+7
source share
6 answers

You can call Clipboard.addFlavorListener to listen to the 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:

  • To start the application, consider the initial threads .
  • Call JFrame.pack to set the frame size.
  • Key bindings are preferred KeyListeners for displaying KeyEvents in Swing.
+7
source

I am using 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 eg for loading huge elements like word 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 another program takes responsibility for the clipboard, it waits for 250 ms and returns the clipboard owner with the updated content.

+10
source

Below SSCCE ... you can run it and select the text and go Ctrl-C, several times ... the selected text will be printed.

As you can see, this is a little more than Ramius' answer. You really need to clear the clipboard (which is hard!) For the taste listener to respond every time you copy new text.

In addition, you need to disable the output caused by the โ€œtaste changeโ€ when cleaning the clipboard ... although there may be a more sensible solution than mine.

 public class ClipboardListenerTest { public static void main(String[] args) throws InvocationTargetException, InterruptedException { SwingUtilities.invokeAndWait(new Runnable() { public void run() { final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.addFlavorListener(new FlavorListener() { // this is needed to prevent output when you clear the clipboard boolean suppressOutput = false; // this is a specially devised Transferable - sole purpose to clear the clipboard Transferable clearingTransferable = new Transferable() { public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[0]; } public boolean isDataFlavorSupported(DataFlavor flavor) { return false; } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { throw new UnsupportedFlavorException(flavor); } }; @Override public void flavorsChanged(FlavorEvent e) { Transferable contentsTransferable = clipboard.getContents(null); // NB the Transferable returned from getContents is NEVER the same as the // clearing Transferable! if (!suppressOutput) { System.out.println(String.format("# clipboard UPDATED, src %s, string %s, clearingT? %b", e.getSource(), e.toString(), contentsTransferable == clearingTransferable)); try { String stringData = (String)clipboard.getData(DataFlavor.stringFlavor); System.out.println(String.format("# string data |%s|", stringData )); } catch (UnsupportedFlavorException | IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } else { // my experiments seem to show that you have to spawn a new Runnable if you want // to leave suppressOutput long enough for it to prevent the "CLEAR" operation // producing output... SwingUtilities.invokeLater(new Runnable() { @Override public void run() { suppressOutput = false; } }); } suppressOutput = true; clipboard.setContents(clearingTransferable, null); } }); } }); int i = 0; while (i < 100) { Thread.sleep(500L); System.out.println("# pibble"); i++; } } } 
+1
source

I found another solution to this problem: the next listener constantly reads the contents of the clipboard using a loop. If text is found, it will be compared with the previous contents of the clipboard that is cached. When the clipboard contains new text that has not previously been cached, it can perform some actions, such as โ€œnotify observers,โ€ as in this example, which may cause the GUI to refresh.

In this example, content changes when the clipboard contains something other than a string are ignored.

In addition to detecting content type changes (i.e., using FlavorListerner), this solution detects changes by continuously comparing strings. Just by reading access to the clipboard, I would expect this code to cause less interference to other applications than, for example, if it becomes the owner 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, eg: 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(); } } 
+1
source

Reimeus recommended using Clipboard.AddFlavorListener. I would like to expand his answer a little. The way I did this in my program looks like this:

 final Clipboard SYSTEM_CLIPBOARD = Toolkit.getDefaultToolkit().getSystemClipboard(); SYSTEM_CLIPBOARD.addFlavorListener(listener -> { string clipboardText = (String) SYSTEM_CLIPBOARD.getData(DataFlavor.stringFlavor); SYSTEM_CLIPBOARD.setContents(new StringSelection(clipboardText), null); System.out.println("The clipboard contains: " + clipboardText); } 

This eliminates the problem that the listener only starts when the new application copies the contents to the clipboard, so the program itself becomes an application that copies text to the clipboard.

A warning that the listener will be called twice for each copy event, but, of course, there are ways to handle this. At least in this situation, the clipboard event will be raised correctly every time something is copied to the clipboard.

0
source

I'm not quite sure if this is the right and optimal approach, but at least it worked just fine for me. The following is an example clipboard handler that implements the clipboard owner interface and reads the clipboard every time it loses ownership. He then gains ownership to be able to lose it the next time a new record gets to the clipboard to read it again. It also allows you to pragmatically set the contents of the clipboard (the following example provides a new line stdin).

 public static class ClipboardHandler implements ClipboardOwner, Runnable { private final Logger logger = LoggerFactory.getLogger(getClass()); private final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); private final Consumer<String> bufferConsumer; public ClipboardHandler(Consumer<String> bufferConsumer) { this.bufferConsumer = bufferConsumer; } @Override public void lostOwnership(Clipboard clipboard, Transferable notUsed) { Transferable contents = clipboard.getContents(this); if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { String string = (String) contents.getTransferData(DataFlavor.stringFlavor); bufferConsumer.accept(string); } catch (Exception e) { logger.error("Unable to read clipboard buffer.", e); } } getOwnership(contents); } @Override public void run() { Transferable transferable = clipboard.getContents(this); getOwnership(transferable); } public void setBuffer(String buffer) { getOwnership(new StringSelection(buffer)); } private void getOwnership(Transferable transferable) { clipboard.setContents(transferable, this); } } public static void main(String[] args) { ClipboardHandler clipboardHandler = new ClipboardHandler(System.out::println); EventQueue.invokeLater(clipboardHandler); Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()) { String buffer = scanner.nextLine(); if (!buffer.trim().isEmpty()) { clipboardHandler.setBuffer(buffer); } } } 
0
source

All Articles