move classpath panel to community
[fedora-idea.git] / platform / lang-impl / src / com / intellij / util / ui / classpath / SimpleClasspathPanel.java
blobaa6db97240f2ff24488f8f3f6c4786186f8ebab3
1 /*
2 * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved.
3 */
5 package com.intellij.util.ui.classpath;
7 import com.intellij.openapi.Disposable;
8 import com.intellij.openapi.actionSystem.*;
9 import com.intellij.openapi.application.ApplicationManager;
10 import com.intellij.openapi.application.ModalityState;
11 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
12 import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl;
13 import com.intellij.openapi.project.ProjectBundle;
14 import com.intellij.openapi.project.ProjectManager;
15 import com.intellij.openapi.roots.OrderRootType;
16 import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
17 import com.intellij.openapi.roots.libraries.Library;
18 import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
19 import com.intellij.openapi.roots.ui.util.OrderEntryCellAppearanceUtils;
20 import com.intellij.openapi.roots.ui.util.SimpleTextCellAppearance;
21 import com.intellij.openapi.ui.Messages;
22 import com.intellij.openapi.ui.ex.MultiLineLabel;
23 import com.intellij.openapi.ui.popup.JBPopup;
24 import com.intellij.openapi.ui.popup.JBPopupFactory;
25 import com.intellij.openapi.ui.popup.PopupStep;
26 import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
27 import com.intellij.openapi.util.Disposer;
28 import com.intellij.openapi.util.IconLoader;
29 import com.intellij.openapi.vfs.VirtualFile;
30 import com.intellij.ui.ColoredListCellRenderer;
31 import com.intellij.ui.ReorderableListController;
32 import com.intellij.ui.ScrollPaneFactory;
33 import com.intellij.util.Icons;
34 import com.intellij.util.PathUtil;
35 import gnu.trove.THashSet;
36 import org.jetbrains.annotations.NotNull;
38 import javax.swing.*;
39 import javax.swing.event.ListDataEvent;
40 import javax.swing.event.ListDataListener;
41 import java.awt.*;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.io.File;
45 import java.net.MalformedURLException;
46 import java.net.URL;
47 import java.net.URLClassLoader;
48 import java.util.*;
49 import java.util.List;
51 /**
52 * @author Gregory.Shrago
54 public class SimpleClasspathPanel extends JPanel {
56 private final JList myList = new JList();
57 private final DefaultListModel myListModel = new DefaultListModel();
58 private URLClassLoader myClassLoader;
59 private final Disposable myDisposable;
62 public SimpleClasspathPanel(final List<SimpleClasspathElement> classpathElements, final Disposable parentDisposable) {
63 myDisposable = parentDisposable;
64 for (Library library : getLibrariesList(classpathElements, parentDisposable)) {
65 myListModel.addElement(library);
67 init();
70 private void init() {
71 setLayout(new BorderLayout());
72 myList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
73 myList.setModel(myListModel);
74 final DefaultActionGroup actionGroup = new DefaultActionGroup();
75 final ReorderableListController<Object> controller = ReorderableListController.create(myList, actionGroup);
76 controller.addAction(new AddAction());
77 controller.addRemoveAction(ProjectBundle.message("module.remove.action"));
78 controller.addMoveUpAction();
79 controller.addMoveDownAction();
80 myList.setCellRenderer(new ColoredListCellRenderer() {
81 protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
82 if (value instanceof Library) {
83 final Library library = (Library)value;
84 if (library.getName() != null && library.getUrls(OrderRootType.CLASSES).length == 0) {
85 SimpleTextCellAppearance.invalid(library.getName(), Icons.LIBRARY_ICON).customize(this);
87 else {
88 OrderEntryCellAppearanceUtils.forLibrary(library).customize(this);
92 });
93 add(ActionManager.getInstance().createActionToolbar(ActionPlaces.PROJECT_VIEW_TOOLBAR, actionGroup, true).getComponent(), BorderLayout.NORTH);
94 final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myList);
95 add(scrollPane, BorderLayout.CENTER);
96 final FontMetrics fontMetrics = myList.getFontMetrics(myList.getFont());
97 scrollPane.setPreferredSize(new Dimension(0, fontMetrics.getHeight() * 12));
98 myList.getModel().addListDataListener(new ListDataListener() {
99 public void intervalAdded(ListDataEvent e) {
102 public void intervalRemoved(ListDataEvent e) {
103 listChanged(e);
106 public void contentsChanged(ListDataEvent e) {
111 private void listChanged(final ListDataEvent e) {
112 myClassLoader = null;
113 processClasspathChanged();
116 public void processClasspathChanged() {
119 public List<Library> getOrderedLibraries() {
120 final ArrayList<Library> result = new ArrayList<Library>();
121 for (final Enumeration<?> enumeration = myListModel.elements(); enumeration.hasMoreElements(); ) {
122 result.add((Library)enumeration.nextElement());
124 return result;
127 public Set<VirtualFile> getVirtualFiles() {
128 final THashSet<VirtualFile> result = new THashSet<VirtualFile>();
129 for (final Enumeration<?> enumeration = myListModel.elements(); enumeration.hasMoreElements(); ) {
130 final Library library = (Library)enumeration.nextElement();
131 result.addAll(Arrays.asList(library.getFiles(OrderRootType.CLASSES)));
133 return result;
136 public URLClassLoader getClasspathLoader(ClassLoader parent) {
137 if (myClassLoader == null || myClassLoader.getParent() != parent) {
138 ArrayList<URL> urlList = new ArrayList<URL>();
139 for (Library library : getOrderedLibraries()) {
140 for (VirtualFile virtualFile : library.getFiles(OrderRootType.CLASSES)) {
141 final File file = new File(PathUtil.toPresentableUrl(virtualFile.getUrl()));
142 try {
143 urlList.add(file.toURL());
145 catch (MalformedURLException e) {
149 URL[] urls = urlList.toArray(new URL[urlList.size()]);
150 myClassLoader = new URLClassLoader(urls, parent);
152 return myClassLoader;
155 public static void scrollSelectionToVisible(JList list){
156 ListSelectionModel selectionModel = list.getSelectionModel();
157 int maxSelectionIndex = selectionModel.getMaxSelectionIndex();
158 int minSelectionIndex = selectionModel.getMinSelectionIndex();
159 if(maxSelectionIndex == -1){
160 return;
162 Rectangle cellRect = list.getCellBounds(minSelectionIndex, maxSelectionIndex);
163 list.scrollRectToVisible(cellRect);
166 private static Collection<Library> ensureApplicationLevel(final Library library, final Set<VirtualFile> existingFiles) {
167 if (library.getTable() == null || !LibraryTablesRegistrar.APPLICATION_LEVEL.equals(library.getTable().getTableLevel())) {
168 final ArrayList<Library> result = new ArrayList<Library>();
169 for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) {
170 if (!existingFiles.add(file)) continue;
171 final Library newLibrary = LibraryTableImplUtil.createModuleLevelLibrary(null, null);
172 final Library.ModifiableModel libModel = newLibrary.getModifiableModel();
173 libModel.addRoot(file, OrderRootType.CLASSES);
174 libModel.commit();
175 result.add(newLibrary);
177 return result;
179 return Collections.singletonList(library);
182 private abstract static class PopupAction implements ActionListener {
183 private final String myTitle;
184 private final Icon myIcon;
185 private final int myIndex;
187 protected PopupAction(String title, Icon icon, final int index) {
188 myTitle = title;
189 myIcon = icon;
190 myIndex = index;
193 public String getTitle() {
194 return myTitle;
197 public Icon getIcon() {
198 return myIcon;
201 public int getIndex() {
202 return myIndex;
205 protected void executeImpl() {
208 public void execute() {
209 executeImpl();
212 public void actionPerformed(ActionEvent e) {
213 executeImpl();
217 private class AddAction extends AnAction {
218 private PopupAction[] myPopupActions;
219 private Icon[] myIcons;
221 public AddAction() {
222 super(ProjectBundle.message("module.add.action"), null, IconLoader.getIcon("/general/add.png"));
225 private void initPopupActions() {
226 if (myPopupActions == null) {
227 int index = 1;
228 final List<PopupAction> actions = new ArrayList<PopupAction>();
229 actions.add(new ChooseAndAddAction(index++, "Jar...", Icons.JAR_ICON) {
230 @NotNull
231 protected List<Library> doChoose() {
232 final ChooseJarDialog dialog = new ChooseJarDialog(SimpleClasspathPanel.this, getVirtualFiles(), myDisposable);
233 dialog.doChoose();
234 return dialog.getChosenElements();
237 actions.add(new ChooseAndAddAction(index++, "Library...", Icons.LIBRARY_ICON) {
239 @NotNull
240 protected List<Library> doChoose() {
241 final Set<VirtualFile> existingFiles = getVirtualFiles();
242 final ChooseLibrariesDialog dialog = new ChooseLibrariesDialog(ProjectManager.getInstance().getDefaultProject(), "Choose Existing Libraries") {
243 @Override
244 protected boolean acceptsElement(final Object element) {
245 if (!(element instanceof Library)) return true;
246 final Library library = (Library)element;
247 return !existingFiles.containsAll(Arrays.asList(library.getFiles(OrderRootType.CLASSES)));
250 @Override
251 protected JComponent createCenterPanel() {
252 final JPanel panel = new JPanel(new BorderLayout());
253 panel.add(super.createCenterPanel(), BorderLayout.CENTER);
254 final MultiLineLabel label = new MultiLineLabel("Please note that project-level and module-level libraries will not be\n\n" +
255 " added as a whole but will be converted to jars and folders instead.");
256 label.setIcon(Messages.getWarningIcon());
257 label.setIcon(Messages.getWarningIcon());
258 panel.add(label, BorderLayout.SOUTH);
259 return panel;
263 dialog.show();
264 final List<Library> libraries = dialog.getSelectedLibraries();
265 final ArrayList<Library> result = new ArrayList<Library>();
266 for (Library o : libraries) {
267 result.addAll(ensureApplicationLevel(o, existingFiles));
269 return result;
272 myPopupActions = actions.toArray(new PopupAction[actions.size()]);
274 myIcons = new Icon[myPopupActions.length];
275 for (int idx = 0; idx < myPopupActions.length; idx++) {
276 myIcons[idx] = myPopupActions[idx].getIcon();
281 public void actionPerformed(final AnActionEvent e) {
282 initPopupActions();
283 final JBPopup popup = JBPopupFactory.getInstance().createWizardStep(new BaseListPopupStep<PopupAction>(null, myPopupActions, myIcons) {
284 public boolean isMnemonicsNavigationEnabled() {
285 return true;
288 public boolean isSelectable(PopupAction value) {
289 return true;
292 public PopupStep onChosen(final PopupAction selectedValue, final boolean finalChoice) {
293 ApplicationManager.getApplication().invokeLater(new Runnable() {
294 public void run() {
295 selectedValue.execute();
297 }, ModalityState.stateForComponent(e.getInputEvent().getComponent()));
298 return PopupStep.FINAL_CHOICE;
301 @NotNull
302 public String getTextFor(PopupAction value) {
303 return "&" + value.getIndex() + " " + value.getTitle();
306 popup.showUnderneathOf(e.getInputEvent().getComponent());
309 public Object fun(final AnActionEvent s) {
310 actionPerformed(s);
311 return null;
316 private abstract class ChooseAndAddAction extends PopupAction {
317 public ChooseAndAddAction(int index, String title, Icon icon) {
318 super(title, icon, index);
321 protected final void executeImpl() {
322 final List<Library> libraries = doChoose();
323 if (libraries.isEmpty()) return;
324 final int index0 = myListModel.size();
325 for (Library library : libraries) {
326 myListModel.addElement(library);
328 final int index1 = myListModel.size()-1;
329 final ListSelectionModel selectionModel = myList.getSelectionModel();
330 final int rowCount = myList.getModel().getSize();
331 selectionModel.setSelectionInterval(rowCount - libraries.size(), rowCount - 1);
332 scrollSelectionToVisible(myList);
333 listChanged(new ListDataEvent(myListModel, ListDataEvent.INTERVAL_ADDED, index0, index1));
336 @NotNull
337 protected abstract List<Library> doChoose();
340 private static class ChooseJarDialog extends FileChooserDialogImpl {
341 private VirtualFile[] myLastChosen;
342 private final Disposable myParentDisposable;
344 public ChooseJarDialog(Component parent, final Set<VirtualFile> existingFiles, final Disposable disposable) {
345 super(new FileChooserDescriptor(false, true, true, false, false, true) {
346 @Override
347 public boolean isFileVisible(final VirtualFile file, final boolean showHiddenFiles) {
348 if (!super.isFileVisible(file, showHiddenFiles)) return false;
349 return file.isDirectory() || !existingFiles.contains(file);
351 }, parent);
352 myParentDisposable = disposable;
355 public List<Library> getChosenElements() {
356 if (myLastChosen == null) {
357 return Collections.emptyList();
359 final VirtualFile[] files = myLastChosen;
360 if (files.length == 0) {
361 return Collections.emptyList();
363 final List<Library> addedLibraries = new ArrayList<Library>(files.length);
364 for (VirtualFile file : files) {
365 final Library library = LibraryTableImplUtil.createModuleLevelLibrary(null, null);
366 Disposer.register(myParentDisposable, library);
367 final Library.ModifiableModel libModel = library.getModifiableModel();
368 libModel.addRoot(file, OrderRootType.CLASSES);
369 libModel.commit();
370 addedLibraries.add(library);
372 return addedLibraries;
375 public void doChoose() {
376 myLastChosen = choose(null, null);
380 private static List<Library> getLibrariesList(final List<SimpleClasspathElement> classpathElements, final Disposable disposable) {
381 ArrayList<Library> result = new ArrayList<Library>();
382 for (SimpleClasspathElement classpathElement : classpathElements) {
383 final Library library = classpathElement.getLibrary();
384 if (library != null && library.getTable() != null) {
385 result.add(library);
387 else {
388 final Library newLibrary = LibraryTableImplUtil.createModuleLevelLibrary(null, null);
389 Disposer.register(disposable, newLibrary);
390 final Library.ModifiableModel libModel = newLibrary.getModifiableModel();
391 final String libName = classpathElement.getLibraryName();
392 if (libName != null) libModel.setName(libName);
393 List<String> fileUrls = classpathElement.getClassesRootUrls();
394 for (final String fileUrl : fileUrls) {
395 libModel.addRoot(fileUrl, OrderRootType.CLASSES);
397 libModel.commit();
398 result.add(newLibrary);
401 return result;