Search code examples
javaclipboard

Java clipboad mistake


There is such a program. It must analyze the clipboard for the presence of a five-digit number in it. But when you first copy the text that meets the condition, the program works fine, but if you copy the second text in the same window, the program that meets the condition does not work. That is, it works only if you periodically change windows.

The question is to get the program to work with each copy?

import java.awt.*;
import java.awt.datatransfer.*;
import java.io.IOException;

public class Main implements FlavorListener {
    private static Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

    public static void main(String[] args) throws InterruptedException {
        clipboard.addFlavorListener(new Main());

        // fall asleep for 100 seconds, otherwise the program will immediately end

        Thread.sleep(100 * 1000);
    }

    @Override
    public void flavorsChanged(FlavorEvent event) {
        try {
            String clipboardContent = (String) clipboard.getData(DataFlavor.stringFlavor);
            handleClipboardContent(clipboardContent);
        } catch (UnsupportedFlavorException | IOException e) {
            // TODO handle error
            e.printStackTrace();
        }
    }

    private void handleClipboardContent(String clipboardContent) {

        // we check that the length of the string is five
       if (clipboardContent != null && clipboardContent.length() == 5)  
         {


      System.out.println(clipboardContent);
      } 
      else {
        System.out.println("condition false");


        }

    }
}
// 12345
// 56789

Solution

  • The FlavorListener will notify you when the "type" of data in the Clipboard has changed, not when the data itself has changed. This means if you copy a String to the Clipboard, you "might" be notified, but if you copy another String to the Clipboard, you won't, because the type of data has not changed.

    The "common" solution to the problem you're facing is to reset the contents of the clipboard to a different flavour. The problem with this is, what happens if some other program wants the data? You've just trampled all over it

    Instead, you could "peek" at the data on a periodical bases and check to see if the contents has changed or not. A basic solution would be to use a Thread which maintained the hashCode of the current String contents, when the hashCode changes, you would then grab a copy and perform what ever operations you wanted on it.

    Maybe something like...

    import java.awt.Toolkit;
    import java.awt.datatransfer.Clipboard;
    import java.awt.datatransfer.DataFlavor;
    import java.awt.datatransfer.FlavorEvent;
    import java.awt.datatransfer.FlavorListener;
    import java.awt.datatransfer.Transferable;
    import java.awt.datatransfer.UnsupportedFlavorException;
    import java.io.IOException;
    
    public class Test {
    
        public static void main(String[] args) {
            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
            clipboard.addFlavorListener(new FlavorListener() {
                @Override
                public void flavorsChanged(FlavorEvent e) {
                    System.out.println("Flavor has changed");
                    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                    if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
                        try {
                            String text = (String) clipboard.getData(DataFlavor.stringFlavor);
                            textDidChangeTo(text);
                        } catch (UnsupportedFlavorException | IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            });
            Thread t = new Thread(new Runnable() {
                private Integer currentHashcode;
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ex) {
                        }
                        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                        Transferable contents = clipboard.getContents(this);
                        if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
                            try {
                                String text = (String) clipboard.getData(DataFlavor.stringFlavor);
                                if (currentHashcode == null) {
                                    currentHashcode = text.hashCode();
                                } else if (currentHashcode != text.hashCode()) {
                                    currentHashcode = text.hashCode();
                                    textDidChangeTo(text);
                                }
                            } catch (UnsupportedFlavorException | IOException ex) {
                                ex.printStackTrace();
                            }
                        } else {
                            currentHashcode = null;
                        }
                    }
                }
            });
            t.start();
        }
    
        public static void textDidChangeTo(String text) {
            System.out.println("Text did change to: " + text);
        }
    }
    

    Now, this is far from perfect. It may generate two events when the contents changes from something other then String to String. In this, based on your needs, you probably don't need the FlavorListener, but I've used it for demonstration purposes