|
fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation) ![]() |
00001 // "$Id: Fl_Native_File_Chooser_MAC.mm 8062 2010-12-19 17:57:19Z manolo $" 00002 // 00003 // FLTK native OS file chooser widget 00004 // 00005 // Copyright 1998-2010 by Bill Spitzak and others. 00006 // Copyright 2004 Greg Ercolano. 00007 // 00008 // This library is free software; you can redistribute it and/or 00009 // modify it under the terms of the GNU Library General Public 00010 // License as published by the Free Software Foundation; either 00011 // version 2 of the License, or (at your option) any later version. 00012 // 00013 // This library is distributed in the hope that it will be useful, 00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 // Library General Public License for more details. 00017 // 00018 // You should have received a copy of the GNU Library General Public 00019 // License along with this library; if not, write to the Free Software 00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00021 // USA. 00022 // 00023 // Please report all bugs and problems to: 00024 // 00025 // http://www.fltk.org/str.php 00026 // 00027 00028 // TODO: 00029 // o When doing 'open file', only dir is preset, not filename. 00030 // Possibly 'preset_file' could be used to select the filename. 00031 // 00032 00033 #ifdef __APPLE__ 00034 00035 #include "Fl_Native_File_Chooser_common.cxx" // strnew/strfree/strapp/chrcat 00036 #include <libgen.h> // dirname(3) 00037 #include <sys/types.h> // stat(2) 00038 #include <sys/stat.h> // stat(2) 00039 00040 00041 #include <FL/Fl.H> 00042 #include <FL/Fl_Native_File_Chooser.H> 00043 #include <FL/filename.H> 00044 00045 // FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS 00046 void Fl_Native_File_Chooser::clear_pathnames() { 00047 if ( _pathnames ) { 00048 while ( --_tpathnames >= 0 ) { 00049 _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]); 00050 } 00051 delete [] _pathnames; 00052 _pathnames = NULL; 00053 } 00054 _tpathnames = 0; 00055 } 00056 00057 // SET A SINGLE PATHNAME 00058 void Fl_Native_File_Chooser::set_single_pathname(const char *s) { 00059 clear_pathnames(); 00060 _pathnames = new char*[1]; 00061 _pathnames[0] = strnew(s); 00062 _tpathnames = 1; 00063 } 00064 00065 // CONSTRUCTOR 00066 Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) { 00067 _btype = val; 00068 _panel = NULL; 00069 _options = NO_OPTIONS; 00070 _pathnames = NULL; 00071 _tpathnames = 0; 00072 _title = NULL; 00073 _filter = NULL; 00074 _filt_names = NULL; 00075 memset(_filt_patt, 0, sizeof(char*) * MAXFILTERS); 00076 _filt_total = 0; 00077 _filt_value = 0; 00078 _directory = NULL; 00079 _preset_file = NULL; 00080 _errmsg = NULL; 00081 } 00082 00083 // DESTRUCTOR 00084 Fl_Native_File_Chooser::~Fl_Native_File_Chooser() { 00085 // _opts // nothing to manage 00086 // _options // nothing to manage 00087 // _keepstate // nothing to manage 00088 // _tempitem // nothing to manage 00089 clear_pathnames(); 00090 _directory = strfree(_directory); 00091 _title = strfree(_title); 00092 _preset_file = strfree(_preset_file); 00093 _filter = strfree(_filter); 00094 //_filt_names // managed by clear_filters() 00095 //_filt_patt[i] // managed by clear_filters() 00096 //_filt_total // managed by clear_filters() 00097 clear_filters(); 00098 //_filt_value // nothing to manage 00099 _errmsg = strfree(_errmsg); 00100 } 00101 00102 // GET TYPE OF BROWSER 00103 int Fl_Native_File_Chooser::type() const { 00104 return(_btype); 00105 } 00106 00107 // SET OPTIONS 00108 void Fl_Native_File_Chooser::options(int val) { 00109 _options = val; 00110 } 00111 00112 // GET OPTIONS 00113 int Fl_Native_File_Chooser::options() const { 00114 return(_options); 00115 } 00116 00117 // SHOW THE BROWSER WINDOW 00118 // Returns: 00119 // 0 - user picked a file 00120 // 1 - user cancelled 00121 // -1 - failed; errmsg() has reason 00122 // 00123 int Fl_Native_File_Chooser::show() { 00124 00125 // Make sure fltk interface updates before posting our dialog 00126 Fl::flush(); 00127 00128 // POST BROWSER 00129 int err = post(); 00130 00131 _filt_total = 0; 00132 00133 return(err); 00134 } 00135 00136 // SET ERROR MESSAGE 00137 // Internal use only. 00138 // 00139 void Fl_Native_File_Chooser::errmsg(const char *msg) { 00140 _errmsg = strfree(_errmsg); 00141 _errmsg = strnew(msg); 00142 } 00143 00144 // RETURN ERROR MESSAGE 00145 const char *Fl_Native_File_Chooser::errmsg() const { 00146 return(_errmsg ? _errmsg : "No error"); 00147 } 00148 00149 // GET FILENAME 00150 const char* Fl_Native_File_Chooser::filename() const { 00151 if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]); 00152 return(""); 00153 } 00154 00155 // GET FILENAME FROM LIST OF FILENAMES 00156 const char* Fl_Native_File_Chooser::filename(int i) const { 00157 if ( _pathnames && i < _tpathnames ) return(_pathnames[i]); 00158 return(""); 00159 } 00160 00161 // GET TOTAL FILENAMES CHOSEN 00162 int Fl_Native_File_Chooser::count() const { 00163 return(_tpathnames); 00164 } 00165 00166 // PRESET PATHNAME 00167 // Value can be NULL for none. 00168 // 00169 void Fl_Native_File_Chooser::directory(const char *val) { 00170 _directory = strfree(_directory); 00171 _directory = strnew(val); 00172 } 00173 00174 // GET PRESET PATHNAME 00175 // Returned value can be NULL if none set. 00176 // 00177 const char* Fl_Native_File_Chooser::directory() const { 00178 return(_directory); 00179 } 00180 00181 // SET TITLE 00182 // Value can be NULL if no title desired. 00183 // 00184 void Fl_Native_File_Chooser::title(const char *val) { 00185 _title = strfree(_title); 00186 _title = strnew(val); 00187 } 00188 00189 // GET TITLE 00190 // Returned value can be NULL if none set. 00191 // 00192 const char *Fl_Native_File_Chooser::title() const { 00193 return(_title); 00194 } 00195 00196 // SET FILTER 00197 // Can be NULL if no filter needed 00198 // 00199 void Fl_Native_File_Chooser::filter(const char *val) { 00200 _filter = strfree(_filter); 00201 _filter = strnew(val); 00202 00203 // Parse filter user specified 00204 // IN: _filter = "C Files\t*.{cxx,h}\nText Files\t*.txt" 00205 // OUT: _filt_names = "C Files\tText Files" 00206 // _filt_patt[0] = "*.{cxx,h}" 00207 // _filt_patt[1] = "*.txt" 00208 // _filt_total = 2 00209 // 00210 parse_filter(_filter); 00211 } 00212 00213 // GET FILTER 00214 // Returned value can be NULL if none set. 00215 // 00216 const char *Fl_Native_File_Chooser::filter() const { 00217 return(_filter); 00218 } 00219 00220 // CLEAR ALL FILTERS 00221 // Internal use only. 00222 // 00223 void Fl_Native_File_Chooser::clear_filters() { 00224 _filt_names = strfree(_filt_names); 00225 for (int i=0; i<_filt_total; i++) { 00226 _filt_patt[i] = strfree(_filt_patt[i]); 00227 } 00228 _filt_total = 0; 00229 } 00230 00231 // PARSE USER'S FILTER SPEC 00232 // Parses user specified filter ('in'), 00233 // breaks out into _filt_patt[], _filt_names, and _filt_total. 00234 // 00235 // Handles: 00236 // IN: OUT:_filt_names OUT: _filt_patt 00237 // ------------------------------------ ------------------ --------------- 00238 // "*.{ma,mb}" "*.{ma,mb} Files" "*.{ma,mb}" 00239 // "*.[abc]" "*.[abc] Files" "*.[abc]" 00240 // "*.txt" "*.txt Files" "*.c" 00241 // "C Files\t*.[ch]" "C Files" "*.[ch]" 00242 // "C Files\t*.[ch]\nText Files\t*.cxx" "C Files" "*.[ch]" 00243 // 00244 // Parsing Mode: 00245 // IN:"C Files\t*.{cxx,h}" 00246 // ||||||| ||||||||| 00247 // mode: nnnnnnn wwwwwwwww 00248 // \_____/ \_______/ 00249 // Name Wildcard 00250 // 00251 void Fl_Native_File_Chooser::parse_filter(const char *in) { 00252 clear_filters(); 00253 if ( ! in ) return; 00254 int has_name = strchr(in, '\t') ? 1 : 0; 00255 00256 char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard 00257 char wildcard[1024] = ""; // parsed wildcard 00258 char name[1024] = ""; 00259 00260 // Parse filter user specified 00261 for ( ; 1; in++ ) { 00262 00266 00267 switch (*in) { 00268 // FINISHED PARSING NAME? 00269 case '\t': 00270 if ( mode != 'n' ) goto regchar; 00271 mode = 'w'; 00272 break; 00273 00274 // ESCAPE NEXT CHAR 00275 case '\\': 00276 ++in; 00277 goto regchar; 00278 00279 // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? 00280 case '\r': 00281 case '\n': 00282 case '\0': 00283 // TITLE 00284 // If user didn't specify a name, make one 00285 // 00286 if ( name[0] == '\0' ) { 00287 sprintf(name, "%.*s Files", (int)sizeof(name)-10, wildcard); 00288 } 00289 // APPEND NEW FILTER TO LIST 00290 if ( wildcard[0] ) { 00291 // Add to filtername list 00292 // Tab delimit if more than one. We later break 00293 // tab delimited string into CFArray with 00294 // CFStringCreateArrayBySeparatingStrings() 00295 // 00296 if ( _filt_total ) { 00297 _filt_names = strapp(_filt_names, "\t"); 00298 } 00299 _filt_names = strapp(_filt_names, name); 00300 00301 // Add filter to the pattern array 00302 _filt_patt[_filt_total++] = strnew(wildcard); 00303 } 00304 // RESET 00305 wildcard[0] = name[0] = '\0'; 00306 mode = strchr(in, '\t') ? 'n' : 'w'; 00307 // DONE? 00308 if ( *in == '\0' ) return; // done 00309 else continue; // not done yet, more filters 00310 00311 // Parse all other chars 00312 default: // handle all non-special chars 00313 regchar: // handle regular char 00314 switch ( mode ) { 00315 case 'n': chrcat(name, *in); continue; 00316 case 'w': chrcat(wildcard, *in); continue; 00317 } 00318 break; 00319 } 00320 } 00321 //NOTREACHED 00322 } 00323 00324 // SET PRESET FILE 00325 // Value can be NULL for none. 00326 // 00327 void Fl_Native_File_Chooser::preset_file(const char* val) { 00328 _preset_file = strfree(_preset_file); 00329 _preset_file = strnew(val); 00330 } 00331 00332 // PRESET FILE 00333 // Returned value can be NULL if none set. 00334 // 00335 const char* Fl_Native_File_Chooser::preset_file() { 00336 return(_preset_file); 00337 } 00338 00339 #import <Cocoa/Cocoa.h> 00340 #define UNLIKELYPREFIX "___fl_very_unlikely_prefix_" 00341 #ifndef MAC_OS_X_VERSION_10_6 00342 #define MAC_OS_X_VERSION_10_6 1060 00343 #endif 00344 00345 int Fl_Native_File_Chooser::get_saveas_basename(void) { 00346 char *q = strdup( [[(NSSavePanel*)_panel filename] fileSystemRepresentation] ); 00347 id delegate = [(NSSavePanel*)_panel delegate]; 00348 if (delegate != nil) { 00349 const char *d = [[(NSSavePanel*)_panel directory] fileSystemRepresentation]; 00350 int l = strlen(d) + 1; 00351 int lu = strlen(UNLIKELYPREFIX); 00352 // Remove UNLIKELYPREFIX between directory and filename parts 00353 memmove(q + l, q + l + lu, strlen(q + l + lu) + 1); 00354 } 00355 set_single_pathname( q ); 00356 free(q); 00357 return 0; 00358 } 00359 00360 // SET THE TYPE OF BROWSER 00361 void Fl_Native_File_Chooser::type(int val) { 00362 _btype = val; 00363 switch (_btype) { 00364 case BROWSE_FILE: 00365 case BROWSE_MULTI_FILE: 00366 case BROWSE_DIRECTORY: 00367 case BROWSE_MULTI_DIRECTORY: 00368 _panel = [NSOpenPanel openPanel]; 00369 break; 00370 case BROWSE_SAVE_DIRECTORY: 00371 case BROWSE_SAVE_FILE: 00372 _panel = [NSSavePanel savePanel]; 00373 break; 00374 } 00375 } 00376 00377 @interface FLopenDelegate : NSObject 00378 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 00379 <NSOpenSavePanelDelegate> 00380 #endif 00381 { 00382 NSPopUpButton *nspopup; 00383 char **filter_pattern; 00384 } 00385 - (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern; 00386 - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename; 00387 @end 00388 @implementation FLopenDelegate 00389 - (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern 00390 { 00391 nspopup = popup; 00392 filter_pattern = pattern; 00393 return self; 00394 } 00395 - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename 00396 { 00397 if ( [nspopup indexOfSelectedItem] == [nspopup numberOfItems] - 1) return YES; 00398 const char *pathname = [filename fileSystemRepresentation]; 00399 if ( fl_filename_isdir(pathname) ) return YES; 00400 if ( fl_filename_match(pathname, filter_pattern[ [nspopup indexOfSelectedItem] ]) ) return YES; 00401 return NO; 00402 } 00403 @end 00404 00405 @interface FLsaveDelegate : NSObject 00406 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 00407 <NSOpenSavePanelDelegate> 00408 #endif 00409 { 00410 } 00411 - (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag; 00412 @end 00413 @implementation FLsaveDelegate 00414 - (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag 00415 { 00416 if (! okFlag) return filename; 00417 // User has clicked save, and no overwrite confirmation should occur. 00418 // To get the latter, we need to change the name we return (hence the prefix): 00419 return [@ UNLIKELYPREFIX stringByAppendingString:filename]; 00420 } 00421 @end 00422 00423 static NSPopUpButton *createPopupAccessory(NSSavePanel *panel, const char *filter, const char *title, int rank) 00424 { 00425 NSPopUpButton *popup; 00426 NSRect rectview = NSMakeRect(5, 5, 350, 30 ); 00427 NSView *view = [[[NSView alloc] initWithFrame:rectview] autorelease]; 00428 NSRect rectbox = NSMakeRect(0, 3, 50, 1 ); 00429 NSBox *box = [[[NSBox alloc] initWithFrame:rectbox] autorelease]; 00430 NSRect rectpop = NSMakeRect(60, 0, 250, 30 ); 00431 popup = [[[NSPopUpButton alloc ] initWithFrame:rectpop pullsDown:NO] autorelease]; 00432 [view addSubview:box]; 00433 [view addSubview:popup]; 00434 [box setBorderType:NSNoBorder]; 00435 NSString *nstitle = [[NSString alloc] initWithUTF8String:title]; 00436 [box setTitle:nstitle]; 00437 [nstitle release]; 00438 NSFont *font = [NSFont controlContentFontOfSize:NSRegularControlSize]; 00439 [box setTitleFont:font]; 00440 [box sizeToFit]; 00441 CFStringRef tab = CFSTR("\n"); 00442 CFStringRef tmp_cfs; 00443 tmp_cfs = CFStringCreateWithCString(NULL, filter, kCFStringEncodingASCII); 00444 CFArrayRef array = CFStringCreateArrayBySeparatingStrings(NULL, tmp_cfs, tab); 00445 CFRelease(tmp_cfs); 00446 CFRelease(tab); 00447 [popup addItemsWithTitles:(NSArray*)array]; 00448 NSMenuItem *item = [popup itemWithTitle:@""]; 00449 if (item) [popup removeItemWithTitle:@""]; 00450 CFRelease(array); 00451 [popup selectItemAtIndex:rank]; 00452 [panel setAccessoryView:view]; 00453 return popup; 00454 } 00455 00456 // POST BROWSER 00457 // Internal use only. 00458 // Assumes '_opts' has been initialized. 00459 // 00460 // Returns: 00461 // 0 - user picked a file 00462 // 1 - user cancelled 00463 // -1 - failed; errmsg() has reason 00464 // 00465 int Fl_Native_File_Chooser::post() { 00466 // INITIALIZE BROWSER 00467 if ( _filt_total == 0 ) { // Make sure they match 00468 _filt_value = 0; // TBD: move to someplace more logical? 00469 } 00470 NSAutoreleasePool *localPool; 00471 localPool = [[NSAutoreleasePool alloc] init]; 00472 int retval; 00473 NSString *nstitle = [NSString stringWithUTF8String: (_title ? _title : "No Title")]; 00474 [(NSSavePanel*)_panel setTitle:nstitle]; 00475 switch (_btype) { 00476 case BROWSE_MULTI_FILE: 00477 [(NSOpenPanel*)_panel setAllowsMultipleSelection:YES]; 00478 break; 00479 case BROWSE_MULTI_DIRECTORY: 00480 [(NSOpenPanel*)_panel setAllowsMultipleSelection:YES]; 00481 case BROWSE_DIRECTORY: 00482 [(NSOpenPanel*)_panel setCanChooseDirectories:YES]; 00483 break; 00484 case BROWSE_SAVE_DIRECTORY: 00485 [(NSSavePanel*)_panel setCanCreateDirectories:YES]; 00486 break; 00487 } 00488 00489 // SHOW THE DIALOG 00490 if ( [(NSSavePanel*)_panel isKindOfClass:[NSOpenPanel class]] ) { 00491 NSPopUpButton *popup = nil; 00492 if (_filt_total) { 00493 char *p; p = _filter; 00494 char *q; q = new char[strlen(p) + 1]; 00495 char *r, *s, *t; 00496 t = q; 00497 do { // copy to t what is in _filter removing what is between \t and \n, if any 00498 r = strchr(p, '\n'); 00499 if (!r) r = p + strlen(p) - 1; 00500 s = strchr(p, '\t'); 00501 if (s && s < r) { memcpy(q, p, s - p); q += s - p; *(q++) = '\n'; } 00502 else { memcpy(q, p, r - p + 1); q += r - p + 1; } 00503 *q = 0; 00504 p = r + 1; 00505 } while(*p); 00506 popup = createPopupAccessory((NSSavePanel*)_panel, t, "Enable:", 0); 00507 delete t; 00508 [[popup menu] addItem:[NSMenuItem separatorItem]]; 00509 [popup addItemWithTitle:@"All Documents"]; 00510 [popup setAction:@selector(validateVisibleColumns)]; 00511 [popup setTarget:(NSObject*)_panel]; 00512 static FLopenDelegate *openDelegate = nil; 00513 if (openDelegate == nil) { 00514 // not to be ever freed 00515 openDelegate = [[FLopenDelegate alloc] init]; 00516 } 00517 [openDelegate setPopup:popup filter_pattern:_filt_patt]; 00518 [(NSOpenPanel*)_panel setDelegate:openDelegate]; 00519 } 00520 NSString *dir = nil; 00521 NSString *fname = nil; 00522 NSString *preset = nil; 00523 if (_preset_file) { 00524 preset = [[NSString alloc] initWithUTF8String:_preset_file]; 00525 if (strchr(_preset_file, '/') != NULL) 00526 dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]]; 00527 fname = [preset lastPathComponent]; 00528 } 00529 if (_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory]; 00530 retval = [(NSOpenPanel*)_panel runModalForDirectory:dir file:fname types:nil]; 00531 [dir release]; 00532 [preset release]; 00533 if (_filt_total) { 00534 _filt_value = [popup indexOfSelectedItem]; 00535 } 00536 if ( retval == NSOKButton ) { 00537 clear_pathnames(); 00538 NSArray *array = [(NSOpenPanel*)_panel filenames]; 00539 _tpathnames = [array count]; 00540 _pathnames = new char*[_tpathnames]; 00541 for(int i = 0; i < _tpathnames; i++) { 00542 _pathnames[i] = strnew([(NSString*)[array objectAtIndex:i] fileSystemRepresentation]); 00543 } 00544 } 00545 } 00546 else { 00547 NSString *dir = nil; 00548 NSString *fname = nil; 00549 NSString *preset = nil; 00550 NSPopUpButton *popup = nil; 00551 [(NSSavePanel*)_panel setAllowsOtherFileTypes:YES]; 00552 if ( !(_options & SAVEAS_CONFIRM) ) { 00553 static FLsaveDelegate *saveDelegate = nil; 00554 if (saveDelegate == nil)saveDelegate = [[FLsaveDelegate alloc] init]; // not to be ever freed 00555 [(NSSavePanel*)_panel setDelegate:saveDelegate]; 00556 } 00557 if (_preset_file) { 00558 preset = [[NSString alloc] initWithUTF8String:_preset_file]; 00559 if (strchr(_preset_file, '/') != NULL) { 00560 dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]]; 00561 } 00562 fname = [preset lastPathComponent]; 00563 } 00564 if (_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory]; 00565 if (_filt_total) { 00566 popup = createPopupAccessory((NSSavePanel*)_panel, _filter, "Format:", _filt_value); 00567 } 00568 retval = [(NSSavePanel*)_panel runModalForDirectory:dir file:fname]; 00569 if (_filt_total) { 00570 _filt_value = [popup indexOfSelectedItem]; 00571 } 00572 [dir release]; 00573 [preset release]; 00574 if ( retval == NSOKButton ) get_saveas_basename(); 00575 } 00576 [localPool release]; 00577 return (retval == NSOKButton ? 0 : 1); 00578 } 00579 00580 #endif // __APPLE__ 00581 00582 // 00583 // End of "$Id: Fl_Native_File_Chooser_MAC.mm 8062 2010-12-19 17:57:19Z manolo $". 00584 //