1
// "$Id: Fl_Native_File_Chooser_MAC.cxx 7354 2010-03-29 11:07:29Z matt $"
3
// FLTK native OS file chooser widget
5
// Copyright 1998-2005 by Bill Spitzak and others.
6
// Copyright 2004 Greg Ercolano.
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.
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.
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
23
// Please report all bugs and problems to:
25
// http://www.fltk.org/str.php
29
// o When doing 'open file', only dir is preset, not filename.
30
// Possibly 'preset_file' could be used to select the filename.
33
#ifndef FL_DOXYGEN // PREVENT DOXYGEN'S USE OF THIS FILE
35
#include "Fl_Native_File_Chooser_common.cxx" // strnew/strfree/strapp/chrcat
36
#include <libgen.h> // dirname(3)
37
#include <sys/types.h> // stat(2)
38
#include <sys/stat.h> // stat(2)
42
#include <FL/Fl_Native_File_Chooser.H>
43
#include <FL/filename.H>
45
// FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS
46
void Fl_Native_File_Chooser::clear_pathnames() {
48
while ( --_tpathnames >= 0 ) {
49
_pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]);
57
// SET A SINGLE PATHNAME
58
void Fl_Native_File_Chooser::set_single_pathname(const char *s) {
60
_pathnames = new char*[1];
61
_pathnames[0] = strnew(s);
66
Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
69
_options = NO_OPTIONS;
75
memset(_filt_patt, 0, sizeof(char*) * MAXFILTERS);
84
Fl_Native_File_Chooser::~Fl_Native_File_Chooser() {
85
// _opts // nothing to manage
86
// _options // nothing to manage
87
// _keepstate // nothing to manage
88
// _tempitem // nothing to manage
90
_directory = strfree(_directory);
91
_title = strfree(_title);
92
_preset_file = strfree(_preset_file);
93
_filter = strfree(_filter);
94
//_filt_names // managed by clear_filters()
95
//_filt_patt[i] // managed by clear_filters()
96
//_filt_total // managed by clear_filters()
98
//_filt_value // nothing to manage
99
_errmsg = strfree(_errmsg);
102
// GET TYPE OF BROWSER
103
int Fl_Native_File_Chooser::type() const {
108
void Fl_Native_File_Chooser::options(int val) {
113
int Fl_Native_File_Chooser::options() const {
117
// SHOW THE BROWSER WINDOW
119
// 0 - user picked a file
120
// 1 - user cancelled
121
// -1 - failed; errmsg() has reason
123
int Fl_Native_File_Chooser::show() {
125
// Make sure fltk interface updates before posting our dialog
137
// Internal use only.
139
void Fl_Native_File_Chooser::errmsg(const char *msg) {
140
_errmsg = strfree(_errmsg);
141
_errmsg = strnew(msg);
144
// RETURN ERROR MESSAGE
145
const char *Fl_Native_File_Chooser::errmsg() const {
146
return(_errmsg ? _errmsg : "No error");
150
const char* Fl_Native_File_Chooser::filename() const {
151
if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]);
155
// GET FILENAME FROM LIST OF FILENAMES
156
const char* Fl_Native_File_Chooser::filename(int i) const {
157
if ( _pathnames && i < _tpathnames ) return(_pathnames[i]);
161
// GET TOTAL FILENAMES CHOSEN
162
int Fl_Native_File_Chooser::count() const {
167
// Value can be NULL for none.
169
void Fl_Native_File_Chooser::directory(const char *val) {
170
_directory = strfree(_directory);
171
_directory = strnew(val);
174
// GET PRESET PATHNAME
175
// Returned value can be NULL if none set.
177
const char* Fl_Native_File_Chooser::directory() const {
182
// Value can be NULL if no title desired.
184
void Fl_Native_File_Chooser::title(const char *val) {
185
_title = strfree(_title);
186
_title = strnew(val);
190
// Returned value can be NULL if none set.
192
const char *Fl_Native_File_Chooser::title() const {
197
// Can be NULL if no filter needed
199
void Fl_Native_File_Chooser::filter(const char *val) {
200
_filter = strfree(_filter);
201
_filter = strnew(val);
203
// Parse filter user specified
204
// IN: _filter = "C Files\t*.{cxx,h}\nText Files\t*.txt"
205
// OUT: _filt_names = "C Files\tText Files"
206
// _filt_patt[0] = "*.{cxx,h}"
207
// _filt_patt[1] = "*.txt"
210
parse_filter(_filter);
214
// Returned value can be NULL if none set.
216
const char *Fl_Native_File_Chooser::filter() const {
221
// Internal use only.
223
void Fl_Native_File_Chooser::clear_filters() {
224
_filt_names = strfree(_filt_names);
225
for (int i=0; i<_filt_total; i++) {
226
_filt_patt[i] = strfree(_filt_patt[i]);
231
// PARSE USER'S FILTER SPEC
232
// Parses user specified filter ('in'),
233
// breaks out into _filt_patt[], _filt_names, and _filt_total.
236
// IN: OUT:_filt_names OUT: _filt_patt
237
// ------------------------------------ ------------------ ---------------
238
// "*.{ma,mb}" "*.{ma,mb} Files" "*.{ma,mb}"
239
// "*.[abc]" "*.[abc] Files" "*.[abc]"
240
// "*.txt" "*.txt Files" "*.c"
241
// "C Files\t*.[ch]" "C Files" "*.[ch]"
242
// "C Files\t*.[ch]\nText Files\t*.cxx" "C Files" "*.[ch]"
245
// IN:"C Files\t*.{cxx,h}"
247
// mode: nnnnnnn wwwwwwwww
251
void Fl_Native_File_Chooser::parse_filter(const char *in) {
254
int has_name = strchr(in, '\t') ? 1 : 0;
256
char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard
257
char wildcard[1024] = ""; // parsed wildcard
258
char name[1024] = "";
260
// Parse filter user specified
264
//// printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n",
265
//// *in, mode, name, wildcard);
268
// FINISHED PARSING NAME?
270
if ( mode != 'n' ) goto regchar;
279
// FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
284
// If user didn't specify a name, make one
286
if ( name[0] == '\0' ) {
287
sprintf(name, "%.*s Files", (int)sizeof(name)-10, wildcard);
289
// APPEND NEW FILTER TO LIST
291
// Add to filtername list
292
// Tab delimit if more than one. We later break
293
// tab delimited string into CFArray with
294
// CFStringCreateArrayBySeparatingStrings()
297
_filt_names = strapp(_filt_names, "\t");
299
_filt_names = strapp(_filt_names, name);
301
// Add filter to the pattern array
302
_filt_patt[_filt_total++] = strnew(wildcard);
305
wildcard[0] = name[0] = '\0';
306
mode = strchr(in, '\t') ? 'n' : 'w';
308
if ( *in == '\0' ) return; // done
309
else continue; // not done yet, more filters
311
// Parse all other chars
312
default: // handle all non-special chars
313
regchar: // handle regular char
315
case 'n': chrcat(name, *in); continue;
316
case 'w': chrcat(wildcard, *in); continue;
325
// Value can be NULL for none.
327
void Fl_Native_File_Chooser::preset_file(const char* val) {
328
_preset_file = strfree(_preset_file);
329
_preset_file = strnew(val);
333
// Returned value can be NULL if none set.
335
const char* Fl_Native_File_Chooser::preset_file() {
336
return(_preset_file);
339
#import <Cocoa/Cocoa.h>
340
#define UNLIKELYPREFIX "___fl_very_unlikely_prefix_"
341
#ifndef MAC_OS_X_VERSION_10_6
342
#define MAC_OS_X_VERSION_10_6 1060
345
int Fl_Native_File_Chooser::get_saveas_basename(void) {
346
char *q = strdup( [[(NSSavePanel*)_panel filename] fileSystemRepresentation] );
347
id delegate = [(NSSavePanel*)_panel delegate];
348
if (delegate != nil) {
349
const char *d = [[(NSSavePanel*)_panel directory] fileSystemRepresentation];
350
int l = strlen(d) + 1;
351
int lu = strlen(UNLIKELYPREFIX);
352
// Remove UNLIKELYPREFIX between directory and filename parts
353
memmove(q + l, q + l + lu, strlen(q + l + lu) + 1);
355
set_single_pathname( q );
360
// SET THE TYPE OF BROWSER
361
void Fl_Native_File_Chooser::type(int val) {
365
case BROWSE_MULTI_FILE:
366
case BROWSE_DIRECTORY:
367
case BROWSE_MULTI_DIRECTORY:
368
_panel = [NSOpenPanel openPanel];
370
case BROWSE_SAVE_DIRECTORY:
371
case BROWSE_SAVE_FILE:
372
_panel = [NSSavePanel savePanel];
377
@interface FLopenDelegate : NSObject
378
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
379
<NSOpenSavePanelDelegate>
382
NSPopUpButton *nspopup;
383
char **filter_pattern;
385
- (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern;
386
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
388
@implementation FLopenDelegate
389
- (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern
392
filter_pattern = pattern;
395
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
397
if ( [nspopup indexOfSelectedItem] == [nspopup numberOfItems] - 1) return YES;
398
const char *pathname = [filename fileSystemRepresentation];
399
if ( fl_filename_isdir(pathname) ) return YES;
400
if ( fl_filename_match(pathname, filter_pattern[ [nspopup indexOfSelectedItem] ]) ) return YES;
405
@interface FLsaveDelegate : NSObject
406
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
407
<NSOpenSavePanelDelegate>
411
- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag;
413
@implementation FLsaveDelegate
414
- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag
416
if (! okFlag) return filename;
417
// User has clicked save, and no overwrite confirmation should occur.
418
// To get the latter, we need to change the name we return (hence the prefix):
419
return [@ UNLIKELYPREFIX stringByAppendingString:filename];
423
static NSPopUpButton *createPopupAccessory(NSSavePanel *panel, const char *filter, const char *title, int rank)
425
NSPopUpButton *popup;
426
NSRect rectview = NSMakeRect(5, 5, 350, 30 );
427
NSView *view = [[[NSView alloc] initWithFrame:rectview] autorelease];
428
NSRect rectbox = NSMakeRect(0, 3, 50, 1 );
429
NSBox *box = [[[NSBox alloc] initWithFrame:rectbox] autorelease];
430
NSRect rectpop = NSMakeRect(60, 0, 250, 30 );
431
popup = [[[NSPopUpButton alloc ] initWithFrame:rectpop pullsDown:NO] autorelease];
432
[view addSubview:box];
433
[view addSubview:popup];
434
[box setBorderType:NSNoBorder];
435
NSString *nstitle = [[NSString alloc] initWithUTF8String:title];
436
[box setTitle:nstitle];
438
NSFont *font = [NSFont controlContentFontOfSize:NSRegularControlSize];
439
[box setTitleFont:font];
441
CFStringRef tab = CFSTR("\n");
443
tmp_cfs = CFStringCreateWithCString(NULL, filter, kCFStringEncodingASCII);
444
CFArrayRef array = CFStringCreateArrayBySeparatingStrings(NULL, tmp_cfs, tab);
447
[popup addItemsWithTitles:(NSArray*)array];
448
NSMenuItem *item = [popup itemWithTitle:@""];
449
if (item) [popup removeItemWithTitle:@""];
451
[popup selectItemAtIndex:rank];
452
[panel setAccessoryView:view];
457
// Internal use only.
458
// Assumes '_opts' has been initialized.
461
// 0 - user picked a file
462
// 1 - user cancelled
463
// -1 - failed; errmsg() has reason
465
int Fl_Native_File_Chooser::post() {
466
// INITIALIZE BROWSER
467
if ( _filt_total == 0 ) { // Make sure they match
468
_filt_value = 0; // TBD: move to someplace more logical?
470
NSAutoreleasePool *localPool;
471
localPool = [[NSAutoreleasePool alloc] init];
473
NSString *nstitle = [NSString stringWithUTF8String: (_title ? _title : "No Title")];
474
[(NSSavePanel*)_panel setTitle:nstitle];
476
case BROWSE_MULTI_FILE:
477
[(NSOpenPanel*)_panel setAllowsMultipleSelection:YES];
479
case BROWSE_MULTI_DIRECTORY:
480
[(NSOpenPanel*)_panel setAllowsMultipleSelection:YES];
481
case BROWSE_DIRECTORY:
482
[(NSOpenPanel*)_panel setCanChooseDirectories:YES];
484
case BROWSE_SAVE_DIRECTORY:
485
[(NSSavePanel*)_panel setCanCreateDirectories:YES];
490
if ( [(NSSavePanel*)_panel isKindOfClass:[NSOpenPanel class]] ) {
491
NSPopUpButton *popup = nil;
493
char *p; p = _filter;
494
char *q; q = new char[strlen(p) + 1];
497
do { // copy to t what is in _filter removing what is between \t and \n, if any
499
if (!r) r = p + strlen(p) - 1;
501
if (s && s < r) { memcpy(q, p, s - p); q += s - p; *(q++) = '\n'; }
502
else { memcpy(q, p, r - p + 1); q += r - p + 1; }
506
popup = createPopupAccessory((NSSavePanel*)_panel, t, "Enable:", 0);
508
[[popup menu] addItem:[NSMenuItem separatorItem]];
509
[popup addItemWithTitle:@"All Documents"];
510
[popup setAction:@selector(validateVisibleColumns)];
511
[popup setTarget:(NSObject*)_panel];
512
static FLopenDelegate *openDelegate = nil;
513
if (openDelegate == nil) {
514
// not to be ever freed
515
openDelegate = [[FLopenDelegate alloc] init];
517
[openDelegate setPopup:popup filter_pattern:_filt_patt];
518
[(NSOpenPanel*)_panel setDelegate:openDelegate];
521
NSString *fname = nil;
522
NSString *preset = nil;
524
preset = [[NSString alloc] initWithUTF8String:_preset_file];
525
if (strchr(_preset_file, '/') != NULL)
526
dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]];
527
fname = [preset lastPathComponent];
529
if (_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory];
530
retval = [(NSOpenPanel*)_panel runModalForDirectory:dir file:fname types:nil];
534
_filt_value = [popup indexOfSelectedItem];
536
if ( retval == NSOKButton ) {
538
NSArray *array = [(NSOpenPanel*)_panel filenames];
539
_tpathnames = [array count];
540
_pathnames = new char*[_tpathnames];
541
for(int i = 0; i < _tpathnames; i++) {
542
_pathnames[i] = strnew([(NSString*)[array objectAtIndex:i] fileSystemRepresentation]);
548
NSString *fname = nil;
549
NSString *preset = nil;
550
NSPopUpButton *popup = nil;
551
if ( !(_options & SAVEAS_CONFIRM) ) {
552
static FLsaveDelegate *saveDelegate = nil;
553
if (saveDelegate == nil)saveDelegate = [[FLsaveDelegate alloc] init]; // not to be ever freed
554
[(NSSavePanel*)_panel setDelegate:saveDelegate];
557
preset = [[NSString alloc] initWithUTF8String:_preset_file];
558
if (strchr(_preset_file, '/') != NULL) {
559
dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]];
561
fname = [preset lastPathComponent];
563
if (_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory];
565
popup = createPopupAccessory((NSSavePanel*)_panel, _filter, "Format:", _filt_value);
567
retval = [(NSSavePanel*)_panel runModalForDirectory:dir file:fname];
569
_filt_value = [popup indexOfSelectedItem];
573
if ( retval == NSOKButton ) get_saveas_basename();
576
return (retval == NSOKButton ? 0 : 1);
579
#endif /*!FL_DOXYGEN*/
582
// End of "$Id: Fl_Native_File_Chooser_MAC.cxx 7354 2010-03-29 11:07:29Z matt $".