I want to find a way to do kind of a custom "paste from clipboard" action. Let's assume the content of clipboard is text for simplicity (not a file). Whenever you press Ctrl+V, it inserts that content (which is text) to a current open file which has a focus.
I have an app for catching a global hotkey. Note this is not a window application, it's a console one and it catches the hotkey globally. Let's say I have the hotkey of Ctrl+U. So what I want to do is when I press Ctrl+U I want to insert some predefined text to a current open file. Just like Ctrl+V does! The differences from a standard Ctrl+V is that I want to insert a predefined text and the hotkey is different.
How do I do this?
I'd prefer a cross-platform solution, however first of all I'm going to do that for Linux, specifically Ubuntu. The language is not important but Java or Scala would be better. Of course, I understand that the solutions is Java uses native OS' API for that.
I'm hoping that this hackish solution would work, but it is still untested, I am unsure how to catch the event for the hotkey.
The idea behind this code is the following five steps:
Get the old text in the clipboard and temporarily save it
Paste our predefined text into the clipboard
Trigger the global paste event
Release the global paste event
Reset the clipboard to the old text
This should give the appearance of a new clipboard (if not, hopefully it inspires you to come up with a better, less hackish solution).
Without further ado, here is my code. First I have a simple helper method to set the value of the clipboard (as we do this twice).
public static void setClipboard(String s) {
StringSelection contents = new StringSelection(s);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(contents, contents);
}
And then, I have a main method where I go through the five steps in order.
public static void main(String[] args) {
// Step 1 ) get old text
String oldText = "";
try {
oldText = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor);
} catch (UnsupportedFlavorException ufe) {
ufe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// Step 2 ) paste our text in clipboard
setClipboard("This lorem ipsum predefined string blows my mind.");
// Step 3 ) trigger paste event
Robot robot = null;
try {
robot = new Robot();
} catch (AWTException awte) {
awte.printStackTrace();
}
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_V);
// Step 4 ) Release paste event
robot.keyRelease(KeyEvent.VK_CONTROL);
robot.keyRelease(KeyEvent.VK_V);
// Step 5 ) Reset clipboard
setClipboard(oldText);
}
[Edit]:
Here is some code to test what kind of contents are in the Clipboard - image, text, etc. The unicode error was coming from the fact that the old contents of the clipboard were something that couldn't be represented by a plain String. To fix this error, you will have to check if the old contents were an image, the old contents were text, and save them accordingly.
public static int kindOfContents() {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable contents = clipboard.getContents(null);
if(contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
// String, save temporarily as string and write back as string
return 0;
} else if(contents.isDataFlavorSupported(DataFlavor.imageFlavor)) {
// Image, save temporarily as BufferedImage and write back as image
return 1;
} else if(contents.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
// List of files, save temporarily as java.util.List interface and write back as the file lists
return 2;
}
}
If the contents are text, then for saving and writing the content you would use the old method, repasted below for convenience.
// Step 1 ) get old text
String oldText = "";
try {
oldText = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor);
} catch (UnsupportedFlavorException ufe) {
ufe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// Step 5 ) Reset clipboard
setClipboard(oldText);
However, if the contents are an image, then for saving temporarily and rewriting you need to do the following. Note that the code for writing the image is not mine, but is taken from the accepted answer at Setting images to Clipboard - Java
// Step 1 ) get old image
BufferedImage img = null;
try {
img = (BufferedImage) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.imageFlavor);
} catch (UnsupportedFlavorException ufe) {
ufe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
Taken from Setting images to Clipboard - Java :
// Step 5 ) Reset clipboard
ImageTransferable transferable = new ImageTransferable( image );
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(transferable, null);
static class ImageTransferable implements Transferable
{
private Image image;
public ImageTransferable (Image image)
{
this.image = image;
}
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException
{
if (isDataFlavorSupported(flavor))
{
return image;
}
else
{
throw new UnsupportedFlavorException(flavor);
}
}
public boolean isDataFlavorSupported (DataFlavor flavor)
{
return flavor == DataFlavor.imageFlavor;
}
public DataFlavor[] getTransferDataFlavors ()
{
return new DataFlavor[] { DataFlavor.imageFlavor };
}
}
Related
I have following trouble, when i copy Intellij Idea Editor text, and run
Toolkit.defaultToolkit.systemClipboard.getData(DataFlavor.stringFlavor)
it will raise:
Exception "java.lang.ClassNotFoundException: com/intellij/codeInsight/editorActions/FoldingData"while constructing DataFlavor for: application/x-java-jvm-local-objectref; class=com.intellij.codeInsight.editorActions.FoldingData
In fact, I hope ignore clip content with FoldingData, how to detect current clipboard DataFlavor
Clipboard data may be available in multiple flavors. Therefore, you should use Clipboard.getAvailableDataFlavors() and iterate through the array to determine if the DataFlavor you are looking for is there.
See http://docs.oracle.com/javase/7/docs/api/java/awt/datatransfer/Clipboard.html#getAvailableDataFlavors()
But if you are getting a ClassNotFoundException, this means that your runtime classpath is missing dependencies, so you need to fix this
This way you can check the DataFlavor earlier to avoid the UnsupportedFlavorException later
public class ClipBoard {
public static void main(String args[]) {
Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
try {
if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
String text = (String)t.getTransferData(DataFlavor.stringFlavor);
text=text.toUpperCase();
StringSelection ss = new StringSelection(text);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Read more: http://mrbool.com/manipulating-clipboard-content-with-java/24758#ixzz4tta4bwNQ
I created a multi page editor with a text editor page and a Master Details Block page. In the Master Details block I have a list with entries which have been parsed from a text file.
These text files have the following structure:
myVar = 23423423;
SEARCH(myVar)
block
{
text = test;
}
When I click on one of the entries, for example myVar, a detail block with two forms will be displayed, one input field for the variable name and one for the corresponding value. If I change the entries through this form, the respective object will be modified. However, it will not mark the multi page editor as dirty and it will not ask me if I want to save my changes. greg-449 told me today, that I need to save the file manually, so I tried the following:
public class MyParser {
private MyModel model;
private long lastModified;
private IFile file;
public MyParser(FileEditorInput input) {
this.file = input.getFile();
this.lastModified = input.getFile().getModificationStamp();
parse();
}
private void parse() {
try {
InputStream in = file.getContents();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String line;
List<MyField> variables = new ArrayList<MyField>();
while ((line = reader.readLine()) != null) {
String[] splittedLine = line.split("=");
if (splittedLine.length >= 2)
variables
.add(new MyVariable(splittedLine[0], splittedLine[1]));
// Implement other entry types
}
System.out.println(out.toString()); // Prints the string content
// read from input stream
reader.close();
this.setModel(new MyModel().add(variables, false));
} catch (CoreException | IOException ex) {
}
}
public void saveChanges() {
FileWriter writer = null;
try {
writer = new FileWriter(file.getLocation().toFile());
try {
saveChanges(writer);
} finally {
writer.close();
}
if (file != null) {
try {
file.refreshLocal(IResource.DEPTH_INFINITE,
new NullProgressMonitor());
} catch (CoreException e) {
}
}
} catch (IOException ex) {
}
}
public void saveChanges(Writer writer) {
//TODO
this.getModel().setDirty(false);
}
public long getLastModified() {
return this.lastModified;
}
public MyModel getModel() {
return model;
}
public void setModel(MyModel model) {
this.model = model;
}
How can I save the modified objects to the original file? Do I need to overwrite the file completely or is it possible to change only the dirty values? Furthermore, the order of the entries in the text files is important. Do I need to remember the line numbers, because this may be a problem when adding new entries somewhere in the middle of the file.
Any help is greatly appreciated.
You have to replace the entire contents of the file. Use the IFile.setContents method to set the contents of an existing file. It is up to you to get the order of the contents correct.
For a FormEditor call editorDirtyStateChanged() to tell the editor that the 'dirty' state has changed. Each IFormPage should implement isDirty as appropriate (or override the FormEditor isDirty method).
If the dirty state is correct a '*' is added to the editor title. A 'do you want' to save dialog will also be displayed on exit if the editor is dirty.
Hello I'm having a little bit of a problem with my text editor like program. I would like to have my Save feature work only if Save As has been called, and if Save is called that it appends the text From a JTextArea to the file created by Save As. I am using ActionListeners from JMenuItems to call the Save and Save As Actions. Here's the code for Save As:
FileDialog fileDialogSave = new FileDialog(frame, "Save", FileDialog.SAVE);
fileDialogSave.setVisible(true);
String userProjectSavePath = fileDialogSave.getDirectory() + fileDialogSave.getFile();
File userProjectSave = new File(userProjectSavePath);
try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(userProjectSave, true)))) {
userProjectSave.createNewFile();
String userProjectNameToSave = codeArea.getText();
out.println(userProjectNameToSave);
} catch (IOException e1) {
e1.printStackTrace();
}
Both the Save and Save As are nested inside static class ActionSaveAs implements ActionListener { public void actionPerformed(ActionEvent e) { ... } }
The problem is I can't access the String userProjectSavePath in the Save class so I can't append the new text to the same file as in the Save As.
Instead, let your notional saveDocument() method invoke saveDocumentAs() if currentFile is null. The following outline suggests the approach taken in Charles Bell's HTMLDocumentEditor, cited here.
public void saveDocument() {
if (currentFile != null) {
// Save in currentFile
} else {
saveDocumentAs();
}
}
public void saveDocumentAs() {
// Check before overwriting existing file
}
I have been trying for a month now to reliably set the system clipboard in my program. Currently it works about 95/100 times. But I keep searching.
Yesterday I came upon this the Java DefaultEditorKit.copyAction and died a little inside seeing there was already something written that might do what I want.
Though the problem is that this is a "Action" for a dialog?
How can I issue the text I want copied to the clipboard? I do not wish to attach this "Action" to any button/component in my app. I want to be able to do
DefaultEditorKit.copyAction("Put this on Clipboard");
But this is undefined. I am not sure how to trigger this "action" and give it some text to work with?
EDIT: Here is my code that causes an exception.
public void setClip2(String arg)
{
while(true)
{
try
{
sysClip.setContents(new StringSelection(arg), null);
}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
return;
}
I just wondered if there was a way to reliably set the clipboard. (this method fails if you do not wait long enough trying to set it, which is usually about 1-2 seconds
Not sure why you have to wait to set the contents of the clipboard.
This program doesn't have any problem refreshing the clipboard every 200ms. That is the number increments by 1 every time is it displayed as expected:
import java.awt.*;
import java.awt.datatransfer.*;
import java.io.*;
class ClipboardLoopTest
{
public static void main(String[] args)
throws InterruptedException
{
for (int i = 0; i < 100; i++)
{
// add data to clipboard
try
{
Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
StringSelection testData;
testData = new StringSelection( "Test: " + i );
c.setContents(testData, testData);
// Get clipboard contents, as a String
Transferable t = c.getContents( null );
if ( t.isDataFlavorSupported(DataFlavor.stringFlavor) )
{
Object o = t.getTransferData( DataFlavor.stringFlavor );
String data = (String)t.getTransferData( DataFlavor.stringFlavor );
System.out.println( "Clipboard contents: " + data );
}
}
catch(Exception e)
{
System.out.println(e);
}
Thread.sleep(200);
}
System.exit(0);
}
}
I'm using JDK 7 on Windows 7.
Maybe you can post your SSCCE that demonstrates the problem.
DefaultEditorKit.copyAction actually use (through some layers) the functionnality in java.awt.datatransfer. There you will find classes to send data to the clipboard.
Basically, if you just want to send a string to the clipboard without using any Swing component, you setup a ClipboardOwner, you create a StringSelection object and you give it to the system clipboard. Here is a most basic example:
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents( new StringSelection("Put this on Clipboard"), new ClipboardOwner() {
#Override
public void lostOwnership(Clipboard clipboard, Transferable contents) {
System.out.println("I am no longer the clipboard owner.");
}
} );
I would like to write a Java program that has a button. When the button is pressed it pastes / drops the content of the system clipboard to the text field that currently has focus within an arbitrary, possibly non-Java app (say MS Word). Essentially the button action has to simulate the sending of CTRL-V (paste) action somehow.
Does any one have any suggestions?
The package java.awt.datatransfer seems to be a solution, according to this article. Here is another article.
From the latter page, the needed imports:
import java.awt.datatransfer.*;
import java.awt.Toolkit;
And the method code is below. The solution is to create a listener and add it to the button. The listener should simply get the contents of the clipboard and insert it to whatever component you wish.
public void setClipboardContents( String aString ){
StringSelection stringSelection = new StringSelection( aString );
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents( stringSelection, this );
}
public String getClipboardContents() {
String result = "";
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
//odd: the Object param of getContents is not currently used
Transferable contents = clipboard.getContents(null);
boolean hasTransferableText =
(contents != null) &&
contents.isDataFlavorSupported(DataFlavor.stringFlavor)
;
if ( hasTransferableText ) {
try {
result = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch (UnsupportedFlavorException ex){
//highly unlikely since we are using a standard DataFlavor
System.out.println(ex);
ex.printStackTrace();
}
catch (IOException ex) {
System.out.println(ex);
ex.printStackTrace();
}
}
return result;
}
Use the Actions provided by the editor kits:
JButton paste = new JButton( new DefaultEditorKit.PasteAction() );