explicit write action isn't needed here
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / fileChooser / ex / FileSystemTreeImpl.java
blob6f126820087e05b1b561273e9cf6d17970a0244c
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.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;
48 import javax.swing.*;
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) {
81 myProject = project;
82 myTreeStructure = new FileTreeStructure(project, descriptor);
83 myDescriptor = descriptor;
84 myTree = tree;
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() {
91 public void run() {
92 myTree.expandPath(new TreePath(treeModel.getRoot()));
93 if (onInitialized != null) {
94 onInitialized.run();
97 });
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,
131 Object value,
132 boolean selected,
133 boolean expanded,
134 boolean leaf,
135 int row,
136 boolean hasFocus) {
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();
175 if (path != null) {
176 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
177 if (node != null && node.isLeaf()) {
178 fireOkAction();
180 else if (toggleNodeState) {
181 if (myTree.isExpanded(path)) {
182 myTree.collapsePath(path);
184 else {
185 myTree.expandPath(path);
191 public void addOkAction(Runnable action) { myOkActions.add(action); }
193 private void fireOkAction() {
194 for (Runnable action : myOkActions) {
195 action.run();
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);
209 updateTree();
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;
228 else {
229 TreeUtil.selectPath(myTree, new TreePath(node.getPath()));
230 return true;
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);
252 @Nullable
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) {
259 return null;
262 else {
263 selectFile = file;
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()));
274 return true;
277 @Nullable
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() {
290 public void run() {
291 ApplicationManager.getApplication().runWriteAction(new Runnable() {
292 public void run() {
293 try {
294 VirtualFile parent = parentDirectory;
295 for (String name : StringUtil.tokenize(newFolderName, "\\/")) {
296 VirtualFile folder = parent.createChildDirectory(this, name);
297 updateTree();
298 select(folder, null);
299 parent = folder;
302 catch (IOException e) {
303 failReason[0] = e;
309 UIBundle.message("file.chooser.create.new.folder.command.name"),
310 null
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() {
319 public void run() {
320 ApplicationManager.getApplication().runWriteAction(new Runnable() {
321 public void run() {
322 try {
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 : "");
326 updateTree();
327 select(file, null);
329 catch (IOException e) {
330 failReason[0] = e;
336 UIBundle.message("file.chooser.create.new.file.command.name"),
337 null
339 return failReason[0];
342 public JTree getTree() { return myTree; }
344 @Nullable
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>() {
360 @Nullable
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) {
391 return true;
393 for (VirtualFile root : roots) {
394 if (root == null) continue;
395 if (VfsUtil.isAncestor(root, file, false)) {
396 return true;
399 return 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();
422 if (paths != null) {
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) {