1/*
2 * @(#)ch_randelshofer_quaqua_filechooser_Files.m  4.0  2008-03-26
3 *
4 * Copyright (c) 2004-2007 Werner Randelshofer
5 * Staldenmattweg 2, Immensee, CH-6405, Switzerland.
6 * All rights reserved.
7 *
8 * The copyright of this software is owned by Werner Randelshofer.
9 * You may not use, copy or modify this software, except in
10 * accordance with the license agreement you entered into with
11 * Werner Randelshofer. For details see accompanying license terms.
12 */
13
14/**
15 * Native code for class ch.randelshofer.quaqua.filechooser.Files.
16 *
17 * @version 4.0 2008-03-26 Added version check.
18 * <br>3.1.1 2007-12-21 Fixed crash when attempting to retrieve the kind
19 * of a file which does not exist.
20 * <br>3.1 2007-11-25 Scale icon images down if they are too big.
21 * <br>3.0 2007-04-28 Rewritten with Cocoa instead of Carbon. Added
22 * functions getKindString, getIconImage, based on code by Rolf Howarth.
23 * <br>2.0 2007-04-18 Rewritten with better function names.
24 * <br>1.0.1 2005-06-18 Fixed signs of variables.
25 * <br>1.0 2004-11-04 Created.
26 */
27
28#include <stdio.h>
29#include <jni.h>
30#include "ch_randelshofer_quaqua_filechooser_Files.h"
31#import <Cocoa/Cocoa.h>
32#import <CoreServices/CoreServices.h>
33
34/*
35 * Related documentation:
36 * ----------------------
37 * Serializing an Alias into a stream of bytes:
38 * http://developer.apple.com/qa/qa2004/qa1350.html
39 */
40
41
42
43/*
44 * Class:     ch_randelshofer_quaqua_filechooser_Files
45 * Method:    getFileType
46 * Signature: (Ljava/lang/String;)I
47 */
48JNIEXPORT jint JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_getFileType
49  (JNIEnv *env, jclass instance, jstring pathJ) {
50
51    // Assert arguments
52    if (pathJ == NULL) return false;
53
54    // Convert Java String to C char array
55    const char *pathC;
56    pathC = (*env)->GetStringUTFChars(env, pathJ, 0);
57
58    // Do the API calls
59    FSRef fileRef;
60    OSErr err;
61    Boolean isAlias, isFolder;
62    err = FSPathMakeRef(pathC, &fileRef, NULL);
63    if (err == 0) {
64        err = FSIsAliasFile(&fileRef, &isAlias, &isFolder);
65    }
66
67    // Release the C char array
68    (*env)->ReleaseStringUTFChars(env, pathJ, pathC);
69
70    // Return the result
71    return (err == 0) ?
72		((isAlias) ? 2 : ((isFolder) ? 1 : 0)) :
73		-1;
74}
75/*
76 * Class:     ch_randelshofer_quaqua_filechooser_Files
77 * Method:    resolveAlias
78 * Signature: (Ljava/lang/String;Z)Ljava/lang/String;
79 */
80JNIEXPORT jstring JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_resolveAlias
81 (JNIEnv *env, jclass instance, jstring aliasPathJ, jboolean noUI)
82{
83    // Assert arguments
84    if (aliasPathJ == NULL) return false;
85
86    // Convert Java filename to C filename
87    const char *aliasPathC;
88    aliasPathC = (*env)->GetStringUTFChars(env, aliasPathJ, 0);
89
90    // Do the API calls
91    FSRef fileRef;
92    OSErr err;
93    OSStatus status;
94    Boolean wasAliased, targetIsFolder;
95    UInt8 resolvedPathC[2048];
96
97    int outputBufLen;
98    err = FSPathMakeRef(aliasPathC, &fileRef, NULL);
99    if (err == 0) {
100        err = FSResolveAliasFileWithMountFlags(
101                             &fileRef,
102                             true, // resolve alias chains
103                             &targetIsFolder,
104                             &wasAliased,
105                             (noUI) ? kResolveAliasFileNoUI : 0 // mount flags
106              );
107    }
108    if (err == 0) {
109        if (wasAliased) {
110            status = FSRefMakePath(&fileRef, resolvedPathC, 2048);
111            if (status != 0) err = 1;
112        }
113    }
114
115    // Release the C filename
116    (*env)->ReleaseStringUTFChars(env, aliasPathJ, aliasPathC);
117
118
119    // Return the result
120    return (err == 0 && wasAliased) ? (*env)->NewStringUTF(env, resolvedPathC) : NULL;
121}
122
123/*
124 * Class:     ch_randelshofer_quaqua_filechooser_Files
125 * Method:    resolveAliasType
126 * Signature: (Ljava/lang/String;Z)I
127 */
128JNIEXPORT jint JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_resolveAliasType
129   (JNIEnv *env, jclass instance, jstring aliasPathJ, jboolean noUI)
130{
131    // Assert arguments
132    if (aliasPathJ == NULL) return false;
133
134    // Convert Java filename to C filename
135    const char *aliasPathC;
136    aliasPathC = (*env)->GetStringUTFChars(env, aliasPathJ, 0);
137
138    // Do the API calls
139    FSRef fileRef;
140    OSErr err;
141    OSStatus status;
142    Boolean wasAliased, targetIsFolder;
143    UInt8 resolvedPathC[2048];
144
145    int outputBufLen;
146    err = FSPathMakeRef(aliasPathC, &fileRef, NULL);
147    if (err == 0) {
148        err = FSResolveAliasFileWithMountFlags(
149                             &fileRef,
150                             false, // resolve alias chains
151                             &targetIsFolder,
152                             &wasAliased,
153                             (noUI) ? kResolveAliasFileNoUI : 0 // mount flags
154              );
155    }
156    if (err == 0) {
157        if (wasAliased) {
158            status = FSRefMakePath(&fileRef, resolvedPathC, 2048);
159            if (status != 0) err = 1;
160        }
161    }
162
163    // Release the C filename
164    (*env)->ReleaseStringUTFChars(env, aliasPathJ, aliasPathC);
165
166
167    // Return the result
168    return (err == 0) ? ((targetIsFolder) ? 1 : 0) : -1;
169}
170
171/*
172 * Class:     ch_randelshofer_quaqua_filechooser_Files
173 * Method:    toSerializedAlias
174 * Signature: (Ljava/lang/String;)[B
175 */
176JNIEXPORT jbyteArray JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_toSerializedAlias
177  (JNIEnv *env, jclass instance, jstring aliasPathJ)
178{
179    // Assert arguments
180    if (aliasPathJ == NULL) return NULL;
181
182    //
183    jbyteArray serializedAlias = NULL;
184
185    // Convert Java filename to C filename
186    const char *aliasPathC;
187    aliasPathC = (*env)->GetStringUTFChars(env, aliasPathJ, 0);
188
189    // Do the API calls
190    FSRef fileRef;
191    OSErr err;
192    AliasHandle aliasHdl;
193    CFDataRef dataRef;
194    const UInt8* dataBytes; // bytes of dataRef
195    int length; // length of the dataBytes array
196
197    err = FSPathMakeRef(aliasPathC, &fileRef, NULL);
198    if (err == 0) {
199        err = FSNewAlias(NULL, &fileRef, &aliasHdl);
200    }
201    if (err == 0) {
202        dataRef = CFDataCreate(
203                        kCFAllocatorDefault,
204                        (UInt8*) *aliasHdl,
205                        GetHandleSize((Handle) aliasHdl)
206                  );
207        err = (NULL == dataRef);
208    }
209    if (err == 0) {
210        length = CFDataGetLength(dataRef);
211        serializedAlias = (*env)->NewByteArray(env, length);
212        err = (NULL == serializedAlias);
213    }
214    if (err == 0) {
215        dataBytes = CFDataGetBytePtr(dataRef);
216        (*env)->SetByteArrayRegion(env, serializedAlias, 0, length, dataBytes);
217    }
218
219    // Release the C filename
220    (*env)->ReleaseStringUTFChars(env, aliasPathJ, aliasPathC);
221
222    // Release the other stuff
223    if (dataRef != NULL) CFRelease(dataRef);
224    if (aliasHdl != NULL) DisposeHandle((Handle) aliasHdl);
225
226    // Return the result
227    return serializedAlias;
228}
229
230/*
231 * Class:     ch_randelshofer_quaqua_filechooser_Files
232 * Method:    jniResolveAlias
233 * Signature: ([BZ)Ljava/lang/String;
234 */
235JNIEXPORT jstring JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_jniResolveAlias
236  (JNIEnv *env, jclass instance, jbyteArray serializedAlias, jboolean noUI)
237{
238    // Assert arguments
239    if (serializedAlias == NULL) return false;
240
241
242    //
243    FSRef fileRef;
244    OSErr err;
245    AliasHandle aliasHdl;
246    CFDataRef dataRef;
247    UInt8* serializedAliasBytes; // bytes of serializedAlias
248    int length; // length of serializedAlias
249    UInt8 resolvedPathC[2048];
250    Boolean wasChanged;
251    OSStatus status;
252
253    length = (*env)->GetArrayLength(env, serializedAlias);
254    serializedAliasBytes = (*env)->GetByteArrayElements(env, serializedAlias, NULL);
255    err = (NULL == serializedAliasBytes);
256
257    if (err == 0) {
258        dataRef = CFDataCreate(kCFAllocatorDefault,
259                               (UInt8*) serializedAliasBytes,
260                               length
261                  );
262
263        aliasHdl = (AliasHandle) NewHandle(length);
264        err = (NULL == aliasHdl);
265    }
266    if (err == 0) {
267        CFDataGetBytes(dataRef,
268                       CFRangeMake(0, length),
269                       (UInt8*) *aliasHdl
270       );
271
272        err = FSResolveAliasWithMountFlags(NULL,
273                             aliasHdl,
274                             &fileRef,
275                             &wasChanged,
276                             (noUI) ? kResolveAliasFileNoUI : 0
277              );
278    }
279    if (err == 0) {
280        status = FSRefMakePath(&fileRef, resolvedPathC, 2048);
281        if (status != 0) err = 1;
282    }
283
284    // Release allocated stuff
285    (*env)->ReleaseByteArrayElements(env, serializedAlias, serializedAliasBytes, JNI_ABORT);
286    if (aliasHdl != NULL) DisposeHandle((Handle) aliasHdl);
287
288    // Return the result
289    return (err == 0) ? (*env)->NewStringUTF(env, resolvedPathC) : NULL;
290}
291
292/*
293 * Class:     ch_randelshofer_quaqua_filechooser_Files
294 * Method:    jniResolveAliasType
295 * Signature: ([BZ)I
296 */
297JNIEXPORT jint JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_jniResolveAliasType
298  (JNIEnv *env, jclass instance, jbyteArray serializedAlias, jboolean noUI)
299{
300    // Assert arguments
301    if (serializedAlias == NULL) return false;
302
303
304    //
305    OSErr err;
306    AliasHandle aliasHdl;
307    CFDataRef dataRef;
308    UInt8* serializedAliasBytes; // bytes of serializedAlias
309    int length; // length of serializedAlias
310    UInt8 resolvedPathC[2048];
311    OSStatus status;
312	FSAliasInfoBitmap whichInfo;
313    FSAliasInfo info;
314
315    length = (*env)->GetArrayLength(env, serializedAlias);
316    serializedAliasBytes = (*env)->GetByteArrayElements(env, serializedAlias, NULL);
317    err = (NULL == serializedAliasBytes);
318
319    if (err == 0) {
320        dataRef = CFDataCreate(kCFAllocatorDefault,
321                               (UInt8*) serializedAliasBytes,
322                               length
323                  );
324
325        aliasHdl = (AliasHandle) NewHandle(length);
326        err = (NULL == aliasHdl);
327    }
328    if (err == 0) {
329        CFDataGetBytes(dataRef,
330                       CFRangeMake(0, length),
331                       (UInt8*) *aliasHdl
332       );
333
334		err = FSCopyAliasInfo (
335			aliasHdl,
336			NULL, //targetName
337			NULL, //volumeName
338			NULL, //pathString
339			&whichInfo,
340			&info
341		);
342    }
343
344    // Release allocated stuff
345    (*env)->ReleaseByteArrayElements(env, serializedAlias, serializedAliasBytes, JNI_ABORT);
346    if (aliasHdl != NULL) DisposeHandle((Handle) aliasHdl);
347
348    // Return the result
349    return (err == 0 && (whichInfo & kFSAliasInfoIsDirectory) != 0) ?
350		 ((info.isDirectory) ? 1 : 0) :
351		 -1;
352}
353
354
355/*
356 * Class:     ch_randelshofer_quaqua_filechooser_Files
357 * Method:    getLabel
358 * Signature: (Ljava/lang/String;)I
359 */
360JNIEXPORT jint JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_getLabel
361  (JNIEnv *env, jclass instance, jstring pathJ) {
362
363    // Assert arguments
364    if (pathJ == NULL) return -1;
365
366    // Convert Java String to C char array
367    const char *pathC = (*env)->GetStringUTFChars(env, pathJ, 0);
368
369    // Do the API calls
370    FSRef fileRef;
371    OSErr err;
372    FSCatalogInfo catalogInfo;
373    FInfo *fileInfo;
374    int fileLabel;
375    err = FSPathMakeRef(pathC, &fileRef, NULL);
376    if (err == 0) {
377        err = FSGetCatalogInfo(&fileRef, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL);
378    }
379    if (err == 0) {
380        fileInfo = (FInfo*) &catalogInfo.finderInfo;
381        fileLabel = (fileInfo->fdFlags & 0xe) >> 1;
382    }
383
384    // Release the C char array
385    (*env)->ReleaseStringUTFChars(env, pathJ, pathC);
386
387    // Return the result
388    return (err == 0) ? fileLabel : -1;
389}
390
391/*
392 * Class:     ch_randelshofer_quaqua_filechooser_Files
393 * Method:    getKindString
394 * Signature: (Ljava/lang/String;)Ljava/lang/String;
395 */
396JNIEXPORT jstring JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_getKindString
397  (JNIEnv *env, jclass instance, jstring pathJ) {
398
399    // Assert arguments
400    if (pathJ == NULL) return -1;
401
402    // Convert Java String to C char array
403    const char *pathC = (*env)->GetStringUTFChars(env, pathJ, 0);
404
405    // Do the API calls
406    FSRef fileRef;
407    OSErr err;
408    CFStringRef outKindString;
409    err = FSPathMakeRef(pathC, &fileRef, NULL);
410    jstring kindJ;
411    if (err == 0) {
412        err = LSCopyKindStringForRef(&fileRef, &outKindString);
413    }
414    if (err == 0) {
415        CFRange range;
416        range.location = 0;
417        // Note that CFStringGetLength returns the number of UTF-16 characters,
418        // which is not necessarily the number of printed/composed characters
419        range.length = CFStringGetLength(outKindString);
420        UniChar charBuf[range.length];
421        CFStringGetCharacters(outKindString, range, charBuf);
422        kindJ = (*env)->NewString(env, (jchar *)charBuf, (jsize)range.length);
423        CFRelease(outKindString);
424    }
425
426    // Release the C char array
427    (*env)->ReleaseStringUTFChars(env, pathJ, pathC);
428
429    // Return the result
430    return (err == 0) ? kindJ : NULL;
431}
432
433/*
434 * Class:     ch_randelshofer_quaqua_filechooser_Files
435 * Method:    getIconImage
436 * Signature: (Ljava/lang/String;I)[B
437 */
438JNIEXPORT jbyteArray JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_getIconImage
439  (JNIEnv *env, jclass javaClass, jstring pathJ, jint size) {
440
441    // Assert arguments
442    if (pathJ == NULL) return NULL;
443
444    jbyteArray result = NULL;
445
446    // Allocate a memory pool
447    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
448
449    // Convert Java String to NS String
450    const jchar *pathC = (*env)->GetStringChars(env, pathJ, NULL);
451    NSString *pathNS = [NSString stringWithCharacters:(UniChar *)pathC
452        length:(*env)->GetStringLength(env, pathJ)];
453
454    // Get the icon image
455    NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
456    NSSize iconSize = { size, size };
457    NSImage* image = [workspace iconForFile:pathNS];
458    if (image != NULL) {
459
460        // Set the desired size of the image
461        [image setSize:iconSize];
462
463        // Unfortunately, setting the desired size does not always have an effect,
464        // we need to choose the best image representation by ourselves.
465        NSArray* reps = [image representations];
466        NSEnumerator *enumerator = [reps objectEnumerator];
467        NSImageRep* imageRep;
468        while (imageRep = [enumerator nextObject]) {
469            if ([imageRep pixelsWide] == size) {
470                image = imageRep;
471                break;
472            }
473        }
474        //NSLog (@"%@", image);
475
476
477        NSData* data = [image TIFFRepresentation];
478        unsigned len = [data length];
479        void* bytes = malloc(len);
480        [data getBytes:bytes];
481
482        result = (*env)->NewByteArray(env, len);
483        (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)bytes);
484        free(bytes);
485    }
486
487
488    // Release the C char array
489    (*env)->ReleaseStringChars(env, pathJ, pathC);
490
491    // Release memory pool
492    [pool release];
493
494    return result;
495}
496
497/*
498 * Class:     ch_randelshofer_quaqua_filechooser_Files
499 * Method:    getBasicItemInfoFlags
500 * Signature: (Ljava/lang/String;)I
501 */
502JNIEXPORT jint JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_getBasicItemInfoFlags
503  (JNIEnv *env, jclass javaClass, jstring pathJ) {
504    // Assert arguments
505    if (pathJ == NULL) return -1;
506
507    // Convert Java String to C char array
508    const char *pathC = (*env)->GetStringUTFChars(env, pathJ, 0);
509
510    // Do the API calls
511    FSRef fileRef;
512    OSErr err;
513    LSItemInfoRecord itemInfoRecord;
514    err = FSPathMakeRef(pathC, &fileRef, NULL);
515    if (err == 0) {
516        err = LSCopyItemInfoForRef(&fileRef, kLSRequestBasicFlagsOnly, &itemInfoRecord);
517    }
518
519    // Release the C char array
520    (*env)->ReleaseStringChars(env, pathJ, pathC);
521
522    // Return the result
523    return (err == 0) ? itemInfoRecord.flags : 0;
524}
525
526JNIEXPORT jstring JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_getDisplayName
527  (JNIEnv *env, jclass javaClass, jstring pathJ) {
528
529    // Assert arguments
530    if (pathJ == NULL) return NULL;
531
532    // Allocate a memory pool
533    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
534
535    // Convert Java String to NS String
536    const jchar *pathC = (*env)->GetStringChars(env, pathJ, NULL);
537    NSString *pathNS = [NSString stringWithCharacters:(UniChar *)pathC
538        length:(*env)->GetStringLength(env, pathJ)];
539    (*env)->ReleaseStringChars(env, pathJ, pathC);
540
541
542    // Do the API calls
543    NSFileManager *fileManager = [NSFileManager defaultManager];
544    NSString *displayNameNS = [fileManager displayNameAtPath: pathNS];
545
546    // Convert NSString to jstring
547    jstring *displayNameJ = (*env)->NewStringUTF(env, [displayNameNS UTF8String]);
548
549    // Release memory pool
550    [pool release];
551
552    // Return the result
553    return displayNameJ;
554}
555
556
557/*
558 * Class:     ch_randelshofer_quaqua_filechooser_Files
559 * Method:    getNativeCodeVersion
560 * Signature: ()I
561 */
562JNIEXPORT jint JNICALL Java_ch_randelshofer_quaqua_filechooser_Files_getNativeCodeVersion
563  (JNIEnv *env, jclass javaClass) {
564    return 2;
565}
566
567
568
569/*JNI function definitions end*/
570