aboutsummaryrefslogtreecommitdiff
path: root/src/lib/FL/Fl_Native_File_Chooser_MAC.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/FL/Fl_Native_File_Chooser_MAC.cxx')
-rw-r--r--src/lib/FL/Fl_Native_File_Chooser_MAC.cxx833
1 files changed, 833 insertions, 0 deletions
diff --git a/src/lib/FL/Fl_Native_File_Chooser_MAC.cxx b/src/lib/FL/Fl_Native_File_Chooser_MAC.cxx
new file mode 100644
index 0000000..e556edf
--- /dev/null
+++ b/src/lib/FL/Fl_Native_File_Chooser_MAC.cxx
@@ -0,0 +1,833 @@
1//
2// Fl_Native_File_Chooser_MAC.cxx -- FLTK native OS file chooser widget
3//
4// Copyright 2004 by Greg Ercolano.
5//
6// This library is free software; you can redistribute it and/or
7// modify it under the terms of the GNU Library General Public
8// License as published by the Free Software Foundation; either
9// version 2 of the License, or (at your option) any later version.
10//
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// Library General Public License for more details.
15//
16// You should have received a copy of the GNU Library General Public
17// License along with this library; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19// USA.
20//
21// Please keep code 80 column compliant.
22//
23// 10 20 30 40 50 60 70
24// | | | | | | |
25// 4567890123456789012345678901234567890123456789012345678901234567890123456789
26//
27// TODO:
28// o When doing 'open file', only dir is preset, not filename.
29// Possibly 'preset_file' could be used to select the filename.
30//
31#include <FL/Fl.H>
32#include <FL/Fl_Native_File_Chooser.H>
33#include "common.cxx" // strnew/strfree/strapp/chrcat
34
35// TRY TO CONVERT AN AEDesc TO AN FSSpec
36// As per Apple Technical Q&A QA1274
37// eg: http://developer.apple.com/qa/qa2001/qa1274.html
38// Returns 'noErr' if OK,
39// or an 'OSX result code' on error.
40//
41static int AEDescToFSSpec(const AEDesc* desc, FSSpec* fsspec) {
42 OSStatus err = noErr;
43 AEDesc coerceDesc;
44 // If AEDesc isn't already an FSSpec, convert it to one
45 if ( desc->descriptorType != typeFSS ) {
46 if ( ( err = AECoerceDesc(desc, typeFSS, &coerceDesc) ) == noErr ) {
47 // Get FSSpec out of AEDesc
48 err = AEGetDescData(&coerceDesc, fsspec, sizeof(FSSpec));
49 AEDisposeDesc(&coerceDesc);
50 }
51 } else {
52 err = AEGetDescData(desc, fsspec, sizeof(FSSpec));
53 }
54 return( err );
55}
56
57// CONVERT AN FSSpec TO A PATHNAME
58static void FSSpecToPath(const FSSpec &spec, char *buff, int bufflen) {
59 FSRef fsRef;
60 FSpMakeFSRef(&spec, &fsRef);
61 FSRefMakePath(&fsRef, (UInt8*)buff, bufflen);
62}
63
64// CONVERT REGULAR PATH -> FSSpec
65// If file does not exist, expect fnfErr.
66// Returns 'noErr' if OK,
67// or an 'OSX result code' on error.
68//
69static OSStatus PathToFSSpec(const char *path, FSSpec &spec) {
70 OSStatus err;
71 FSRef ref;
72 if ((err = FSPathMakeRef((const UInt8*)path, &ref, NULL)) != noErr) {
73 return(err);
74 }
75 // FSRef -> FSSpec
76 if ((err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, &spec,
77 NULL)) != noErr) {
78 return(err);
79 }
80 return(noErr);
81}
82
83// NAVREPLY: CTOR
84Fl_Native_File_Chooser::NavReply::NavReply() {
85 _valid_reply = 0;
86}
87
88// NAVREPLY: DTOR
89Fl_Native_File_Chooser::NavReply::~NavReply() {
90 if ( _valid_reply ) {
91 NavDisposeReply(&_reply);
92 }
93}
94
95// GET REPLY FROM THE NAV* DIALOG
96int Fl_Native_File_Chooser::NavReply::get_reply(NavDialogRef& ref) {
97 if ( _valid_reply ) {
98 NavDisposeReply(&_reply); // dispose of previous
99 _valid_reply = 0;
100 }
101 if ( ref == NULL || NavDialogGetReply(ref, &_reply) != noErr ) {
102 return(-1);
103 }
104 _valid_reply = 1;
105 return(0);
106}
107
108// RETURN THE BASENAME USER WANTS TO 'Save As'
109int Fl_Native_File_Chooser::NavReply::get_saveas_basename(char *s, int slen) {
110 if (CFStringGetCString(_reply.saveFileName, s, slen-1,
111 kCFStringEncodingUTF8) == false) {
112 s[0] = '\0';
113 return(-1);
114 }
115 return(0);
116}
117
118// RETURN THE DIRECTORY NAME
119// Returns 0 on success, -1 on error.
120//
121int Fl_Native_File_Chooser::NavReply::get_dirname(char *s, int slen) {
122 FSSpec fsspec;
123 if ( AEDescToFSSpec(&_reply.selection, &fsspec) != noErr ) {
124 // Conversion failed? Return empty name
125 s[0] = 0;
126 return(-1);
127 }
128 FSSpecToPath(fsspec, s, slen);
129 return(0);
130}
131
132// RETURN MULTIPLE DIRECTORIES
133// Returns: 0 on success with pathnames[] containing pathnames selected,
134// -1 on error
135//
136int Fl_Native_File_Chooser::NavReply::get_pathnames(char **&pathnames,
137 int& tpathnames) {
138 // How many items selected?
139 long count = 0;
140 if ( AECountItems(&_reply.selection, &count) != noErr )
141 { return(-1); }
142
143 // Allocate space for that many pathnames
144 pathnames = new char*[count];
145 memset((void*)pathnames, 0, count*sizeof(char*));
146 tpathnames = count;
147
148 // Walk list of pathnames selected
149 for (short index=1; index<=count; index++) {
150 AEKeyword keyWord;
151 AEDesc desc;
152 if (AEGetNthDesc(&_reply.selection, index, typeFSS, &keyWord,
153 &desc) != noErr) {
154 pathnames[index-1] = strnew("");
155 continue;
156 }
157 FSSpec fsspec;
158 if (AEGetDescData(&desc, &fsspec, sizeof(FSSpec)) != noErr ) {
159 pathnames[index-1] = strnew("");
160 continue;
161 }
162 char s[4096];
163 FSSpecToPath(fsspec, s, sizeof(s)-1);
164 pathnames[index-1] = strnew(s);
165 AEDisposeDesc(&desc);
166 }
167 return(0);
168}
169
170// FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS
171void Fl_Native_File_Chooser::clear_pathnames() {
172 if ( _pathnames ) {
173 while ( --_tpathnames >= 0 ) {
174 _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]);
175 }
176 delete [] _pathnames;
177 _pathnames = NULL;
178 }
179 _tpathnames = 0;
180}
181
182// SET A SINGLE PATHNAME
183void Fl_Native_File_Chooser::set_single_pathname(const char *s) {
184 clear_pathnames();
185 _pathnames = new char*[1];
186 _pathnames[0] = strnew(s);
187 _tpathnames = 1;
188}
189
190// GET THE 'Save As' FILENAME
191// Returns -1 on error, errmsg() has reason, filename == "".
192// 0 if OK, filename() has filename chosen.
193//
194int Fl_Native_File_Chooser::get_saveas_basename(NavDialogRef& ref) {
195 if ( ref == NULL ) {
196 errmsg("get_saveas_basename: ref is NULL");
197 return(-1);
198 }
199 NavReply reply;
200 OSStatus err;
201 if ((err = reply.get_reply(ref)) != noErr ) {
202 errmsg("NavReply::get_reply() failed");
203 clear_pathnames();
204 return(-1);
205 }
206
207 char pathname[4096] = "";
208 // Directory name..
209 // -2 leaves room to append '/'
210 //
211 if ( reply.get_dirname(pathname, sizeof(pathname)-2) < 0 ) {
212 clear_pathnames();
213 errmsg("NavReply::get_dirname() failed");
214 return(-1);
215 }
216 // Append '/'
217 int len = strlen(pathname);
218 pathname[len++] = '/';
219 pathname[len] = '\0';
220 // Basename..
221 if ( reply.get_saveas_basename(pathname+len, sizeof(pathname)-len) < 0 ) {
222 clear_pathnames();
223 errmsg("NavReply::get_saveas_basename() failed");
224 return(-1);
225 }
226 set_single_pathname(pathname);
227 return(0);
228}
229
230// GET (POTENTIALLY) MULTIPLE FILENAMES
231// Returns:
232// -1 -- error, errmsg() has reason, filename == ""
233// 0 -- OK, pathnames()/filename() has pathname(s) chosen
234//
235int Fl_Native_File_Chooser::get_pathnames(NavDialogRef& ref) {
236 if ( ref == NULL ) {
237 errmsg("get_saveas_basename: ref is NULL");
238 return(-1);
239 }
240 NavReply reply;
241 OSStatus err;
242 if ((err = reply.get_reply(ref)) != noErr ) {
243 errmsg("NavReply::get_reply() failed");
244 clear_pathnames();
245 return(-1);
246 }
247 // First, clear pathnames array of any previous contents
248 clear_pathnames();
249 if ( reply.get_pathnames(_pathnames, _tpathnames) < 0 ) {
250 clear_pathnames();
251 errmsg("NavReply::get_dirname() failed");
252 return(-1);
253 }
254 return(0);
255}
256
257// NAV CALLBACK EVENT HANDLER
258void Fl_Native_File_Chooser::event_handler(
259 NavEventCallbackMessage callBackSelector,
260 NavCBRecPtr cbparm,
261 void *data) {
262 OSStatus err;
263 Fl_Native_File_Chooser *nfb = (Fl_Native_File_Chooser*)data;
264 switch (callBackSelector) {
265 case kNavCBStart:
266 if ( nfb->directory() || nfb->preset_file() ) {
267 const char *pathname = nfb->directory() ? nfb->directory() : nfb->preset_file();
268 FSSpec spec;
269 if ( ( err = PathToFSSpec(pathname, spec) ) != noErr ) {
270 fprintf(stderr, "PathToFSSpec(%s) failed: err=%d\n",
271 pathname, (int)err);
272 break;
273 }
274 AEDesc desc;
275 if ((err = AECreateDesc(typeFSS,
276 &spec, sizeof(FSSpec), &desc)) != noErr) {
277 fprintf(stderr, "AECreateDesc() failed: err=%d\n",
278 (int)err);
279 }
280 if ((err = NavCustomControl(cbparm->context,
281 kNavCtlSetLocation, &desc)) != noErr) {
282 fprintf(stderr, "NavCustomControl() failed: err=%d\n",
283 (int)err);
284 }
285 AEDisposeDesc(&desc);
286 }
287 if ( nfb->_btype == BROWSE_SAVE_FILE && nfb->preset_file() ) {
288 CFStringRef namestr =
289 CFStringCreateWithCString(NULL,
290 nfb->preset_file(),
291 kCFStringEncodingASCII);
292 NavDialogSetSaveFileName(cbparm->context, namestr);
293 CFRelease(namestr);
294 }
295 NavCustomControl(cbparm->context, kNavCtlSetActionState,
296 &nfb->_keepstate );
297
298 // Select the right filter in pop-up menu
299 if ( nfb->_filt_value == nfb->_filt_total ) {
300 // Select All Documents
301 NavPopupMenuItem kAll = kNavAllFiles;
302 NavCustomControl(cbparm->context, kNavCtlSelectAllType, &kAll);
303 } else if (nfb->_filt_value < nfb->_filt_total) {
304 // Select custom filter
305 nfb->_tempitem.version = kNavMenuItemSpecVersion;
306 nfb->_tempitem.menuCreator = 'extn';
307 nfb->_tempitem.menuType = nfb->_filt_value;
308 *nfb->_tempitem.menuItemName = '\0'; // needed on 10.3+
309 NavCustomControl(cbparm->context,
310 kNavCtlSelectCustomType,
311 &(nfb->_tempitem));
312 }
313 break;
314
315 case kNavCBPopupMenuSelect:
316 NavMenuItemSpecPtr ptr;
317 // they really buried this one!
318 ptr = (NavMenuItemSpecPtr)cbparm->eventData.eventDataParms.param;
319 if ( ptr->menuCreator ) {
320 // Gets index to filter ( menuCreator = 'extn' )
321 nfb->_filt_value = ptr->menuType;
322 } else {
323 // All docs filter selected ( menuCreator = '\0\0\0\0' )
324 nfb->_filt_value = nfb->_filt_total;
325 }
326 break;
327
328 case kNavCBSelectEntry:
329 NavActionState astate;
330 switch ( nfb->_btype ) {
331 // these don't need selection override
332 case BROWSE_MULTI_FILE:
333 case BROWSE_MULTI_DIRECTORY:
334 case BROWSE_SAVE_FILE:
335 break;
336
337 // These need to allow only one item, so disable
338 // Open button if user tries to select multiple files
339 case BROWSE_SAVE_DIRECTORY:
340 case BROWSE_DIRECTORY:
341 case BROWSE_FILE:
342 SInt32 selectcount;
343 AECountItems((AEDescList*)cbparm->
344 eventData.eventDataParms.param,
345 &selectcount);
346 if ( selectcount > 1 ) {
347 NavCustomControl(cbparm->context,
348 kNavCtlSetSelection,
349 NULL);
350 astate = nfb->_keepstate |
351 kNavDontOpenState |
352 kNavDontChooseState;
353 NavCustomControl(cbparm->context,
354 kNavCtlSetActionState,
355 &astate );
356 }
357 else {
358 astate= nfb->_keepstate | kNavNormalState;
359 NavCustomControl(cbparm->context,
360 kNavCtlSetActionState,
361 &astate );
362 }
363 break;
364 }
365 break;
366 }
367}
368
369// CONSTRUCTOR
370Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
371 _btype = val;
372 NavGetDefaultDialogCreationOptions(&_opts);
373 _opts.optionFlags |= kNavDontConfirmReplacement; // no confirms for "save as"
374 _options = NO_OPTIONS;
375 _ref = NULL;
376 memset(&_tempitem, 0, sizeof(_tempitem));
377 _pathnames = NULL;
378 _tpathnames = 0;
379 _title = NULL;
380 _filter = NULL;
381 _filt_names = NULL;
382 memset(_filt_patt, 0, sizeof(char*) * MAXFILTERS);
383 _filt_total = 0;
384 _filt_value = 0;
385 _directory = NULL;
386 _preset_file = NULL;
387 _errmsg = NULL;
388 _keepstate = kNavNormalState;
389}
390
391// DESTRUCTOR
392Fl_Native_File_Chooser::~Fl_Native_File_Chooser() {
393 // _opts // nothing to manage
394 if (_ref) { NavDialogDispose(_ref); _ref = NULL; }
395 // _options // nothing to manage
396 // _keepstate // nothing to manage
397 // _tempitem // nothing to manage
398 clear_pathnames();
399 _directory = strfree(_directory);
400 _title = strfree(_title);
401 _preset_file = strfree(_preset_file);
402 _filter = strfree(_filter);
403 //_filt_names // managed by clear_filters()
404 //_filt_patt[i] // managed by clear_filters()
405 //_filt_total // managed by clear_filters()
406 clear_filters();
407 //_filt_value // nothing to manage
408 _errmsg = strfree(_errmsg);
409}
410
411// SET THE TYPE OF BROWSER
412void Fl_Native_File_Chooser::type(int val) {
413 _btype = val;
414}
415
416// GET TYPE OF BROWSER
417int Fl_Native_File_Chooser::type() const {
418 return(_btype);
419}
420
421// SET OPTIONS
422void Fl_Native_File_Chooser::options(int val) {
423 _options = val;
424}
425
426// GET OPTIONS
427int Fl_Native_File_Chooser::options() const {
428 return(_options);
429}
430
431// SHOW THE BROWSER WINDOW
432// Returns:
433// 0 - user picked a file
434// 1 - user cancelled
435// -1 - failed; errmsg() has reason
436//
437int Fl_Native_File_Chooser::show() {
438 // Make sure fltk interface updates before posting our dialog
439 Fl::flush();
440
441 // BROWSER TITLE
442 CFStringRef cfs_title;
443 cfs_title = CFStringCreateWithCString(NULL,
444 _title ? _title : "No Title",
445 kCFStringEncodingASCII);
446 _opts.windowTitle = cfs_title;
447
448 _keepstate = kNavNormalState;
449
450 // BROWSER FILTERS
451 CFArrayRef filter_array = NULL;
452 {
453 // One or more filters specified?
454 if ( _filt_total ) {
455 // NAMES -> CFArrayRef
456 CFStringRef tab = CFSTR("\t");
457 CFStringRef tmp_cfs;
458 tmp_cfs = CFStringCreateWithCString(NULL, _filt_names,
459 kCFStringEncodingASCII);
460 filter_array = CFStringCreateArrayBySeparatingStrings(
461 NULL, tmp_cfs, tab);
462 CFRelease(tmp_cfs);
463 CFRelease(tab);
464 _opts.popupExtension = filter_array;
465 _opts.optionFlags |= kNavAllFilesInPopup;
466 } else {
467 filter_array = NULL;
468 _opts.popupExtension = NULL;
469 _opts.optionFlags |= kNavAllFilesInPopup;
470 }
471 }
472
473 // HANDLE OPTIONS WE SUPPORT
474 if ( _options & SAVEAS_CONFIRM ) {
475 _opts.optionFlags &= ~kNavDontConfirmReplacement; // enables confirm
476 } else {
477 _opts.optionFlags |= kNavDontConfirmReplacement; // disables confirm
478 }
479
480 // POST BROWSER
481 int err = post();
482
483 // RELEASE _FILT_ARR
484 if ( filter_array ) CFRelease(filter_array);
485 filter_array = NULL;
486 _opts.popupExtension = NULL;
487 _filt_total = 0;
488
489 // RELEASE TITLE
490 if ( cfs_title ) CFRelease(cfs_title);
491 cfs_title = NULL;
492
493 return(err);
494}
495
496// POST BROWSER
497// Internal use only.
498// Assumes '_opts' has been initialized.
499//
500// Returns:
501// 0 - user picked a file
502// 1 - user cancelled
503// -1 - failed; errmsg() has reason
504//
505int Fl_Native_File_Chooser::post() {
506
507 // INITIALIZE BROWSER
508 OSStatus err;
509 if ( _filt_total == 0 ) { // Make sure they match
510 _filt_value = 0; // TBD: move to someplace more logical?
511 }
512
513 if ( ! ( _options & NEW_FOLDER ) ) {
514 _keepstate |= kNavDontNewFolderState;
515 }
516
517 switch (_btype) {
518 case BROWSE_FILE:
519 case BROWSE_MULTI_FILE:
520 // Prompt user for one or more files
521 if ((err = NavCreateGetFileDialog(
522 &_opts, // options
523 0, // file types
524 event_handler, // event handler
525 0, // preview callback
526 filter_proc_cb, // filter callback
527 (void*)this, // callback data
528 &_ref)) != noErr ) { // dialog ref
529 errmsg("NavCreateGetFileDialog: failed");
530 return(-1);
531 }
532 break;
533
534 case BROWSE_DIRECTORY:
535 case BROWSE_MULTI_DIRECTORY:
536 case BROWSE_SAVE_DIRECTORY:
537 // Prompts user for one or more files or folders
538 if ((err = NavCreateChooseFolderDialog(
539 &_opts, // options
540 event_handler, // event callback
541 0, // filter callback
542 (void*)this, // callback data
543 &_ref)) != noErr ) { // dialog ref
544 errmsg("NavCreateChooseFolderDialog: failed");
545 return(-1);
546 }
547 break;
548
549 case BROWSE_SAVE_FILE:
550 // Prompt user for filename to 'save as'
551 if ((err = NavCreatePutFileDialog(
552 &_opts, // options
553 0, // file types
554 0, // file creator
555 event_handler, // event handler
556 (void*)this, // callback data
557 &_ref)) != noErr ) { // dialog ref
558 errmsg("NavCreatePutFileDialog: failed");
559 return(-1);
560 }
561 break;
562 }
563
564 // SHOW THE DIALOG
565 if ( ( err = NavDialogRun(_ref) ) != 0 ) {
566 char msg[80];
567 sprintf(msg, "NavDialogRun: failed (err=%d)", (int)err);
568 errmsg(msg);
569 return(-1);
570 }
571
572 // WHAT ACTION DID USER CHOOSE?
573 NavUserAction act = NavDialogGetUserAction(_ref);
574 if ( act == kNavUserActionNone ) {
575 errmsg("Nothing happened yet (dialog still open)");
576 return(-1);
577 }
578 else if ( act == kNavUserActionCancel ) { // user chose 'cancel'
579 return(1);
580 }
581 else if ( act == kNavUserActionSaveAs ) { // user chose 'save as'
582 return(get_saveas_basename(_ref));
583 }
584
585 // TOO MANY FILES CHOSEN?
586 int ret = get_pathnames(_ref);
587 if ( _btype == BROWSE_FILE && ret == 0 && _tpathnames != 1 ) {
588 char msg[80];
589 sprintf(msg, "Expected only one file to be chosen.. you chose %d.",
590 (int)_tpathnames);
591 errmsg(msg);
592 return(-1);
593 }
594 return(err);
595}
596
597// SET ERROR MESSAGE
598// Internal use only.
599//
600void Fl_Native_File_Chooser::errmsg(const char *msg) {
601 _errmsg = strfree(_errmsg);
602 _errmsg = strnew(msg);
603}
604
605// RETURN ERROR MESSAGE
606const char *Fl_Native_File_Chooser::errmsg() const {
607 return(_errmsg ? _errmsg : "No error");
608}
609
610// GET FILENAME
611const char* Fl_Native_File_Chooser::filename() const {
612 if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]);
613 return("");
614}
615
616// GET FILENAME FROM LIST OF FILENAMES
617const char* Fl_Native_File_Chooser::filename(int i) const {
618 if ( _pathnames && i < _tpathnames ) return(_pathnames[i]);
619 return("");
620}
621
622// GET TOTAL FILENAMES CHOSEN
623int Fl_Native_File_Chooser::count() const {
624 return(_tpathnames);
625}
626
627// PRESET PATHNAME
628// Value can be NULL for none.
629//
630void Fl_Native_File_Chooser::directory(const char *val) {
631 _directory = strfree(_directory);
632 _directory = strnew(val);
633}
634
635// GET PRESET PATHNAME
636// Returned value can be NULL if none set.
637//
638const char* Fl_Native_File_Chooser::directory() const {
639 return(_directory);
640}
641
642// SET TITLE
643// Value can be NULL if no title desired.
644//
645void Fl_Native_File_Chooser::title(const char *val) {
646 _title = strfree(_title);
647 _title = strnew(val);
648}
649
650// GET TITLE
651// Returned value can be NULL if none set.
652//
653const char *Fl_Native_File_Chooser::title() const {
654 return(_title);
655}
656
657// SET FILTER
658// Can be NULL if no filter needed
659//
660void Fl_Native_File_Chooser::filter(const char *val) {
661 _filter = strfree(_filter);
662 _filter = strnew(val);
663
664 // Parse filter user specified
665 // IN: _filter = "C Files\t*.{cxx,h}\nText Files\t*.txt"
666 // OUT: _filt_names = "C Files\tText Files"
667 // _filt_patt[0] = "*.{cxx,h}"
668 // _filt_patt[1] = "*.txt"
669 // _filt_total = 2
670 //
671 parse_filter(_filter);
672}
673
674// GET FILTER
675// Returned value can be NULL if none set.
676//
677const char *Fl_Native_File_Chooser::filter() const {
678 return(_filter);
679}
680
681// CLEAR ALL FILTERS
682// Internal use only.
683//
684void Fl_Native_File_Chooser::clear_filters() {
685 _filt_names = strfree(_filt_names);
686 for (int i=0; i<_filt_total; i++) {
687 _filt_patt[i] = strfree(_filt_patt[i]);
688 }
689 _filt_total = 0;
690}
691
692// PARSE USER'S FILTER SPEC
693// Parses user specified filter ('in'),
694// breaks out into _filt_patt[], _filt_names, and _filt_total.
695//
696// Handles:
697// IN: OUT:_filt_names OUT: _filt_patt
698// ------------------------------------ ------------------ ---------------
699// "*.{ma,mb}" "*.{ma,mb} Files" "*.{ma,mb}"
700// "*.[abc]" "*.[abc] Files" "*.[abc]"
701// "*.txt" "*.txt Files" "*.c"
702// "C Files\t*.[ch]" "C Files" "*.[ch]"
703// "C Files\t*.[ch]\nText Files\t*.cxx" "C Files" "*.[ch]"
704//
705// Parsing Mode:
706// IN:"C Files\t*.{cxx,h}"
707// ||||||| |||||||||
708// mode: nnnnnnn wwwwwwwww
709// \_____/ \_______/
710// Name Wildcard
711//
712void Fl_Native_File_Chooser::parse_filter(const char *in) {
713 clear_filters();
714 if ( ! in ) return;
715 int has_name = strchr(in, '\t') ? 1 : 0;
716
717 char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard
718 char wildcard[1024] = ""; // parsed wildcard
719 char name[1024] = "";
720
721 // Parse filter user specified
722 for ( ; 1; in++ ) {
723
724 //// DEBUG
725 //// printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n",
726 //// *in, mode, name, wildcard);
727
728 switch (*in) {
729 // FINISHED PARSING NAME?
730 case '\t':
731 if ( mode != 'n' ) goto regchar;
732 mode = 'w';
733 break;
734
735 // ESCAPE NEXT CHAR
736 case '\\':
737 ++in;
738 goto regchar;
739
740 // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
741 case '\r':
742 case '\n':
743 case '\0':
744 // TITLE
745 // If user didn't specify a name, make one
746 //
747 if ( name[0] == '\0' ) {
748 sprintf(name, "%.*s Files", (int)sizeof(name)-10, wildcard);
749 }
750 // APPEND NEW FILTER TO LIST
751 if ( wildcard[0] ) {
752 // Add to filtername list
753 // Tab delimit if more than one. We later break
754 // tab delimited string into CFArray with
755 // CFStringCreateArrayBySeparatingStrings()
756 //
757 if ( _filt_total ) {
758 _filt_names = strapp(_filt_names, "\t");
759 }
760 _filt_names = strapp(_filt_names, name);
761
762 // Add filter to the pattern array
763 _filt_patt[_filt_total++] = strnew(wildcard);
764 }
765 // RESET
766 wildcard[0] = name[0] = '\0';
767 mode = strchr(in, '\t') ? 'n' : 'w';
768 // DONE?
769 if ( *in == '\0' ) return; // done
770 else continue; // not done yet, more filters
771
772 // Parse all other chars
773 default: // handle all non-special chars
774 regchar: // handle regular char
775 switch ( mode ) {
776 case 'n': chrcat(name, *in); continue;
777 case 'w': chrcat(wildcard, *in); continue;
778 }
779 break;
780 }
781 }
782 //NOTREACHED
783}
784
785// STATIC: FILTER CALLBACK
786Boolean Fl_Native_File_Chooser::filter_proc_cb(AEDesc *theItem,
787 void *info,
788 void *callBackUD,
789 NavFilterModes filterMode) {
790 return((Fl_Native_File_Chooser*)callBackUD)->filter_proc_cb2(
791 theItem, info, callBackUD, filterMode);
792}
793
794// FILTER CALLBACK
795// Return true if match,
796// false if no match.
797//
798Boolean Fl_Native_File_Chooser::filter_proc_cb2(AEDesc *theItem,
799 void *info,
800 void *callBackUD,
801 NavFilterModes filterMode) {
802 // All files chosen or no filters
803 if ( _filt_value == _filt_total ) return(true);
804
805 FSSpec fsspec;
806 char pathname[4096];
807
808 // On fail, filter should return true by default
809 if ( AEDescToFSSpec(theItem, &fsspec) != noErr ) {
810 return(true);
811 }
812 FSSpecToPath(fsspec, pathname, sizeof(pathname)-1);
813
814 if ( fl_filename_isdir(pathname) ) return(true);
815 if ( fl_filename_match(pathname, _filt_patt[_filt_value]) ) return(true);
816 else return(false);
817}
818
819// SET PRESET FILE
820// Value can be NULL for none.
821//
822void Fl_Native_File_Chooser::preset_file(const char* val) {
823 _preset_file = strfree(_preset_file);
824 _preset_file = strnew(val);
825}
826
827// PRESET FILE
828// Returned value can be NULL if none set.
829//
830const char* Fl_Native_File_Chooser::preset_file() {
831 return(_preset_file);
832}
833