1 /*
   2  * Copyright (c) 2011, 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              withEnv:(JNIEnv*)env;
  47 {
  48     if (self == [super init]) {
  49         fHasFileFilter = inHasFilter;
  50         fFileDialog = JNFNewGlobalRef(env, inDialog);
  51         fDirectory = inPath;
  52         [fDirectory retain];
  53         fFile = inFile;
  54         [fFile retain];
  55         fTitle = inTitle;
  56         [fTitle retain];
  57         fMode = inMode;
  58         fMultipleMode = inMultipleMode;
  59         fNavigateApps = inNavigateApps;
  60         fPanelResult = NSCancelButton;
  61     }
  62 
  63     return self;
  64 }
  65 
  66 -(void) disposer {
  67     if (fFileDialog != NULL) {
  68         JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
  69         JNFDeleteGlobalRef(env, fFileDialog);
  70         fFileDialog = NULL;
  71     }
  72 }
  73 
  74 -(void) dealloc {
  75     [fDirectory release];
  76     fDirectory = nil;
  77 
  78     [fFile release];
  79     fFile = nil;
  80 
  81     [fTitle release];
  82     fTitle = nil;
  83 
  84     [fURLs release];
  85     fURLs = nil;
  86 
  87     [super dealloc];
  88 }
  89 //- (void)finalize { [super finalize]; }
  90 
  91 - (void)safeSaveOrLoad {
  92     NSSavePanel *thePanel = nil;
  93 
  94     if (fMode == java_awt_FileDialog_SAVE) {
  95         thePanel = [NSSavePanel savePanel];
  96         [thePanel setAllowsOtherFileTypes:YES];
  97     } else {
  98         thePanel = [NSOpenPanel openPanel];
  99     }
 100 
 101     if (thePanel != nil) {
 102         [thePanel setTitle:fTitle];
 103 
 104         if (fNavigateApps) {
 105             [thePanel setTreatsFilePackagesAsDirectories:YES];
 106         }
 107 
 108         if (fMode == java_awt_FileDialog_LOAD) {
 109             NSOpenPanel *openPanel = (NSOpenPanel *)thePanel;
 110             [openPanel setAllowsMultipleSelection:fMultipleMode];
 111             [openPanel setCanChooseFiles:YES];
 112             [openPanel setCanChooseDirectories:NO];
 113             [openPanel setCanCreateDirectories:YES];
 114         }
 115 
 116         [thePanel setDelegate:self];
 117         fPanelResult = [thePanel runModalForDirectory:fDirectory file:fFile];
 118         [thePanel setDelegate:nil];
 119 
 120         if (fMode == java_awt_FileDialog_LOAD) {
 121             NSOpenPanel *openPanel = (NSOpenPanel *)thePanel;
 122             fURLs = [openPanel URLs];
 123         } else {
 124             fURLs = [NSArray arrayWithObject:[thePanel URL]];
 125         }
 126         [fURLs retain];
 127     }
 128 
 129     [self disposer];
 130 }
 131 
 132 - (BOOL) askFilenameFilter:(NSString *)filename {
 133     JNIEnv *env = [ThreadUtilities getJNIEnv];
 134     jstring jString = JNFNormalizedJavaStringForPath(env, filename);
 135 
 136     static JNF_CLASS_CACHE(jc_CFileDialog, "sun/lwawt/macosx/CFileDialog");
 137     static JNF_MEMBER_CACHE(jm_queryFF, jc_CFileDialog, "queryFilenameFilter", "(Ljava/lang/String;)Z");
 138     BOOL returnValue = JNFCallBooleanMethod(env, fFileDialog, jm_queryFF, jString); // AWT_THREADING Safe (AWTRunLoopMode)
 139     (*env)->DeleteLocalRef(env, jString);
 140 
 141     return returnValue;
 142 }
 143 
 144 - (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url {
 145     if (!fHasFileFilter) return YES; // no filter, no problem!
 146 
 147     // check if it's not a normal file
 148     NSNumber *isFile = nil;
 149     if ([url getResourceValue:&isFile forKey:NSURLIsRegularFileKey error:nil]) {
 150         if (![isFile boolValue]) return YES; // always show directories and non-file entities (browsing servers/mounts, etc)
 151     }
 152 
 153     // if in directory-browsing mode, don't offer files
 154     if ((fMode != java_awt_FileDialog_LOAD) && (fMode != java_awt_FileDialog_SAVE)) {
 155         return NO;
 156     }
 157 
 158     // ask the file filter up in Java
 159     CFStringRef filePath = CFURLCopyFileSystemPath((CFURLRef)url, kCFURLPOSIXPathStyle);
 160     BOOL shouldEnableFile = [self askFilenameFilter:(NSString *)filePath];
 161     CFRelease(filePath);
 162     return shouldEnableFile;
 163 }
 164 
 165 - (BOOL) userClickedOK {
 166     return fPanelResult == NSOKButton;
 167 }
 168 
 169 - (NSArray *)URLs {
 170     return [[fURLs retain] autorelease];
 171 }
 172 @end
 173 
 174 /*
 175  * Class:     sun_lwawt_macosx_CFileDialog
 176  * Method:    nativeRunFileDialog
 177  * Signature: (Ljava/lang/String;ILjava/io/FilenameFilter;
 178  *             Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;
 179  */
 180 JNIEXPORT jobjectArray JNICALL
 181 Java_sun_lwawt_macosx_CFileDialog_nativeRunFileDialog
 182 (JNIEnv *env, jobject peer, jstring title, jint mode, jboolean multipleMode,
 183  jboolean navigateApps, jboolean hasFilter, jstring directory, jstring file)
 184 {
 185     jobjectArray returnValue = NULL;
 186 
 187 JNF_COCOA_ENTER(env);
 188     NSString *dialogTitle = JNFJavaToNSString(env, title);
 189     if ([dialogTitle length] == 0) {
 190         dialogTitle = @" ";
 191     }
 192 
 193     CFileDialog *dialogDelegate = [[CFileDialog alloc] initWithFilter:hasFilter
 194                                                            fileDialog:peer
 195                                                                 title:dialogTitle
 196                                                             directory:JNFJavaToNSString(env, directory)
 197                                                                  file:JNFJavaToNSString(env, file)
 198                                                                  mode:mode
 199                                                          multipleMode:multipleMode
 200                                                        shouldNavigate:navigateApps
 201                                                               withEnv:env];
 202 
 203     [JNFRunLoop performOnMainThread:@selector(safeSaveOrLoad)
 204                                  on:dialogDelegate
 205                          withObject:nil
 206                       waitUntilDone:YES];
 207 
 208     if ([dialogDelegate userClickedOK]) {
 209         NSArray *urls = [dialogDelegate URLs];
 210         jsize count = [urls count];
 211 
 212         jclass stringClass = (*env)->FindClass(env, "java/lang/String");
 213         returnValue = (*env)->NewObjectArray(env, count, stringClass, NULL);
 214         (*env)->DeleteLocalRef(env, stringClass);
 215 
 216         NSUInteger i;
 217         for (i = 0; i < count; i++) {
 218             jstring filename = JNFNSToJavaString(env, [[urls objectAtIndex:i] absoluteString]);
 219             (*env)->SetObjectArrayElement(env, returnValue, i, filename);
 220             (*env)->DeleteLocalRef(env, filename);
 221         }
 222     }
 223 
 224     [dialogDelegate release];
 225 JNF_COCOA_EXIT(env);
 226     return returnValue;
 227 }