1 /* 2 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #import <sys/stat.h> 27 #import <Cocoa/Cocoa.h> 28 #import <JavaNativeFoundation/JavaNativeFoundation.h> 29 30 #import "CFileDialog.h" 31 #import "ThreadUtilities.h" 32 33 #import "java_awt_FileDialog.h" 34 #import "sun_lwawt_macosx_CFileDialog.h" 35 36 @implementation CFileDialog 37 38 - (id)initWithFilter:(jboolean)inHasFilter 39 fileDialog:(jobject)inDialog 40 title:(NSString *)inTitle 41 directory:(NSString *)inPath 42 file:(NSString *)inFile 43 mode:(jint)inMode 44 multipleMode:(BOOL)inMultipleMode 45 shouldNavigate:(BOOL)inNavigateApps 46 canChooseDirectories:(BOOL)inChooseDirectories 47 withEnv:(JNIEnv*)env; 48 { 49 if (self == [super init]) { 50 fHasFileFilter = inHasFilter; 51 fFileDialog = JNFNewGlobalRef(env, inDialog); 52 fDirectory = inPath; 53 [fDirectory retain]; 54 fFile = inFile; 55 [fFile retain]; 56 fTitle = inTitle; 57 [fTitle retain]; 58 fMode = inMode; 59 fMultipleMode = inMultipleMode; 60 fNavigateApps = inNavigateApps; 61 fChooseDirectories = inChooseDirectories; 62 fPanelResult = NSCancelButton; 63 } 64 65 return self; 66 } 67 68 -(void) disposer { 69 if (fFileDialog != NULL) { 70 JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; 71 JNFDeleteGlobalRef(env, fFileDialog); 72 fFileDialog = NULL; 73 } 74 } 75 76 -(void) dealloc { 77 [fDirectory release]; 78 fDirectory = nil; 79 80 [fFile release]; 81 fFile = nil; 82 83 [fTitle release]; 84 fTitle = nil; 85 86 [fURLs release]; 87 fURLs = nil; 88 89 [super dealloc]; 90 } 91 92 - (void)safeSaveOrLoad { 93 NSSavePanel *thePanel = nil; 94 95 /* 96 * 8013553: turns off extension hiding for the native file dialog. 97 * This way is used because setExtensionHidden(NO) doesn't work 98 * as expected. 99 */ 100 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 101 [defaults setBool:NO forKey:@"NSNavLastUserSetHideExtensionButtonState"]; 102 103 if (fMode == java_awt_FileDialog_SAVE) { 104 thePanel = [NSSavePanel savePanel]; 105 [thePanel setAllowsOtherFileTypes:YES]; 106 } else { 107 thePanel = [NSOpenPanel openPanel]; 108 } 109 110 if (thePanel != nil) { 111 [thePanel setTitle:fTitle]; 112 113 if (fNavigateApps) { 114 [thePanel setTreatsFilePackagesAsDirectories:YES]; 115 } 116 117 if (fMode == java_awt_FileDialog_LOAD) { 118 NSOpenPanel *openPanel = (NSOpenPanel *)thePanel; 119 [openPanel setAllowsMultipleSelection:fMultipleMode]; 120 [openPanel setCanChooseFiles:!fChooseDirectories]; 121 [openPanel setCanChooseDirectories:fChooseDirectories]; 122 [openPanel setCanCreateDirectories:YES]; 123 if (fChooseDirectories) { 124 NSString *buttonPrompt = NSLocalizedString(@ " Choose ", 125 @ " Choose " ); 126 if (buttonPrompt == nil) { 127 buttonPrompt = @ " Choose " ; 128 } 129 [openPanel setPrompt:buttonPrompt]; 130 } 131 } 132 133 [thePanel setDelegate:self]; 134 fPanelResult = [thePanel runModalForDirectory:fDirectory file:fFile]; 135 [thePanel setDelegate:nil]; 136 137 if ([self userClickedOK]) { 138 if (fMode == java_awt_FileDialog_LOAD) { 139 NSOpenPanel *openPanel = (NSOpenPanel *)thePanel; 140 fURLs = [openPanel URLs]; 141 } else { 142 fURLs = [NSArray arrayWithObject:[thePanel URL]]; 143 } 144 [fURLs retain]; 145 } 146 } 147 148 [self disposer]; 149 } 150 151 - (BOOL) askFilenameFilter:(NSString *)filename { 152 JNIEnv *env = [ThreadUtilities getJNIEnv]; 153 jstring jString = JNFNormalizedJavaStringForPath(env, filename); 154 155 static JNF_CLASS_CACHE(jc_CFileDialog, "sun/lwawt/macosx/CFileDialog"); 156 static JNF_MEMBER_CACHE(jm_queryFF, jc_CFileDialog, "queryFilenameFilter", "(Ljava/lang/String;)Z"); 157 BOOL returnValue = JNFCallBooleanMethod(env, fFileDialog, jm_queryFF, jString); // AWT_THREADING Safe (AWTRunLoopMode) 158 (*env)->DeleteLocalRef(env, jString); 159 160 return returnValue; 161 } 162 163 - (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url { 164 if (!fHasFileFilter) return YES; // no filter, no problem! 165 166 // check if it's not a normal file 167 NSNumber *isFile = nil; 168 if ([url getResourceValue:&isFile forKey:NSURLIsRegularFileKey error:nil]) { 169 if (![isFile boolValue]) return YES; // always show directories and non-file entities (browsing servers/mounts, etc) 170 } 171 172 // if in directory-browsing mode, don't offer files 173 if ((fMode != java_awt_FileDialog_LOAD) && (fMode != java_awt_FileDialog_SAVE)) { 174 return NO; 175 } 176 177 // ask the file filter up in Java 178 NSString* filePath = (NSString*)CFURLCopyFileSystemPath((CFURLRef)url, kCFURLPOSIXPathStyle); 179 BOOL shouldEnableFile = [self askFilenameFilter:filePath]; 180 [filePath release]; 181 return shouldEnableFile; 182 } 183 184 - (BOOL) userClickedOK { 185 return fPanelResult == NSOKButton; 186 } 187 188 - (NSArray *)URLs { 189 return [[fURLs retain] autorelease]; 190 } 191 @end 192 193 /* 194 * Class: sun_lwawt_macosx_CFileDialog 195 * Method: nativeRunFileDialog 196 * Signature: (Ljava/lang/String;ILjava/io/FilenameFilter; 197 * Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String; 198 */ 199 JNIEXPORT jobjectArray JNICALL 200 Java_sun_lwawt_macosx_CFileDialog_nativeRunFileDialog 201 (JNIEnv *env, jobject peer, jstring title, jint mode, jboolean multipleMode, 202 jboolean navigateApps, jboolean chooseDirectories, jboolean hasFilter, 203 jstring directory, jstring file) 204 { 205 jobjectArray returnValue = NULL; 206 207 JNF_COCOA_ENTER(env); 208 NSString *dialogTitle = JNFJavaToNSString(env, title); 209 if ([dialogTitle length] == 0) { 210 dialogTitle = @" "; 211 } 212 213 CFileDialog *dialogDelegate = [[CFileDialog alloc] initWithFilter:hasFilter 214 fileDialog:peer 215 title:dialogTitle 216 directory:JNFJavaToNSString(env, directory) 217 file:JNFJavaToNSString(env, file) 218 mode:mode 219 multipleMode:multipleMode 220 shouldNavigate:navigateApps 221 canChooseDirectories:chooseDirectories 222 withEnv:env]; 223 224 [JNFRunLoop performOnMainThread:@selector(safeSaveOrLoad) 225 on:dialogDelegate 226 withObject:nil 227 waitUntilDone:YES]; 228 229 if ([dialogDelegate userClickedOK]) { 230 NSArray *urls = [dialogDelegate URLs]; 231 jsize count = [urls count]; 232 233 static JNF_CLASS_CACHE(jc_String, "java/lang/String"); 234 returnValue = JNFNewObjectArray(env, &jc_String, count); 235 236 [urls enumerateObjectsUsingBlock:^(id url, NSUInteger index, BOOL *stop) { 237 jstring filename = JNFNormalizedJavaStringForPath(env, [url path]); 238 (*env)->SetObjectArrayElement(env, returnValue, index, filename); 239 (*env)->DeleteLocalRef(env, filename); 240 }]; 241 } 242 243 [dialogDelegate release]; 244 JNF_COCOA_EXIT(env); 245 return returnValue; 246 }