PasteAction not called when running through IntelliJ?

Answered

Hi all,

for my plugin I created my own JTextPane that listen to drap and drop and copy/paste of pictures

This Component is named DragDropTextPane

 

Basically I registered my own action in my DragDropTextPane constructor

 

getActionMap().put(DefaultEditorKit.pasteAction, new DrapDropPasteAction());

 

and here is the source code of my DragDropPastAction

 

class DrapDropPasteAction extends DefaultEditorKit.PasteAction{
@Override
public void actionPerformed(ActionEvent e) {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
if(clipboard.isDataFlavorAvailable(DataFlavor.imageFlavor)){
ImageIcon imageFromClipboard = ImageUtility.getInstance().getImageFromClipboard();
if(imageFromClipboard != null) {
insertIcon(imageFromClipboard);
}
}else {
super.actionPerformed(e);
}
}
}

 

 

I i test it with following code : 

JFrame frame = new JFrame();
DragDropTextPane comp = new DragDropTextPane();
comp.setPreferredSize(new Dimension(500, 400));
JPanel p = new JPanel();
p.add(comp);
frame.add(p);
frame.pack();
frame.show();

when I go to a picture editing tool and copy something from it

when i do a paste ( CTRL + V) within my DragDropTextField the picture is paste like expected

 

 

But when run in IntelliJ and do a CTRL + V , put the cursor on my DrapDropTextPane (that is in JDialog that is build with the Form builder) it did not work. In fact it does not event go in the actionPerformed.

The drag and drop of a picture howerver is working in all cases.

 

Is there something that prevent the plugin from reading the clipboard ? Or to override the defaut paste action ? 


If needed I just commited my changes in the dev branch of my plugin : https://github.com/alain57/swissas-dev-tools

 

Thanks in advance for your help.

 

Best regards,

Alain

 

 

 

0
9 comments

Implement com.intellij.openapi.actionSystem.DataProvider in your DragDropTextPane and return your com.intellij.ide.PasteProvider implementation for com.intellij.openapi.actionSystem.PlatformDataKeys#PASTE_PROVIDER

0

Thanks Yann :)

by any chances, do you have a source code I could look at ?

My actual solution has no paste_provider and i admit that i'm a bit lost .

 

Thank you in advance for your help.

 

0

You can find a number of implementations in IntelliJ Community sources https://github.com/JetBrains/intellij-community

0

Sorry to disturb you again.

I implement the class like you said and wrote this inside of the method

 

@Nullable
@Override
public Object getData(@NotNull String dataId) {
if(dataId == null) {
return null;
}
if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) {
return this.picturePasteProvider;
}
return null;
}

 

I put a breakpoint in the return.this.picturePasteProvider but it never goes in (i'm doing CTRL + V ) 

 

0

maybe it is easier if I share the entire class ?

I'm not a big fan of implementing all logic in one single class, but here i did it to simplify the code and have just one file.

 

 

package com.swissas.ui;

import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.activation.MimetypesFileTypeMap;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JTextPane;
import javax.swing.text.Element;
import javax.swing.text.StyledDocument;

import com.intellij.ide.PasteProvider;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ide.CopyPasteManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static javax.swing.text.StyleConstants.IconAttribute;

/**
* A TextPane component supporting drag and drop and returns the images that were added to it
*
* @author Tavan Alain
*/
public class DragDropTextPane extends JTextPane implements DropTargetListener, DataProvider, PasteProvider {
private static final Logger LOGGER = Logger.getInstance("Swiss-as");

public DragDropTextPane() {
new DropTarget(this, this);
setDragEnabled(true);
}

@Override
public void dragEnter(DropTargetDragEvent dtde) {
//Method isn't needed but required bw the DropTargetListener
}

@Override
public void dragOver(DropTargetDragEvent dtde) {
//Method isn't needed but required bw the DropTargetListener
}

@Override
public void dropActionChanged(DropTargetDragEvent dtde) {
//Method isn't needed but required bw the DropTargetListener
}

@Override
public void dragExit(DropTargetEvent dte) {
//Method isn't needed but required bw the DropTargetListener
}

@Override
public void drop(DropTargetDropEvent dropTargetDropEvent) {
Transferable transferable = dropTargetDropEvent.getTransferable();
for (DataFlavor d : transferable.getTransferDataFlavors()) {
dropTargetDropEvent
.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
try {
((List<File>) transferable.getTransferData(d)).forEach(this::insertIfImage);
} catch (UnsupportedFlavorException | IOException e) {
LOGGER.error(e);
}
}
dropTargetDropEvent.getDropTargetContext().dropComplete(true);
}

private void insertIfImage(File file) {
String mimetype = new MimetypesFileTypeMap().getContentType(file);
String type = mimetype.split("/")[0];
if ("image".equals(type)) {
try {
insertIcon(new ImageIcon(ImageIO.read(file)));
} catch (IOException e) {
LOGGER.error(e);
}
}
}

public List<ImageIcon> getImages() {
List<ImageIcon> result = new ArrayList<>();
StyledDocument doc = getStyledDocument();
for (int i = 0; i < doc.getLength(); i++) {
Element element = doc.getCharacterElement(i);
if ("icon".equals(element.getName())) {
result.add((ImageIcon) element.getAttributes().getAttribute(IconAttribute));
}
}
return result;
}

@Nullable
@Override
public Object getData(@NotNull String dataId) {
if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) {
return this;
}
return null;
}

@Override
public void performPaste(@NotNull DataContext dataContext) {
Transferable transferable = CopyPasteManager.getInstance().getContents();
if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
ImageIcon imageIcon;
try {
imageIcon = new ImageIcon(
(Image) transferable.getTransferData(DataFlavor.imageFlavor));
insertIcon(imageIcon);
} catch (UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
}
}

@Override
public boolean isPastePossible(@NotNull DataContext dataContext) {
return true;
}

@Override
public boolean isPasteEnabled(@NotNull DataContext dataContext) {
return CopyPasteManager.getInstance().getContents(DataFlavor.imageFlavor) != null;
}
}
0

with the help of someone on the slack page of intellij I implemented following working solution

 

public class PasteImageHandler extends EditorActionHandler implements EditorTextInsertHandler {
	
	private final EditorActionHandler originalActionHandler;
	
	public PasteImageHandler(EditorActionHandler originalActionHandler){
		this.originalActionHandler = originalActionHandler;
	}
	
	@Override
	public void execute(Editor editor, DataContext dataContext, Producer<Transferable> producer) {
		Caret caret =  editor.getCaretModel().getPrimaryCaret();
		doExecute(editor, caret, dataContext);
	}
	
	@Override
	protected void doExecute(@NotNull Editor editor, @Nullable Caret caret,
	                         DataContext dataContext) {
		if(editor.getComponent() instanceof DragDropTextPane) {
			((DragDropTextPane) editor.getComponent()).pastePicture();
		}
		
		if (this.originalActionHandler != null) {
			this.originalActionHandler.execute(editor, null, dataContext);
		}
	}
}


 

here the pastePicture of my component

 

	public void pastePicture() {
		Transferable transferable = CopyPasteManager.getInstance().getContents();
		if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
			ImageIcon imageIcon;
			try {
				imageIcon = new ImageIcon(
						(Image) transferable.getTransferData(DataFlavor.imageFlavor));
				insertIcon(imageIcon);
			} catch (UnsupportedFlavorException | IOException e) {
				e.printStackTrace();
			}
		}
	}

 

 

and last important part : needed following change on my plugin.xml file

 

<editorActionHandler action="EditorPaste" implementationClass="com.swissas.handler.PasteImageHandler" order="first" />

of course all is avalaible open source one my github account ;) 

This case can therefore be mark as resolved. 

0

Alain, you may want to add code to catch exceptions generated by the original handler and direct the error report to the plugin causing the exception.

Otherwise, your plugin will get the blame in the IDE exception report and the party responsible will not be the wiser. Over the last couple of years I got over 800 exception reports which were caused by other plugins or the IDE.

See last post in: https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000438790-What-is-the-proper-way-to-delegate-paste-handler-execute-method-when-no-customization-is-needed- 

 

0

Oh nice catch I wouldn't through of that one.

Thanks a lot for sharing. I'll add this for the next version of the plugin :) 

0

I didn't think of it either until I started to get flooded with exception reports that had nothing to do with my plugin. Overall, about 20% of all reports I get are for original paste handler failure.

0

Please sign in to leave a comment.