PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/fltk/src/Fl_Native_File_Chooser_MAC.cxx

http://luafltk.googlecode.com/
C++ | 583 lines | 378 code | 51 blank | 154 comment | 63 complexity | 1e233480c3426fa425940dc8ef3e1dad MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-3.0, 0BSD
  1. // "$Id: Fl_Native_File_Chooser_MAC.cxx 7354 2010-03-29 11:07:29Z matt $"
  2. //
  3. // FLTK native OS file chooser widget
  4. //
  5. // Copyright 1998-2005 by Bill Spitzak and others.
  6. // Copyright 2004 Greg Ercolano.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems to:
  24. //
  25. // http://www.fltk.org/str.php
  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. #ifndef FL_DOXYGEN // PREVENT DOXYGEN'S USE OF THIS FILE
  32. #include "Fl_Native_File_Chooser_common.cxx" // strnew/strfree/strapp/chrcat
  33. #include <libgen.h> // dirname(3)
  34. #include <sys/types.h> // stat(2)
  35. #include <sys/stat.h> // stat(2)
  36. #include <FL/Fl.H>
  37. #include <FL/Fl_Native_File_Chooser.H>
  38. #include <FL/filename.H>
  39. // FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS
  40. void Fl_Native_File_Chooser::clear_pathnames() {
  41. if ( _pathnames ) {
  42. while ( --_tpathnames >= 0 ) {
  43. _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]);
  44. }
  45. delete [] _pathnames;
  46. _pathnames = NULL;
  47. }
  48. _tpathnames = 0;
  49. }
  50. // SET A SINGLE PATHNAME
  51. void Fl_Native_File_Chooser::set_single_pathname(const char *s) {
  52. clear_pathnames();
  53. _pathnames = new char*[1];
  54. _pathnames[0] = strnew(s);
  55. _tpathnames = 1;
  56. }
  57. // CONSTRUCTOR
  58. Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
  59. _btype = val;
  60. _panel = NULL;
  61. _options = NO_OPTIONS;
  62. _pathnames = NULL;
  63. _tpathnames = 0;
  64. _title = NULL;
  65. _filter = NULL;
  66. _filt_names = NULL;
  67. memset(_filt_patt, 0, sizeof(char*) * MAXFILTERS);
  68. _filt_total = 0;
  69. _filt_value = 0;
  70. _directory = NULL;
  71. _preset_file = NULL;
  72. _errmsg = NULL;
  73. }
  74. // DESTRUCTOR
  75. Fl_Native_File_Chooser::~Fl_Native_File_Chooser() {
  76. // _opts // nothing to manage
  77. // _options // nothing to manage
  78. // _keepstate // nothing to manage
  79. // _tempitem // nothing to manage
  80. clear_pathnames();
  81. _directory = strfree(_directory);
  82. _title = strfree(_title);
  83. _preset_file = strfree(_preset_file);
  84. _filter = strfree(_filter);
  85. //_filt_names // managed by clear_filters()
  86. //_filt_patt[i] // managed by clear_filters()
  87. //_filt_total // managed by clear_filters()
  88. clear_filters();
  89. //_filt_value // nothing to manage
  90. _errmsg = strfree(_errmsg);
  91. }
  92. // GET TYPE OF BROWSER
  93. int Fl_Native_File_Chooser::type() const {
  94. return(_btype);
  95. }
  96. // SET OPTIONS
  97. void Fl_Native_File_Chooser::options(int val) {
  98. _options = val;
  99. }
  100. // GET OPTIONS
  101. int Fl_Native_File_Chooser::options() const {
  102. return(_options);
  103. }
  104. // SHOW THE BROWSER WINDOW
  105. // Returns:
  106. // 0 - user picked a file
  107. // 1 - user cancelled
  108. // -1 - failed; errmsg() has reason
  109. //
  110. int Fl_Native_File_Chooser::show() {
  111. // Make sure fltk interface updates before posting our dialog
  112. Fl::flush();
  113. // POST BROWSER
  114. int err = post();
  115. _filt_total = 0;
  116. return(err);
  117. }
  118. // SET ERROR MESSAGE
  119. // Internal use only.
  120. //
  121. void Fl_Native_File_Chooser::errmsg(const char *msg) {
  122. _errmsg = strfree(_errmsg);
  123. _errmsg = strnew(msg);
  124. }
  125. // RETURN ERROR MESSAGE
  126. const char *Fl_Native_File_Chooser::errmsg() const {
  127. return(_errmsg ? _errmsg : "No error");
  128. }
  129. // GET FILENAME
  130. const char* Fl_Native_File_Chooser::filename() const {
  131. if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]);
  132. return("");
  133. }
  134. // GET FILENAME FROM LIST OF FILENAMES
  135. const char* Fl_Native_File_Chooser::filename(int i) const {
  136. if ( _pathnames && i < _tpathnames ) return(_pathnames[i]);
  137. return("");
  138. }
  139. // GET TOTAL FILENAMES CHOSEN
  140. int Fl_Native_File_Chooser::count() const {
  141. return(_tpathnames);
  142. }
  143. // PRESET PATHNAME
  144. // Value can be NULL for none.
  145. //
  146. void Fl_Native_File_Chooser::directory(const char *val) {
  147. _directory = strfree(_directory);
  148. _directory = strnew(val);
  149. }
  150. // GET PRESET PATHNAME
  151. // Returned value can be NULL if none set.
  152. //
  153. const char* Fl_Native_File_Chooser::directory() const {
  154. return(_directory);
  155. }
  156. // SET TITLE
  157. // Value can be NULL if no title desired.
  158. //
  159. void Fl_Native_File_Chooser::title(const char *val) {
  160. _title = strfree(_title);
  161. _title = strnew(val);
  162. }
  163. // GET TITLE
  164. // Returned value can be NULL if none set.
  165. //
  166. const char *Fl_Native_File_Chooser::title() const {
  167. return(_title);
  168. }
  169. // SET FILTER
  170. // Can be NULL if no filter needed
  171. //
  172. void Fl_Native_File_Chooser::filter(const char *val) {
  173. _filter = strfree(_filter);
  174. _filter = strnew(val);
  175. // Parse filter user specified
  176. // IN: _filter = "C Files\t*.{cxx,h}\nText Files\t*.txt"
  177. // OUT: _filt_names = "C Files\tText Files"
  178. // _filt_patt[0] = "*.{cxx,h}"
  179. // _filt_patt[1] = "*.txt"
  180. // _filt_total = 2
  181. //
  182. parse_filter(_filter);
  183. }
  184. // GET FILTER
  185. // Returned value can be NULL if none set.
  186. //
  187. const char *Fl_Native_File_Chooser::filter() const {
  188. return(_filter);
  189. }
  190. // CLEAR ALL FILTERS
  191. // Internal use only.
  192. //
  193. void Fl_Native_File_Chooser::clear_filters() {
  194. _filt_names = strfree(_filt_names);
  195. for (int i=0; i<_filt_total; i++) {
  196. _filt_patt[i] = strfree(_filt_patt[i]);
  197. }
  198. _filt_total = 0;
  199. }
  200. // PARSE USER'S FILTER SPEC
  201. // Parses user specified filter ('in'),
  202. // breaks out into _filt_patt[], _filt_names, and _filt_total.
  203. //
  204. // Handles:
  205. // IN: OUT:_filt_names OUT: _filt_patt
  206. // ------------------------------------ ------------------ ---------------
  207. // "*.{ma,mb}" "*.{ma,mb} Files" "*.{ma,mb}"
  208. // "*.[abc]" "*.[abc] Files" "*.[abc]"
  209. // "*.txt" "*.txt Files" "*.c"
  210. // "C Files\t*.[ch]" "C Files" "*.[ch]"
  211. // "C Files\t*.[ch]\nText Files\t*.cxx" "C Files" "*.[ch]"
  212. //
  213. // Parsing Mode:
  214. // IN:"C Files\t*.{cxx,h}"
  215. // ||||||| |||||||||
  216. // mode: nnnnnnn wwwwwwwww
  217. // \_____/ \_______/
  218. // Name Wildcard
  219. //
  220. void Fl_Native_File_Chooser::parse_filter(const char *in) {
  221. clear_filters();
  222. if ( ! in ) return;
  223. int has_name = strchr(in, '\t') ? 1 : 0;
  224. char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard
  225. char wildcard[1024] = ""; // parsed wildcard
  226. char name[1024] = "";
  227. // Parse filter user specified
  228. for ( ; 1; in++ ) {
  229. //// DEBUG
  230. //// printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n",
  231. //// *in, mode, name, wildcard);
  232. switch (*in) {
  233. // FINISHED PARSING NAME?
  234. case '\t':
  235. if ( mode != 'n' ) goto regchar;
  236. mode = 'w';
  237. break;
  238. // ESCAPE NEXT CHAR
  239. case '\\':
  240. ++in;
  241. goto regchar;
  242. // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
  243. case '\r':
  244. case '\n':
  245. case '\0':
  246. // TITLE
  247. // If user didn't specify a name, make one
  248. //
  249. if ( name[0] == '\0' ) {
  250. sprintf(name, "%.*s Files", (int)sizeof(name)-10, wildcard);
  251. }
  252. // APPEND NEW FILTER TO LIST
  253. if ( wildcard[0] ) {
  254. // Add to filtername list
  255. // Tab delimit if more than one. We later break
  256. // tab delimited string into CFArray with
  257. // CFStringCreateArrayBySeparatingStrings()
  258. //
  259. if ( _filt_total ) {
  260. _filt_names = strapp(_filt_names, "\t");
  261. }
  262. _filt_names = strapp(_filt_names, name);
  263. // Add filter to the pattern array
  264. _filt_patt[_filt_total++] = strnew(wildcard);
  265. }
  266. // RESET
  267. wildcard[0] = name[0] = '\0';
  268. mode = strchr(in, '\t') ? 'n' : 'w';
  269. // DONE?
  270. if ( *in == '\0' ) return; // done
  271. else continue; // not done yet, more filters
  272. // Parse all other chars
  273. default: // handle all non-special chars
  274. regchar: // handle regular char
  275. switch ( mode ) {
  276. case 'n': chrcat(name, *in); continue;
  277. case 'w': chrcat(wildcard, *in); continue;
  278. }
  279. break;
  280. }
  281. }
  282. //NOTREACHED
  283. }
  284. // SET PRESET FILE
  285. // Value can be NULL for none.
  286. //
  287. void Fl_Native_File_Chooser::preset_file(const char* val) {
  288. _preset_file = strfree(_preset_file);
  289. _preset_file = strnew(val);
  290. }
  291. // PRESET FILE
  292. // Returned value can be NULL if none set.
  293. //
  294. const char* Fl_Native_File_Chooser::preset_file() {
  295. return(_preset_file);
  296. }
  297. #import <Cocoa/Cocoa.h>
  298. #define UNLIKELYPREFIX "___fl_very_unlikely_prefix_"
  299. #ifndef MAC_OS_X_VERSION_10_6
  300. #define MAC_OS_X_VERSION_10_6 1060
  301. #endif
  302. int Fl_Native_File_Chooser::get_saveas_basename(void) {
  303. char *q = strdup( [[(NSSavePanel*)_panel filename] fileSystemRepresentation] );
  304. id delegate = [(NSSavePanel*)_panel delegate];
  305. if (delegate != nil) {
  306. const char *d = [[(NSSavePanel*)_panel directory] fileSystemRepresentation];
  307. int l = strlen(d) + 1;
  308. int lu = strlen(UNLIKELYPREFIX);
  309. // Remove UNLIKELYPREFIX between directory and filename parts
  310. memmove(q + l, q + l + lu, strlen(q + l + lu) + 1);
  311. }
  312. set_single_pathname( q );
  313. free(q);
  314. return 0;
  315. }
  316. // SET THE TYPE OF BROWSER
  317. void Fl_Native_File_Chooser::type(int val) {
  318. _btype = val;
  319. switch (_btype) {
  320. case BROWSE_FILE:
  321. case BROWSE_MULTI_FILE:
  322. case BROWSE_DIRECTORY:
  323. case BROWSE_MULTI_DIRECTORY:
  324. _panel = [NSOpenPanel openPanel];
  325. break;
  326. case BROWSE_SAVE_DIRECTORY:
  327. case BROWSE_SAVE_FILE:
  328. _panel = [NSSavePanel savePanel];
  329. break;
  330. }
  331. }
  332. @interface FLopenDelegate : NSObject
  333. #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
  334. <NSOpenSavePanelDelegate>
  335. #endif
  336. {
  337. NSPopUpButton *nspopup;
  338. char **filter_pattern;
  339. }
  340. - (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern;
  341. - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
  342. @end
  343. @implementation FLopenDelegate
  344. - (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern
  345. {
  346. nspopup = popup;
  347. filter_pattern = pattern;
  348. return self;
  349. }
  350. - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
  351. {
  352. if ( [nspopup indexOfSelectedItem] == [nspopup numberOfItems] - 1) return YES;
  353. const char *pathname = [filename fileSystemRepresentation];
  354. if ( fl_filename_isdir(pathname) ) return YES;
  355. if ( fl_filename_match(pathname, filter_pattern[ [nspopup indexOfSelectedItem] ]) ) return YES;
  356. return NO;
  357. }
  358. @end
  359. @interface FLsaveDelegate : NSObject
  360. #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
  361. <NSOpenSavePanelDelegate>
  362. #endif
  363. {
  364. }
  365. - (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag;
  366. @end
  367. @implementation FLsaveDelegate
  368. - (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag
  369. {
  370. if (! okFlag) return filename;
  371. // User has clicked save, and no overwrite confirmation should occur.
  372. // To get the latter, we need to change the name we return (hence the prefix):
  373. return [@ UNLIKELYPREFIX stringByAppendingString:filename];
  374. }
  375. @end
  376. static NSPopUpButton *createPopupAccessory(NSSavePanel *panel, const char *filter, const char *title, int rank)
  377. {
  378. NSPopUpButton *popup;
  379. NSRect rectview = NSMakeRect(5, 5, 350, 30 );
  380. NSView *view = [[[NSView alloc] initWithFrame:rectview] autorelease];
  381. NSRect rectbox = NSMakeRect(0, 3, 50, 1 );
  382. NSBox *box = [[[NSBox alloc] initWithFrame:rectbox] autorelease];
  383. NSRect rectpop = NSMakeRect(60, 0, 250, 30 );
  384. popup = [[[NSPopUpButton alloc ] initWithFrame:rectpop pullsDown:NO] autorelease];
  385. [view addSubview:box];
  386. [view addSubview:popup];
  387. [box setBorderType:NSNoBorder];
  388. NSString *nstitle = [[NSString alloc] initWithUTF8String:title];
  389. [box setTitle:nstitle];
  390. [nstitle release];
  391. NSFont *font = [NSFont controlContentFontOfSize:NSRegularControlSize];
  392. [box setTitleFont:font];
  393. [box sizeToFit];
  394. CFStringRef tab = CFSTR("\n");
  395. CFStringRef tmp_cfs;
  396. tmp_cfs = CFStringCreateWithCString(NULL, filter, kCFStringEncodingASCII);
  397. CFArrayRef array = CFStringCreateArrayBySeparatingStrings(NULL, tmp_cfs, tab);
  398. CFRelease(tmp_cfs);
  399. CFRelease(tab);
  400. [popup addItemsWithTitles:(NSArray*)array];
  401. NSMenuItem *item = [popup itemWithTitle:@""];
  402. if (item) [popup removeItemWithTitle:@""];
  403. CFRelease(array);
  404. [popup selectItemAtIndex:rank];
  405. [panel setAccessoryView:view];
  406. return popup;
  407. }
  408. // POST BROWSER
  409. // Internal use only.
  410. // Assumes '_opts' has been initialized.
  411. //
  412. // Returns:
  413. // 0 - user picked a file
  414. // 1 - user cancelled
  415. // -1 - failed; errmsg() has reason
  416. //
  417. int Fl_Native_File_Chooser::post() {
  418. // INITIALIZE BROWSER
  419. if ( _filt_total == 0 ) { // Make sure they match
  420. _filt_value = 0; // TBD: move to someplace more logical?
  421. }
  422. NSAutoreleasePool *localPool;
  423. localPool = [[NSAutoreleasePool alloc] init];
  424. int retval;
  425. NSString *nstitle = [NSString stringWithUTF8String: (_title ? _title : "No Title")];
  426. [(NSSavePanel*)_panel setTitle:nstitle];
  427. switch (_btype) {
  428. case BROWSE_MULTI_FILE:
  429. [(NSOpenPanel*)_panel setAllowsMultipleSelection:YES];
  430. break;
  431. case BROWSE_MULTI_DIRECTORY:
  432. [(NSOpenPanel*)_panel setAllowsMultipleSelection:YES];
  433. case BROWSE_DIRECTORY:
  434. [(NSOpenPanel*)_panel setCanChooseDirectories:YES];
  435. break;
  436. case BROWSE_SAVE_DIRECTORY:
  437. [(NSSavePanel*)_panel setCanCreateDirectories:YES];
  438. break;
  439. }
  440. // SHOW THE DIALOG
  441. if ( [(NSSavePanel*)_panel isKindOfClass:[NSOpenPanel class]] ) {
  442. NSPopUpButton *popup = nil;
  443. if (_filt_total) {
  444. char *p; p = _filter;
  445. char *q; q = new char[strlen(p) + 1];
  446. char *r, *s, *t;
  447. t = q;
  448. do { // copy to t what is in _filter removing what is between \t and \n, if any
  449. r = strchr(p, '\n');
  450. if (!r) r = p + strlen(p) - 1;
  451. s = strchr(p, '\t');
  452. if (s && s < r) { memcpy(q, p, s - p); q += s - p; *(q++) = '\n'; }
  453. else { memcpy(q, p, r - p + 1); q += r - p + 1; }
  454. *q = 0;
  455. p = r + 1;
  456. } while(*p);
  457. popup = createPopupAccessory((NSSavePanel*)_panel, t, "Enable:", 0);
  458. delete t;
  459. [[popup menu] addItem:[NSMenuItem separatorItem]];
  460. [popup addItemWithTitle:@"All Documents"];
  461. [popup setAction:@selector(validateVisibleColumns)];
  462. [popup setTarget:(NSObject*)_panel];
  463. static FLopenDelegate *openDelegate = nil;
  464. if (openDelegate == nil) {
  465. // not to be ever freed
  466. openDelegate = [[FLopenDelegate alloc] init];
  467. }
  468. [openDelegate setPopup:popup filter_pattern:_filt_patt];
  469. [(NSOpenPanel*)_panel setDelegate:openDelegate];
  470. }
  471. NSString *dir = nil;
  472. NSString *fname = nil;
  473. NSString *preset = nil;
  474. if (_preset_file) {
  475. preset = [[NSString alloc] initWithUTF8String:_preset_file];
  476. if (strchr(_preset_file, '/') != NULL)
  477. dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]];
  478. fname = [preset lastPathComponent];
  479. }
  480. if (_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory];
  481. retval = [(NSOpenPanel*)_panel runModalForDirectory:dir file:fname types:nil];
  482. [dir release];
  483. [preset release];
  484. if (_filt_total) {
  485. _filt_value = [popup indexOfSelectedItem];
  486. }
  487. if ( retval == NSOKButton ) {
  488. clear_pathnames();
  489. NSArray *array = [(NSOpenPanel*)_panel filenames];
  490. _tpathnames = [array count];
  491. _pathnames = new char*[_tpathnames];
  492. for(int i = 0; i < _tpathnames; i++) {
  493. _pathnames[i] = strnew([(NSString*)[array objectAtIndex:i] fileSystemRepresentation]);
  494. }
  495. }
  496. }
  497. else {
  498. NSString *dir = nil;
  499. NSString *fname = nil;
  500. NSString *preset = nil;
  501. NSPopUpButton *popup = nil;
  502. if ( !(_options & SAVEAS_CONFIRM) ) {
  503. static FLsaveDelegate *saveDelegate = nil;
  504. if (saveDelegate == nil)saveDelegate = [[FLsaveDelegate alloc] init]; // not to be ever freed
  505. [(NSSavePanel*)_panel setDelegate:saveDelegate];
  506. }
  507. if (_preset_file) {
  508. preset = [[NSString alloc] initWithUTF8String:_preset_file];
  509. if (strchr(_preset_file, '/') != NULL) {
  510. dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]];
  511. }
  512. fname = [preset lastPathComponent];
  513. }
  514. if (_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory];
  515. if (_filt_total) {
  516. popup = createPopupAccessory((NSSavePanel*)_panel, _filter, "Format:", _filt_value);
  517. }
  518. retval = [(NSSavePanel*)_panel runModalForDirectory:dir file:fname];
  519. if (_filt_total) {
  520. _filt_value = [popup indexOfSelectedItem];
  521. }
  522. [dir release];
  523. [preset release];
  524. if ( retval == NSOKButton ) get_saveas_basename();
  525. }
  526. [localPool release];
  527. return (retval == NSOKButton ? 0 : 1);
  528. }
  529. #endif /*!FL_DOXYGEN*/
  530. //
  531. // End of "$Id: Fl_Native_File_Chooser_MAC.cxx 7354 2010-03-29 11:07:29Z matt $".
  532. //