update copyright
[fedora-idea.git] / java / idea-ui / src / com / intellij / openapi / projectRoots / ui / PathEditor.java
blobe87441e3cb0ce1935095d4fd37eeb69efdf61fec
1 /*
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;
40 import javax.swing.*;
41 import javax.swing.event.ListSelectionEvent;
42 import javax.swing.event.ListSelectionListener;
43 import java.awt.*;
44 import java.awt.event.ActionEvent;
45 import java.awt.event.ActionListener;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Set;
50 /**
51 * @author MYakovlev
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;
61 private JList myList;
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(){
78 return null;
81 protected void setModified(boolean modified){
82 this.myModified = modified;
85 public boolean isModified(){
86 return myModified;
89 public void apply(SdkModificator sdkModificator) {
90 final OrderRootType rootType = getRootType();
91 sdkModificator.removeRoots(rootType);
92 // add all items
93 for (int i = 0; i < getRowCount(); i++){
94 sdkModificator.addRoot(getValueAt(i), rootType);
96 setModified(false);
97 updateButtons();
100 public VirtualFile[] getRoots() {
101 final int count = getRowCount();
102 if (count == 0) {
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);
109 return roots;
112 public void reset(VirtualFile[] files) {
113 keepSelectionState();
114 clearList();
115 myEnabled = files != null;
116 if(myEnabled){
117 for (int i = 0; i < files.length; i++){
118 addElement(files[i]);
121 setModified(false);
122 updateButtons();
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){
133 updateButtons();
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){
148 setModified(true);
150 updateButtons();
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);
166 setModified(true);
167 updateButtons();
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));
181 return myPanel;
184 private void itemsRemoved(java.util.List removedItems) {
185 myAllFiles.removeAll(removedItems);
186 if (removedItems.size() > 0){
187 setModified(true);
189 updateButtons();
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)){
201 added.add(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) {
218 return 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;
233 return false;
236 private void requestDefaultFocus(){
237 if (myList != null){
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)){
248 added = true;
251 if (added){
252 setModified(true);
253 updateButtons();
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());
268 itemsRemoved(list);
271 /** Method adds element only if it is not added yet. */
272 protected boolean addElement(VirtualFile item){
273 if(item == null){
274 return false;
276 if (myAllFiles.contains(item)){
277 return false;
279 if(isUrlInserted()){
280 myModel.insertElementAt(item, myModel.size() - 1);
282 else{
283 myModel.addElement(item);
285 myAllFiles.add(item);
286 return true;
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];
293 if(root != null){
294 rootsList.add(root);
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(){
312 public void run(){
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(){
333 myModel.clear();
334 myAllFiles.clear();
335 setModified(true);
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;
354 } ).booleanValue();
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()){
365 return ICON_INVALID;
367 else if (isHttpRoot(file)){
368 return Icons.WEB_ICON;
370 else{
371 return isJarFile(file) ? Icons.JAR_ICON : Icons.FILE_ICON;
374 return ICON_EMPTY;
377 private static boolean isHttpRoot(VirtualFile virtualFileOrProjectRoot){
378 if(virtualFileOrProjectRoot != null){
379 return (virtualFileOrProjectRoot.getFileSystem() instanceof HttpFileSystem);
381 return false;
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);
396 if (isSelected){
397 setForeground(UIUtil.getListSelectionForeground());
399 else{
400 if (value instanceof VirtualFile){
401 VirtualFile file = (VirtualFile)value;
402 if (!file.isValid()){
403 setForeground(INVALID_COLOR);
407 setIcon(getIconForRoot(value));
408 return this;