diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java index d29a453d1a51..ab8ebc915a1c 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java @@ -58,20 +58,50 @@ public class MacFileChooserDialogImpl implements PathChooserDialog { private final Project myProject; private Consumer> myCallback; - private static final Callback SHOULD_ENABLE_URL = new Callback() { - @SuppressWarnings("UnusedDeclaration") - public boolean callback(ID self, String selector, ID panel, ID url) { + private static boolean checkFile(@NotNull ID self, ID url, boolean checkDirectories) { MacFileChooserDialogImpl dialog = ourImplMap.get(self); if (dialog == null) { // Since it has already been removed from the map, the file is likely to be valid if the user was able to select it return true; } - if (url == null || url.intValue() == 0) return false; - final ID filename = Foundation.invoke(url, "path"); - final String fileName = Foundation.toStringViaUTF8(filename); - if (fileName == null) return false; - final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName); - return virtualFile == null || virtualFile.isDirectory() || dialog.myChooserDescriptor.isFileSelectable(virtualFile); + + if (url == null || url.intValue() == 0) { + return false; + } + + ID filename = Foundation.invoke(url, "path"); + String fileName = Foundation.toStringViaUTF8(filename); + if (fileName == null) { + return false; + } + + VirtualFile file = LocalFileSystem.getInstance().findFileByPath(fileName); + return file == null || (!checkDirectories && file.isDirectory()) || dialog.myChooserDescriptor.isFileSelectable(file); + } + + private static final Callback SHOULD_ENABLE_CALLBACK = new Callback() { + @SuppressWarnings("UnusedDeclaration") + public boolean callback(ID self, String selector, ID panel, ID url) { + // allow any directory - ability to select nested directories + return checkFile(self, url, false); + } + }; + + private static final Callback VALIDATE_URL_CALLBACK = new Callback() { + @SuppressWarnings("UnusedDeclaration") + public boolean callback(ID self, String selector, ID panel, ID url, ID outError) { + if (checkFile(self, url, true)) { + return true; + } + + /* + if (!outError.equals(ID.NIL)) { + ID error = Foundation.invoke("NSError", "errorWithDomain:code:userInfo:", Foundation.nsString("org.jetbrains"), + Foundation.createDict(new String[]{"NSLocalizedDescriptionKey"}, new Object[]{"Not allowed"})); + // todo "*outError = error" + } + */ + return false; } }; @@ -242,19 +272,20 @@ public class MacFileChooserDialogImpl implements PathChooserDialog { }; static { - final ID delegate = Foundation.allocateObjcClassPair(Foundation.getObjcClass("NSObject"), "NSOpenPanelDelegate_"); - if (!Foundation.addMethod(delegate, Foundation.createSelector("showOpenPanel:"), MAIN_THREAD_RUNNABLE, "v*")) { - throw new RuntimeException("Unable to add method to objective-c delegate class!"); - } - if (!Foundation.addMethod(delegate, Foundation.createSelector("openPanelDidEnd:returnCode:contextInfo:"), OPEN_PANEL_DID_END, "v*i")) { - throw new RuntimeException("Unable to add method to objective-c delegate class!"); - } - if (!Foundation.addMethod(delegate, Foundation.createSelector("panel:shouldEnableURL:"), SHOULD_ENABLE_URL, "B@@")) { - throw new RuntimeException("Unable to add method to objective-c delegate class!"); - } + ID delegate = Foundation.allocateObjcClassPair(Foundation.getObjcClass("NSObject"), "NSOpenPanelDelegate_"); + addFoundationMethod(delegate, "showOpenPanel:", MAIN_THREAD_RUNNABLE, "v*"); + addFoundationMethod(delegate, "openPanelDidEnd:returnCode:contextInfo:", OPEN_PANEL_DID_END, "v*i"); + addFoundationMethod(delegate, "panel:shouldEnableURL:", SHOULD_ENABLE_CALLBACK, "B@@"); + addFoundationMethod(delegate, "panel:validateURL:error:", VALIDATE_URL_CALLBACK, "B@@o"); Foundation.registerObjcClassPair(delegate); } + private static void addFoundationMethod(@NotNull ID delegate, @NotNull String selector, @NotNull Callback callback, @NotNull String types) { + if (!Foundation.addMethod(delegate, Foundation.createSelector(selector), callback, types)) { + throw new RuntimeException("Unable to add method " + selector + " to objective-c delegate class!"); + } + } + public MacFileChooserDialogImpl(@NotNull final FileChooserDescriptor chooserDescriptor, final Project project) { myChooserDescriptor = chooserDescriptor; myProject = project; diff --git a/platform/util/src/com/intellij/ui/mac/foundation/Foundation.java b/platform/util/src/com/intellij/ui/mac/foundation/Foundation.java index ce90ff388942..8b4ed40228f7 100644 --- a/platform/util/src/com/intellij/ui/mac/foundation/Foundation.java +++ b/platform/util/src/com/intellij/ui/mac/foundation/Foundation.java @@ -27,6 +27,7 @@ import java.util.Map; /** * @author spleaner + * @@see http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html */ public class Foundation { private static final FoundationLibrary myFoundationLibrary;