2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.openapi
.projectRoots
.ui
;
18 import com
.intellij
.openapi
.application
.ApplicationManager
;
19 import com
.intellij
.openapi
.diagnostic
.Logger
;
20 import com
.intellij
.openapi
.fileChooser
.FileChooser
;
21 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptor
;
22 import com
.intellij
.openapi
.fileTypes
.FileTypeManager
;
23 import com
.intellij
.openapi
.fileTypes
.FileTypes
;
24 import com
.intellij
.openapi
.project
.ProjectBundle
;
25 import com
.intellij
.openapi
.projectRoots
.SdkModificator
;
26 import com
.intellij
.openapi
.roots
.OrderRootType
;
27 import com
.intellij
.openapi
.util
.Computable
;
28 import com
.intellij
.openapi
.util
.IconLoader
;
29 import com
.intellij
.openapi
.vfs
.JarFileSystem
;
30 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
31 import com
.intellij
.openapi
.vfs
.VirtualFile
;
32 import com
.intellij
.openapi
.vfs
.ex
.http
.HttpFileSystem
;
33 import com
.intellij
.ui
.ListUtil
;
34 import com
.intellij
.ui
.ScrollPaneFactory
;
35 import com
.intellij
.util
.Icons
;
36 import com
.intellij
.util
.containers
.HashSet
;
37 import com
.intellij
.util
.ui
.UIUtil
;
38 import gnu
.trove
.TIntArrayList
;
41 import javax
.swing
.event
.ListSelectionEvent
;
42 import javax
.swing
.event
.ListSelectionListener
;
44 import java
.awt
.event
.ActionEvent
;
45 import java
.awt
.event
.ActionListener
;
46 import java
.util
.ArrayList
;
47 import java
.util
.Arrays
;
53 public abstract class PathEditor
{
54 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.projectRoots.ui.PathEditor");
55 public static final Color INVALID_COLOR
= new Color(210, 0, 0);
57 private JPanel myPanel
;
58 private JButton myRemoveButton
;
59 private JButton myAddButton
;
60 private JButton mySpecifyUrlButton
;
62 private DefaultListModel myModel
;
63 private final Set
<VirtualFile
> myAllFiles
= new HashSet
<VirtualFile
>();
64 private boolean myModified
= false;
65 private boolean myEnabled
= false;
66 private static final Icon ICON_INVALID
= IconLoader
.getIcon("/nodes/ppInvalid.png");
67 private static final Icon ICON_EMPTY
= IconLoader
.getIcon("/nodes/emptyNode.png");
69 protected abstract boolean isShowUrlButton();
71 protected abstract OrderRootType
getRootType();
73 protected abstract FileChooserDescriptor
createFileChooserDescriptor();
75 public abstract String
getDisplayName();
77 public Icon
getIcon(){
81 protected void setModified(boolean modified
){
82 this.myModified
= modified
;
85 public boolean isModified(){
89 public void apply(SdkModificator sdkModificator
) {
90 final OrderRootType rootType
= getRootType();
91 sdkModificator
.removeRoots(rootType
);
93 for (int i
= 0; i
< getRowCount(); i
++){
94 sdkModificator
.addRoot(getValueAt(i
), rootType
);
100 public VirtualFile
[] getRoots() {
101 final int count
= getRowCount();
103 return VirtualFile
.EMPTY_ARRAY
;
105 final VirtualFile
[] roots
= new VirtualFile
[count
];
106 for (int i
= 0; i
< count
; i
++){
107 roots
[i
] = getValueAt(i
);
112 public void reset(VirtualFile
[] files
) {
113 keepSelectionState();
115 myEnabled
= files
!= null;
117 for (int i
= 0; i
< files
.length
; i
++){
118 addElement(files
[i
]);
125 public JComponent
createComponent(){
126 myPanel
= new JPanel(new GridBagLayout());
127 Insets anInsets
= new Insets(2, 2, 2, 2);
129 myModel
= new DefaultListModel();
130 myList
= new JList(myModel
);
131 myList
.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
132 public void valueChanged(ListSelectionEvent e
){
136 myList
.setCellRenderer(new MyCellRenderer());
138 myRemoveButton
= new JButton(ProjectBundle
.message("button.remove"));
139 myAddButton
= new JButton(ProjectBundle
.message("button.add"));
140 mySpecifyUrlButton
= new JButton(ProjectBundle
.message("sdk.paths.specify.url.button"));
142 mySpecifyUrlButton
.setVisible(isShowUrlButton());
144 myAddButton
.addActionListener(new ActionListener(){
145 public void actionPerformed(ActionEvent e
){
146 final VirtualFile
[] added
= doAdd();
147 if (added
.length
> 0){
151 requestDefaultFocus();
152 setSelectedRoots(added
);
155 myRemoveButton
.addActionListener(new ActionListener(){
156 public void actionPerformed(ActionEvent e
){
157 java
.util
.List removedItems
= ListUtil
.removeSelectedItems(myList
);
158 itemsRemoved(removedItems
);
161 mySpecifyUrlButton
.addActionListener(new ActionListener(){
162 public void actionPerformed(ActionEvent e
){
163 VirtualFile virtualFile
= Util
.showSpecifyJavadocUrlDialog(myPanel
);
164 if(virtualFile
!= null){
165 addElement(virtualFile
);
168 requestDefaultFocus();
169 setSelectedRoots(new Object
[]{virtualFile
});
174 JScrollPane scrollPane
= ScrollPaneFactory
.createScrollPane(myList
);
175 scrollPane
.setPreferredSize(new Dimension(500, 500));
176 myPanel
.add(scrollPane
, new GridBagConstraints(0, 0, 1, 8, 1.0, 1.0, GridBagConstraints
.CENTER
, GridBagConstraints
.BOTH
, anInsets
, 0, 0));
177 myPanel
.add(myAddButton
, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints
.CENTER
, GridBagConstraints
.HORIZONTAL
, anInsets
, 0, 0));
178 myPanel
.add(myRemoveButton
, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints
.CENTER
, GridBagConstraints
.HORIZONTAL
, anInsets
, 0, 0));
179 myPanel
.add(mySpecifyUrlButton
, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints
.CENTER
, GridBagConstraints
.HORIZONTAL
, anInsets
, 0, 0));
180 myPanel
.add(Box
.createRigidArea(new Dimension(mySpecifyUrlButton
.getPreferredSize().width
, 4)), new GridBagConstraints(1, 5, 1, 1, 0.0, 0.0, GridBagConstraints
.CENTER
, GridBagConstraints
.NONE
, anInsets
, 0, 0));
184 private void itemsRemoved(java
.util
.List removedItems
) {
185 myAllFiles
.removeAll(removedItems
);
186 if (removedItems
.size() > 0){
190 requestDefaultFocus();
193 private VirtualFile
[] doAdd(){
194 FileChooserDescriptor descriptor
= createFileChooserDescriptor();
195 VirtualFile
[] files
= FileChooser
.chooseFiles(myPanel
, descriptor
);
196 files
= adjustAddedFileSet(myPanel
, files
);
197 java
.util
.List
<VirtualFile
> added
= new ArrayList
<VirtualFile
>(files
.length
);
198 for (int i
= 0; i
< files
.length
; i
++){
199 VirtualFile vFile
= files
[i
];
200 if(addElement(vFile
)){
204 return added
.toArray(new VirtualFile
[added
.size()]);
208 * Implement this method to ajust adding behavior, this method is called right after the files
209 * or directories are selected for added. This method allows adding UI that modify file set.
211 * The default implemenation returns a value passed the parameter files and does nothing.
213 * @param component a component that could be used as a parent.
214 * @param files a selected file set
215 * @return ajusted file set
217 protected VirtualFile
[] adjustAddedFileSet(final Component component
, final VirtualFile
[] files
) {
221 private void updateButtons(){
222 Object
[] values
= getSelectedRoots();
223 myRemoveButton
.setEnabled((values
.length
> 0) && myEnabled
);
224 myAddButton
.setEnabled(myEnabled
);
225 mySpecifyUrlButton
.setEnabled(myEnabled
&& !isUrlInserted());
226 mySpecifyUrlButton
.setVisible(isShowUrlButton());
229 private boolean isUrlInserted(){
230 if(getRowCount() > 0){
231 return ((VirtualFile
)myModel
.lastElement()).getFileSystem() instanceof HttpFileSystem
;
236 private void requestDefaultFocus(){
238 myList
.requestFocus();
242 public void addPaths(VirtualFile
... paths
){
243 boolean added
= false;
244 keepSelectionState();
245 for (int i
= 0; i
< paths
.length
; i
++){
246 final VirtualFile path
= paths
[i
];
247 if(addElement(path
)){
257 public void removePaths(VirtualFile
... paths
) {
258 final Set
<VirtualFile
> pathsSet
= new java
.util
.HashSet
<VirtualFile
>(Arrays
.asList(paths
));
259 int size
= getRowCount();
260 final TIntArrayList indicesToRemove
= new TIntArrayList(paths
.length
);
261 for (int idx
= 0; idx
< size
; idx
++) {
262 VirtualFile path
= getValueAt(idx
);
263 if (pathsSet
.contains(path
)) {
264 indicesToRemove
.add(idx
);
267 final java
.util
.List list
= ListUtil
.removeIndices(myList
, indicesToRemove
.toNativeArray());
271 /** Method adds element only if it is not added yet. */
272 protected boolean addElement(VirtualFile item
){
276 if (myAllFiles
.contains(item
)){
280 myModel
.insertElementAt(item
, myModel
.size() - 1);
283 myModel
.addElement(item
);
285 myAllFiles
.add(item
);
289 private void setSelectedRoots(Object
[] roots
){
290 ArrayList rootsList
= new ArrayList(roots
.length
);
291 for (int i
= 0; i
< roots
.length
; i
++){
292 Object root
= roots
[i
];
297 myList
.getSelectionModel().clearSelection();
298 int rowCount
= myModel
.getSize();
299 for (int i
= 0; i
< rowCount
; i
++){
300 Object currObject
= myModel
.get(i
);
301 LOG
.assertTrue(currObject
!= null);
302 if (rootsList
.contains(currObject
)){
303 myList
.getSelectionModel().addSelectionInterval(i
, i
);
308 private void keepSelectionState(){
309 final Object
[] selectedItems
= getSelectedRoots();
311 SwingUtilities
.invokeLater(new Runnable(){
313 if (selectedItems
!= null){
314 setSelectedRoots(selectedItems
);
320 protected Object
[] getSelectedRoots(){
321 return myList
.getSelectedValues();
324 private int getRowCount(){
325 return myModel
.getSize();
328 private VirtualFile
getValueAt(int row
){
329 return (VirtualFile
)myModel
.get(row
);
332 public void clearList(){
338 private static boolean isJarFile(final VirtualFile file
){
339 return ApplicationManager
.getApplication().runReadAction(new Computable
<Boolean
>() {
340 public Boolean
compute() {
341 VirtualFile tempFile
= file
;
342 if ((file
.getFileSystem() instanceof JarFileSystem
) && file
.getParent() == null){
343 //[myakovlev] It was bug - directories with *.jar extensions was saved as files of JarFileSystem.
344 // so we can not just return true, we should filter such directories.
345 String path
= file
.getPath().substring(0, file
.getPath().length() - JarFileSystem
.JAR_SEPARATOR
.length());
346 tempFile
= LocalFileSystem
.getInstance().findFileByPath(path
);
348 if (tempFile
!= null && !tempFile
.isDirectory()){
349 return Boolean
.valueOf(FileTypeManager
.getInstance().getFileTypeByFile(tempFile
).equals(FileTypes
.ARCHIVE
));
351 return Boolean
.FALSE
;
358 * @return icon for displaying parameter (ProjectRoot or VirtualFile)
359 * If parameter is not ProjectRoot or VirtualFile, returns empty icon "/nodes/emptyNode.png"
361 private static Icon
getIconForRoot(Object projectRoot
){
362 if (projectRoot
instanceof VirtualFile
){
363 final VirtualFile file
= (VirtualFile
)projectRoot
;
364 if (!file
.isValid()){
367 else if (isHttpRoot(file
)){
368 return Icons
.WEB_ICON
;
371 return isJarFile(file
) ? Icons
.JAR_ICON
: Icons
.FILE_ICON
;
377 private static boolean isHttpRoot(VirtualFile virtualFileOrProjectRoot
){
378 if(virtualFileOrProjectRoot
!= null){
379 return (virtualFileOrProjectRoot
.getFileSystem() instanceof HttpFileSystem
);
384 private final class MyCellRenderer
extends DefaultListCellRenderer
{
385 private String
getPresentableString(final Object value
){
386 return ApplicationManager
.getApplication().runReadAction(new Computable
<String
>() {
387 public String
compute() {
388 //noinspection HardCodedStringLiteral
389 return (value
instanceof VirtualFile
)?
((VirtualFile
)value
).getPresentableUrl() : "UNKNOWN OBJECT";
394 public Component
getListCellRendererComponent(JList list
, Object value
, int index
, boolean isSelected
, boolean cellHasFocus
){
395 super.getListCellRendererComponent(list
, getPresentableString(value
), index
, isSelected
, cellHasFocus
);
397 setForeground(UIUtil
.getListSelectionForeground());
400 if (value
instanceof VirtualFile
){
401 VirtualFile file
= (VirtualFile
)value
;
402 if (!file
.isValid()){
403 setForeground(INVALID_COLOR
);
407 setIcon(getIconForRoot(value
));