2 * Copyright 2000-2010 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.ui.mac;
18 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
19 import com.intellij.openapi.fileChooser.FileChooserDialog;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.vfs.LocalFileSystem;
22 import com.intellij.openapi.vfs.VirtualFile;
23 import com.intellij.openapi.wm.WindowManager;
24 import com.intellij.ui.mac.foundation.Foundation;
25 import com.intellij.ui.mac.foundation.ID;
26 import com.sun.jna.Callback;
27 import com.sun.jna.Pointer;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
33 import java.util.ArrayList;
34 import java.util.List;
39 public class MacFileChooserDialog implements FileChooserDialog {
40 private static final int OK = 1;
42 private static JDialog myFakeDialog;
43 private static List<VirtualFile> myResultFiles;
45 private static FileChooserDescriptor myChooserDescriptor;
46 private static boolean myFileChooserActive = false;
48 private Project myProject;
50 private static final Callback SHOULD_SHOW_FILENAME_CALLBACK = new Callback() {
51 public boolean callback(ID self, String selector, ID panel, ID filename) {
52 final String fileName = Foundation.toStringViaUTF8(filename);
53 final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName);
54 return virtualFile != null && (virtualFile.isDirectory() || getDescriptor().isFileSelectable(virtualFile));
58 private static final Callback IS_VALID_FILENAME_CALLBACK = new Callback() {
59 public boolean callback(ID self, String selector, ID panel, ID filename) {
60 final String fileName = Foundation.toStringViaUTF8(filename);
61 final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName);
62 return virtualFile != null && (!virtualFile.isDirectory() || getDescriptor().isFileSelectable(virtualFile));
66 private static final Callback MAIN_THREAD_RUNNABLE = new Callback() {
67 public void callback(ID self, String selector, ID toSelect) {
68 final ID chooser = invoke("NSOpenPanel", "openPanel");
70 invoke(chooser, "setPrompt:", Foundation.cfString("Choose"));
71 invoke(chooser, "setCanChooseFiles:", myChooserDescriptor.isChooseFiles());
72 invoke(chooser, "setCanChooseDirectories:", myChooserDescriptor.isChooseFolders());
73 invoke(chooser, "setAllowsMultipleSelection:", myChooserDescriptor.isChooseMultiple());
74 if (Foundation.isClassRespondsToSelector(Foundation.getClass("NSOpenPanel"), Foundation.createSelector("_setIncludeNewFolderButton:"))) {
75 invoke(chooser, "_setIncludeNewFolderButton:", true);
78 invoke(chooser, "setDelegate:", self);
80 final String toSelectPath = toSelect.intValue() == 0 ? null : Foundation.toStringViaUTF8(toSelect);
81 final VirtualFile toSelectFile = toSelectPath == null ? null : LocalFileSystem.getInstance().findFileByPath(toSelectPath);
82 final ID directory = toSelectFile == null ? null : toSelectFile.isDirectory() ? toSelect : null;
83 final ID file = toSelectFile == null ? null : !toSelectFile.isDirectory() ? toSelect : null;
84 final ID result = invoke(chooser, "runModalForDirectory:file:", directory, file);
86 SwingUtilities.invokeLater(new Runnable() {
88 if (myFakeDialog != null) {
89 myFakeDialog.dispose();
95 final List<VirtualFile> resultFiles = new ArrayList<VirtualFile>();
96 if (result != null && OK == result.intValue()) {
97 ID fileNamesArray = invoke(chooser, "filenames");
98 ID enumerator = invoke(fileNamesArray, "objectEnumerator");
101 final ID filename = invoke(enumerator, "nextObject");
102 if (0 == filename.intValue()) break;
104 String s = Foundation.toStringViaUTF8(filename);
106 VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(s);
107 if (virtualFile != null && virtualFile.isValid()) resultFiles.add(virtualFile);
111 myResultFiles = resultFiles;
117 final ID delegateClass = Foundation.registerObjcClass(Foundation.getClass("NSObject"), "NSOpenPanelDelegate_");
118 if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:shouldShowFilename:"), SHOULD_SHOW_FILENAME_CALLBACK, "B@:*"))
119 throw new RuntimeException("Unable to add method to objective-c delegate class!");
120 if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:isValidFilename:"), IS_VALID_FILENAME_CALLBACK, "B@:*"))
121 throw new RuntimeException("Unable to add method to objective-c delegate class!");
122 if (!Foundation.addMethod(delegateClass, Foundation.createSelector("showOpenPanel:"), MAIN_THREAD_RUNNABLE, "v@:*"))
123 throw new RuntimeException("Unable to add method to objective-c delegate class!");
124 Foundation.registerObjcClassPair(delegateClass);
127 public MacFileChooserDialog(final FileChooserDescriptor chooserDescriptor, final Project project) {
128 setDescriptor(chooserDescriptor);
132 public MacFileChooserDialog(final FileChooserDescriptor chooserDescriptor, final Component component) {
133 setDescriptor(chooserDescriptor);
136 private static void setDescriptor(@Nullable final FileChooserDescriptor descriptor) {
137 myChooserDescriptor = descriptor;
140 private static FileChooserDescriptor getDescriptor() {
141 return myChooserDescriptor;
144 private static void showNativeChooser(@Nullable VirtualFile toSelect) {
145 final ID autoReleasePool = createAutoReleasePool();
148 final ID delegate = invoke(Foundation.getClass("NSOpenPanelDelegate_"), "new");
150 final Pointer select = toSelect == null ? null : Foundation.cfString(toSelect.getPath());
151 Foundation.cfRetain(delegate);
153 invoke(delegate, "performSelectorOnMainThread:withObject:waitUntilDone:", Foundation.createSelector("showOpenPanel:"), select, false);
156 invoke(autoReleasePool, "release");
161 public VirtualFile[] choose(@Nullable final VirtualFile toSelect, @Nullable Project project) {
162 assert !myFileChooserActive: "Current native file chooser should finish before next usage!";
164 myFileChooserActive = true;
166 final VirtualFile[] selectFile = new VirtualFile[] {null};
167 if (toSelect == null) {
168 if (project != null && project.getBaseDir() != null) {
169 selectFile[0] = project.getBaseDir();
172 selectFile[0] = toSelect.isValid() ? toSelect : null;
175 SwingUtilities.invokeLater(new Runnable() {
177 showNativeChooser(selectFile[0]);
181 myFakeDialog = new JDialog(project != null ? WindowManager.getInstance().getFrame(project) : myProject != null ? WindowManager.getInstance().getFrame(myProject) : null);
182 myFakeDialog.setModal(true);
183 myFakeDialog.setUndecorated(true);
184 myFakeDialog.getRootPane().putClientProperty( "Window.shadow", Boolean.FALSE );
186 myFakeDialog.setBounds(0, 0, 0, 0);
187 myFakeDialog.setVisible(true);
190 if (myResultFiles == null) {
191 return new VirtualFile[0];
193 return myResultFiles.toArray(new VirtualFile[myResultFiles.size()]);
197 myFileChooserActive = false;
198 myResultFiles = null;
202 private static ID createAutoReleasePool() {
203 return invoke("NSAutoreleasePool", "new");
206 private static ID invoke(@NotNull final String className, @NotNull final String selector, Object... args) {
207 return invoke(Foundation.getClass(className), selector, args);
210 private static ID invoke(@NotNull final ID id, @NotNull final String selector, Object... args) {
211 return Foundation.invoke(id, Foundation.createSelector(selector), args);