| /* |
| * 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; |
| } |
| } |