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 }