FileSaverDialog
[fedora-idea.git] / platform-impl / src / com / intellij / openapi / fileChooser / ex / FileChooserDialogImpl.java
blobc9b34a6c7329ab069270bcd4490600990bd203db
1 package com.intellij.openapi.fileChooser.ex;
3 import com.intellij.ide.util.treeView.NodeRenderer;
4 import com.intellij.ide.IdeBundle;
5 import com.intellij.idea.ActionsBundle;
6 import com.intellij.openapi.Disposable;
7 import com.intellij.openapi.actionSystem.*;
8 import com.intellij.openapi.application.ApplicationManager;
9 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
10 import com.intellij.openapi.fileChooser.FileChooserDialog;
11 import com.intellij.openapi.fileChooser.FileElement;
12 import com.intellij.openapi.fileChooser.FileSystemTree;
13 import com.intellij.openapi.fileChooser.impl.FileChooserFactoryImpl;
14 import com.intellij.openapi.project.Project;
15 import com.intellij.openapi.ui.DialogWrapper;
16 import com.intellij.openapi.ui.Messages;
17 import com.intellij.openapi.ui.TitlePanel;
18 import com.intellij.openapi.util.Disposer;
19 import com.intellij.openapi.vfs.LocalFileSystem;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.ui.UIBundle;
22 import com.intellij.ui.components.labels.LinkLabel;
23 import com.intellij.ui.components.labels.LinkListener;
24 import com.intellij.util.containers.HashMap;
25 import com.intellij.util.ui.update.MergingUpdateQueue;
26 import com.intellij.util.ui.update.UiNotifyConnector;
27 import com.intellij.util.ui.update.Update;
28 import org.jetbrains.annotations.NonNls;
29 import org.jetbrains.annotations.NotNull;
31 import javax.swing.*;
32 import javax.swing.border.EmptyBorder;
33 import javax.swing.event.TreeExpansionEvent;
34 import javax.swing.event.TreeExpansionListener;
35 import javax.swing.event.TreeSelectionEvent;
36 import javax.swing.event.TreeSelectionListener;
37 import javax.swing.tree.DefaultMutableTreeNode;
38 import javax.swing.tree.TreePath;
39 import java.awt.*;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.List;
43 import java.util.Map;
45 public class FileChooserDialogImpl extends DialogWrapper implements FileChooserDialog, FileLookup {
46 private final FileChooserDescriptor myChooserDescriptor;
47 protected FileSystemTreeImpl myFileSystemTree;
49 private static VirtualFile ourLastFile;
50 private Project myProject;
51 private VirtualFile[] myChosenFiles = VirtualFile.EMPTY_ARRAY;
53 private final List<Disposable> myDisposables = new ArrayList<Disposable>();
54 private JPanel myNorthPanel;
56 private static boolean ourToShowTextField = true;
57 private FileChooserDialogImpl.TextFieldAction myTextFieldAction;
59 protected FileTextFieldImpl myPathTextField;
61 private JComponent myPathTextFieldWrapper;
63 private MergingUpdateQueue myUiUpdater;
65 private boolean myTreeIsUpdating;
67 public static DataKey<FileChooserDialogImpl> KEY = DataKey.create("FileChooserDialog");
69 public FileChooserDialogImpl(FileChooserDescriptor chooserDescriptor, Project project) {
70 super(project, true);
71 myProject = project;
72 myChooserDescriptor = chooserDescriptor;
73 setTitle(UIBundle.message("file.chooser.default.title"));
76 public FileChooserDialogImpl(FileChooserDescriptor chooserDescriptor, Component parent) {
77 super(parent, true);
78 myChooserDescriptor = chooserDescriptor;
79 setTitle(UIBundle.message("file.chooser.default.title"));
82 @NotNull
83 public VirtualFile[] choose(VirtualFile toSelect, Project project) {
84 init();
86 VirtualFile selectFile = null;
88 if (toSelect == null && ourLastFile == null) {
89 if (project != null && project.getBaseDir() != null) {
90 selectFile = project.getBaseDir();
92 } else {
93 selectFile = (toSelect == null) ? ourLastFile : toSelect;
96 final VirtualFile file = selectFile;
98 if (file != null && file.isValid()) {
99 myFileSystemTree.select(file, new Runnable() {
100 public void run() {
101 if (!file.equals(myFileSystemTree.getSelectedFile())) {
102 VirtualFile parent = file.getParent();
103 if (parent != null) {
104 myFileSystemTree.select(parent, null);
111 show();
113 return myChosenFiles;
116 protected DefaultActionGroup createActionGroup() {
117 registerFileChooserShortcut(IdeActions.ACTION_DELETE, "FileChooser.Delete");
118 registerFileChooserShortcut(IdeActions.ACTION_SYNCHRONIZE, "FileChooser.Refresh");
120 return (DefaultActionGroup) ActionManager.getInstance().getAction("FileChooserToolbar");
123 private void registerFileChooserShortcut(@NonNls final String baseActionId, @NonNls final String fileChooserActionId) {
124 final JTree tree = myFileSystemTree.getTree();
125 final AnAction syncAction = ActionManager.getInstance().getAction(fileChooserActionId);
127 AnAction original = ActionManager.getInstance().getAction(baseActionId);
128 syncAction.registerCustomShortcutSet(original.getShortcutSet(), tree);
129 myDisposables.add(new Disposable() {
130 public void dispose() {
131 syncAction.unregisterCustomShortcutSet(tree);
136 private void addToGroup(DefaultActionGroup group, AnAction action) {
137 group.add(action);
138 if (action instanceof Disposable) {
139 myDisposables.add(((Disposable)action));
143 protected final JComponent createTitlePane() {
144 return new TitlePanel(myChooserDescriptor.getTitle(), myChooserDescriptor.getDescription());
147 protected JComponent createCenterPanel() {
148 JPanel panel = new MyPanel();
150 myUiUpdater = new MergingUpdateQueue("FileChooserUpdater", 200, false, panel);
151 Disposer.register(myDisposable, myUiUpdater);
152 new UiNotifyConnector(panel, myUiUpdater);
154 panel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
156 createTree();
158 final DefaultActionGroup group = createActionGroup();
159 ActionToolbar toolBar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true);
161 final JPanel toolbarPanel = new JPanel(new BorderLayout());
162 toolbarPanel.add(toolBar.getComponent(), BorderLayout.CENTER);
164 myTextFieldAction = new TextFieldAction();
165 toolbarPanel.add(myTextFieldAction, BorderLayout.EAST);
167 myPathTextFieldWrapper = new JPanel(new BorderLayout());
168 myPathTextFieldWrapper.setBorder(new EmptyBorder(0, 0, 2, 0));
169 myPathTextField = new FileTextFieldImpl.Vfs(myChooserDescriptor, myFileSystemTree.areHiddensShown(), myUiUpdater,
170 FileChooserFactoryImpl.getMacroMap()) {
171 protected void onTextChanged(final String newValue) {
172 updateTreeFromPath(newValue);
175 myDisposables.add(myPathTextField);
176 myPathTextFieldWrapper.add(myPathTextField.getField(), BorderLayout.CENTER);
178 myNorthPanel = new JPanel(new BorderLayout());
179 myNorthPanel.add(toolbarPanel, BorderLayout.NORTH);
182 updateTextFieldShowing();
184 panel.add(myNorthPanel, BorderLayout.NORTH);
186 registerMouseListener(group);
188 JScrollPane scrollPane = new JScrollPane(myFileSystemTree.getTree());
189 //scrollPane.setBorder(BorderFactory.createLineBorder(new Color(148, 154, 156)));
190 panel.add(scrollPane, BorderLayout.CENTER);
191 panel.setPreferredSize(new Dimension(400, 400));
194 panel.add(new JLabel("<html><center><small><font color=gray>Drag and drop a file into the space above to quickly locate it in the tree.</font></small></center></html>", JLabel.CENTER), BorderLayout.SOUTH);
196 return panel;
200 public JComponent getPreferredFocusedComponent() {
201 if (ourToShowTextField) {
202 return myPathTextField != null ? myPathTextField.getField() : null;
203 } else {
204 return myFileSystemTree != null ? myFileSystemTree.getTree() : null;
208 public final void dispose() {
209 for (Disposable disposable : myDisposables) {
210 Disposer.dispose(disposable);
212 myDisposables.clear();
213 myFileSystemTree.dispose();
214 LocalFileSystem.getInstance().removeWatchedRoots(myRequests.values());
215 super.dispose();
218 private boolean isTextFieldActive() {
219 return myPathTextField.getField().getRootPane() != null;
222 protected void doOKAction() {
223 if (!isOKActionEnabled()) {
224 return;
227 if (isTextFieldActive()) {
228 final String text = myPathTextField.getTextFieldText();
229 if (text == null || myPathTextField.getFile() == null || !myPathTextField.getFile().exists()) {
230 setErrorText("Specified path cannot be found");
231 return;
236 final VirtualFile[] selectedFiles = getSelectedFiles();
237 try {
238 myChooserDescriptor.validateSelectedFiles(selectedFiles);
240 catch (Exception e) {
241 Messages.showErrorDialog(getContentPane(), e.getMessage(), UIBundle.message("file.chooser.default.title"));
242 return;
245 myChosenFiles = selectedFiles;
246 if (selectedFiles.length == 0) {
247 close(CANCEL_EXIT_CODE);
248 return;
250 ourLastFile = selectedFiles[selectedFiles.length - 1];
252 super.doOKAction();
255 public final void doCancelAction() {
256 myChosenFiles = VirtualFile.EMPTY_ARRAY;
257 super.doCancelAction();
260 protected JTree createTree() {
261 myFileSystemTree = new FileSystemTreeImpl(myProject, myChooserDescriptor);
263 myFileSystemTree.addOkAction(new Runnable() {
264 public void run() {doOKAction(); }
266 JTree tree = myFileSystemTree.getTree();
267 tree.setCellRenderer(new NodeRenderer());
268 tree.addTreeSelectionListener(new FileTreeSelectionListener());
269 tree.addTreeExpansionListener(new FileTreeExpansionListener());
270 setOKActionEnabled(false);
272 myFileSystemTree.addListener(new FileSystemTree.Listener() {
273 public void selectionChanged(final List<VirtualFile> selection) {
274 updatePathFromTree(selection, false);
276 }, myDisposable);
278 new FileDrop(tree, new FileDrop.Target() {
279 public FileChooserDescriptor getDescriptor() {
280 return myChooserDescriptor;
283 public boolean isHiddenShown() {
284 return myFileSystemTree.areHiddensShown();
287 public void dropFiles(final List<VirtualFile> files) {
288 if (!myChooserDescriptor.isChooseMultiple() && files.size() > 0) {
289 selectInTree(new VirtualFile[] {files.get(0)}, true);
290 } else {
291 selectInTree(files.toArray(new VirtualFile[files.size()]), true);
296 return tree;
299 protected final void registerMouseListener(final ActionGroup group) {
300 myFileSystemTree.registerMouseListener(group);
303 protected VirtualFile[] getSelectedFiles() {
304 return myFileSystemTree.getChoosenFiles();
307 private final Map<String, LocalFileSystem.WatchRequest> myRequests = new HashMap<String, LocalFileSystem.WatchRequest>();
309 private final class FileTreeExpansionListener implements TreeExpansionListener {
310 public void treeExpanded(TreeExpansionEvent event) {
311 final Object[] path = event.getPath().getPath();
312 if (path.length == 2) {
313 // top node has been expanded => watch disk recursively
314 final DefaultMutableTreeNode node = (DefaultMutableTreeNode)path [1];
315 Object userObject = node.getUserObject();
316 if (userObject instanceof FileNodeDescriptor) {
317 final VirtualFile file = ((FileNodeDescriptor)userObject).getElement().getFile();
318 if (file != null && file.isDirectory()) {
319 final String rootPath = file.getPath();
320 if (myRequests.get(rootPath) == null) {
321 final LocalFileSystem.WatchRequest watchRequest = LocalFileSystem.getInstance().addRootToWatch(rootPath, true);
322 myRequests.put(rootPath, watchRequest);
329 public void treeCollapsed(TreeExpansionEvent event) {
330 //Do not unwatch here!!!
334 private final class FileTreeSelectionListener implements TreeSelectionListener {
335 public void valueChanged(TreeSelectionEvent e) {
336 TreePath[] paths = e.getPaths();
338 boolean enabled = true;
339 for (TreePath treePath : paths) {
340 if (!e.isAddedPath(treePath)) {
341 continue;
343 DefaultMutableTreeNode node = (DefaultMutableTreeNode)treePath.getLastPathComponent();
344 Object userObject = node.getUserObject();
345 if (!(userObject instanceof FileNodeDescriptor)) {
346 enabled = false;
347 break;
349 FileElement descriptor = ((FileNodeDescriptor)userObject).getElement();
350 VirtualFile file = descriptor.getFile();
351 enabled = file != null && myChooserDescriptor.isFileSelectable(file);
353 setOKActionEnabled(enabled);
357 protected final class MyPanel extends JPanel implements DataProvider {
358 public MyPanel() {
359 super(new BorderLayout(0, 0));
362 public Object getData(String dataId) {
363 if (DataConstants.VIRTUAL_FILE_ARRAY.equals(dataId)) {
364 return getSelectedFiles();
365 } else if (KEY.getName().equals(dataId)) {
366 return FileChooserDialogImpl.this;
368 else if (FileSystemTree.DATA_KEY.getName().equals(dataId)) {
369 return myFileSystemTree;
371 return myChooserDescriptor.getUserData(dataId);
375 public void toggleShowTextField() {
376 ourToShowTextField =!ourToShowTextField;
377 updateTextFieldShowing();
380 private void updateTextFieldShowing() {
381 myTextFieldAction.update();
382 myNorthPanel.remove(myPathTextFieldWrapper);
383 if (ourToShowTextField) {
384 if (myFileSystemTree.getSelectedFile() != null) {
385 final ArrayList<VirtualFile> selection = new ArrayList<VirtualFile>();
386 selection.add(myFileSystemTree.getSelectedFile());
387 updatePathFromTree(selection, true);
389 myNorthPanel.add(myPathTextFieldWrapper, BorderLayout.CENTER);
390 } else {
391 setErrorText(null);
393 myPathTextField.getField().requestFocus();
395 myNorthPanel.revalidate();
396 myNorthPanel.repaint();
400 private class TextFieldAction extends LinkLabel implements LinkListener {
401 public TextFieldAction() {
402 super("", null);
403 setListener(this, null);
404 update();
407 protected void onSetActive(final boolean active) {
408 final String tooltip = AnAction
409 .createTooltipText(ActionsBundle.message("action.FileChooser.TogglePathShowing.text"), ActionManager.getInstance().getAction("FileChooser.TogglePathShowing"));
410 setToolTipText(tooltip);
413 protected String getStatusBarText() {
414 return ActionsBundle.message("action.FileChooser.TogglePathShowing.text");
417 public void update() {
418 setVisible(true);
419 setText(ourToShowTextField ? IdeBundle.message("file.chooser.hide.path") : IdeBundle.message("file.chooser.show.path"));
422 public void linkSelected(final LinkLabel aSource, final Object aLinkData) {
423 toggleShowTextField();
427 private void updatePathFromTree(final List<VirtualFile> selection, boolean now) {
428 if (!ourToShowTextField || myTreeIsUpdating) return;
430 String text = "";
431 if (selection.size() > 0) {
432 final VirtualFile vFile = selection.get(0);
433 if (vFile.isInLocalFileSystem()) {
434 text = vFile.getPresentableUrl();
435 } else {
436 text = vFile.getUrl();
440 myPathTextField.setText(text, now, new Runnable() {
441 public void run() {
442 myPathTextField.getField().selectAll();
443 setErrorText(null);
448 private void updateTreeFromPath(final String text) {
449 if (!ourToShowTextField) return;
450 if (myPathTextField.isPathUpdating()) return;
451 if (text == null) return;
453 myUiUpdater.queue(new Update("treeFromPath.1") {
454 public void run() {
455 ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
456 public void run() {
457 final LocalFsFinder.VfsFile toFind = (LocalFsFinder.VfsFile)myPathTextField.getFile();
458 if (toFind == null || !toFind.exists()) return;
460 myUiUpdater.queue(new Update("treeFromPath.2") {
461 public void run() {
462 selectInTree(toFind.getFile(), text);
471 private void selectInTree(final VirtualFile vFile, String fromText) {
472 if (vFile != null && vFile.isValid()) {
473 if (fromText == null || fromText.equalsIgnoreCase(myPathTextField.getTextFieldText())) {
474 selectInTree(new VirtualFile[] {vFile}, false);
476 } else {
477 reportFileNotFound();
481 private void selectInTree(final VirtualFile[] vFile, final boolean requestFocus) {
482 myTreeIsUpdating = true;
483 if (!Arrays.asList(myFileSystemTree.getSelectedFiles()).contains(vFile)) {
484 myFileSystemTree.select(vFile, new Runnable() {
485 public void run() {
486 myTreeIsUpdating = false;
487 setErrorText(null);
488 if (requestFocus) {
489 SwingUtilities.invokeLater(new Runnable() {
490 public void run() {
491 myFileSystemTree.getTree().requestFocus();
497 } else {
498 myTreeIsUpdating = false;
499 setErrorText(null);
503 private void reportFileNotFound() {
504 myTreeIsUpdating = false;
505 setErrorText(null);