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
;
34 import java
.awt
.event
.ActionEvent
;
35 import java
.awt
.event
.KeyEvent
;
37 import java
.util
.ArrayList
;
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
){
54 protected JComponent
createCenterPanel(){
55 final Runnable runnable
= new Runnable() {
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
);
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()) {
84 else if (oldAction
!= null) {
85 oldAction
.actionPerformed(e
);
91 protected String
getDimensionServiceKey() {
92 return "chooseDestDirectoryDialog";
95 private static String
[] splitPath(String path
) {
96 ArrayList
<String
> list
= new ArrayList
<String
>();
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
));
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);
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();
175 protected String mySeparator
= "";
177 public FragmentBuilder(ArrayList
<String
[]> pathes
) {
182 public int getIndex() { return myIndex
; }
185 public String
execute() {
187 String commonHead
= getCommonFragment(myIndex
);
188 if (commonHead
== null) break;
189 append(commonHead
, myBuffer
);
190 mySeparator
= File
.separator
;
193 return myIndex
> 0 ? myBuffer
.toString() : null;
196 protected abstract void append(String fragment
, StringBuffer buffer
);
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
];
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() {
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){
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
)) {
282 if (selectionIndex
< 0 && directories
.length
== 1) {
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
;
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
;
308 postfixForDirectory
= postfixes
.get(directory
);
310 final ItemWrapper itemWrapper
= new ItemWrapper(directory
, postfixForDirectory
);
311 myView
.addItem(itemWrapper
);
312 if (selectionIndex
< 0 && isParent(directory
, defaultSelection
)) {
318 if (directories
.length
> 0) {
319 myView
.selectItemByIndex(selectionIndex
);
322 myView
.clearSelection();
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();
336 private void enableButtons() {
337 setOKActionEnabled(myView
.getSelectedItem() != null);
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
) {
357 public String
getText() {
361 public boolean isCommon() {