1/* 2 Native File Dialog 3 4 http://www.frogtoss.com/labs 5 */ 6 7#include <AppKit/AppKit.h> 8#include "nfd.h" 9#include "nfd_common.h" 10 11static NSArray *BuildAllowedFileTypes( const char *filterList ) 12{ 13 // Commas and semicolons are the same thing on this platform 14 15 NSMutableArray *buildFilterList = [[NSMutableArray alloc] init]; 16 17 char typebuf[NFD_MAX_STRLEN] = {0}; 18 19 size_t filterListLen = strlen(filterList); 20 char *p_typebuf = typebuf; 21 for ( size_t i = 0; i < filterListLen+1; ++i ) 22 { 23 if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' ) 24 { 25 ++p_typebuf; 26 *p_typebuf = '\0'; 27 NSString *thisType = [NSString stringWithUTF8String: typebuf]; 28 [buildFilterList addObject:thisType]; 29 p_typebuf = typebuf; 30 *p_typebuf = '\0'; 31 } 32 else 33 { 34 *p_typebuf = filterList[i]; 35 ++p_typebuf; 36 37 } 38 } 39 40 NSArray *returnArray = [NSArray arrayWithArray:buildFilterList]; 41 42 [buildFilterList release]; 43 return returnArray; 44} 45 46static void AddFilterListToDialog( NSSavePanel *dialog, const char *filterList ) 47{ 48 if ( !filterList || strlen(filterList) == 0 ) 49 return; 50 51 NSArray *allowedFileTypes = BuildAllowedFileTypes( filterList ); 52 if ( [allowedFileTypes count] != 0 ) 53 { 54 [dialog setAllowedFileTypes:allowedFileTypes]; 55 } 56} 57 58static void SetDefaultPath( NSSavePanel *dialog, const nfdchar_t *defaultPath ) 59{ 60 if ( !defaultPath || strlen(defaultPath) == 0 ) 61 return; 62 63 NSString *defaultPathString = [NSString stringWithUTF8String: defaultPath]; 64 NSURL *url = [NSURL fileURLWithPath:defaultPathString isDirectory:YES]; 65 [dialog setDirectoryURL:url]; 66} 67 68 69/* fixme: pathset should be pathSet */ 70static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset ) 71{ 72 assert(pathset); 73 assert([urls count]); 74 75 pathset->count = (size_t)[urls count]; 76 pathset->indices = NFDi_Malloc( sizeof(size_t)*pathset->count ); 77 if ( !pathset->indices ) 78 { 79 return NFD_ERROR; 80 } 81 82 // count the total space needed for buf 83 size_t bufsize = 0; 84 for ( NSURL *url in urls ) 85 { 86 NSString *path = [url path]; 87 bufsize += [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; 88 } 89 90 pathset->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufsize ); 91 if ( !pathset->buf ) 92 { 93 return NFD_ERROR; 94 } 95 96 // fill buf 97 nfdchar_t *p_buf = pathset->buf; 98 size_t count = 0; 99 for ( NSURL *url in urls ) 100 { 101 NSString *path = [url path]; 102 const nfdchar_t *utf8Path = [path UTF8String]; 103 size_t byteLen = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; 104 memcpy( p_buf, utf8Path, byteLen ); 105 106 ptrdiff_t index = p_buf - pathset->buf; 107 assert( index >= 0 ); 108 pathset->indices[count] = (size_t)index; 109 110 p_buf += byteLen; 111 ++count; 112 } 113 114 return NFD_OKAY; 115} 116 117/* public */ 118 119 120nfdresult_t NFD_OpenDialog( const char *filterList, 121 const nfdchar_t *defaultPath, 122 nfdchar_t **outPath ) 123{ 124 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 125 126 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; 127 NSOpenPanel *dialog = [NSOpenPanel openPanel]; 128 [dialog setAllowsMultipleSelection:NO]; 129 130 // Build the filter list 131 AddFilterListToDialog(dialog, filterList); 132 133 // Set the starting directory 134 SetDefaultPath(dialog, defaultPath); 135 136 nfdresult_t nfdResult = NFD_CANCEL; 137 if ( [dialog runModal] == NSModalResponseOK ) 138 { 139 NSURL *url = [dialog URL]; 140 const char *utf8Path = [[url path] UTF8String]; 141 142 // byte count, not char count 143 size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); 144 145 *outPath = NFDi_Malloc( len+1 ); 146 if ( !*outPath ) 147 { 148 [pool release]; 149 return NFD_ERROR; 150 } 151 memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ 152 nfdResult = NFD_OKAY; 153 } 154 [pool release]; 155 156 [keyWindow makeKeyAndOrderFront:nil]; 157 return nfdResult; 158} 159 160 161nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, 162 const nfdchar_t *defaultPath, 163 nfdpathset_t *outPaths ) 164{ 165 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 166 167 NSOpenPanel *dialog = [NSOpenPanel openPanel]; 168 [dialog setAllowsMultipleSelection:YES]; 169 170 // Build the fiter list. 171 AddFilterListToDialog(dialog, filterList); 172 173 // Set the starting directory 174 SetDefaultPath(dialog, defaultPath); 175 176 nfdresult_t nfdResult = NFD_CANCEL; 177 if ( [dialog runModal] == NSModalResponseOK ) 178 { 179 NSArray *urls = [dialog URLs]; 180 181 if ( [urls count] == 0 ) 182 { 183 [pool release]; 184 return NFD_CANCEL; 185 } 186 187 if ( AllocPathSet( urls, outPaths ) == NFD_ERROR ) 188 { 189 [pool release]; 190 return NFD_ERROR; 191 } 192 193 nfdResult = NFD_OKAY; 194 } 195 [pool release]; 196 197 return nfdResult; 198} 199 200 201nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, 202 const nfdchar_t *defaultPath, 203 nfdchar_t **outPath ) 204{ 205 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 206 207 NSSavePanel *dialog = [NSSavePanel savePanel]; 208 [dialog setExtensionHidden:NO]; 209 210 // Build the filter list. 211 AddFilterListToDialog(dialog, filterList); 212 213 // Set the starting directory 214 SetDefaultPath(dialog, defaultPath); 215 216 nfdresult_t nfdResult = NFD_CANCEL; 217 if ( [dialog runModal] == NSModalResponseOK ) 218 { 219 NSURL *url = [dialog URL]; 220 const char *utf8Path = [[url path] UTF8String]; 221 222 size_t byteLen = [url.path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; 223 224 *outPath = NFDi_Malloc( byteLen ); 225 if ( !*outPath ) 226 { 227 [pool release]; 228 return NFD_ERROR; 229 } 230 memcpy( *outPath, utf8Path, byteLen ); 231 nfdResult = NFD_OKAY; 232 } 233 234 [pool release]; 235 236 return nfdResult; 237} 238 239nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, 240 nfdchar_t **outPath) 241{ 242 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 243 244 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; 245 NSOpenPanel *dialog = [NSOpenPanel openPanel]; 246 [dialog setAllowsMultipleSelection:NO]; 247 [dialog setCanChooseDirectories:YES]; 248 [dialog setCanCreateDirectories:YES]; 249 [dialog setCanChooseFiles:NO]; 250 251 // Set the starting directory 252 SetDefaultPath(dialog, defaultPath); 253 254 nfdresult_t nfdResult = NFD_CANCEL; 255 if ( [dialog runModal] == NSModalResponseOK ) 256 { 257 NSURL *url = [dialog URL]; 258 const char *utf8Path = [[url path] UTF8String]; 259 260 // byte count, not char count 261 size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); 262 263 *outPath = NFDi_Malloc( len+1 ); 264 if ( !*outPath ) 265 { 266 [pool release]; 267 return NFD_ERROR; 268 } 269 memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ 270 nfdResult = NFD_OKAY; 271 } 272 [pool release]; 273 274 [keyWindow makeKeyAndOrderFront:nil]; 275 return nfdResult; 276} 277