kopia lustrzana https://github.com/jamescoxon/dl-fldigi
835 wiersze
23 KiB
C++
835 wiersze
23 KiB
C++
//
|
|
// MAC_chooser_MAC.cxx -- FLTK native OS file chooser widget
|
|
//
|
|
// Copyright 2004 by Greg Ercolano.
|
|
// FLTK2/MAC port by Greg Ercolano 2007.
|
|
// Dave Freese, W1HKJ 2012
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Library General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Library General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "flnfc_common.cxx" // strnew/strfree/strapp/chrcat
|
|
|
|
#include <FL/Fl.H>
|
|
#include "FL/MAC_chooser.h"
|
|
#include <FL/filename.H>
|
|
|
|
// TRY TO CONVERT AN AEDesc TO AN FSSpec
|
|
// As per Apple Technical Q&A QA1274
|
|
// eg: http://developer.apple.com/qa/qa2001/qa1274.html
|
|
// Returns 'noErr' if OK,
|
|
// or an 'OSX result code' on error.
|
|
//
|
|
static int AEDescToFSSpec(const AEDesc* desc, FSSpec* fsspec) {
|
|
OSStatus err = noErr;
|
|
AEDesc coerceDesc;
|
|
// If AEDesc isn't already an FSSpec, convert it to one
|
|
if ( desc->descriptorType != typeFSS ) {
|
|
if ( ( err = AECoerceDesc(desc, typeFSS, &coerceDesc) ) == noErr ) {
|
|
// Get FSSpec out of AEDesc
|
|
err = AEGetDescData(&coerceDesc, fsspec, sizeof(FSSpec));
|
|
AEDisposeDesc(&coerceDesc);
|
|
}
|
|
} else {
|
|
err = AEGetDescData(desc, fsspec, sizeof(FSSpec));
|
|
}
|
|
return( err );
|
|
}
|
|
|
|
// CONVERT AN FSSpec TO A PATHNAME
|
|
static void FSSpecToPath(const FSSpec &spec, char *buff, int bufflen) {
|
|
FSRef fsRef;
|
|
FSpMakeFSRef(&spec, &fsRef);
|
|
FSRefMakePath(&fsRef, (UInt8*)buff, bufflen);
|
|
}
|
|
|
|
// CONVERT REGULAR PATH -> FSSpec
|
|
// If file does not exist, expect fnfErr.
|
|
// Returns 'noErr' if OK,
|
|
// or an 'OSX result code' on error.
|
|
//
|
|
static OSStatus PathToFSSpec(const char *path, FSSpec &spec) {
|
|
OSStatus err;
|
|
FSRef ref;
|
|
if ((err = FSPathMakeRef((const UInt8*)path, &ref, NULL)) != noErr) {
|
|
return(err);
|
|
}
|
|
// FSRef -> FSSpec
|
|
if ((err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, &spec,
|
|
NULL)) != noErr) {
|
|
return(err);
|
|
}
|
|
return(noErr);
|
|
}
|
|
|
|
// NAVREPLY: CTOR
|
|
MAC_chooser::NavReply::NavReply() {
|
|
_valid_reply = 0;
|
|
}
|
|
|
|
// NAVREPLY: DTOR
|
|
MAC_chooser::NavReply::~NavReply() {
|
|
if ( _valid_reply ) {
|
|
NavDisposeReply(&_reply);
|
|
}
|
|
}
|
|
|
|
// GET REPLY FROM THE NAV* DIALOG
|
|
int MAC_chooser::NavReply::get_reply(NavDialogRef& ref) {
|
|
if ( _valid_reply ) {
|
|
NavDisposeReply(&_reply); // dispose of previous
|
|
_valid_reply = 0;
|
|
}
|
|
if ( ref == NULL || NavDialogGetReply(ref, &_reply) != noErr ) {
|
|
return(-1);
|
|
}
|
|
_valid_reply = 1;
|
|
return(0);
|
|
}
|
|
|
|
// RETURN THE BASENAME USER WANTS TO 'Save As'
|
|
int MAC_chooser::NavReply::get_saveas_basename(char *s, int slen) {
|
|
if (CFStringGetCString(_reply.saveFileName, s, slen-1,
|
|
kCFStringEncodingUTF8) == false) {
|
|
s[0] = '\0';
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
// RETURN THE DIRECTORY NAME
|
|
// Returns 0 on success, -1 on error.
|
|
//
|
|
int MAC_chooser::NavReply::get_dirname(char *s, int slen) {
|
|
FSSpec fsspec;
|
|
if ( AEDescToFSSpec(&_reply.selection, &fsspec) != noErr ) {
|
|
// Conversion failed? Return empty name
|
|
s[0] = 0;
|
|
return(-1);
|
|
}
|
|
FSSpecToPath(fsspec, s, slen);
|
|
return(0);
|
|
}
|
|
|
|
// RETURN MULTIPLE DIRECTORIES
|
|
// Returns: 0 on success with pathnames[] containing pathnames selected,
|
|
// -1 on error
|
|
//
|
|
int MAC_chooser::NavReply::get_pathnames(char **&pathnames,
|
|
int& tpathnames) {
|
|
// How many items selected?
|
|
long count = 0;
|
|
if ( AECountItems(&_reply.selection, &count) != noErr )
|
|
{ return(-1); }
|
|
|
|
// Allocate space for that many pathnames
|
|
pathnames = new char*[count];
|
|
memset((void*)pathnames, 0, count*sizeof(char*));
|
|
tpathnames = count;
|
|
|
|
// Walk list of pathnames selected
|
|
for (short index=1; index<=count; index++) {
|
|
AEKeyword keyWord;
|
|
AEDesc desc;
|
|
if (AEGetNthDesc(&_reply.selection, index, typeFSS, &keyWord,
|
|
&desc) != noErr) {
|
|
pathnames[index-1] = strnew("");
|
|
continue;
|
|
}
|
|
FSSpec fsspec;
|
|
if (AEGetDescData(&desc, &fsspec, sizeof(FSSpec)) != noErr ) {
|
|
pathnames[index-1] = strnew("");
|
|
continue;
|
|
}
|
|
char s[4096];
|
|
FSSpecToPath(fsspec, s, sizeof(s)-1);
|
|
pathnames[index-1] = strnew(s);
|
|
AEDisposeDesc(&desc);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
// FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS
|
|
void MAC_chooser::clear_pathnames() {
|
|
if ( _pathnames ) {
|
|
while ( --_tpathnames >= 0 ) {
|
|
_pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]);
|
|
}
|
|
delete [] _pathnames;
|
|
_pathnames = NULL;
|
|
}
|
|
_tpathnames = 0;
|
|
}
|
|
|
|
// SET A SINGLE PATHNAME
|
|
void MAC_chooser::set_single_pathname(const char *s) {
|
|
clear_pathnames();
|
|
_pathnames = new char*[1];
|
|
_pathnames[0] = strnew(s);
|
|
_tpathnames = 1;
|
|
}
|
|
|
|
// GET THE 'Save As' FILENAME
|
|
// Returns -1 on error, errmsg() has reason, filename == "".
|
|
// 0 if OK, filename() has filename chosen.
|
|
//
|
|
int MAC_chooser::get_saveas_basename(NavDialogRef& ref) {
|
|
if ( ref == NULL ) {
|
|
errmsg("get_saveas_basename: ref is NULL");
|
|
return(-1);
|
|
}
|
|
NavReply reply;
|
|
OSStatus err;
|
|
if ((err = reply.get_reply(ref)) != noErr ) {
|
|
errmsg("NavReply::get_reply() failed");
|
|
clear_pathnames();
|
|
return(-1);
|
|
}
|
|
|
|
char pathname[4096] = "";
|
|
// Directory name..
|
|
// -2 leaves room to append '/'
|
|
//
|
|
if ( reply.get_dirname(pathname, sizeof(pathname)-2) < 0 ) {
|
|
clear_pathnames();
|
|
errmsg("NavReply::get_dirname() failed");
|
|
return(-1);
|
|
}
|
|
// Append '/'
|
|
int len = strlen(pathname);
|
|
pathname[len++] = '/';
|
|
pathname[len] = '\0';
|
|
// Basename..
|
|
if ( reply.get_saveas_basename(pathname+len, sizeof(pathname)-len) < 0 ) {
|
|
clear_pathnames();
|
|
errmsg("NavReply::get_saveas_basename() failed");
|
|
return(-1);
|
|
}
|
|
set_single_pathname(pathname);
|
|
return(0);
|
|
}
|
|
|
|
// GET (POTENTIALLY) MULTIPLE FILENAMES
|
|
// Returns:
|
|
// -1 -- error, errmsg() has reason, filename == ""
|
|
// 0 -- OK, pathnames()/filename() has pathname(s) chosen
|
|
//
|
|
int MAC_chooser::get_pathnames(NavDialogRef& ref) {
|
|
if ( ref == NULL ) {
|
|
errmsg("get_saveas_basename: ref is NULL");
|
|
return(-1);
|
|
}
|
|
NavReply reply;
|
|
OSStatus err;
|
|
if ((err = reply.get_reply(ref)) != noErr ) {
|
|
errmsg("NavReply::get_reply() failed");
|
|
clear_pathnames();
|
|
return(-1);
|
|
}
|
|
// First, clear pathnames array of any previous contents
|
|
clear_pathnames();
|
|
if ( reply.get_pathnames(_pathnames, _tpathnames) < 0 ) {
|
|
clear_pathnames();
|
|
errmsg("NavReply::get_dirname() failed");
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
// NAV CALLBACK EVENT HANDLER
|
|
void MAC_chooser::event_handler(
|
|
NavEventCallbackMessage callBackSelector,
|
|
NavCBRecPtr cbparm,
|
|
void *data) {
|
|
OSStatus err;
|
|
MAC_chooser *nfb = (MAC_chooser*)data;
|
|
switch (callBackSelector) {
|
|
case kNavCBStart:
|
|
if ( nfb->directory() || nfb->preset_file() ) {
|
|
const char *pathname = nfb->directory()
|
|
? nfb->directory()
|
|
: nfb->preset_file();
|
|
FSSpec spec;
|
|
if ( ( err = PathToFSSpec(pathname, spec) ) != noErr ) {
|
|
fprintf(stderr, "PathToFSSpec(%s) failed: err=%d\n",
|
|
pathname, (int)err);
|
|
break;
|
|
}
|
|
AEDesc desc;
|
|
if ((err = AECreateDesc(typeFSS, &spec, sizeof(FSSpec),
|
|
&desc)) != noErr) {
|
|
fprintf(stderr, "AECreateDesc() failed: err=%d\n",
|
|
(int)err);
|
|
}
|
|
if ((err = NavCustomControl(cbparm->context,
|
|
kNavCtlSetLocation,
|
|
&desc)) != noErr) {
|
|
fprintf(stderr, "NavCustomControl() failed: err=%d\n",
|
|
(int)err);
|
|
}
|
|
AEDisposeDesc(&desc);
|
|
}
|
|
if ( nfb->_btype == BROWSE_SAVE_FILE && nfb->preset_file() ) {
|
|
CFStringRef namestr =
|
|
CFStringCreateWithCString(NULL,
|
|
nfb->preset_file(),
|
|
kCFStringEncodingASCII);
|
|
NavDialogSetSaveFileName(cbparm->context, namestr);
|
|
CFRelease(namestr);
|
|
}
|
|
NavCustomControl(cbparm->context,
|
|
kNavCtlSetActionState,
|
|
&nfb->_keepstate );
|
|
|
|
// Select the right filter in pop-up menu
|
|
if ( nfb->_filt_value == nfb->_filt_total ) {
|
|
// Select All Documents
|
|
NavPopupMenuItem kAll = kNavAllFiles;
|
|
NavCustomControl(cbparm->context, kNavCtlSelectAllType, &kAll);
|
|
} else if (nfb->_filt_value < nfb->_filt_total) {
|
|
// Select custom filter
|
|
nfb->_tempitem.version = kNavMenuItemSpecVersion;
|
|
nfb->_tempitem.menuCreator = 'extn';
|
|
nfb->_tempitem.menuType = nfb->_filt_value;
|
|
*nfb->_tempitem.menuItemName = '\0'; // needed on 10.3+
|
|
NavCustomControl(cbparm->context,
|
|
kNavCtlSelectCustomType,
|
|
&(nfb->_tempitem));
|
|
}
|
|
break;
|
|
|
|
case kNavCBPopupMenuSelect:
|
|
NavMenuItemSpecPtr ptr;
|
|
// they really buried this one!
|
|
ptr = (NavMenuItemSpecPtr)cbparm->eventData.eventDataParms.param;
|
|
if ( ptr->menuCreator ) {
|
|
// Gets index to filter ( menuCreator = 'extn' )
|
|
nfb->_filt_value = ptr->menuType;
|
|
} else {
|
|
// All docs filter selected ( menuCreator = '\0\0\0\0' )
|
|
nfb->_filt_value = nfb->_filt_total;
|
|
}
|
|
break;
|
|
|
|
case kNavCBSelectEntry:
|
|
NavActionState astate;
|
|
switch ( nfb->_btype ) {
|
|
// these don't need selection override
|
|
case BROWSE_MULTI_FILE:
|
|
case BROWSE_MULTI_DIRECTORY:
|
|
case BROWSE_SAVE_FILE:
|
|
break;
|
|
|
|
// These need to allow only one item, so disable
|
|
// Open button if user tries to select multiple files
|
|
case BROWSE_SAVE_DIRECTORY:
|
|
case BROWSE_DIRECTORY:
|
|
case BROWSE_FILE:
|
|
SInt32 selectcount;
|
|
AECountItems((AEDescList*)cbparm->
|
|
eventData.eventDataParms.param,
|
|
&selectcount);
|
|
if ( selectcount > 1 ) {
|
|
NavCustomControl(cbparm->context,
|
|
kNavCtlSetSelection,
|
|
NULL);
|
|
astate = nfb->_keepstate |
|
|
kNavDontOpenState |
|
|
kNavDontChooseState;
|
|
NavCustomControl(cbparm->context,
|
|
kNavCtlSetActionState,
|
|
&astate );
|
|
}
|
|
else {
|
|
astate= nfb->_keepstate | kNavNormalState;
|
|
NavCustomControl(cbparm->context,
|
|
kNavCtlSetActionState,
|
|
&astate );
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// CONSTRUCTOR
|
|
MAC_chooser::MAC_chooser(int val) {
|
|
_btype = val;
|
|
NavGetDefaultDialogCreationOptions(&_opts);
|
|
_opts.optionFlags |= kNavDontConfirmReplacement; // no confirms for "save as"
|
|
_options = NO_OPTIONS;
|
|
_ref = NULL;
|
|
memset(&_tempitem, 0, sizeof(_tempitem));
|
|
_pathnames = NULL;
|
|
_tpathnames = 0;
|
|
_title = NULL;
|
|
_filter = NULL;
|
|
_filt_names = NULL;
|
|
memset(_filt_patt, 0, sizeof(char*) * MAXFILTERS);
|
|
_filt_total = 0;
|
|
_filt_value = 0;
|
|
_directory = NULL;
|
|
_preset_file = NULL;
|
|
_errmsg = NULL;
|
|
_keepstate = kNavNormalState;
|
|
}
|
|
|
|
// DESTRUCTOR
|
|
MAC_chooser::~MAC_chooser() {
|
|
// _opts // nothing to manage
|
|
if (_ref) { NavDialogDispose(_ref); _ref = NULL; }
|
|
// _options // nothing to manage
|
|
// _keepstate // nothing to manage
|
|
// _tempitem // nothing to manage
|
|
clear_pathnames();
|
|
_directory = strfree(_directory);
|
|
_title = strfree(_title);
|
|
_preset_file = strfree(_preset_file);
|
|
_filter = strfree(_filter);
|
|
//_filt_names // managed by clear_filters()
|
|
//_filt_patt[i] // managed by clear_filters()
|
|
//_filt_total // managed by clear_filters()
|
|
clear_filters();
|
|
//_filt_value // nothing to manage
|
|
_errmsg = strfree(_errmsg);
|
|
}
|
|
|
|
// SET THE TYPE OF BROWSER
|
|
void MAC_chooser::type(int val) {
|
|
_btype = val;
|
|
}
|
|
|
|
// GET TYPE OF BROWSER
|
|
int MAC_chooser::type() const {
|
|
return(_btype);
|
|
}
|
|
|
|
// SET OPTIONS
|
|
void MAC_chooser::options(int val) {
|
|
_options = val;
|
|
}
|
|
|
|
// GET OPTIONS
|
|
int MAC_chooser::options() const {
|
|
return(_options);
|
|
}
|
|
|
|
// SHOW THE BROWSER WINDOW
|
|
// Returns:
|
|
// 0 - user picked a file
|
|
// 1 - user cancelled
|
|
// -1 - failed; errmsg() has reason
|
|
//
|
|
int MAC_chooser::show() {
|
|
|
|
// Make sure fltk interface updates before posting our dialog
|
|
Fl::awake();
|
|
|
|
// BROWSER TITLE
|
|
CFStringRef cfs_title;
|
|
cfs_title = CFStringCreateWithCString(NULL,
|
|
_title ? _title : "No Title",
|
|
kCFStringEncodingASCII);
|
|
_opts.windowTitle = cfs_title;
|
|
|
|
_keepstate = kNavNormalState;
|
|
|
|
// BROWSER FILTERS
|
|
CFArrayRef filter_array = NULL;
|
|
{
|
|
// One or more filters specified?
|
|
if ( _filt_total ) {
|
|
// NAMES -> CFArrayRef
|
|
CFStringRef tab = CFSTR("\t");
|
|
CFStringRef tmp_cfs;
|
|
tmp_cfs = CFStringCreateWithCString(NULL, _filt_names,
|
|
kCFStringEncodingASCII);
|
|
filter_array = CFStringCreateArrayBySeparatingStrings(
|
|
NULL, tmp_cfs, tab);
|
|
CFRelease(tmp_cfs);
|
|
CFRelease(tab);
|
|
_opts.popupExtension = filter_array;
|
|
_opts.optionFlags |= kNavAllFilesInPopup;
|
|
} else {
|
|
filter_array = NULL;
|
|
_opts.popupExtension = NULL;
|
|
_opts.optionFlags |= kNavAllFilesInPopup;
|
|
}
|
|
}
|
|
|
|
// HANDLE OPTIONS WE SUPPORT
|
|
if ( _options & SAVEAS_CONFIRM ) {
|
|
_opts.optionFlags &= ~kNavDontConfirmReplacement; // enables confirm
|
|
} else {
|
|
_opts.optionFlags |= kNavDontConfirmReplacement; // disables confirm
|
|
}
|
|
|
|
// POST BROWSER
|
|
int err = post();
|
|
|
|
// RELEASE _FILT_ARR
|
|
if ( filter_array ) CFRelease(filter_array);
|
|
filter_array = NULL;
|
|
_opts.popupExtension = NULL;
|
|
_filt_total = 0;
|
|
|
|
// RELEASE TITLE
|
|
if ( cfs_title ) CFRelease(cfs_title);
|
|
cfs_title = NULL;
|
|
|
|
return(err);
|
|
}
|
|
|
|
// POST BROWSER
|
|
// Internal use only.
|
|
// Assumes '_opts' has been initialized.
|
|
//
|
|
// Returns:
|
|
// 0 - user picked a file
|
|
// 1 - user cancelled
|
|
// -1 - failed; errmsg() has reason
|
|
//
|
|
int MAC_chooser::post() {
|
|
|
|
// INITIALIZE BROWSER
|
|
OSStatus err;
|
|
if ( _filt_total == 0 ) { // Make sure they match
|
|
_filt_value = 0; // TBD: move to someplace more logical?
|
|
}
|
|
|
|
switch (_btype) {
|
|
case BROWSE_FILE:
|
|
case BROWSE_MULTI_FILE:
|
|
//_keepstate = kNavDontNewFolderState;
|
|
// Prompt user for one or more files
|
|
if ((err = NavCreateGetFileDialog(
|
|
&_opts, // options
|
|
0, // file types
|
|
event_handler, // event handler
|
|
0, // preview callback
|
|
filter_proc_cb, // filter callback
|
|
(void*)this, // callback data
|
|
&_ref)) != noErr ) { // dialog ref
|
|
errmsg("NavCreateGetFileDialog: failed");
|
|
return(-1);
|
|
}
|
|
break;
|
|
|
|
case BROWSE_DIRECTORY:
|
|
case BROWSE_MULTI_DIRECTORY:
|
|
_keepstate = kNavDontNewFolderState;
|
|
//FALLTHROUGH
|
|
|
|
case BROWSE_SAVE_DIRECTORY:
|
|
// Prompts user for one or more files or folders
|
|
if ((err = NavCreateChooseFolderDialog(
|
|
&_opts, // options
|
|
event_handler, // event callback
|
|
0, // filter callback
|
|
(void*)this, // callback data
|
|
&_ref)) != noErr ) { // dialog ref
|
|
errmsg("NavCreateChooseFolderDialog: failed");
|
|
return(-1);
|
|
}
|
|
break;
|
|
|
|
case BROWSE_SAVE_FILE:
|
|
// Prompt user for filename to 'save as'
|
|
if ((err = NavCreatePutFileDialog(
|
|
&_opts, // options
|
|
0, // file types
|
|
0, // file creator
|
|
event_handler, // event handler
|
|
(void*)this, // callback data
|
|
&_ref)) != noErr ) { // dialog ref
|
|
errmsg("NavCreatePutFileDialog: failed");
|
|
return(-1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// SHOW THE DIALOG
|
|
if ( ( err = NavDialogRun(_ref) ) != 0 ) {
|
|
char msg[80];
|
|
sprintf(msg, "NavDialogRun: failed (err=%d)", (int)err);
|
|
errmsg(msg);
|
|
return(-1);
|
|
}
|
|
|
|
// WHAT ACTION DID USER CHOOSE?
|
|
NavUserAction act = NavDialogGetUserAction(_ref);
|
|
if ( act == kNavUserActionNone ) {
|
|
errmsg("Nothing happened yet (dialog still open)");
|
|
return(-1);
|
|
}
|
|
else if ( act == kNavUserActionCancel ) { // user chose 'cancel'
|
|
return(1);
|
|
}
|
|
else if ( act == kNavUserActionSaveAs ) { // user chose 'save as'
|
|
return(get_saveas_basename(_ref));
|
|
}
|
|
|
|
// TOO MANY FILES CHOSEN?
|
|
int ret = get_pathnames(_ref);
|
|
if ( _btype == BROWSE_FILE && ret == 0 && _tpathnames != 1 ) {
|
|
char msg[80];
|
|
sprintf(msg, "Expected only one file to be chosen.. you chose %d.",
|
|
(int)_tpathnames);
|
|
errmsg(msg);
|
|
return(-1);
|
|
}
|
|
return(err);
|
|
}
|
|
|
|
// SET ERROR MESSAGE
|
|
// Internal use only.
|
|
//
|
|
void MAC_chooser::errmsg(const char *msg) {
|
|
_errmsg = strfree(_errmsg);
|
|
_errmsg = strnew(msg);
|
|
}
|
|
|
|
// RETURN ERROR MESSAGE
|
|
const char *MAC_chooser::errmsg() const {
|
|
return(_errmsg ? _errmsg : "No error");
|
|
}
|
|
|
|
// GET FILENAME
|
|
const char* MAC_chooser::filename() const {
|
|
if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]);
|
|
return("");
|
|
}
|
|
|
|
// GET FILENAME FROM LIST OF FILENAMES
|
|
const char* MAC_chooser::filename(int i) const {
|
|
if ( _pathnames && i < _tpathnames ) return(_pathnames[i]);
|
|
return("");
|
|
}
|
|
|
|
// GET TOTAL FILENAMES CHOSEN
|
|
int MAC_chooser::count() const {
|
|
return(_tpathnames);
|
|
}
|
|
|
|
// PRESET PATHNAME
|
|
// Value can be NULL for none.
|
|
//
|
|
void MAC_chooser::directory(const char *val) {
|
|
_directory = strfree(_directory);
|
|
_directory = strnew(val);
|
|
}
|
|
|
|
// GET PRESET PATHNAME
|
|
// Returned value can be NULL if none set.
|
|
//
|
|
const char* MAC_chooser::directory() const {
|
|
return(_directory);
|
|
}
|
|
|
|
#include <string>
|
|
// SET TITLE
|
|
// Value can be NULL if no title desired.
|
|
//
|
|
void MAC_chooser::title(const char *val) {
|
|
std::string tt = "My MAC ";
|
|
tt.append(val);
|
|
_title = strfree(_title);
|
|
_title = strnew(tt.c_str());
|
|
// _title = strnew(val);
|
|
}
|
|
|
|
// GET TITLE
|
|
// Returned value can be NULL if none set.
|
|
//
|
|
const char *MAC_chooser::title() const {
|
|
return(_title);
|
|
}
|
|
|
|
// SET FILTER
|
|
// Can be NULL if no filter needed
|
|
//
|
|
void MAC_chooser::filter(const char *val) {
|
|
_filter = strfree(_filter);
|
|
_filter = strnew(val);
|
|
|
|
// Parse filter user specified
|
|
// IN: _filter = "C Files\t*.{cxx,h}\nText Files\t*.txt"
|
|
// OUT: _filt_names = "C Files\tText Files"
|
|
// _filt_patt[0] = "*.{cxx,h}"
|
|
// _filt_patt[1] = "*.txt"
|
|
// _filt_total = 2
|
|
//
|
|
parse_filter(_filter);
|
|
}
|
|
|
|
// GET FILTER
|
|
// Returned value can be NULL if none set.
|
|
//
|
|
const char *MAC_chooser::filter() const {
|
|
return(_filter);
|
|
}
|
|
|
|
// CLEAR ALL FILTERS
|
|
// Internal use only.
|
|
//
|
|
void MAC_chooser::clear_filters() {
|
|
_filt_names = strfree(_filt_names);
|
|
for (int i=0; i<_filt_total; i++) {
|
|
_filt_patt[i] = strfree(_filt_patt[i]);
|
|
}
|
|
_filt_total = 0;
|
|
}
|
|
|
|
// PARSE USER'S FILTER SPEC
|
|
// Parses user specified filter ('in'),
|
|
// breaks out into _filt_patt[], _filt_names, and _filt_total.
|
|
//
|
|
// Handles:
|
|
// IN: OUT:_filt_names OUT: _filt_patt
|
|
// ------------------------------------ ------------------ ---------------
|
|
// "*.{ma,mb}" "*.{ma,mb} Files" "*.{ma,mb}"
|
|
// "*.[abc]" "*.[abc] Files" "*.[abc]"
|
|
// "*.txt" "*.txt Files" "*.c"
|
|
// "C Files\t*.[ch]" "C Files" "*.[ch]"
|
|
// "C Files\t*.[ch]\nText Files\t*.cxx" "C Files" "*.[ch]"
|
|
//
|
|
// Parsing Mode:
|
|
// IN:"C Files\t*.{cxx,h}"
|
|
// ||||||| |||||||||
|
|
// mode: nnnnnnn wwwwwwwww
|
|
// \_____/ \_______/
|
|
// Name Wildcard
|
|
//
|
|
void MAC_chooser::parse_filter(const char *in) {
|
|
clear_filters();
|
|
if ( ! in ) return;
|
|
int has_name = strchr(in, '\t') ? 1 : 0;
|
|
|
|
char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard
|
|
char wildcard[1024] = ""; // parsed wildcard
|
|
char name[1024] = "";
|
|
|
|
// Parse filter user specified
|
|
for ( ; 1; in++ ) {
|
|
|
|
//// DEBUG
|
|
//// printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n",
|
|
//// *in, mode, name, wildcard);
|
|
|
|
switch (*in) {
|
|
// FINISHED PARSING NAME?
|
|
case '\t':
|
|
if ( mode != 'n' ) goto regchar;
|
|
mode = 'w';
|
|
break;
|
|
|
|
// ESCAPE NEXT CHAR
|
|
case '\\':
|
|
++in;
|
|
goto regchar;
|
|
|
|
// FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
|
|
case '\r':
|
|
case '\n':
|
|
case '\0':
|
|
// TITLE
|
|
// If user didn't specify a name, make one
|
|
//
|
|
if ( name[0] == '\0' ) {
|
|
sprintf(name, "%.*s Files", (int)sizeof(name)-10, wildcard);
|
|
}
|
|
// APPEND NEW FILTER TO LIST
|
|
if ( wildcard[0] ) {
|
|
// Add to filtername list
|
|
// Tab delimit if more than one. We later break
|
|
// tab delimited string into CFArray with
|
|
// CFStringCreateArrayBySeparatingStrings()
|
|
//
|
|
if ( _filt_total ) {
|
|
_filt_names = strapp(_filt_names, "\t");
|
|
}
|
|
_filt_names = strapp(_filt_names, name);
|
|
|
|
// Add filter to the pattern array
|
|
_filt_patt[_filt_total++] = strnew(wildcard);
|
|
}
|
|
// RESET
|
|
wildcard[0] = name[0] = '\0';
|
|
mode = strchr(in, '\t') ? 'n' : 'w';
|
|
// DONE?
|
|
if ( *in == '\0' ) return; // done
|
|
else continue; // not done yet, more filters
|
|
|
|
// Parse all other chars
|
|
default: // handle all non-special chars
|
|
regchar: // handle regular char
|
|
switch ( mode ) {
|
|
case 'n': chrcat(name, *in); continue;
|
|
case 'w': chrcat(wildcard, *in); continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
//NOTREACHED
|
|
}
|
|
|
|
// STATIC: FILTER CALLBACK
|
|
Boolean MAC_chooser::filter_proc_cb(AEDesc *theItem,
|
|
void *info,
|
|
void *callBackUD,
|
|
NavFilterModes filterMode) {
|
|
return((MAC_chooser*)callBackUD)->filter_proc_cb2(
|
|
theItem, info, callBackUD, filterMode);
|
|
}
|
|
|
|
// FILTER CALLBACK
|
|
// Return true if match,
|
|
// false if no match.
|
|
//
|
|
Boolean MAC_chooser::filter_proc_cb2(AEDesc *theItem,
|
|
void *info,
|
|
void *callBackUD,
|
|
NavFilterModes filterMode) {
|
|
// All files chosen or no filters
|
|
if ( _filt_value == _filt_total ) return(true);
|
|
|
|
FSSpec fsspec;
|
|
char pathname[4096];
|
|
|
|
// On fail, filter should return true by default
|
|
if ( AEDescToFSSpec(theItem, &fsspec) != noErr ) {
|
|
return(true);
|
|
}
|
|
FSSpecToPath(fsspec, pathname, sizeof(pathname)-1);
|
|
|
|
if ( fl_filename_isdir(pathname) ) return(true);
|
|
if ( fl_filename_match(pathname, _filt_patt[_filt_value]) ) return(true);
|
|
else return(false);
|
|
}
|
|
|
|
// SET PRESET FILE
|
|
// Value can be NULL for none.
|
|
//
|
|
void MAC_chooser::preset_file(const char* val) {
|
|
_preset_file = strfree(_preset_file);
|
|
_preset_file = strnew(val);
|
|
}
|
|
|
|
// PRESET FILE
|
|
// Returned value can be NULL if none set.
|
|
//
|
|
const char* MAC_chooser::preset_file() {
|
|
return(_preset_file);
|
|
}
|
|
|