2 * Copyright (c) 2000-2006 JetBrains s.r.o. All Rights Reserved.
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
;
39 import javax
.swing
.event
.ListDataEvent
;
40 import javax
.swing
.event
.ListDataListener
;
42 import java
.awt
.event
.ActionEvent
;
43 import java
.awt
.event
.ActionListener
;
45 import java
.net
.MalformedURLException
;
47 import java
.net
.URLClassLoader
;
49 import java
.util
.List
;
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
);
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);
88 OrderEntryCellAppearanceUtils
.forLibrary(library
).customize(this);
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
) {
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());
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
)));
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()));
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){
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
);
175 result
.add(newLibrary
);
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
) {
193 public String
getTitle() {
197 public Icon
getIcon() {
201 public int getIndex() {
205 protected void executeImpl() {
208 public void execute() {
212 public void actionPerformed(ActionEvent e
) {
217 private class AddAction
extends AnAction
{
218 private PopupAction
[] myPopupActions
;
219 private Icon
[] myIcons
;
222 super(ProjectBundle
.message("module.add.action"), null, IconLoader
.getIcon("/general/add.png"));
225 private void initPopupActions() {
226 if (myPopupActions
== null) {
228 final List
<PopupAction
> actions
= new ArrayList
<PopupAction
>();
229 actions
.add(new ChooseAndAddAction(index
++, "Jar...", Icons
.JAR_ICON
) {
231 protected List
<Library
> doChoose() {
232 final ChooseJarDialog dialog
= new ChooseJarDialog(SimpleClasspathPanel
.this, getVirtualFiles(), myDisposable
);
234 return dialog
.getChosenElements();
237 actions
.add(new ChooseAndAddAction(index
++, "Library...", Icons
.LIBRARY_ICON
) {
240 protected List
<Library
> doChoose() {
241 final Set
<VirtualFile
> existingFiles
= getVirtualFiles();
242 final ChooseLibrariesDialog dialog
= new ChooseLibrariesDialog(ProjectManager
.getInstance().getDefaultProject(), "Choose Existing Libraries") {
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
)));
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
);
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
));
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
) {
283 final JBPopup popup
= JBPopupFactory
.getInstance().createWizardStep(new BaseListPopupStep
<PopupAction
>(null, myPopupActions
, myIcons
) {
284 public boolean isMnemonicsNavigationEnabled() {
288 public boolean isSelectable(PopupAction value
) {
292 public PopupStep
onChosen(final PopupAction selectedValue
, final boolean finalChoice
) {
293 ApplicationManager
.getApplication().invokeLater(new Runnable() {
295 selectedValue
.execute();
297 }, ModalityState
.stateForComponent(e
.getInputEvent().getComponent()));
298 return PopupStep
.FINAL_CHOICE
;
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
) {
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
));
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) {
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
);
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
);
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) {
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
);
398 result
.add(newLibrary
);