blob: d94bc7bbdb9f17a4c34cd5919ad024905d57368f [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide;
import com.intellij.ide.dnd.LinuxDragAndDropSupport;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerAdapter;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.ArrayUtilRt;
import org.jetbrains.annotations.Nullable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.InvalidDnDOperationException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PsiCopyPasteManager {
public static PsiCopyPasteManager getInstance() {
return ServiceManager.getService(PsiCopyPasteManager.class);
}
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.PsiCopyPasteManagerImpl");
private MyData myRecentData;
private final CopyPasteManagerEx myCopyPasteManager;
public PsiCopyPasteManager(CopyPasteManager copyPasteManager, ProjectManager projectManager) {
myCopyPasteManager = (CopyPasteManagerEx) copyPasteManager;
projectManager.addProjectManagerListener(new ProjectManagerAdapter() {
@Override
public void projectClosing(Project project) {
if (myRecentData != null && myRecentData.getProject() == project) {
myRecentData = null;
}
}
});
}
@Nullable
public PsiElement[] getElements(boolean[] isCopied) {
try {
Object transferData = myCopyPasteManager.getContents(ourDataFlavor);
if (!(transferData instanceof MyData)) {
return null;
}
MyData dataProxy = (MyData)transferData;
if (!Comparing.equal(dataProxy, myRecentData)) {
return null;
}
if (isCopied != null) {
isCopied[0] = myRecentData.isCopied();
}
return myRecentData.getElements();
}
catch (Exception e) {
LOG.debug(e);
return null;
}
}
@Nullable
static PsiElement[] getElements(final Transferable content) {
if (content == null) return null;
Object transferData;
try {
transferData = content.getTransferData(ourDataFlavor);
}
catch (UnsupportedFlavorException e) {
return null;
}
catch (IOException e) {
return null;
}
catch (InvalidDnDOperationException e) {
return null;
}
return transferData instanceof MyData ? ((MyData)transferData).getElements() : null;
}
public void clear() {
myRecentData = null;
myCopyPasteManager.setContents(new StringSelection(""));
}
public void setElements(PsiElement[] elements, boolean copied) {
myRecentData = new MyData(elements, copied);
myCopyPasteManager.setContents(new MyTransferable(myRecentData));
}
public boolean isCutElement(Object element) {
if (myRecentData == null) return false;
if (myRecentData.isCopied()) return false;
PsiElement[] elements = myRecentData.getElements();
if (elements == null) return false;
for (PsiElement aElement : elements) {
if (aElement == element) return true;
}
return false;
}
private static final DataFlavor ourDataFlavor;
static {
try {
final Class<MyData> flavorClass = MyData.class;
final Thread currentThread = Thread.currentThread();
final ClassLoader currentLoader = currentThread.getContextClassLoader();
try {
currentThread.setContextClassLoader(flavorClass.getClassLoader());
ourDataFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=" + flavorClass.getName());
}
finally {
currentThread.setContextClassLoader(currentLoader);
}
}
catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public static class MyData {
private PsiElement[] myElements;
private final boolean myIsCopied;
public MyData(PsiElement[] elements, boolean copied) {
myElements = elements;
myIsCopied = copied;
}
public PsiElement[] getElements() {
if (myElements == null) return PsiElement.EMPTY_ARRAY;
int validElementsCount = 0;
final AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
try {
for (PsiElement element : myElements) {
if (element.isValid()) {
validElementsCount++;
}
}
if (validElementsCount == myElements.length) {
return myElements;
}
PsiElement[] validElements = new PsiElement[validElementsCount];
int j=0;
for (PsiElement element : myElements) {
if (element.isValid()) {
validElements[j++] = element;
}
}
myElements = validElements;
}
finally {
token.finish();
}
return myElements;
}
public boolean isCopied() {
return myIsCopied;
}
@Nullable
public Project getProject() {
if (myElements == null || myElements.length == 0) {
return null;
}
final PsiElement element = myElements[0];
return element.isValid() ? element.getProject() : null;
}
}
public static class MyTransferable implements Transferable {
private static final DataFlavor[] DATA_FLAVORS_COPY = {
ourDataFlavor, DataFlavor.stringFlavor, DataFlavor.javaFileListFlavor,
LinuxDragAndDropSupport.uriListFlavor, LinuxDragAndDropSupport.gnomeFileListFlavor
};
private static final DataFlavor[] DATA_FLAVORS_CUT = {
ourDataFlavor, DataFlavor.stringFlavor, DataFlavor.javaFileListFlavor,
LinuxDragAndDropSupport.uriListFlavor, LinuxDragAndDropSupport.gnomeFileListFlavor, LinuxDragAndDropSupport.kdeCutMarkFlavor
};
private final MyData myDataProxy;
public MyTransferable(MyData data) {
myDataProxy = data;
}
public MyTransferable(PsiElement[] selectedValues) {
this(new PsiCopyPasteManager.MyData(selectedValues, true));
}
@Override
@Nullable
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (ourDataFlavor.equals(flavor)) {
return myDataProxy;
}
else if (DataFlavor.stringFlavor.equals(flavor)) {
return getDataAsText();
}
else if (DataFlavor.javaFileListFlavor.equals(flavor)) {
return getDataAsFileList();
}
else if (flavor.equals(LinuxDragAndDropSupport.uriListFlavor)) {
final List<File> files = getDataAsFileList();
if (files != null) {
return LinuxDragAndDropSupport.toUriList(files);
}
}
else if (flavor.equals(LinuxDragAndDropSupport.gnomeFileListFlavor)) {
final List<File> files = getDataAsFileList();
if (files != null) {
final String string = (myDataProxy.isCopied() ? "copy\n" : "cut\n") + LinuxDragAndDropSupport.toUriList(files);
return new ByteArrayInputStream(string.getBytes(CharsetToolkit.UTF8_CHARSET));
}
}
else if (flavor.equals(LinuxDragAndDropSupport.kdeCutMarkFlavor) && !myDataProxy.isCopied()) {
return new ByteArrayInputStream("1".getBytes(CharsetToolkit.UTF8_CHARSET));
}
return null;
}
@Nullable
private String getDataAsText() {
final AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
try {
final List<String> names = new ArrayList<String>();
for (PsiElement element : myDataProxy.getElements()) {
if (element instanceof PsiNamedElement) {
String name = ((PsiNamedElement)element).getName();
if (name != null) {
names.add(name);
}
}
}
return names.isEmpty() ? null : StringUtil.join(names, "\n");
}
finally {
token.finish();
}
}
@Nullable
private List<File> getDataAsFileList() {
final AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
try {
return asFileList(myDataProxy.getElements());
}
finally {
token.finish();
}
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return myDataProxy.isCopied() ? DATA_FLAVORS_COPY : DATA_FLAVORS_CUT;
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return ArrayUtilRt.find(getTransferDataFlavors(), flavor) != -1;
}
public PsiElement[] getElements() {
return myDataProxy.getElements();
}
}
@Nullable
public static List<File> asFileList(final PsiElement[] elements) {
final List<File> result = new ArrayList<File>();
for (PsiElement element : elements) {
final PsiFileSystemItem psiFile;
if (element instanceof PsiFileSystemItem) {
psiFile = (PsiFileSystemItem)element;
}
else if (element instanceof PsiDirectoryContainer) {
final PsiDirectory[] directories = ((PsiDirectoryContainer)element).getDirectories();
psiFile = directories[0];
}
else {
psiFile = element.getContainingFile();
}
if (psiFile != null) {
VirtualFile vFile = psiFile.getVirtualFile();
if (vFile != null && vFile.getFileSystem() instanceof LocalFileSystem) {
result.add(new File(vFile.getPath()));
}
}
}
return result.isEmpty() ? null : result;
}
}