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