From 805fe455e2aed6c5070e5b63a731d64a013fc7a8 Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Thu, 29 Oct 2009 22:13:12 +0300 Subject: [PATCH] move classpath panel to community --- .../util/ui/classpath/ChooseLibrariesDialog.java | 345 ++++++++++++++++++ .../classpath/GlobalLibraryReferenceElement.java | 69 ++++ .../util/ui/classpath/SimpleClasspathElement.java | 26 ++ .../classpath/SimpleClasspathElementFactory.java | 71 ++++ .../util/ui/classpath/SimpleClasspathPanel.java | 404 +++++++++++++++++++++ .../ui/classpath/SingleRootClasspathElement.java | 51 +++ .../src/com/intellij/ide/DefaultTreeExpander.java | 15 +- 7 files changed, 978 insertions(+), 3 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/util/ui/classpath/ChooseLibrariesDialog.java create mode 100644 platform/lang-impl/src/com/intellij/util/ui/classpath/GlobalLibraryReferenceElement.java create mode 100644 platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathElement.java create mode 100644 platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathElementFactory.java create mode 100644 platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathPanel.java create mode 100644 platform/lang-impl/src/com/intellij/util/ui/classpath/SingleRootClasspathElement.java diff --git a/platform/lang-impl/src/com/intellij/util/ui/classpath/ChooseLibrariesDialog.java b/platform/lang-impl/src/com/intellij/util/ui/classpath/ChooseLibrariesDialog.java new file mode 100644 index 0000000000..caf671f2bd --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/ui/classpath/ChooseLibrariesDialog.java @@ -0,0 +1,345 @@ +package com.intellij.util.ui.classpath; + +import com.intellij.ide.CommonActionsManager; +import com.intellij.ide.DefaultTreeExpander; +import com.intellij.ide.TreeExpander; +import com.intellij.ide.util.treeView.AbstractTreeBuilder; +import com.intellij.ide.util.treeView.AbstractTreeStructure; +import com.intellij.ide.util.treeView.NodeDescriptor; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.roots.LibraryOrderEntry; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; +import com.intellij.openapi.roots.ui.util.OrderEntryCellAppearanceUtils; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.treeStructure.SimpleNode; +import com.intellij.ui.treeStructure.SimpleTree; +import com.intellij.ui.treeStructure.WeightBasedComparator; +import com.intellij.util.CommonProcessors; +import com.intellij.util.Icons; +import com.intellij.util.Processor; +import com.intellij.util.ui.UIUtil; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.*; +import java.util.List; + +/** + * @author Gregory.Shrago + */ + +public class ChooseLibrariesDialog extends DialogWrapper{ + + private SimpleTree myTree = new SimpleTree(); + private AbstractTreeBuilder myBuilder; + + private List myResult; + private Map myLibraryMap = new THashMap(); + + protected ChooseLibrariesDialog(final Project project, final String title) { + super(project, false); + setTitle(title); + init(); + updateOKAction(); + } + + @Override + protected String getDimensionServiceKey() { + return "#com.intellij.javaee.module.view.dataSource.ChooseLibrariesDialog"; + } + + @Override + protected void doOKAction() { + processSelection(new CommonProcessors.CollectProcessor(myResult = new ArrayList())); + super.doOKAction(); + } + + private void updateOKAction() { + setOKActionEnabled(!processSelection(new CommonProcessors.FindFirstProcessor())); + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myTree; + } + + @NotNull + public List getSelectedLibraries() { + return myResult == null? Collections.emptyList() : myResult; + } + + private boolean processSelection(final Processor processor) { + for (Object element : myBuilder.getSelectedElements()) { + if (element instanceof Library) { + if (!processor.process((Library)element)) return false; + } + } + return true; + } + + protected boolean acceptsElement(final Object element) { + return true; + } + + @Override + protected JComponent createNorthPanel() { + final DefaultActionGroup group = new DefaultActionGroup(); + final TreeExpander expander = new DefaultTreeExpander(myTree); + final CommonActionsManager actionsManager = CommonActionsManager.getInstance(); + group.add(actionsManager.createExpandAllAction(expander, myTree)); + group.add(actionsManager.createCollapseAllAction(expander, myTree)); + final JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.PROJECT_VIEW_TOOLBAR, group, true).getComponent(); + component.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.darkGray), component.getBorder())); + return component; + } + + @Nullable + protected JComponent createCenterPanel() { + myBuilder = new AbstractTreeBuilder(myTree, new DefaultTreeModel(new DefaultMutableTreeNode()), + new MyStructure(ProjectManager.getInstance().getDefaultProject()), + WeightBasedComparator.FULL_INSTANCE); + myBuilder.initRootNode(); + + myTree.setDragEnabled(false); + myTree.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + myTree.setRowHeight(Icons.CLASS_ICON.getIconHeight()); + + myTree.setShowsRootHandles(true); + UIUtil.setLineStyleAngled(myTree); + + myTree.setRootVisible(false); + myTree.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + myTree.addTreeSelectionListener(new TreeSelectionListener() { + public void valueChanged(final TreeSelectionEvent e) { + updateOKAction(); + } + }); + myTree.addMouseListener(new MouseAdapter() { + public void mouseClicked(final MouseEvent e) { + if (e.getClickCount() != 2) return; + if (isOKActionEnabled()) { + doOKAction(); + } + } + }); + myTree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "ENTER"); + myTree.getActionMap().put("ENTER", getOKAction()); + return ScrollPaneFactory.createScrollPane(myTree); + } + + protected void dispose() { + Disposer.dispose(myBuilder); + super.dispose(); + } + + private static class MyNode extends SimpleNode { + private final T myElement; + + private MyNode(Project project, NodeDescriptor parentDescriptor, T element) { + super(project, parentDescriptor); + myElement = element; + } + + public T getElement() { + return myElement; + } + + @Override + public SimpleNode[] getChildren() { + return NO_CHILDREN; + } + + @Override + public int getWeight() { + return 0; + } + + @Override + public Object[] getEqualityObjects() { + return new Object[] {myElement}; + } + } + + private static class RootDescriptor extends MyNode { + protected RootDescriptor(final Project project) { + super(project, null, ApplicationManager.getApplication()); + } + } + + private static class ProjectDescriptor extends MyNode { + protected ProjectDescriptor(final Project project, final Project element) { + super(project, null, element); + } + + @Override + protected void doUpdate() { + setIcons(Icons.PROJECT_ICON, Icons.PROJECT_ICON); + final String nodeText = getElement().getName(); + setNodeText(StringUtil.isNotEmpty(nodeText) ? nodeText : "", null, false); + } + } + + private static class ModuleDescriptor extends MyNode { + protected ModuleDescriptor(final Project project, final NodeDescriptor parentDescriptor, final Module element) { + super(project, parentDescriptor, element); + } + + @Override + protected void doUpdate() { + setIcons(getElement().getModuleType().getNodeIcon(false), getElement().getModuleType().getNodeIcon(true)); + final String nodeText = getElement().getName(); + setNodeText(StringUtil.isNotEmpty(nodeText) ? nodeText : "", null, false); + } + + @Override + public int getWeight() { + return 1; + } + } + + private static class LibraryDescriptor extends MyNode { + private final Icon myIcon; + + protected LibraryDescriptor(final Project project, final NodeDescriptor parentDescriptor, final Library element) { + super(project, parentDescriptor, element); + final SimpleColoredComponent coloredComponent = new SimpleColoredComponent(); + OrderEntryCellAppearanceUtils.forLibrary(getElement()).customize(coloredComponent); + myIcon = coloredComponent.getIcon(); + } + + @Override + protected void doUpdate() { + setIcons(myIcon, myIcon); + final String nodeText = OrderEntryCellAppearanceUtils.forLibrary(getElement()).getText(); + setNodeText(StringUtil.isNotEmpty(nodeText) ? nodeText : "", null, false); + } + } + + private static class NamedDescriptor extends MyNode { + private final int myWeight; + + protected NamedDescriptor(final Project project, final NodeDescriptor parentDescriptor, final LibraryTable table, final int weight) { + super(project, parentDescriptor, table); + myWeight = weight; + } + + @Override + protected void doUpdate() { + setIcons(Icons.DIRECTORY_CLOSED_ICON, Icons.DIRECTORY_OPEN_ICON); + final String nodeText = getElement().getPresentation().getDisplayName(true); + setNodeText(StringUtil.isNotEmpty(nodeText) ? nodeText : "", null, false); + } + + @Override + public int getWeight() { + return myWeight; + } + } + + private class MyStructure extends AbstractTreeStructure { + private final Project myProject; + + public MyStructure(Project project) { + myProject = project; + } + + @Override + public Object getRootElement() { + return ApplicationManager.getApplication(); + } + + @Override + public Object[] getChildElements(Object element) { + final ArrayList result = new ArrayList(); + if (element instanceof Application) { + Collections.addAll(result, ProjectManager.getInstance().getOpenProjects()); + final LibraryTablesRegistrar instance = LibraryTablesRegistrar.getInstance(); + result.add(instance.getLibraryTable()); //1 + result.addAll(instance.getCustomLibraryTables()); //2 + } + else if (element instanceof Project) { + Collections.addAll(result, ModuleManager.getInstance((Project)element).getModules()); + result.add(LibraryTablesRegistrar.getInstance().getLibraryTable((Project)element)); + } + else if (element instanceof LibraryTable) { + Collections.addAll(result, ((LibraryTable)element).getLibraries()); + } + else if (element instanceof Module) { + for (OrderEntry entry : ModuleRootManager.getInstance((Module)element).getOrderEntries()) { + if (entry instanceof LibraryOrderEntry) { + final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)entry; + if (LibraryTableImplUtil.MODULE_LEVEL.equals(libraryOrderEntry.getLibraryLevel())) { + final Library library = libraryOrderEntry.getLibrary(); + result.add(library); + } + } + } + } + final Iterator it = result.iterator(); + while (it.hasNext()) { + if (!acceptsElement(it.next())) it.remove(); + } + for (Object o : result) { + myLibraryMap.put(o, element); + } + return result.toArray(); + } + + @Override + public Object getParentElement(Object element) { + if (element instanceof Application) return null; + if (element instanceof Project) return ApplicationManager.getApplication(); + if (element instanceof Module) return ((Module)element).getProject(); + if (element instanceof LibraryTable) return myLibraryMap.get(element); + if (element instanceof Library) return myLibraryMap.get(element); + throw new AssertionError(); + } + + @NotNull + @Override + public NodeDescriptor createDescriptor(Object element, NodeDescriptor parentDescriptor) { + if (element instanceof Application) return new RootDescriptor(myProject); + if (element instanceof Project) return new ProjectDescriptor(myProject, (Project)element); + if (element instanceof Module) return new ModuleDescriptor(myProject, parentDescriptor, (Module)element); + if (element instanceof LibraryTable) return new NamedDescriptor(myProject, parentDescriptor, (LibraryTable)element, 0); + if (element instanceof Library) return new LibraryDescriptor(myProject, parentDescriptor, (Library)element); + throw new AssertionError(); + } + + @Override + public void commit() { + } + + @Override + public boolean hasSomethingToCommit() { + return false; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/util/ui/classpath/GlobalLibraryReferenceElement.java b/platform/lang-impl/src/com/intellij/util/ui/classpath/GlobalLibraryReferenceElement.java new file mode 100644 index 0000000000..c02c597547 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/ui/classpath/GlobalLibraryReferenceElement.java @@ -0,0 +1,69 @@ +package com.intellij.util.ui.classpath; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NonNls; +import org.jdom.Element; +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; +import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.vfs.VirtualFile; + +import java.util.List; +import java.util.Collections; +import java.util.ArrayList; +import java.io.IOException; + +/** + * @author nik + */ +public class GlobalLibraryReferenceElement implements SimpleClasspathElement { + @NonNls public static final String NAME_ATTRIBUTE = "name"; + @NonNls public static final String LEVEL_ATTRIBUTE = "level"; + private final String myLibraryName; + + public GlobalLibraryReferenceElement(@NotNull String libraryName) { + myLibraryName = libraryName; + } + + public GlobalLibraryReferenceElement(@NotNull Element element) { + myLibraryName = element.getAttributeValue(NAME_ATTRIBUTE); + } + + public String getPresentableName() { + return myLibraryName; + } + + public void writeExternal(Element element) { + element.setAttribute(NAME_ATTRIBUTE, myLibraryName); + //todo[nik,greg] remote later. this is needed only for forward compatibility with version before 8 + element.setAttribute(LEVEL_ATTRIBUTE, LibraryTablesRegistrar.APPLICATION_LEVEL); + } + + public Library getLibrary() { + final LibraryTable libraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(); + return libraryTable.getLibraryByName(myLibraryName); + } + + public String getLibraryName() { + return myLibraryName; + } + + public void serialize(Element element) throws IOException { + element.setAttribute(NAME_ATTRIBUTE, myLibraryName); + //todo[nik,greg] remote later. this is needed only for forward compatibility with version before 8 + element.setAttribute(LEVEL_ATTRIBUTE, LibraryTablesRegistrar.APPLICATION_LEVEL); + } + + public List getClassesRootUrls() { + final Library library = getLibrary(); + if (library != null) { + final List list = new ArrayList(); + for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) { + list.add(file.getUrl()); + } + return list; + } + return Collections.emptyList(); + } +} diff --git a/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathElement.java b/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathElement.java new file mode 100644 index 0000000000..b353a92502 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathElement.java @@ -0,0 +1,26 @@ +package com.intellij.util.ui.classpath; + +import com.intellij.openapi.roots.libraries.Library; +import org.jdom.Element; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.List; + +/** + * @author nik + */ +public interface SimpleClasspathElement { + + String getPresentableName(); + + List getClassesRootUrls(); + + @Nullable + Library getLibrary(); + + @Nullable + String getLibraryName(); + + void serialize(Element element) throws IOException; +} diff --git a/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathElementFactory.java b/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathElementFactory.java new file mode 100644 index 0000000000..883daa1516 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathElementFactory.java @@ -0,0 +1,71 @@ +package com.intellij.util.ui.classpath; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author nik + */ +public class SimpleClasspathElementFactory { + private SimpleClasspathElementFactory() { + } + + + public static List createElements(@Nullable Project project, @NotNull Element element) { + final String name = element.getAttributeValue(GlobalLibraryReferenceElement.NAME_ATTRIBUTE); + final String level = element.getAttributeValue(GlobalLibraryReferenceElement.LEVEL_ATTRIBUTE); + final String url = element.getChildText(SingleRootClasspathElement.URL_ELEMENT); + if (!StringUtil.isEmpty(url)) { + return Collections.singletonList(new SingleRootClasspathElement(url)); + } + if (name == null || level == null) { + return Collections.emptyList(); + } + if (LibraryTablesRegistrar.APPLICATION_LEVEL.equals(level)) { + return Collections.singletonList(new GlobalLibraryReferenceElement(name)); + } + //this is needed only for backward compatibility with version before 8 + if (project != null) { + final LibraryTable libraryTable = LibraryTablesRegistrar.getInstance().getLibraryTableByLevel(level, project); + if (libraryTable != null) { + final Library library = libraryTable.getLibraryByName(name); + if (library != null) { + return createElements(library); + } + } + } + return Collections.emptyList(); + } + + public static List createElements(@NotNull Library library) { + final LibraryTable table = library.getTable(); + if (table != null && LibraryTablesRegistrar.APPLICATION_LEVEL.equals(table.getTableLevel())) { + return Collections.singletonList(new GlobalLibraryReferenceElement(library.getName())); + } + final List elements = new ArrayList(); + for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) { + elements.add(new SingleRootClasspathElement(file.getUrl())); + } + return elements; + } + + public static List createElements(String... urls) { + final List list = new ArrayList(); + for (String url : urls) { + list.add(new SingleRootClasspathElement(url)); + } + return list; + } +} diff --git a/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathPanel.java b/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathPanel.java new file mode 100644 index 0000000000..aa6db97240 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/ui/classpath/SimpleClasspathPanel.java @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved. + */ + +package com.intellij.util.ui.classpath; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl; +import com.intellij.openapi.project.ProjectBundle; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; +import com.intellij.openapi.roots.ui.util.OrderEntryCellAppearanceUtils; +import com.intellij.openapi.roots.ui.util.SimpleTextCellAppearance; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.ex.MultiLineLabel; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.ReorderableListController; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.util.Icons; +import com.intellij.util.PathUtil; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; +import java.util.List; + +/** + * @author Gregory.Shrago + */ +public class SimpleClasspathPanel extends JPanel { + + private final JList myList = new JList(); + private final DefaultListModel myListModel = new DefaultListModel(); + private URLClassLoader myClassLoader; + private final Disposable myDisposable; + + + public SimpleClasspathPanel(final List classpathElements, final Disposable parentDisposable) { + myDisposable = parentDisposable; + for (Library library : getLibrariesList(classpathElements, parentDisposable)) { + myListModel.addElement(library); + } + init(); + } + + private void init() { + setLayout(new BorderLayout()); + myList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + myList.setModel(myListModel); + final DefaultActionGroup actionGroup = new DefaultActionGroup(); + final ReorderableListController controller = ReorderableListController.create(myList, actionGroup); + controller.addAction(new AddAction()); + controller.addRemoveAction(ProjectBundle.message("module.remove.action")); + controller.addMoveUpAction(); + controller.addMoveDownAction(); + myList.setCellRenderer(new ColoredListCellRenderer() { + protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { + if (value instanceof Library) { + final Library library = (Library)value; + if (library.getName() != null && library.getUrls(OrderRootType.CLASSES).length == 0) { + SimpleTextCellAppearance.invalid(library.getName(), Icons.LIBRARY_ICON).customize(this); + } + else { + OrderEntryCellAppearanceUtils.forLibrary(library).customize(this); + } + } + } + }); + add(ActionManager.getInstance().createActionToolbar(ActionPlaces.PROJECT_VIEW_TOOLBAR, actionGroup, true).getComponent(), BorderLayout.NORTH); + final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myList); + add(scrollPane, BorderLayout.CENTER); + final FontMetrics fontMetrics = myList.getFontMetrics(myList.getFont()); + scrollPane.setPreferredSize(new Dimension(0, fontMetrics.getHeight() * 12)); + myList.getModel().addListDataListener(new ListDataListener() { + public void intervalAdded(ListDataEvent e) { + } + + public void intervalRemoved(ListDataEvent e) { + listChanged(e); + } + + public void contentsChanged(ListDataEvent e) { + } + }); + } + + private void listChanged(final ListDataEvent e) { + myClassLoader = null; + processClasspathChanged(); + } + + public void processClasspathChanged() { + } + + public List getOrderedLibraries() { + final ArrayList result = new ArrayList(); + for (final Enumeration enumeration = myListModel.elements(); enumeration.hasMoreElements(); ) { + result.add((Library)enumeration.nextElement()); + } + return result; + } + + public Set getVirtualFiles() { + final THashSet result = new THashSet(); + for (final Enumeration enumeration = myListModel.elements(); enumeration.hasMoreElements(); ) { + final Library library = (Library)enumeration.nextElement(); + result.addAll(Arrays.asList(library.getFiles(OrderRootType.CLASSES))); + } + return result; + } + + public URLClassLoader getClasspathLoader(ClassLoader parent) { + if (myClassLoader == null || myClassLoader.getParent() != parent) { + ArrayList urlList = new ArrayList(); + for (Library library : getOrderedLibraries()) { + for (VirtualFile virtualFile : library.getFiles(OrderRootType.CLASSES)) { + final File file = new File(PathUtil.toPresentableUrl(virtualFile.getUrl())); + try { + urlList.add(file.toURL()); + } + catch (MalformedURLException e) { + } + } + } + URL[] urls = urlList.toArray(new URL[urlList.size()]); + myClassLoader = new URLClassLoader(urls, parent); + } + return myClassLoader; + } + + public static void scrollSelectionToVisible(JList list){ + ListSelectionModel selectionModel = list.getSelectionModel(); + int maxSelectionIndex = selectionModel.getMaxSelectionIndex(); + int minSelectionIndex = selectionModel.getMinSelectionIndex(); + if(maxSelectionIndex == -1){ + return; + } + Rectangle cellRect = list.getCellBounds(minSelectionIndex, maxSelectionIndex); + list.scrollRectToVisible(cellRect); + } + + private static Collection ensureApplicationLevel(final Library library, final Set existingFiles) { + if (library.getTable() == null || !LibraryTablesRegistrar.APPLICATION_LEVEL.equals(library.getTable().getTableLevel())) { + final ArrayList result = new ArrayList(); + for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) { + if (!existingFiles.add(file)) continue; + final Library newLibrary = LibraryTableImplUtil.createModuleLevelLibrary(null, null); + final Library.ModifiableModel libModel = newLibrary.getModifiableModel(); + libModel.addRoot(file, OrderRootType.CLASSES); + libModel.commit(); + result.add(newLibrary); + } + return result; + } + return Collections.singletonList(library); + } + + private abstract static class PopupAction implements ActionListener { + private final String myTitle; + private final Icon myIcon; + private final int myIndex; + + protected PopupAction(String title, Icon icon, final int index) { + myTitle = title; + myIcon = icon; + myIndex = index; + } + + public String getTitle() { + return myTitle; + } + + public Icon getIcon() { + return myIcon; + } + + public int getIndex() { + return myIndex; + } + + protected void executeImpl() { + } + + public void execute() { + executeImpl(); + } + + public void actionPerformed(ActionEvent e) { + executeImpl(); + } + } + + private class AddAction extends AnAction { + private PopupAction[] myPopupActions; + private Icon[] myIcons; + + public AddAction() { + super(ProjectBundle.message("module.add.action"), null, IconLoader.getIcon("/general/add.png")); + } + + private void initPopupActions() { + if (myPopupActions == null) { + int index = 1; + final List actions = new ArrayList(); + actions.add(new ChooseAndAddAction(index++, "Jar...", Icons.JAR_ICON) { + @NotNull + protected List doChoose() { + final ChooseJarDialog dialog = new ChooseJarDialog(SimpleClasspathPanel.this, getVirtualFiles(), myDisposable); + dialog.doChoose(); + return dialog.getChosenElements(); + } + }); + actions.add(new ChooseAndAddAction(index++, "Library...", Icons.LIBRARY_ICON) { + + @NotNull + protected List doChoose() { + final Set existingFiles = getVirtualFiles(); + final ChooseLibrariesDialog dialog = new ChooseLibrariesDialog(ProjectManager.getInstance().getDefaultProject(), "Choose Existing Libraries") { + @Override + protected boolean acceptsElement(final Object element) { + if (!(element instanceof Library)) return true; + final Library library = (Library)element; + return !existingFiles.containsAll(Arrays.asList(library.getFiles(OrderRootType.CLASSES))); + } + + @Override + protected JComponent createCenterPanel() { + final JPanel panel = new JPanel(new BorderLayout()); + panel.add(super.createCenterPanel(), BorderLayout.CENTER); + final MultiLineLabel label = new MultiLineLabel("Please note that project-level and module-level libraries will not be\n\n" + + " added as a whole but will be converted to jars and folders instead."); + label.setIcon(Messages.getWarningIcon()); + label.setIcon(Messages.getWarningIcon()); + panel.add(label, BorderLayout.SOUTH); + return panel; + } + + }; + dialog.show(); + final List libraries = dialog.getSelectedLibraries(); + final ArrayList result = new ArrayList(); + for (Library o : libraries) { + result.addAll(ensureApplicationLevel(o, existingFiles)); + } + return result; + } + }); + myPopupActions = actions.toArray(new PopupAction[actions.size()]); + + myIcons = new Icon[myPopupActions.length]; + for (int idx = 0; idx < myPopupActions.length; idx++) { + myIcons[idx] = myPopupActions[idx].getIcon(); + } + } + } + + public void actionPerformed(final AnActionEvent e) { + initPopupActions(); + final JBPopup popup = JBPopupFactory.getInstance().createWizardStep(new BaseListPopupStep(null, myPopupActions, myIcons) { + public boolean isMnemonicsNavigationEnabled() { + return true; + } + + public boolean isSelectable(PopupAction value) { + return true; + } + + public PopupStep onChosen(final PopupAction selectedValue, final boolean finalChoice) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + selectedValue.execute(); + } + }, ModalityState.stateForComponent(e.getInputEvent().getComponent())); + return PopupStep.FINAL_CHOICE; + } + + @NotNull + public String getTextFor(PopupAction value) { + return "&" + value.getIndex() + " " + value.getTitle(); + } + }); + popup.showUnderneathOf(e.getInputEvent().getComponent()); + } + + public Object fun(final AnActionEvent s) { + actionPerformed(s); + return null; + } + } + + + private abstract class ChooseAndAddAction extends PopupAction { + public ChooseAndAddAction(int index, String title, Icon icon) { + super(title, icon, index); + } + + protected final void executeImpl() { + final List libraries = doChoose(); + if (libraries.isEmpty()) return; + final int index0 = myListModel.size(); + for (Library library : libraries) { + myListModel.addElement(library); + } + final int index1 = myListModel.size()-1; + final ListSelectionModel selectionModel = myList.getSelectionModel(); + final int rowCount = myList.getModel().getSize(); + selectionModel.setSelectionInterval(rowCount - libraries.size(), rowCount - 1); + scrollSelectionToVisible(myList); + listChanged(new ListDataEvent(myListModel, ListDataEvent.INTERVAL_ADDED, index0, index1)); + } + + @NotNull + protected abstract List doChoose(); + } + + private static class ChooseJarDialog extends FileChooserDialogImpl { + private VirtualFile[] myLastChosen; + private final Disposable myParentDisposable; + + public ChooseJarDialog(Component parent, final Set existingFiles, final Disposable disposable) { + super(new FileChooserDescriptor(false, true, true, false, false, true) { + @Override + public boolean isFileVisible(final VirtualFile file, final boolean showHiddenFiles) { + if (!super.isFileVisible(file, showHiddenFiles)) return false; + return file.isDirectory() || !existingFiles.contains(file); + } + }, parent); + myParentDisposable = disposable; + } + + public List getChosenElements() { + if (myLastChosen == null) { + return Collections.emptyList(); + } + final VirtualFile[] files = myLastChosen; + if (files.length == 0) { + return Collections.emptyList(); + } + final List addedLibraries = new ArrayList(files.length); + for (VirtualFile file : files) { + final Library library = LibraryTableImplUtil.createModuleLevelLibrary(null, null); + Disposer.register(myParentDisposable, library); + final Library.ModifiableModel libModel = library.getModifiableModel(); + libModel.addRoot(file, OrderRootType.CLASSES); + libModel.commit(); + addedLibraries.add(library); + } + return addedLibraries; + } + + public void doChoose() { + myLastChosen = choose(null, null); + } + } + + private static List getLibrariesList(final List classpathElements, final Disposable disposable) { + ArrayList result = new ArrayList(); + for (SimpleClasspathElement classpathElement : classpathElements) { + final Library library = classpathElement.getLibrary(); + if (library != null && library.getTable() != null) { + result.add(library); + } + else { + final Library newLibrary = LibraryTableImplUtil.createModuleLevelLibrary(null, null); + Disposer.register(disposable, newLibrary); + final Library.ModifiableModel libModel = newLibrary.getModifiableModel(); + final String libName = classpathElement.getLibraryName(); + if (libName != null) libModel.setName(libName); + List fileUrls = classpathElement.getClassesRootUrls(); + for (final String fileUrl : fileUrls) { + libModel.addRoot(fileUrl, OrderRootType.CLASSES); + } + libModel.commit(); + result.add(newLibrary); + } + } + return result; + } + +} diff --git a/platform/lang-impl/src/com/intellij/util/ui/classpath/SingleRootClasspathElement.java b/platform/lang-impl/src/com/intellij/util/ui/classpath/SingleRootClasspathElement.java new file mode 100644 index 0000000000..5a716ff18d --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/ui/classpath/SingleRootClasspathElement.java @@ -0,0 +1,51 @@ +package com.intellij.util.ui.classpath; + +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.vfs.JarFileSystem; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * @author nik + */ +public class SingleRootClasspathElement implements SimpleClasspathElement { + @NonNls public static final String URL_ELEMENT = "url"; + private final String myUrl; + + public SingleRootClasspathElement(@NotNull String url) { + myUrl = url; + } + + public String getPresentableName() { + String url; + if (myUrl.endsWith(JarFileSystem.JAR_SEPARATOR)) { + url = myUrl.substring(0, myUrl.length() - JarFileSystem.JAR_SEPARATOR.length()); + } + else { + url = myUrl; + } + int startIndex = Math.min(url.lastIndexOf('/') + 1, url.length() - 1); + return url.substring(startIndex); + } + + public Library getLibrary() { + return null; + } + + public String getLibraryName() { + return null; + } + + public void serialize(Element element) throws IOException { + element.addContent(new Element(URL_ELEMENT).setText(myUrl)); + } + + public List getClassesRootUrls() { + return Collections.singletonList(myUrl); + } +} diff --git a/platform/platform-api/src/com/intellij/ide/DefaultTreeExpander.java b/platform/platform-api/src/com/intellij/ide/DefaultTreeExpander.java index fafc81e221..c0173f4745 100644 --- a/platform/platform-api/src/com/intellij/ide/DefaultTreeExpander.java +++ b/platform/platform-api/src/com/intellij/ide/DefaultTreeExpander.java @@ -32,17 +32,26 @@ public class DefaultTreeExpander implements TreeExpander { public void expandAll() { TreeUtil.expandAll(myTree); + showSelectionCentered(); } public boolean canExpand() { - return true; + return myTree.isShowing(); } public void collapseAll() { - TreeUtil.collapseAll(myTree, 0); + TreeUtil.collapseAll(myTree, 1); + showSelectionCentered(); + } + + private void showSelectionCentered() { + final int[] rowz = myTree.getSelectionRows(); + if (rowz != null && rowz.length > 0) { + TreeUtil.showRowCentered(myTree, rowz[0], false); + } } public boolean canCollapse() { - return true; + return myTree.isShowing(); } } -- 2.11.4.GIT