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
.fileChooser
.ex
;
18 import com
.intellij
.ide
.util
.treeView
.AbstractTreeBuilder
;
19 import com
.intellij
.ide
.util
.treeView
.AbstractTreeStructure
;
20 import com
.intellij
.ide
.util
.treeView
.NodeDescriptor
;
21 import com
.intellij
.openapi
.Disposable
;
22 import com
.intellij
.openapi
.actionSystem
.ActionGroup
;
23 import com
.intellij
.openapi
.actionSystem
.ActionManager
;
24 import com
.intellij
.openapi
.application
.ApplicationManager
;
25 import com
.intellij
.openapi
.command
.CommandProcessor
;
26 import com
.intellij
.openapi
.diagnostic
.Logger
;
27 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptor
;
28 import com
.intellij
.openapi
.fileChooser
.FileElement
;
29 import com
.intellij
.openapi
.fileChooser
.FileSystemTree
;
30 import com
.intellij
.openapi
.fileChooser
.impl
.FileComparator
;
31 import com
.intellij
.openapi
.fileChooser
.impl
.FileTreeBuilder
;
32 import com
.intellij
.openapi
.fileChooser
.impl
.FileTreeStructure
;
33 import com
.intellij
.openapi
.fileTypes
.FileType
;
34 import com
.intellij
.openapi
.project
.Project
;
35 import com
.intellij
.openapi
.util
.Disposer
;
36 import com
.intellij
.openapi
.util
.text
.StringUtil
;
37 import com
.intellij
.openapi
.vfs
.JarFileSystem
;
38 import com
.intellij
.openapi
.vfs
.VfsUtil
;
39 import com
.intellij
.openapi
.vfs
.VirtualFile
;
40 import com
.intellij
.ui
.*;
41 import com
.intellij
.ui
.treeStructure
.SimpleNodeRenderer
;
42 import com
.intellij
.util
.containers
.ConvertingIterator
;
43 import com
.intellij
.util
.containers
.Convertor
;
44 import com
.intellij
.ui
.treeStructure
.Tree
;
45 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
46 import org
.jetbrains
.annotations
.Nullable
;
49 import javax
.swing
.event
.TreeExpansionEvent
;
50 import javax
.swing
.event
.TreeExpansionListener
;
51 import javax
.swing
.event
.TreeSelectionEvent
;
52 import javax
.swing
.event
.TreeSelectionListener
;
53 import javax
.swing
.tree
.*;
54 import java
.awt
.event
.*;
55 import java
.io
.IOException
;
56 import java
.util
.ArrayList
;
57 import java
.util
.Comparator
;
58 import java
.util
.List
;
60 public class FileSystemTreeImpl
implements FileSystemTree
{
61 private static final Logger LOG
= Logger
.getInstance("#com.intellij.chooser.FileSystemTreeImpl");
63 private final Tree myTree
;
64 private final FileTreeStructure myTreeStructure
;
65 private final AbstractTreeBuilder myTreeBuilder
;
66 private final Project myProject
;
67 private final ArrayList
<Runnable
> myOkActions
= new ArrayList
<Runnable
>(2);
68 private final FileChooserDescriptor myDescriptor
;
70 private final List
<Listener
> myListeners
= new ArrayList
<Listener
>();
71 private final MyExpansionListener myExpansionListener
= new MyExpansionListener();
73 public FileSystemTreeImpl(@Nullable Project project
, FileChooserDescriptor descriptor
) {
74 this(project
, descriptor
, new Tree(), null, null);
75 myTree
.setRootVisible(descriptor
.isTreeRootVisible());
76 myTree
.setShowsRootHandles(true);
79 public FileSystemTreeImpl(@Nullable Project project
, FileChooserDescriptor descriptor
, Tree tree
, TreeCellRenderer renderer
,
80 final Runnable onInitialized
) {
82 myTreeStructure
= new FileTreeStructure(project
, descriptor
);
83 myDescriptor
= descriptor
;
85 final DefaultTreeModel treeModel
= new DefaultTreeModel(new DefaultMutableTreeNode());
86 myTree
.setModel(treeModel
);
88 myTree
.addTreeExpansionListener(myExpansionListener
);
90 myTreeBuilder
= createTreeBuilder(myTree
, treeModel
, myTreeStructure
, FileComparator
.getInstance(), descriptor
, new Runnable() {
92 myTree
.expandPath(new TreePath(treeModel
.getRoot()));
93 if (onInitialized
!= null) {
99 Disposer
.register(myTreeBuilder
, new Disposable() {
100 public void dispose() {
101 myTree
.removeTreeExpansionListener(myExpansionListener
);
105 if (project
!= null) {
106 Disposer
.register(project
, myTreeBuilder
);
109 myTree
.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
110 public void valueChanged(final TreeSelectionEvent e
) {
111 processSelectionChange();
115 new TreeSpeedSearch(myTree
);
116 myTree
.setLineStyleAngled();
117 TreeToolTipHandler
.install(myTree
);
118 TreeUtil
.installActions(myTree
);
120 myTree
.getSelectionModel().setSelectionMode(
121 myTreeStructure
.getChooserDescriptor().getChooseMultiple() ?
122 TreeSelectionModel
.DISCONTIGUOUS_TREE_SELECTION
:
123 TreeSelectionModel
.SINGLE_TREE_SELECTION
125 registerTreeActions();
127 if (renderer
== null) {
128 renderer
= new SimpleNodeRenderer() {
130 public void customizeCellRenderer(JTree tree
,
137 super.customizeCellRenderer(tree
, value
, selected
, expanded
, leaf
, row
, hasFocus
);
138 final Object userObject
= ((DefaultMutableTreeNode
)value
).getUserObject();
139 if (userObject
instanceof FileNodeDescriptor
) {
140 String comment
= ((FileNodeDescriptor
)userObject
).getComment();
141 if (comment
!= null) {
142 append(comment
, SimpleTextAttributes
.REGULAR_ATTRIBUTES
);
148 myTree
.setCellRenderer(renderer
);
152 protected AbstractTreeBuilder
createTreeBuilder(final JTree tree
, DefaultTreeModel treeModel
, final AbstractTreeStructure treeStructure
,
153 final Comparator
<NodeDescriptor
> comparator
, FileChooserDescriptor descriptor
,
154 @Nullable final Runnable onInitialized
) {
155 return new FileTreeBuilder(tree
, treeModel
, treeStructure
, comparator
, descriptor
, onInitialized
);
158 private void registerTreeActions() {
159 myTree
.registerKeyboardAction(
160 new ActionListener() {
161 public void actionPerformed(ActionEvent e
) {
162 performEnterAction(true);
164 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_ENTER
, 0), JComponent
.WHEN_FOCUSED
166 myTree
.addMouseListener(new MouseAdapter() {
167 public void mouseClicked(MouseEvent e
) {
168 if (e
.getClickCount() == 2) performEnterAction(false);
173 private void performEnterAction(boolean toggleNodeState
) {
174 TreePath path
= myTree
.getSelectionPath();
176 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
177 if (node
!= null && node
.isLeaf()) {
180 else if (toggleNodeState
) {
181 if (myTree
.isExpanded(path
)) {
182 myTree
.collapsePath(path
);
185 myTree
.expandPath(path
);
191 public void addOkAction(Runnable action
) { myOkActions
.add(action
); }
193 private void fireOkAction() {
194 for (Runnable action
: myOkActions
) {
199 public void registerMouseListener(final ActionGroup group
) {
200 PopupHandler
.installUnknownPopupHandler(myTree
, group
, ActionManager
.getInstance());
203 public boolean areHiddensShown() {
204 return myTreeStructure
.areHiddensShown();
207 public void showHiddens(boolean showHiddens
) {
208 myTreeStructure
.showHiddens(showHiddens
);
212 public void updateTree() {
213 myTreeBuilder
.updateFromRoot();
216 public void dispose() {
217 if (myTreeBuilder
!= null) {
218 Disposer
.dispose(myTreeBuilder
);
223 * @deprecated since tree updating is an asynchronous operation
225 public boolean select(final VirtualFile file
) {
226 DefaultMutableTreeNode node
= getNodeForFile(file
);
227 if (node
== null) return false;
229 TreeUtil
.selectPath(myTree
, new TreePath(node
.getPath()));
234 public void select(VirtualFile file
, @Nullable final Runnable onDone
) {
235 select(new VirtualFile
[] {file
}, onDone
);
238 public void select(VirtualFile
[] file
, @Nullable final Runnable onDone
) {
239 Object
[] elements
= new Object
[file
.length
];
240 for (int i
= 0; i
< file
.length
; i
++) {
241 VirtualFile eachFile
= file
[i
];
242 elements
[i
] = getFileElementFor(eachFile
);
245 myTreeBuilder
.select(elements
, onDone
);
248 public void expand(final VirtualFile file
, @Nullable final Runnable onDone
) {
249 myTreeBuilder
.expand(getFileElementFor(file
), onDone
);
253 private static FileElement
getFileElementFor(VirtualFile file
) {
254 VirtualFile selectFile
;
256 if ((file
.getFileSystem() instanceof JarFileSystem
) && file
.getParent() == null) {
257 selectFile
= JarFileSystem
.getInstance().getVirtualFileForJar(file
);
258 if (selectFile
== null) {
266 return new FileElement(selectFile
, selectFile
.getName());
269 public boolean expand(VirtualFile directory
) {
270 if (!directory
.isDirectory()) return false;
271 DefaultMutableTreeNode node
= getNodeForFile(directory
);
272 if (node
== null) return false;
273 myTree
.expandPath(new TreePath(node
.getPath()));
278 private DefaultMutableTreeNode
getNodeForFile(VirtualFile file
) {
279 FileElement descriptor
= getFileElementFor(file
);
280 if (descriptor
== null) return null;
282 myTreeBuilder
.buildNodeForElement(descriptor
);
283 return myTreeBuilder
.getNodeForElement(descriptor
);
286 public Exception
createNewFolder(final VirtualFile parentDirectory
, final String newFolderName
) {
287 final Exception
[] failReason
= new Exception
[] { null };
288 CommandProcessor
.getInstance().executeCommand(
289 myProject
, new Runnable() {
291 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
294 VirtualFile parent
= parentDirectory
;
295 for (String name
: StringUtil
.tokenize(newFolderName
, "\\/")) {
296 VirtualFile folder
= parent
.createChildDirectory(this, name
);
298 select(folder
, null);
302 catch (IOException e
) {
309 UIBundle
.message("file.chooser.create.new.folder.command.name"),
312 return failReason
[0];
315 public Exception
createNewFile(final VirtualFile parentDirectory
, final String newFileName
, final FileType fileType
, final String initialContent
) {
316 final Exception
[] failReason
= new Exception
[] { null };
317 CommandProcessor
.getInstance().executeCommand(
318 myProject
, new Runnable() {
320 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
323 final String newFileNameWithExtension
= newFileName
.endsWith('.'+fileType
.getDefaultExtension())? newFileName
: newFileName
+'.'+fileType
.getDefaultExtension();
324 final VirtualFile file
= parentDirectory
.createChildData(this, newFileNameWithExtension
);
325 VfsUtil
.saveText(file
, initialContent
!= null ? initialContent
: "");
329 catch (IOException e
) {
336 UIBundle
.message("file.chooser.create.new.file.command.name"),
339 return failReason
[0];
342 public JTree
getTree() { return myTree
; }
345 public VirtualFile
getSelectedFile() {
346 final TreePath path
= myTree
.getSelectionPath();
347 if (path
== null) return null;
348 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
349 if (!(node
.getUserObject() instanceof FileNodeDescriptor
)) return null;
350 FileNodeDescriptor descriptor
= (FileNodeDescriptor
)node
.getUserObject();
351 return descriptor
.getElement().getFile();
354 public VirtualFile
[] getSelectedFiles() {
355 return collectSelectedFiles(new ConvertingIterator
.IdConvertor
<VirtualFile
>());
358 public VirtualFile
[] getChoosenFiles() {
359 return collectSelectedFiles(new Convertor
<VirtualFile
, VirtualFile
>() {
361 public VirtualFile
convert(VirtualFile file
) {
362 if (file
== null || !file
.isValid()) return null;
363 return myTreeStructure
.getChooserDescriptor().getFileToSelect(file
);
368 private VirtualFile
[] collectSelectedFiles(Convertor
<VirtualFile
, VirtualFile
> fileConvertor
) {
369 TreePath
[] paths
= myTree
.getSelectionPaths();
370 if (paths
== null) return VirtualFile
.EMPTY_ARRAY
;
371 ArrayList
<VirtualFile
> files
= new ArrayList
<VirtualFile
>(paths
.length
);
373 for (TreePath path
: paths
) {
374 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
375 if (!(node
.getUserObject() instanceof FileNodeDescriptor
)) return VirtualFile
.EMPTY_ARRAY
;
376 FileNodeDescriptor descriptor
= (FileNodeDescriptor
)node
.getUserObject();
377 VirtualFile file
= fileConvertor
.convert(descriptor
.getElement().getFile());
378 if (file
!= null && file
.isValid()) files
.add(file
);
380 return files
.toArray(new VirtualFile
[files
.size()]);
383 public boolean selectionExists() {
384 TreePath
[] selectedPaths
= myTree
.getSelectionPaths();
385 return selectedPaths
!= null && selectedPaths
.length
!= 0;
388 public boolean isUnderRoots(VirtualFile file
) {
389 final List
<VirtualFile
> roots
= myDescriptor
.getRoots();
390 if (roots
.size() == 0) {
393 for (VirtualFile root
: roots
) {
394 if (root
== null) continue;
395 if (VfsUtil
.isAncestor(root
, file
, false)) {
402 public void addListener(final Listener listener
, final Disposable parent
) {
403 myListeners
.add(listener
);
404 Disposer
.register(parent
, new Disposable() {
405 public void dispose() {
406 myListeners
.remove(listener
);
411 private void fireSelection(List
<VirtualFile
> selection
) {
412 for (Listener each
: myListeners
) {
413 each
.selectionChanged(selection
);
417 private void processSelectionChange() {
418 if (myListeners
.size() == 0) return;
419 List
<VirtualFile
> selection
= new ArrayList
<VirtualFile
>();
421 final TreePath
[] paths
= myTree
.getSelectionPaths();
423 for (TreePath each
: paths
) {
424 final Object last
= each
.getLastPathComponent();
425 if (last
instanceof DefaultMutableTreeNode
) {
426 final Object object
= ((DefaultMutableTreeNode
)last
).getUserObject();
427 if (object
instanceof FileNodeDescriptor
) {
428 final FileElement element
= ((FileNodeDescriptor
)object
).getElement();
429 selection
.add(element
.getFile());
435 fireSelection(selection
);
438 private class MyExpansionListener
implements TreeExpansionListener
{
439 public void treeExpanded(final TreeExpansionEvent event
) {
440 if (myTreeBuilder
== null || !myTreeBuilder
.isNodeBeingBuilt(event
.getPath())) return;
442 TreePath path
= event
.getPath();
443 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
444 if (node
.getUserObject() instanceof FileNodeDescriptor
) {
445 FileNodeDescriptor nodeDescriptor
= (FileNodeDescriptor
)node
.getUserObject();
446 FileElement fileDescriptor
= nodeDescriptor
.getElement();
447 VirtualFile virtualFile
= fileDescriptor
.getFile();
448 if (virtualFile
!= null) {
449 virtualFile
.refresh(false, false);
454 public void treeCollapsed(TreeExpansionEvent event
) {