add file color for navigation list items which are not PsiElements but could fetch...
[fedora-idea.git] / platform / lang-impl / src / com / intellij / ide / util / DirectoryChooser.java
bloba7e74e8059f127d65182fff97bbe832073e297b9
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.
17 package com.intellij.ide.util;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.roots.FileIndex;
21 import com.intellij.openapi.roots.ProjectRootManager;
22 import com.intellij.openapi.ui.DialogWrapper;
23 import com.intellij.openapi.util.Comparing;
24 import com.intellij.openapi.util.SystemInfo;
25 import com.intellij.openapi.vfs.VirtualFile;
26 import com.intellij.psi.PsiDirectory;
27 import com.intellij.psi.PsiManager;
28 import com.intellij.util.ArrayUtil;
29 import com.intellij.util.Icons;
30 import org.jetbrains.annotations.Nullable;
32 import javax.swing.*;
33 import java.awt.*;
34 import java.awt.event.ActionEvent;
35 import java.awt.event.KeyEvent;
36 import java.io.File;
37 import java.util.ArrayList;
38 import java.util.Map;
40 public class DirectoryChooser extends DialogWrapper {
41 private final DirectoryChooserView myView;
43 public DirectoryChooser(Project project){
44 this(project, new DirectoryChooserModuleTreeView(project));
47 public DirectoryChooser(Project project, DirectoryChooserView view){
48 super(project, true);
49 myView = view;
50 init();
54 protected JComponent createCenterPanel(){
55 final Runnable runnable = new Runnable() {
56 public void run() {
57 enableButtons();
60 myView.onSelectionChange(runnable);
61 final JComponent component = myView.getComponent();
62 final JScrollPane jScrollPane = new JScrollPane(component);
63 //noinspection HardCodedStringLiteral
64 int prototypeWidth = component.getFontMetrics(component.getFont()).stringWidth("X:\\1234567890\\1234567890\\com\\company\\system\\subsystem");
65 jScrollPane.setPreferredSize(new Dimension(Math.max(300, prototypeWidth),300));
67 installEnterAction(component);
69 return jScrollPane;
72 private void installEnterAction(final JComponent component) {
73 final KeyStroke enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0);
74 final InputMap inputMap = component.getInputMap();
75 final ActionMap actionMap = component.getActionMap();
76 final Object oldActionKey = inputMap.get(enterKeyStroke);
77 final Action oldAction = oldActionKey != null ? actionMap.get(oldActionKey) : null;
78 inputMap.put(enterKeyStroke, "clickButton");
79 actionMap.put("clickButton", new AbstractAction() {
80 public void actionPerformed(ActionEvent e) {
81 if (isOKActionEnabled()) {
82 doOKAction();
84 else if (oldAction != null) {
85 oldAction.actionPerformed(e);
88 });
91 protected String getDimensionServiceKey() {
92 return "chooseDestDirectoryDialog";
95 private static String[] splitPath(String path) {
96 ArrayList<String> list = new ArrayList<String>();
97 int index = 0;
98 int nextSeparator;
99 while ((nextSeparator = path.indexOf(File.separatorChar, index)) != -1) {
100 list.add(path.substring(index, nextSeparator));
101 index = nextSeparator + 1;
103 list.add(path.substring(index, path.length()));
104 return ArrayUtil.toStringArray(list);
107 private void buildFragments() {
108 ArrayList<String[]> pathes = new ArrayList<String[]>();
109 for (int i = 0; i < myView.getItemsSize(); i++) {
110 ItemWrapper item = myView.getItemByIndex(i);
111 pathes.add(splitPath(item.getPresentableUrl()));
113 FragmentBuilder headBuilder = new FragmentBuilder(pathes){
114 protected void append(String fragment, StringBuffer buffer) {
115 buffer.append(mySeparator);
116 buffer.append(fragment);
119 protected int getFragmentIndex(String[] path, int index) {
120 return path.length > index ? index : -1;
123 String commonHead = headBuilder.execute();
124 final int headLimit = headBuilder.getIndex();
125 FragmentBuilder tailBuilder = new FragmentBuilder(pathes){
126 protected void append(String fragment, StringBuffer buffer) {
127 buffer.insert(0, fragment + mySeparator);
130 protected int getFragmentIndex(String[] path, int index) {
131 int result = path.length - 1 - index;
132 return result > headLimit ? result : -1;
135 String commonTail = tailBuilder.execute();
136 int tailLimit = tailBuilder.getIndex();
137 for (int i = 0; i < myView.getItemsSize(); i++) {
138 ItemWrapper item = myView.getItemByIndex(i);
139 String special = concat(pathes.get(i), headLimit, tailLimit);
140 item.setFragments(createFragments(commonHead, special, commonTail));
144 @Nullable
145 private static String concat(String[] strings, int headLimit, int tailLimit) {
146 if (strings.length <= headLimit + tailLimit) return null;
147 StringBuffer buffer = new StringBuffer();
148 String separator = "";
149 for (int i = headLimit; i < strings.length - tailLimit; i++) {
150 buffer.append(separator);
151 buffer.append(strings[i]);
152 separator = File.separator;
154 return buffer.toString();
157 private static PathFragment[] createFragments(String head, String special, String tail) {
158 ArrayList<PathFragment> list = new ArrayList<PathFragment>(3);
159 if (head != null) {
160 if (special != null || tail != null) list.add(new PathFragment(head + File.separatorChar, true));
161 else return new PathFragment[]{new PathFragment(head, true)};
163 if (special != null) {
164 if (tail != null) list.add(new PathFragment(special + File.separatorChar, false));
165 else list.add(new PathFragment(special, false));
167 if (tail != null) list.add(new PathFragment(tail, true));
168 return list.toArray(new PathFragment[list.size()]);
171 private static abstract class FragmentBuilder {
172 private final ArrayList<String[]> myPaths;
173 private final StringBuffer myBuffer = new StringBuffer();
174 private int myIndex;
175 protected String mySeparator = "";
177 public FragmentBuilder(ArrayList<String[]> pathes) {
178 myPaths = pathes;
179 myIndex = 0;
182 public int getIndex() { return myIndex; }
184 @Nullable
185 public String execute() {
186 while (true) {
187 String commonHead = getCommonFragment(myIndex);
188 if (commonHead == null) break;
189 append(commonHead, myBuffer);
190 mySeparator = File.separator;
191 myIndex++;
193 return myIndex > 0 ? myBuffer.toString() : null;
196 protected abstract void append(String fragment, StringBuffer buffer);
198 @Nullable
199 private String getCommonFragment(int count) {
200 String commonFragment = null;
201 for (String[] path : myPaths) {
202 int index = getFragmentIndex(path, count);
203 if (index == -1) return null;
204 if (commonFragment == null) {
205 commonFragment = path[index];
206 continue;
208 if (!Comparing.strEqual(commonFragment, path[index], SystemInfo.isFileSystemCaseSensitive)) return null;
210 return commonFragment;
213 protected abstract int getFragmentIndex(String[] path, int index);
216 public static class ItemWrapper {
217 final PsiDirectory myDirectory;
218 private PathFragment[] myFragments;
219 private final String myPostfix;
221 ItemWrapper(PsiDirectory directory, String postfix) {
222 myDirectory = directory;
223 myPostfix = postfix != null && postfix.length() > 0 ? postfix : null;
226 public PathFragment[] getFragments() { return myFragments; }
228 public void setFragments(PathFragment[] fragments) {
229 myFragments = fragments;
232 public Icon getIcon(FileIndex fileIndex) {
233 if (myDirectory != null) {
234 VirtualFile virtualFile = myDirectory.getVirtualFile();
235 if (fileIndex.isInTestSourceContent(virtualFile)){
236 return Icons.TEST_SOURCE_FOLDER;
238 else if (fileIndex.isInSourceContent(virtualFile)){
239 return Icons.SOURCE_FOLDERS_ICON;
242 return Icons.FOLDER_ICON;
245 public String getPresentableUrl() {
246 String directoryUrl = myDirectory != null ? myDirectory.getVirtualFile().getPresentableUrl() : "";
247 return myPostfix != null ? directoryUrl + myPostfix : directoryUrl;
250 public PsiDirectory getDirectory() {
251 return myDirectory;
255 public JComponent getPreferredFocusedComponent(){
256 return myView.getComponent();
259 public void fillList(PsiDirectory[] directories, @Nullable PsiDirectory defaultSelection, Project project, String postfixToShow) {
260 fillList(directories, defaultSelection, project, postfixToShow, null);
263 public void fillList(PsiDirectory[] directories, @Nullable PsiDirectory defaultSelection, Project project, Map<PsiDirectory,String> postfixes) {
264 fillList(directories, defaultSelection, project, null, postfixes);
267 private void fillList(PsiDirectory[] directories, @Nullable PsiDirectory defaultSelection, Project project, String postfixToShow, Map<PsiDirectory,String> postfixes) {
268 if (myView.getItemsSize() > 0){
269 myView.clearItems();
271 if (defaultSelection == null && directories.length > 0) {
272 defaultSelection = directories[0];
274 int selectionIndex = -1;
275 for(int i = 0; i < directories.length; i++){
276 PsiDirectory directory = directories[i];
277 if (directory.equals(defaultSelection)) {
278 selectionIndex = i;
279 break;
282 if (selectionIndex < 0 && directories.length == 1) {
283 selectionIndex = 0;
286 if (selectionIndex < 0) {
287 // find source root corresponding to defaultSelection
288 final PsiManager manager = PsiManager.getInstance(project);
289 VirtualFile[] sourceRoots = ProjectRootManager.getInstance(project).getContentSourceRoots();
290 for (VirtualFile sourceRoot : sourceRoots) {
291 if (sourceRoot.isDirectory()) {
292 PsiDirectory directory = manager.findDirectory(sourceRoot);
293 if (directory != null && isParent(defaultSelection, directory)) {
294 defaultSelection = directory;
295 break;
301 for(int i = 0; i < directories.length; i++){
302 PsiDirectory directory = directories[i];
303 final String postfixForDirectory;
304 if (postfixes == null) {
305 postfixForDirectory = postfixToShow;
307 else {
308 postfixForDirectory = postfixes.get(directory);
310 final ItemWrapper itemWrapper = new ItemWrapper(directory, postfixForDirectory);
311 myView.addItem(itemWrapper);
312 if (selectionIndex < 0 && isParent(directory, defaultSelection)) {
313 selectionIndex = i;
316 buildFragments();
317 myView.listFilled();
318 if (directories.length > 0) {
319 myView.selectItemByIndex(selectionIndex);
321 else {
322 myView.clearSelection();
324 enableButtons();
325 myView.getComponent().repaint();
328 private static boolean isParent(PsiDirectory directory, PsiDirectory parentCandidate) {
329 while (directory != null) {
330 if (directory.equals(parentCandidate)) return true;
331 directory = directory.getParentDirectory();
333 return false;
336 private void enableButtons() {
337 setOKActionEnabled(myView.getSelectedItem() != null);
340 @Nullable
341 public PsiDirectory getSelectedDirectory() {
342 ItemWrapper wrapper = myView.getSelectedItem();
343 if (wrapper == null) return null;
344 return wrapper.myDirectory;
348 public static class PathFragment {
349 private final String myText;
350 private final boolean myCommon;
352 public PathFragment(String text, boolean isCommon) {
353 myText = text;
354 myCommon = isCommon;
357 public String getText() {
358 return myText;
361 public boolean isCommon() {
362 return myCommon;