Using Navigation Services
Volume Number: 14
Issue Number: 8
Column Tag: Emerging Technologies
Using Navigation Services
by Keith Mortensen
Edited by the MacTech Magazine Editorial Staff
Every Macintosh programmer knows you can't write an application without going
through the experience of using the StandardFile package. One discovers almost
immediately the limitations and constraints of StandardFile, and begins to realize the
standard dialogs don't support the features most developers need. Thus, adding your
own features by customizing them is the only alternative.
If you have had to customize any of the StandardFile dialogs, can you remember your
first dialog hook function? Have you wondered what "pseudo items" meant? Do you
remember "copying" the System file's 'DLOG' and 'DITL' resources (ID -6042) to
make your very own open dialog? How about the feelings of uncertainty when
"changing" these resources? Have you resorted to writing a filter callback procedure
just because the SFTypeList holds no more than 4 file types?
Over the years your applications have changed, but those StandardFile dialogs haven't.
I'm glad to say those days are over. In this article we will introduce the tools of Apple's
successor to the StandardFile package -- Navigation Services.
Figure 1. The Open Dialog.
Meet Navigation Services, a new set of tools for document management. It hosts a suite
of "utility dialogs" for opening and saving documents, choosing volumes, choosing and
creating folders. More additional standard alerts are also provided to free you from
having to design your own.
Navigation Services provides a set of navigation tools for the user, including:
• Shortcuts - for quick access to mounted volumes and other desktop items.
• Favorites - for easy navigation to frequently used items.
• Recent - to provide a history of the users work.
• Browser List - a hierarchical Finder-like view of the file system.
Figure 2. Shortcuts, Favorites and Recent menus.
There are several areas where Navigation Services provides "memory assistance" to
improve the user experience. Each time you open a document, it will remember: your
last location, what you last opened, where the dialog box was displayed on your screen,
the dimensions of the dialog box, and the browser's sort key and sort order. It also
remembers the selection for every folder or container you visit. More importantly,
this persistence is on a per-application and per-dialog basis. Thus, an Open dialog's
position and size for a word processor application may be different than of a
spreadsheet application.
Navigation Services even gives you automatic built-in translation of your documents.
If SimpleText were to use Navigation Services, it would be capable of opening any
document that can be translated into text, picture, or movie formats. Lastly, it also
gives you the capabilities to add your own interface to these utility dialogs through a
well-defined system in tune with the over-all user interface.
A Navigation Services "Savvy" Application
We will be going through the process of making your application Navigation Services
"savvy" by studying some simple code examples. They are designed to make the
transition quick and painless. You needn't worry about how your application is built.
The interface can be accessed using 68K, CFM-68K and PowerPC code. This gives you
the flexibility to adapt and remain compatible to a wider variety of systems. By calling
NavServicesAvailable, you can determine if Navigation Services is installed and ready
to run.
NavGetFile
The first call I recommend using is NavGetFile, a fully featured "get" dialog of
Navigation Services. Here you will find most of the features you need. The following
example shows how to use NavGetFile for opening multiple documents.
OSErr OpenDocument( )
{
OSErr theErr = noErr;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
long count;
NavTypeListHandle openTypeList = NULL;
NavEventUPP eventProc =
NewNavEventProc( myEventProc );
NavObjectFilterUPP filterProc =
NewNavObjectFilterProc( myFilterProc );

// default behavior for browser and dialog:
theErr = NavGetDefaultDialogOptions( &dialogOptions );
// setup our NavTypeList for filtering:
openTypeList = (NavTypeListHandle)GetResource('open', 128);
theErr = NavGetFile( NULL, &theReply, &dialogOptions,
eventProc, NULL, filterProc,
openTypeList, NULL );
if ( theReply.validRecord && theErr == noErr )
{
FSSpec finalFSSpec;
AEDesc resultDesc;
AEKeyword keyWord;
if ( theErr == noErr )
{
// we are ready to open the document(s), grab
// information about each file for opening:
if ((theErr = AECountItems( &(theReply.selection),
&count )) == noErr)
for (long index=1;index<=count;index++)
if ((theErr = AEGetNthDesc( &(theReply.selection),
index,typeFSS,&keyWord,&resultDesc)) == noErr)
{
BlockMoveData( *resultDesc.dataHandle,
&finalFSSpec, sizeof(FSSpec) );
if ((theErr = DoOpenFile(&finalFSSpec))== noErr)
theErr = AEDisposeDesc( &resultDesc );
}
}
theErr = NavDisposeReply( &theReply );
}
DisposeRoutineDescriptor( filterProc );
DisposeRoutineDescriptor( eventProc );
if (openTypeList != NULL)
ReleaseResource( (Handle)openTypeList );
return theErr;
}
Opening Documents with AppleEvents
Navigation Services returns in the selection field of the NavReplyRecord an AEDescList
or AppleEvent Descriptor List. It contains one or more items to be opened. To open the
documents, one nifty technique is to send to your application an 'odoc' AppleEvent with
the items found in this list. This method saves you from having to deal with AppleEvent
Descriptors. Use your AppleEvent handler to perform the open, as if you received the
AppleEvent from the Finder.
theErr = SendOpenAE(theReply.selection);
static OSStatus SendOpenAE(AEDescList list)
{
OSStatus err;
AEAddressDesc theAddress;
AppleEvent dummyReply, theEvent;

theAddress.descriptorType = typeNull;
theAddress.dataHandle = NULL;
do {
ProcessSerialNumber psn;

err = GetCurrentProcess(&psn);
if ( err != noErr) break;

err = AECreateDesc(typeProcessSerialNumber, &psn,
sizeof(ProcessSerialNumber), &theAddress);
if ( err != noErr) break;

dummyReply.descriptorType = typeNull;
dummyReply.dataHandle = NULL;
err = AECreateAppleEvent(kCoreEventClass,
kAEOpenDocuments, &theAddress,
kAutoGenerateReturnID, kAnyTransactionID,
&theEvent);
if ( err != noErr) break;

err = AEPutParamDesc(&theEvent, keyDirectObject, &list);
if ( err != noErr) break;

err = AESend(&theEvent, &dummyReply, kAEWaitReply,
kAENormalPriority, kAEDefaultTimeout,
NULL, NULL);
if ( err != noErr) break;
} while (false);

return err;
}
NavPutFile
Programming the save dialog is similar to using NavGetFile with two additions: you
provide a "suggested" saved file name and file format. Once you are done with
NavPutFile, call NavCompleteSave to perform translation of the saved document in case
the user chose a different save format than your own. It is important you call
NavCompleteSave, as this call will provide more "save" features in future versions.
The next example shows how to use NavPutFile. No object filtering is necessary and the
Favorites and Recent menus will show only containers.
Figure 3. The Save Dialog.
OSErr SaveDocument(WindowPtr theDocument)
{
OSErr theErr = noErr;
NavReplyRecord theReply;
NavDialogOptions dialogOptions;
OSType fileTypeToSave = 'TEXT';
OSType creatorType = 'xAPP';
NavEventUPP eventProc = NewNavEventProc(myEventProc);

// default behavior for browser and dialog:
theErr = NavGetDefaultDialogOptions( &dialogOptions );
GetWTitle( theDocument, dialogOptions.savedFileName );