Skip to main content Skip to navigation

creating a file open dialog box in a 64 bit C++ application on a MAC

On macs the 'Carbon' API provide access to system functions such as opening dialog boxes, but this is only available for 32 bit applications. Apple are forcing all 64 bit applications to use the 64 bit Objective C based API. This requires the use of 64 bit objective C code, which fortunately can be mixed with C++ within a gcc compilation. The following objective C code (fileDialog.mm) provides a simple file open dialog box API which can be used within a C++ program.

#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#include <string>
#include <vector>

std::vector<std::string>openFileDialog(
char const * const aTitle ,
char const * const aDefaultPathAndFile ,
const std::vector<std::string> & filters) {

int i;
std::vector<std::string> fileList;
// Create a File Open Dialog class.
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg setLevel:CGShieldingWindowLevel()];
// Set array of file types

NSMutableArray * fileTypesArray = [NSMutableArray array];
for (i = 0;i < filters.size(); i++)
{
NSString * filt =[NSString stringWithUTF8String:filters[i].c_str()];
[fileTypesArray addObject:filt];
}

// Enable options in the dialog.
[openDlg setCanChooseFiles:YES];
[openDlg setAllowedFileTypes:fileTypesArray];
[openDlg setAllowsMultipleSelection:TRUE];
[openDlg setDirectoryURL:[NSURL URLWithString:[NSString stringWithUTF8String:aDefaultPathAndFile ] ] ];

// Display the dialog box. If the OK pressed,
// process the files.
if ( [openDlg runModal] == NSOKButton ) {
// Gets list of all files selected
NSArray *files = [openDlg URLs];
// Loop through the files and process them.
for( i = 0; i < [files count]; i++ ) {
// Do something with the filename.
fileList.push_back(std::string([[[files objectAtIndex:i] path] UTF8String]));
}
}
returnfileList;
}

The simplest way to use this within a cpp file is simply to declare it and then use it.

extern std::vector<std::string> openFileDialog();
...
std::vector<std::string> files = openFileDialog();

In the gcc makefile, the fileDialog.o should be added as one of the objects to be linked, e.g.

COREOBJS = $(CORE:%.cpp=$(BUILD)%.o) $(BUILD)fileDialog.o ...

where $(BUILD) has been set to the directory where the .o files are being placed, and the following build rule will ensure that it is built, putting the files into the $(BUILD) directory.

$(BUILD)%.o : %.mm
$(CC) -c -o $@ $<

and the following additional link options will be required

LINKFLAGS = -framework Carbon -framework Cocoa

Open file dialog boxes in multithreading code

The dialog boxes in macs are not thread safe (!), so Apple's answer to this is to only allow them to be opened on the main thread in a program, which is both a cop-out and a nuisance if the program is multithreaded as the information has to be passed back and forth between the other threads and the main thread.

There is a bug somewhere in macs cocoa architecture that means that the dialog box will open and freeze if the first time that it is opened is after data has been passed from a secondary thread to a main thread. This probably arises from the fact that cocoa is no thread safe and its 'fix' of only allowing calls from the main thread is not sufficient to deal with the underlying problem within the cocoa architecture. The solution to this is to call a dummy routine such as the following that creates, but does not display a dialog box and call this right at the start of the program before any real dialog boxes are created

void initialiseDialog() {
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
}