TweetFollow Us on Twitter

Using Navigation Services

Volume Number: 14 (1998)
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 );

   theErr = NavPutFile( NULL, &theReply, &dialogOptions, 
                  eventProc, fileTypeToSave, creatorType, NULL );

   if (theReply.validRecord && theErr == noErr)
   {
      FSSpec         finalFSSpec;   
      AEDesc         resultDesc;   
      AEKeyword      keyWord;

      // retrieve the returned selection:
      if (( theErr = AEGetNthDesc( &(theReply.selection), 1, 
                     typeFSS, &keyWord, &resultDesc )) == noErr )
      {
         BlockMoveData( *resultDesc.dataHandle, &finalFSSpec, 
                      sizeof(FSSpec) );
         if (theReply.replacing)
            theErr = FSpDelete( &finalFSSpec );   
         if ( theErr == noErr )
            if (( theErr = FSpCreate( &finalFSSpec, creatorType, 
                     fileTypeToSave, smSystemScript )) == noErr )
               if (( theErr = WriteNewFile(&finalFSSpec))==noErr)
                  theErr = NavCompleteSave( &theReply, 
                                                      kNavTranslateInPlace);
         AEDisposeDesc( &resultDesc );
      }
      theErr = NavDisposeReply( &theReply );
   }   
   DisposeRoutineDescriptor( eventProc );
   return theErr;
}

Dialog and Browser Options

A wide variety of features and options to change the look and behavior of the interface are available by setting up a NavDialogOptions structure:

struct NavDialogOptions {
   UInt16    version;
   NavDialogOptionFlags    dialogOptionFlags; // dialog/browser options
   Point       location;          // the dialog's screen coordinates
   Str255    clientName;          // your program's name
   Str255    windowTitle;         // a complete window title
   Str255    actionButtonLabel;   // the action button's text
   Str255    cancelButtonLabel;   // the cancel button's text
   Str255    savedFileName;       // for NavPutFile, the default file name
   Str255    message;             // the banner or prompt message string
   UInt32    preferenceKey;   // unique value used to map to set of preferences
   Handle    popupExtension;      // client's added popupMenu items
};
typedef struct NavDialogOptions NavDialogOptions;

enum {
   kNavDefaultNavDlogOptions   = 0x000000E4,   
                                // the default features
   kNavNoTypePopup = 0x00000001,
                                // turn off using the type popup menu
   kNavDontAutoTranslate   = 0x00000002,    
                                // do not automatically translate documents
   kNavDontAddTranslateItems   = 0x00000004,    
                                // add translated types to type popup menu
   kNavAllFilesInPopup   = 0x00000010,    
                                // add "All Documents" to type popup
   kNavAllowStationery = 0x00000020,
                                // add Stationery menu option to type popup
   kNavAllowPreviews = 0x00000040,    
                                // use the Previews option for documents
   kNavAllowMultipleFiles = 0x00000080,    
                                // browser selects and returns multiple objects
   kNavAllowInvisibleFiles = 0x00000100,    
                                // browser showss invisible objects
   kNavDontResolveAliases = 0x00000200,    
                                // don't resolve aliases, return the alise file
   kNavSelectDefaultLocation = 0x00000400,   
                                // select the default location
   kNavSelectAllReadableItem = 0x00000800
                                // select "All Readable Documents"

};
typedef UInt32 NavDialogOptionFlags;

You can easily setup this structure to use the default settings by calling NavGetDefaultDialogOptions. It is better to call this routine anyway since it initializes the structure automatically for you. Then you can turn on or off particular features by adding or subtracting option flags.

Filtering Objects

Navigation Services does "object filtering" to determine what objects to display in the Recent Documents list, Favorites list, and the browser list. Your application has two ways of filtering objects. One method is to use a filter callback procedure. A simpler way is to use a NavTypeList structure. This data structure is identical to the 'open' resource format defined by the Translation Manager. It contains the clients creator, and a list of OSTypes which represent the file types your application can open.

By using a filter callback procedure and/or NavTypeList structure, clients can filter out unwanted file objects from the interface. You can directly filter any object, including files, folders, volumes, aliases, etc. You can use both the NavTypeList and filter callback procedure if you wish. Both are designed to work together. For example, if the NavTypeList contains TEXT and PICT types, only TEXT and PICT files will be passed into your filter callback procedure.

pascal Boolean myFilterProc(    AEDesc* theItem, void* info, 
                                NavCallBackUserData callBackUD, 
                                NavFilterModes filterMode)
{
   OSErr       theErr = noErr;
   Boolean    display = true;
   NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*)info;
   
   if ( theItem->descriptorType == typeFSS )
      if ( !theInfo->isFolder )
         if (   theInfo->
               fileAndFolder.fileInfo.finderInfo.fdType!='TEXT')
            display = false;
   return display;
}

Handling Events

An event callback routine is used by your application to respond to events. Events can be Mac OS or Navigation Services events. By providing an event callback routine, you can keep your application's windows updated, handle idle events as well as communicate and interact with Navigation Services. For example, to respond to operating system events check for the kNavCBEvent message. This is one of a set of useful event messages defined in the API. Each message yields different data used for a particular event. The following shows the NavCBRec structure used in handling events:

struct NavCBRec {
   UInt16       version;
   NavContext context;          // used to refer to Navigation Services
   WindowPtr    window;         // the Navigation dialog
   Rect          customRect;    // coordinate rectangle of customization area
   Rect          previewRect;   // coordinate rectangle of the preview area
   NavEventDataInfo    eventData; // event info to examine/interpret events
   char          reserved[226];
};

pascal void myEventProc(    NavEventCallbackMessage 
                                  callBackSelector, 
                                  NavCBRecPtr callBackParms, 
                                  NavCallBackUserData callBackUD )
{
   WindowPtr pWindow = (WindowPtr)callBackParms->
                                  eventData.event->message;
   myApp* curApp = (myApp*)callBackUD;      // context to ourselves
   
   switch ( callBackSelector )
   {
      case kNavCBEvent:
         switch ( callBackParms->eventData.event->what )
         {
         case updateEvt:
            curApp->DoUpdate(pWindow,
                     (EventRecord*)callBackParms->eventData.event);
            break;
         }
         break;
   }
}

Customizing the Dialogs

Throughout the years, StandardFile has been heavily customized in almost every piece of software. As a result, the user experience is often inconsistent and confusing. Up until now, most of us have learned the behaviors of StandardFile, and we all have become used to them. As a result, Apple gets stuck with the difficulties of trying to advance the human interface without breaking current applications.

Navigation Services provides a number of calls and options which address needs in the past that required customization of the StandardFile dialogs. By using the features of this API, you will not have to customize the dialogs in a manner that will cause incompatibilities with future versions of Navigation Services. The need to change the Navigation Services dialogs, however, is greatly reduced.

The example in Figure 4 is how Sampler, an example application included with the Navigation Services SDK, customizes NavGetFile. It adds a "Commands" popup menu control. Sampler is responsible for allocating any controls and responding to mouseDown and keyDown events.

Figure 4. An example of dialog customization.

Dialog Space Negotiations

If you need to add your own interface you will need to negotiate a customization space by responding to this kNavCBCustomize event. The callBackParms->customRect field describes the custom area in local coordinates anchored to the lower left of the dialog's type popup or its browser. If you want to customize the dialog, you must complete the dimensions of the rectangular area by responding to kNavCBCustomize in your event callback procedure. Complete the dimensions in callBackParms->customRect for your custom area.

The following example is in response to kNavCBCustomize and shows you how to tell Navigation Services the space you need. Agreeing upon the customized space is like a round of negotiations between you and Navigation Services, coming up with an amount of space both of you can agree upon. Once you are satisfied with the given size, exit your event callback procedure with the same custom area. This will tell Navigation Services that an agreement has been made. The minimum guaranteed space is 402 pixels wide by 40 pixels high. The next example shows you how to complete the area for customization:

{
// here are the desired dimensions for our custom area:
short neededWidth =
            callBackParms->customRect.left + kCustomWidth;
short neededHeight =
            callBackParms->customRect.top + kCustomHeight;
            
// check to see if this is the first round of negotiations:
if (( callBackParms->customRect.right == 0) && 
         (callBackParms->customRect.bottom == 0 ))
{
   // it is, so tell NavServices what dimensions we want:
   callBackParms->customRect.right = neededWidth;
   callBackParms->customRect.bottom = neededHeight;
}
else
{
   // we are in the middle of negotiating:
   if ( globals->gLastTryWidth !=
            callBackParms->customRect.right )
      if ( callBackParms->customRect.right < neededWidth )   
         // is the given width too small for us?
         callBackParms->customRect.right = neededWidth;

   // is the given height too small for us?
   if ( globals->gLastTryHeight != 
         callBackParms->customRect.bottom )
      if ( callBackParms->customRect.bottom < neededHeight )
         callBackParms->customRect.bottom = neededHeight;
}
            
// remember our last size so the next time we can re-negotiate:
globals->gLastTryWidth = callBackParms->customRect.right;
globals->gLastTryHeight = callBackParms->customRect.bottom;
}

Sending "Nav" Commands

If you supply an event callback routine, you can in turn "call back" to Navigation Services to control the dialog and its browser. You can do this by calling NavCustomControl. NavCustomControl accepts a "context" or reference to Navigation Services which is provided by the NavCBRec, when the client's event routine is called. Its selector field determines the type of control call being made, and a generic pointer is used to convey parameter data. Using a set of custom control messages you can control the dialog and browser in various ways. Below are some examples of how to use NavCustomControl:

Str255 fileName;
theErr = NavCustomControl(callBackParms>context,
                           kNavCtlGetEditFileName,&fileName);

Str255 newFolderName = "\puntitled folder";
theErr = NavCustomControl(callBackParms->context,
                           kNavCtlNewFolder,&newFolderName);

theErr = NavCustomControl(callBackParms->context,
                           kNavCtlShowDesktop,NULL);

The following is a list of commands or NavCustomControlMessages you can send to Navigation Services. These commands can be used in your event or preview callback procedures, where appropriate. The arrows illustrate how data is passed to and from NavCustomControl.

enum {
   kNavCtlShowDesktop   = 0,     // show desktop, parms = nil
   kNavCtlSortBy = 1,            // sort key field, parms->NavSortKeyField
   kNavCtlSortOrder = 2,         // sort order,parms->NavSortOrder
   kNavCtlScrollHome    = 3,     // scroll list home, parms = nil
   kNavCtlScrollEnd = 4,         // scroll list end, parms = nil
   kNavCtlPageUp = 5,            // page list up, parms = nil
   kNavCtlPageDown = 6,          // page list down, parms = nil
   kNavCtlGetLocation = 7,       // get current location, parms<-AEDesc
   kNavCtlSetLocation = 8,       // set current location, parms->AEDesc
   kNavCtlGetSelection   = 9,    // get current selection, parms<-AEDescList
   kNavCtlSetSelection   = 10,   // set current selection, parms->AEDescList
   kNavCtlShowSelection   = 11,  // make selection visible, parms = nil
   kNavCtlOpenSelection   = 12,  // open view of selection, parms = nil
   kNavCtlEjectVolume = 13,      // eject volume, parms->vRefNum
   kNavCtlNewFolder = 14,        // create a new folder, parms->StringPtr
   kNavCtlCancel = 15,           // cancel dialog, parms = nil
   kNavCtlAccept = 16,           // accept dialog default, parms = nil
   kNavCtlIsPreviewShowing   = 17, // get preview status, parms<-Boolean
   kNavCtlAddControl   = 18,     // add one control, parms->ControlHandle
   kNavCtlAddControlList   = 19, // add control list, parms->Handle (DITL)
   kNavCtlGetFirstControlID = 20, // get 1st control ID, parms<-UInt16
   kNavCtlSelectCustomType   = 21,
                  // select custom menu item. parms->NavMenuItemSpec
   kNavCtlSelectAllType = 22,
                  // select an "All" menu item, parms->SInt16
   kNavCtlGetEditFileName = 23,
                  // get save dialog's file name, parms<-StringPtr
   kNavCtlSetEditFileName = 24
                  // set save dialog's file name, parms->StringPtr
};
typedef SInt32 NavCustomControlMessage;

Adding Your Controls

After the kNavCBCustomize message has been processed, your own interface elements can be added to any dialog after you receive the kNavCBStart message. The easiest way to set this up is to create a 'DITL' resource (in local coordinates relative to the customization area). Use the next example for this purpose:

gDitlList = GetResource ( 'DITL', 3000 );
theErr = NavCustomControl ( callBackParms->context, 
                             kNavCtlAddControlList,gDitlList);

Then, when the dialog is being closed, respond to the kNavCBTerminate event message by disposing of this resource. Of course, a 'DITL' resource is not the only method. You can call NewControl and then send the ControlHandle to Navigation Services by using the next example. Likewise with a ControlHandle, be sure to dispose of it after receiving the kNavCBTerminate event message.

gCustomControl = NewControl( callBackParms->window, 
                 &itemRect, "\pcheckbox", false, 
                 1, 0, 1, checkBoxProc, NULL );
theErr = NavCustomControl( callBackParms->context, 
                 kNavCtlAddControl,gCustomControl);

Handling Your Controls

To work with your controls, use your event callback procedure to respond to mouseDown events. You can use the Dialog Manager to find out which one of your controls was clicked but you must use a designated offset to map the Dialog Manager's control ID to your custom controls. The next example shows how to respond to a mouseDown event:

void HandleCustomMouseDown( NavCBRecPtr callBackParms )
{
   OSErr           theErr = noErr;
   ControlHandle   whichControl;            
   Point           curMousePt;   
   short           theItem = 0;   
   UInt16          firstItem = 0;
   short           realItem = 0;
   SInt16          partResult = 0;
   
   GetMouse( &curMousePt );

   // find the control's item number:
   theItem = FindDialogItem( callBackParms->window, 
                              curMousePt);
   
   // find the control itself:
   whichControl = FindControlUnderMouse(curMousePt, 
                              callBackParms->window, &partResult );
   if ( whichControl != NULL && partResult != 0 )
   {
      // get the item number of the control 
      theErr = NavCustomControl( callBackParms->context,       
                              kNavCtlGetFirstControlID, &firstItem );   
      // map it to our DITL constants:
      realItem = theItem - firstItem + 1;   
      
      // finally handle your control:
      switch ( realItem )
      {}
   }
}

Drawing Previews

Navigation Services gives you built in preview support for common preview types by using QuickTime's Preview components. These components are also used by the Image Compression Manager. If, however, you introduce new special file types to preview different types of data, you may want override the appearance of how file previews are displayed. To implement your own previews, set the kAllowPreviews flag in the browserOptions field of NavDialogOptions record. Then provide a preview callback routine to draw the preview. Your preview callback procedure gets called whenever an object is selected from the browser. I chose to do a simple preview example in Figure 5 by drawing the file name and include an extra "Preview Info" button.

Figure 5. A Customized Preview.

Use the callBackParms->eventData->param field that contains an AppleEvent Descriptor of the file to preview. To use my preview info button, I respond to the kNavCBAdjustPreview event in my event callback procedure. This event determines if the preview area has been turned on or off (the user clicked Show/Hide Preview button), or if the preview areas has been resized. To check if preview is on or off, I use the next example:

Boolean previewShowing;
theErr = NavCustomControl( callBackParms->context, 
                 kNavCtlIsPreviewShowing, &previewShowing );

To determine the preview area that was resized, I reference the callBackParms->previewRect field. This will require that I move my preview info button as a result of the resizing. The minimum preview area is 145 pixels wide by 118 pixels high.

Some file selections may require that I remove my preview info button. Using the callBackParms->eventData>param field, I can add or remove my button based, depending on if the selection is a file or a folder. Each time a different file object is clicked, I may want to have a preview with different sets of controls for each file type. So every time the preview callback procedure is called, I must decide to create and remove my individual controls accordingly.

In order to respond to my controls I have to call TrackControl in my event callback procedure. This is similar to the way you interact with your own controls in the customization area. Finally, you needn't worry about errors. Navigation Services will output any possible file system error on a given file to the upper left corner of the preview area.

pascal Boolean myPreviewProc( NavCBRecPtr callBackParms, 
                             NavCallBackUserData callBackUD )
{   
   OSErr         theErr = noErr;
   Boolean       previewShowing = false;
   AEDesc        resultDesc;
   FSSpec        previewFileSpec;
   Boolean       result = false;
   myGlobalsPtr  globals = (myGlobalsPtr)callBackUD;
   // find out of the preview area is showing:
   theErr = NavCustomControl( callBackParms->context, 
                 kNavCtlIsPreviewShowing, &previewShowing);
if ( theErr == noErr && previewShowing )   
   {         
      // make sure we have the right descriptor type:
      if ((theErr = AECoerceDesc(
                      (AEDesc*)callBackParms->eventData.param,
                      typeFSS, &resultDesc )) == noErr)
      {
         FInfo info;   
         // get the FSSpec from the descriptor:
         BlockMoveData( *resultDesc.dataHandle,
                      &previewFileSpec, sizeof( FSSpec ) );   
         if ((theErr = FSpGetFInfo(
                      &previewFileSpec, &info )) == noErr)
         {
            // the finder info was successfully retrieved, so we have a file to preview:
            short      saveTxFont;
            short      saveTxSize;   
            GrafPtr    savePort;
            Rect       previewInfoButtonRect;
            // setup and create our Preview Info button:
            SetRect( &previewInfoButtonRect,
                        callBackParms->previewRect.left+10,
                         callBackParms->previewRect.bottom-30,
                        callBackParms->previewRect.right-10,
                        callBackParms->previewRect.bottom-10 );
            if ( globals->gPreviewInfoButton == NULL )
               // the control does not exist, so create one:
               globals->gPreviewInfoButton = 
               NewControl( callBackParms->window, &previewInfoButtonRect,
                          "\pPreview Info", false, 1, 0, 1,
                          pushButProc, NULL);
            saveTxFont = callBackParms->window->txFont;
            saveTxSize = callBackParms->window->txSize;
            GetPort( &savePort );
            SetPort( callBackParms->window );
            // draw our preview information:
            TextSize( 10 );
            TextFont( applFont );
            MoveTo(    callBackParms->previewRect.left + 8,
                        callBackParms->previewRect.top + 15 );
            DrawString( previewFileSpec.name );
            SetPort( savePort );
            TextFont( saveTxFont );
            TextSize( saveTxSize );
            // show our Preview Info button:
            if (globals->gPreviewInfoButton != NULL )
            {
               ShowControl( globals->gPreviewInfoButton );
               Draw1Control( globals->gPreviewInfoButton );
            }
            // return true since we handled drawing the preview:
            result = true;
         }
         else
         {
 // failed trying to get the file to preview, so remove our Preview Info button
 // since we don't plan to draw the preview:
            if ( globals->gPreviewInfoButton != NULL )
            {
               DisposeControl( globals->gPreviewInfoButton );
               globals->gPreviewInfoButton = NULL;
            }
         }
      }   
   }      
   return result;
}

Extending the Type Popup Menu

If you would like to add your own menu items to the type popup menu found in the Open and Save dialog boxes, use NavMenuItemSpec to describe each menu item you want to add. This lets you add specific document types to be opened, or different ways of saving a file (i.e. HTML format, Text with line breaks, etc.). The menu items can be added to the menu by adding a Handle to one or more NavMenuItemSpecs to the popupExtension field in the NavDialogOptions record.

To handle your custom popup menu items, respond to the kNavCBPopupMenuSelect event when Navigation Services calls your event callback procedure. It is up to you to interpret the menu item data and respond accordingly. Extending the popup menu also requires a filter callback procedure if you are to re-filter the browser after the menu selection is made.

Conclusion

As you can see, Navigation Services is a powerful and easy to use component in the Mac OS. At last you have a modern interface to navigate and organize your file system. Your experience with this technology will be rewarding from the start. It is easy to get started and once you have a basic familiarity with the API, you can then tackle the more advanced features. You will soon discover that writing a Navigation Services "savvy" application will be well worth the time and investment.

References

  • "Programming with Navigation Services", (Apple Computer, 1998).
  • Inside Macintosh, Volume 1, The Standard File Package, (Addison-Wesley Publishing, 1985).
  • Inside Macintosh: Files, Standard File Package, (Addison-Wesley Publishing, 1992).
  • Inside Macintosh: QuickTime Components, (Addison-Wesley Publishing, 1993).

Acknowledgements

Thanks to Sean Findley, Yan Arrouye, and Otto Schlosser.

Mac OS, Macintosh, QuickTime are registered trademarks of Apple Computer, Inc. PowerPC is a trademark of International Business Machines Corporation, used under license therefrom. Screenshots of NavGetFile and NavPutFile, © 1997-98, Apple Computer, Inc. All Rights Reserved.


Keith Mortensen (morten.k@apple.com) is a software engineer for Apple Computer working on various human interface products for the Mac OS, currently contributing to Navigation Services. When Keith is off work, he's busy programming, working on his Suburban, and spends his spare time four wheeling and enjoying the outdoors at the Russian River and Tuolumne County areas of California.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Freeway Pro 7.0.3 - Drag-and-drop Web de...
Freeway Pro lets you build websites with speed and precision... without writing a line of code! With its user-oriented drag-and-drop interface, Freeway Pro helps you piece together the website of... Read more
Cloud 3.3.0 - File sharing from your men...
Cloud is simple file sharing for the Mac. Drag a file from your Mac to the CloudApp icon in the menubar and we take care of the rest. A link to the file will automatically be copied to your clipboard... Read more
Cyberduck 4.6.5 - FTP and SFTP browser....
Cyberduck is a robust FTP/FTP-TLS/SFTP browser for the Mac whose lack of visual clutter and cleverly intuitive features make it easy to use. Support for external editors and system technologies such... Read more
Firefox 36.0 - Fast, safe Web browser. (...
Firefox for Mac offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals and... Read more
Thunderbird 31.5.0 - Email client from M...
As of July 2012, Thunderbird has transitioned to a new governance model, with new features being developed by the broader free software and open source community, and security fixes and improvements... Read more
VOX 2.4 - Music player that supports man...
VoxIt just sounds better! The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features & support for all audio formats you should ever need... Read more
A Better Finder Rename 9.46 - File, phot...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
WALTR 1.0.9 - Drag-and-drop any media fi...
WALTR is designed to make it easy to upload and convert any music or video file to an iPad or iPhone format for native playback. It supports a huge variety of media file types, including MP3, MP4,... Read more
Default Folder X 4.6.14 - Enhances Open...
Default Folder X attaches a toolbar to the right side of the Open and Save dialogs in any OS X-native application. The toolbar gives you fast access to various folders and commands. You just click on... Read more
Boom 2 1.1 - System-wide pro audio app f...
Boom 2 is a system-wide volume booster and equalizer app that is designed especially for OS X 10.10 Yosemite. It comes with a smart interface, self-calibrates itself according to your Mac, offers... Read more

Legacy Quest is an Upcoming Rouge-like T...
Legacy Quest is an Upcoming Rouge-like That’ll Kill the Whole Family Posted by Jessica Fisher on February 26th, 2015 [ permalink ] Nexon Co. | Read more »
Grudgeball: Enter the Chaosphere Review
Grudgeball: Enter the Chaosphere Review By Jordan Minor on February 26th, 2015 Our Rating: :: MUSCLE MENUniversal App - Designed for iPhone and iPad Regular Show gets an above average game.   | Read more »
Action RPG League of Angels – Fire Raide...
Gaia is being invaded by the Devil Prince and the demonic Devil Army at his disposal, and it’s up to you and your avatar to defeat him in League of Angels – Fire Raiders. Raise a mighty army from hundreds of recruitable angel heroes and take the... | Read more »
Burn Rubber on the Ice With a New Cars:...
Burn Rubber on the Ice With a New Cars: Fast as Lightning Update Posted by Jessica Fisher on February 26th, 2015 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
AdVenture Capitalist Review
AdVenture Capitalist Review By Jordan Minor on February 26th, 2015 Our Rating: :: DAS KAPITALUniversal App - Designed for iPhone and iPad An inadvertent Marxist manifesto.   | Read more »
Monster vs Sheep Review
Monster vs Sheep Review By Jennifer Allen on February 25th, 2015 Our Rating: :: SAMEY FUNUniversal App - Designed for iPhone and iPad What Monster vs Sheep lacks in variety it makes up for with stress relieving fun. At least, for a... | Read more »
Is Your Face Ready for the New Outwitter...
Is Your Face Ready for the New Outwitters 2.0 Trailer? Posted by Jessica Fisher on February 25th, 2015 [ permalink ] One Man Left Studios has announced that their turn-based strategy game, | Read more »
HowToFormat Review
HowToFormat Review By Jennifer Allen on February 25th, 2015 Our Rating: :: USEFUL TIPSiPhone App - Designed for the iPhone, compatible with the iPad Making a presentation and want to get it just right? HowToFormat teaches you how... | Read more »
Thermo Diem Review
Thermo Diem Review By Jennifer Allen on February 25th, 2015 Our Rating: :: GETS TO THE POINTUniversal App - Designed for iPhone and iPad Want to know whether it’s warmer or colder tomorrow? That’s precisely what Thermo Diem will... | Read more »
Tap Heroes Review
Tap Heroes Review By Jennifer Allen on February 25th, 2015 Our Rating: :: TAPPING & GRINDINGUniversal App - Designed for iPhone and iPad Tap like crazy, grind like crazy, in this tapping game that overcomplicates the mix.   | Read more »

Price Scanner via MacPrices.net

New Travel Health App “My Travel Health” iOS...
Rochester, Minnesota based Travel Health and Wellness LLC has announced that its new iOS app help safeguard the user’s health when traveling abroad — “My Travel Health” is now available on the Apple... Read more
Sale! MacBook Airs for up to $115 off MSRP
B&H Photo has MacBook Airs on sale for up to $100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 11″ 128GB MacBook Air: $799 100 off MSRP - 11″ 256GB MacBook Air: $999 $100... Read more
15-inch 2.0GHz Retina MacBook Pro (refurbishe...
The Apple Store has Apple Certified Refurbished previous-generation 15″ 2.0GHz Retina MacBook Pros available for $1489 including free shipping plus Apple’s standard one-year warranty. Their price is... Read more
Wither The iPad mini? End Of The Road Imminen...
AppleDailyReport’s Dennis Sellers predicts that the iPad mini is going to be left to wither on the vine, as it were, and then just allowed to fade away — a casualty of the IPhone 6 Plus and other... Read more
Android and iOS Duopoly Owns 96.3% of Smartph...
IDC reports that Android and iOS inched closer to total domination of the worldwide smartphone market in both the fourth quarter (4Q14) and the calendar year 2014 (CY14). According to data from the... Read more
13-inch 2.4GHz Retina MacBook Pro available f...
MacMall has the 2013 13″ 2.4GHz/128GB Retina MacBook Pro available for $999.99 for a limited time. Shipping is free. Their price is $300 off original MSRP, and it’s the only sub-$1000 new Retina... Read more
Save up to $300 on a new Mac, $30 on an iPad,...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
Mac minis available for up to $75 off MSRP
MacMall has Mac minis on sale for up to $75 off MSRP including free shipping. Their prices are the lowest available for these models from any reseller: - 1.4GHz Mac mini: $459.99 $40 off - 2.6GHz Mac... Read more
WaterField Unveils Versatile Padded Gear Pouc...
San Francisco manufacturer WaterField Design’s new Padded Gear Pouch is a light and handy-sized, yet protective, organizer for every kind of take-along gear: technology, travel, toiletries,... Read more
College Student Deals: Additional $50 off Mac...
Take an additional $50 off all MacBooks and iMacs at Best Buy Online with their College Students Deals Savings, valid through April 11, 2015. Anyone with a valid .EDU email address can take advantage... Read more

Jobs Board

*Apple* Solutions Consultant - Retail Sales...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Solutions Consultant (ASC)- Retail S...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
Sr. Technical Services Consultant, *Apple*...
**Job Summary** Apple Professional Services (APS) has an opening for a senior technical position that contributes to Apple 's efforts for strategic and transactional Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.