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