Implementing an Open Dialog Box in OS X
You'll make use of a number of Navigation Services routines to display and handfile an Open dialog box that is similar to the one TextEdit and other Mac OS X applications use. To do that, your code will perform the following:
Create and display the standard Open dialog box.
Become aware of the user's action in the Open dialog box.
Respond to the user's action (for instance, open the appropriate file if the user clicks the Open button).
Clean up by disposing of the Open dialog box when appropriate.
Figure 1 - A typical Open dialog box (as displayed in TextEdit).
The overall look and behavior of an Open dialog box usually is the same. Such a dialog box includes Cancel and Open buttons and a list view of the folders and files on the user's machine. The general behavior of this type of dialog box is the same from one implementation to another as well; the user navigates through the file list, clicks the name of a file to open within the list, and then clicks the Open button to open the selected file. To promote this consistent look and behavior, Navigation Services defines the NavDialogCreationOptions data structure as the following:
struct NavDialogCreationOptions { UInt16 version; NavDialogOptionFlags optionFlags; Point location; CFStringRef clientName; CFStringRef windowTitle; CFStringRef actionButtonLabel; CFStringRef cancelButtonLabel; CFStringRef saveFileName; CFStringRef message; UInt32 preferenceKey; CFArrayRef popupExtension; WindowModality modality; WindowRef parentWindow; char reserved[16]; }; typedef struct NavDialogCreationOptions NavDialogCreationOptions;
The NavDialogCreationOptions structure defines the features (such as size and location) of an Open dialog box. The Navigation Services routine NavGetDefaultDialogCreationOptions is used to fill the fields of a NavDialogCreationOptions structure with default values. Use this routine by declaring a variable of type NavDialogCreationOptions and then passing that variable's address as the routine's one argument:
OSStatus err; NavDialogCreationOptions dialogAttributes; err = NavGetDefaultDialogCreationOptions( &dialogAttributes )
After setting the values of the members of a structure to default values, you can customize the structure by changing the value of any individual member. For instance, to make the Open dialog box take over the application and disallow other application actions to take its place, the value of the dialog box's NavDialogCreationOptions modality member can be set to the Apple-defined constant kWindowModalityAppModal:
dialogAttributes.modality = kWindowModalityAppModal;
You've seen how a program includes an application-defined event handler routine that's associated with a window or other object. The Open dialog box also needs an application-defined event handler routine associated with it. This event handler will be called by the system when the user dismisses the Open dialog box. Navigation Services creates, displays, and runs the Open dialog box, but it is this event handler that should perform the actual work of opening a user-selected file. Like other event handlers, this Open dialog box event handler can have a name of your choosing, but it must include arguments of specific types. Here's the prototype for such a routine:
pascal void MyNavEventCallback( NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void* callBackUD );
In a moment, you'll pass a pointer to this event handler to the Navigation Services routine that creates the Open dialog box. The pointer should be of type NavEventUPP. The UPP in NavEventUPP stands for universal procedure pointer, which is a pointer that is capable of referencing procedures, or routines, in different executable formats. In this case, a NavEventUPP can point to a routine that is in native Mac OS X executable format or in pre-Mac OS X executable format. You'll also need this pointer elsewhere in your program, so declaring this pointer globally makes sense:
NavEventUPP gNavEventHandlerPtr;Use the Navigation Services routine NewNavEventUPP to set this routine pointer variable to point to the Open dialog box event handler:
gNavEventHandlerPtr = NewNavEventUPP( MyNavEventCallback );Now it's time to make a call to the Navigation Services routine NavCreateGetFileDialog to create the Open dialog box. This routine requires seven arguments, many of which can typically get set to NULL. Here's the function prototype:
NavCreateGetFileDialog( const NavDialogCreationOptions * inOptions, NavTypeListHandle inTypeList, NavEventUPP inEventProc, NavPreviewUPP inPreviewProc, NavObjectFilterUPP inFilterProc, void * inClientData, NavDialogRef * outDialog );
Using the previously declared dialogAttributes and gNavEventHandlerPtr variables, here's how a call to NavCreateGetFileDialog could look:
NavDialogRef openDialog; err = NavCreateGetFileDialog( &dialogAttributes, NULL, gNavEventHandlerPtr, NULL, NULL, NULL, &openDialog );
The inOptions parameter is a pointer to the set of Open dialog box features that was returned by a prior call to NavGetDefaultDialogCreationOptions. In the preceding code snippet, dialogAttributes holds that set of default values, with the exception of the modality that was altered after NavGetDefaultDialogCreationOptions was called.
The inTypeList is a list of file types to display in the Open dialog box's browser; pass NULL to display all file types.
The inEventProc parameter is the procedure pointer that points to the Open dialog box's event handler routine. In the preceding snippet, the global UPP variable gNavEventHandlerPtr, which was assigned its value from a call to NewNavEventUPP, is used.
The next three arguments each can be set to NULL. The inPreviewProc parameter is a pointer to a custom file preview routine. The inFilterProc parameter is a pointer to a custom file filter routine. The inClientData parameter is a value that gets passed to either of the just-mentioned custom routines (if present). The preceding snippet uses NULL for each of these three arguments.
The last argument is a pointer to a variable of type NavDialogRef. After NavCreateGetFileDialog executes, this argument will hold a reference to the newly created Open dialog box.
NavCreateGetFileDialog creates an Open dialog box, but it doesn't display or control it. To do those chores, call the Navigation Services routine NavDialogRun:
err = NavDialogRun( openDialog );
NavDialogRun handles the user's interaction with the Open dialog box, so you don't need to write any code to follow the user's actions as he or she uses the dialog box to browse for a file to open. When the user clicks the Cancel or Open button, the application-defined event handler associated with this Open dialog box is called. In doing this, Navigation Services passes on information about the event that initiated the event handler call.
The event handler takes care of the opening of the selected file and the dismissing of the Open dialog box. Control then returns to the code that follows the call to NavDialogRun. That code should look something like this:
if ( err != noErr ) { NavDialogDispose( openDialog ); DisposeNavEventUPP( gNavEventHandlerPtr ); }
If NavDialogRun completes without an error, your work is done. If there was an error, the variable err will have a nonzero (non-noErr) value. Your code should call the Navigation Services routines NavDialogDispose to dispose of the Open dialog box reference and DisposeNavEventUPP to dispose of the pointer to the Open dialog box event handler.
Whew. That covers the process of displaying and running the Open dialog box. Now it's time to take a look at all the code as it might appear in an application-defined routine that is used to enable a user to choose a file to open:
void DisplayOpenFileDialog( void ) { OSStatus err; NavDialogRef openDialog; NavDialogCreationOptions dialogAttributes; err = NavGetDefaultDialogCreationOptions( &dialogAttributes ); dialogAttributes.modality = kWindowModalityAppModal; gNavEventHandlerPtr = NewNavEventUPP( MyNavEventCallback ); err = NavCreateGetFileDialog( &dialogAttributes, NULL, gNavEventHandlerPtr, NULL, NULL, NULL, &openDialog ); err = NavDialogRun( openDialog ); if ( err != noErr ) { NavDialogDispose( openDialog ); DisposeNavEventUPP( gNavEventHandlerPtr ); } }