| /* |
| * Copyright 2000-2013 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.jetbrains.python.configuration; |
| |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.actionSystem.AnActionEvent; |
| import com.intellij.openapi.application.PathManager; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptor; |
| import com.intellij.openapi.fileTypes.FileTypes; |
| import com.intellij.openapi.projectRoots.SdkAdditionalData; |
| import com.intellij.openapi.projectRoots.SdkModificator; |
| import com.intellij.openapi.projectRoots.ui.SdkPathEditor; |
| import com.intellij.openapi.roots.OrderRootType; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.vfs.JarFileSystem; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.ui.AnActionButton; |
| import com.intellij.ui.ListCellRendererWrapper; |
| import com.intellij.ui.ListUtil; |
| import com.intellij.ui.ToolbarDecorator; |
| import com.intellij.ui.components.JBList; |
| import com.intellij.util.ArrayUtil; |
| import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil; |
| import com.jetbrains.python.sdk.PythonSdkAdditionalData; |
| import com.jetbrains.python.sdk.PythonSdkType; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * @author traff |
| */ |
| public class PythonPathEditor extends SdkPathEditor { |
| private final PathListModel myPathListModel; |
| |
| public PythonPathEditor(final String displayName, |
| final OrderRootType orderRootType, |
| final FileChooserDescriptor descriptor) { |
| super(displayName, orderRootType, descriptor); |
| myPathListModel = new PathListModel(orderRootType, getListModel()); |
| } |
| |
| public void reset(@Nullable SdkModificator modificator) { |
| if (modificator != null) { |
| List<VirtualFile> list = Lists.newArrayList(modificator.getRoots(getOrderRootType())); |
| resetPath(myPathListModel.reset(list, modificator)); |
| } |
| else { |
| setEnabled(false); |
| } |
| } |
| |
| public void reload(@Nullable SdkModificator sdkModificator) { |
| if (sdkModificator != null) { |
| List<VirtualFile> list = Lists.newArrayList(sdkModificator.getRoots(getOrderRootType())); |
| resetPath(myPathListModel.reload(list)); |
| setModified(true); |
| } |
| else { |
| setEnabled(false); |
| } |
| } |
| |
| @Override |
| public void apply(SdkModificator sdkModificator) { |
| sdkModificator.removeRoots(getOrderRootType()); |
| // add all items |
| for (int i = 0; i < myPathListModel.getRowCount(); i++) { |
| VirtualFile path = myPathListModel.getValueAt(i); |
| if (!myPathListModel.isExcluded(path)) { |
| sdkModificator.addRoot(path, getOrderRootType()); |
| } |
| } |
| setModified(false); |
| myPathListModel.apply(sdkModificator); |
| } |
| |
| @Override |
| protected VirtualFile[] adjustAddedFileSet(Component component, VirtualFile[] files) { |
| for (int i = 0, filesLength = files.length; i < filesLength; i++) { |
| if (files[i].getFileType() == FileTypes.ARCHIVE) { |
| files[i] = JarFileSystem.getInstance().getJarRootForLocalFile(files[i]); |
| } |
| } |
| if (myPathListModel.add(Lists.newArrayList(files))) { |
| setModified(true); |
| } |
| return files; |
| } |
| |
| @Override |
| protected void doRemoveItems(int[] idxs, JList list) { |
| List<Pair<VirtualFile, Integer>> removed = Lists.newArrayList(); |
| for (int i : idxs) { |
| removed.add(Pair.create((VirtualFile)getListModel().get(i), i)); |
| } |
| ListUtil.removeIndices(list, myPathListModel.remove(removed)); |
| list.updateUI(); |
| setModified(true); |
| } |
| |
| @Override |
| protected ListCellRenderer createListCellRenderer(JBList list) { |
| return new PythonPathListCellRenderer(super.createListCellRenderer(list), myPathListModel); |
| } |
| |
| @Override |
| protected void addToolbarButtons(ToolbarDecorator toolbarDecorator) { |
| AnActionButton reloadButton = new AnActionButton("Reload list of paths", AllIcons.Actions.Refresh) { |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| onReloadButtonClicked(); |
| } |
| }; |
| toolbarDecorator.addExtraAction(reloadButton); |
| } |
| |
| protected void onReloadButtonClicked() { |
| } |
| |
| private static class PathListModel { |
| private Set<VirtualFile> myAdded = Sets.newHashSet(); |
| private Set<VirtualFile> myExcluded = Sets.newHashSet(); |
| private Set<VirtualFile> myFoundFiles = Sets.newHashSet(); |
| private List<VirtualFile> myFilteredOut = Lists.newArrayList(); |
| private final DefaultListModel myListModel; |
| private final OrderRootType myOrderRootType; |
| private Set<VirtualFile> myUserAddedToRemove = Sets.newHashSet(); |
| |
| public PathListModel(OrderRootType orderRootType, DefaultListModel listModel) { |
| myOrderRootType = orderRootType; |
| myListModel = listModel; |
| } |
| |
| private int getRowCount() { |
| return myListModel.getSize(); |
| } |
| |
| private VirtualFile getValueAt(int row) { |
| return (VirtualFile)myListModel.get(row); |
| } |
| |
| public boolean add(List<VirtualFile> files) { |
| for (VirtualFile file : files) { |
| if (!myFoundFiles.contains(file)) { |
| if (!myExcluded.remove(file)) { //if it was excluded we only delete exclusion mark |
| myAdded.add(file); |
| myUserAddedToRemove.remove(file); |
| } |
| else { |
| myFoundFiles.add(file); |
| return true; |
| } |
| } else { |
| myExcluded.remove(file); |
| } |
| } |
| return false; |
| } |
| |
| public int[] remove(List<Pair<VirtualFile, Integer>> files) { |
| List<Integer> toRemove = Lists.newArrayList(); |
| for (Pair<VirtualFile, Integer> e : files) { |
| if (myAdded.contains(e.first)) { |
| toRemove.add(e.second); |
| myAdded.remove(e.first); |
| myUserAddedToRemove.add(e.first); |
| } |
| else if (myExcluded.contains(e.first)) { |
| myExcluded.remove(e.first); |
| } |
| else { |
| myExcluded.add(e.first); |
| } |
| } |
| return ArrayUtil.toIntArray(toRemove); |
| } |
| |
| public void apply(SdkModificator sdkModificator) { |
| sdkModificator.setSdkAdditionalData(collectSdkAdditionalData(sdkModificator)); |
| addFilteredOutRoots(sdkModificator); |
| } |
| |
| private void addFilteredOutRoots(SdkModificator sdkModificator) { |
| for (VirtualFile file : myFilteredOut) { |
| sdkModificator.addRoot(file, myOrderRootType); |
| } |
| } |
| |
| private SdkAdditionalData collectSdkAdditionalData(SdkModificator sdkModificator) { |
| PythonSdkAdditionalData data = (PythonSdkAdditionalData)sdkModificator.getSdkAdditionalData(); |
| if (data == null) { |
| data = new PythonSdkAdditionalData(null); |
| } |
| data.setAddedPathsFromVirtualFiles(myAdded); |
| data.setExcludedPathsFromVirtualFiles(myExcluded); |
| return data; |
| } |
| |
| public void setAdded(Set<VirtualFile> added) { |
| myAdded = Sets.newHashSet(added); |
| } |
| |
| public void setExcluded(Set<VirtualFile> excluded) { |
| myExcluded = Sets.newHashSet(excluded); |
| } |
| |
| public String getPresentationSuffix(VirtualFile file) { |
| if (myAdded.contains(file)) { |
| return "(added by user)"; |
| } |
| if (myExcluded.contains(file)) { |
| return "(removed by user)"; |
| } |
| return ""; |
| } |
| |
| public List<VirtualFile> reload(List<VirtualFile> list) { |
| myFoundFiles.clear(); |
| myFoundFiles.addAll(list); |
| List<VirtualFile> result = filterOutStubs(list, myFilteredOut); |
| result.removeAll(myUserAddedToRemove); |
| result.addAll(myAdded); |
| |
| return result; |
| } |
| |
| public List<VirtualFile> reset(List<VirtualFile> list, SdkModificator modificator) { |
| myFilteredOut.clear(); |
| List<VirtualFile> result = filterOutStubs(list, myFilteredOut); |
| |
| myFoundFiles.clear(); |
| myFoundFiles.addAll(list); |
| myUserAddedToRemove.clear(); |
| |
| if (modificator.getSdkAdditionalData() instanceof PythonSdkAdditionalData) { |
| PythonSdkAdditionalData data = (PythonSdkAdditionalData)modificator.getSdkAdditionalData(); |
| setAdded(data.getAddedPathFiles()); |
| setExcluded(data.getExcludedPathFiles()); |
| result.addAll(myExcluded); |
| } |
| else if (modificator.getSdkAdditionalData() == null) { |
| myAdded.clear(); |
| myExcluded.clear(); |
| } |
| return result; |
| } |
| |
| private static List<VirtualFile> filterOutStubs(List<VirtualFile> list, List<VirtualFile> filteredOut) { |
| List<VirtualFile> result = Lists.newArrayList(); |
| filteredOut.clear(); |
| for (VirtualFile file : list) { |
| if (!isStubPath(file)) { |
| result.add(file); |
| } |
| else { |
| filteredOut.add(file); |
| } |
| } |
| return result; |
| } |
| |
| private static boolean isStubPath(@NotNull VirtualFile file) { |
| final String path = PythonSdkType.getSkeletonsRootPath(PathManager.getSystemPath()); |
| final VirtualFile skeletonRoot = LocalFileSystem.getInstance().findFileByPath(path); |
| if (skeletonRoot != null && file.getPath().startsWith(skeletonRoot.getPath())) { |
| return true; |
| } |
| else if (file.equals(PyUserSkeletonsUtil.getUserSkeletonsDirectory())) { |
| return true; |
| } |
| else { |
| return false; |
| } |
| } |
| |
| public boolean isExcluded(VirtualFile path) { |
| return myExcluded.contains(path); |
| } |
| } |
| |
| |
| private class PythonPathListCellRenderer extends ListCellRendererWrapper<VirtualFile> { |
| private final PathListModel model; |
| |
| public PythonPathListCellRenderer(final ListCellRenderer listCellRenderer, PathListModel model) { |
| super(); |
| this.model = model; |
| } |
| |
| @Override |
| public void customize(JList list, VirtualFile value, int index, boolean selected, boolean hasFocus) { |
| String suffix = model.getPresentationSuffix(value); |
| if (suffix.length() > 0) { |
| suffix = " " + suffix; |
| } |
| setText(value != null ? getPresentablePath(value) + suffix : ""); |
| } |
| } |
| |
| protected String getPresentablePath(VirtualFile value) { |
| return value.getPresentableUrl(); |
| } |
| } |