TweetFollow Us on Twitter

X Files Carbonara

Volume Number: 19 (2003)
Issue Number: 8
Column Tag: Recipes

X Files Carbonara

Making Navigation Easier for the Impatient

by Richard Patterson

The Good ol' ways

Before I was coerced into being carbonized, I had a simple scrap of code I could grab and use whenever I needed my application to create and write a file.

   long            byteCounter;
   StandardFileReply   reply;
   FSSpec         asciFile;
   short         asciFileNum;
   char            *textData;
   OSErr         err = noErr;
StandardPutFile("\pSave Text Data as:", 
"\pFullLookupTable.txt", &reply);
   if(reply.sfGood)
   {
      asciFile = reply.sfFile;
      FSpDelete(&asciFile);
// ignore any error caused if there is no such file
      err = FSpCreate(&asciFile,'XCEL','TEXT', -1);   /* -1 = system script */
      err = FSpOpenDF(&asciFile, fsCurPerm, &asciFileNum);
      if (err != noErr) return err;
      err = FSWrite(asciFileNum, &byteCounter, textData);
      if (err != noErr) return err;
      err = FSClose(asciFileNum);
   }
   return err;

It couldn't get much more straightforward than that. Those were the days when the Toolbox really made life simpler for a programmer. It helped me tell the user (generally myself) what he was supposed to do and even suggest a default name for the file. The only suspect part of this code was the shortcut method of insuring that the file was indeed a virgin file by attempting to delete it before (re)creating it. I knew there were more elegant ways to let StandardPutFile tell me that the user was replacing an existing file rather than just creating a brand new one, but most of the time I didn't care. I just wanted to write the file and get on with it. (Most of my programming is for in-house use only, so I can get away with quick-and-dirty solutions that you should only try at home.)

Recently I was desperately trying to debug an After Effects plug-in to send images to a film recorder, and I realized the only way I was going to be able tell what was going on would be to save a file capturing the state of the image at a certain point. I don't write applications that write files all that often, but years ago I acquired the prejudice that reading and writing files is one of the most basic functions the operating system needs to do and should therefore be a very simple programming task. So I resurrected a scrap of code designed to save an image buffer as a simple Photoshop file and threw it into the soup.

The next thing I knew I was spending two days trying to figure out how to replace the method shown above with something that would work inside my carbon code running on OS-X. I found myself floating around in all kinds of convoluted discussions of Apple Events and Unicode text. I eventually vented my frustration on Apple Developer Support whose suggestions for further reading and examples only seemed to complicate what I thought I was beginning to grasp. Fortunately the support technician was very patient, and I was able to crystallize my ferment into a rational suggestion that OS-X should provide a much simpler higher level function to help the less experienced programmer create and write a file. The support technician agreed that was a good idea, but indicated that it was not at the top of their priorites. When I finally saw the light thanks to Mr. K.J. Bricknell's indispensable tome, Carbon Programming; I realized that perhaps I should write a sample function that might spare someone else the agony I had just experienced.

Non Standard

The functions for opening and writing to a file (FspOpenDF and FSWrite) still work in Carbon on OS-X, but the StandardPutFile and StandardGetFile functions have been replaced by Navigation Services. The system had just outgrown the functionality provided by the Standard File Package. There is a Navigation Services function NavPutFile that was the original replacement for StandardPutFile, but with OS-X Apple recommends that we use NavCreatePutFileDialog. If I had been keeping up, the transition from StandardPutFile to NavPutFile to NavCreatePutFileDialog might have been smooth and effortless. Instead I woke up and found the following definition staring me in the face:

OSStatus NavCreatePutFileDialog (
   const NavDialogCreationOptions * inOptions,
   OSType inFileType,
   OSType inFileCreator,
   NavEventUPP inEventProc,
   void * inClientData,
   NavDialogRef * outDialog
);

Then I discovered there were 11 other functions I must call before I can have an FSSpec to use in the familiar way.

What I wanted was one function that took care of all the user interaction and just gave me a ready-to-wear FSSpec. It would need to know what kind of file I am trying to create, so there are three things I need to give it: the file type, the file creator and a pointer to the FSSpec.

OSErr SimpleNavPutFile(   OSType fileType, 
OSType fileCreator, 
FSSpec *theFileSpec)
{
   OSStatus         theStatus;
   NavDialogRef   theDialog;
   NavReplyRecord theReply;
   AEDesc            aeDesc;
   FSRef            fsRefParent, fsRefDelete;
   UniChar         *nameBuffer;
   UniCharCount   nameLength;
   FInfo            fileInfo;
   OSErr            err = noErr;
   theStatus = NavCreatePutFileDialog(NULL, fileType, fileCreator,
NULL, NULL,
&theDialog);
   NavDialogRun(theDialog);
   theStatus = NavDialogGetReply ( theDialog, &theReply); 
   NavDialogDispose(theDialog);
         
   if(!theReply.validRecord)
   {
      // Assuming the user changed his/her mind? No harm; no foul.
      // Still need to indicate that a file has not been created
      return -1;   
   }   
                        
   err = AECoerceDesc(&theReply.selection, typeFSRef, &aeDesc);
   if(err != noErr) return err;
   err = AEGetDescData(&aeDesc, &fsRefParent, sizeof(FSRef));
   if(err != noErr) return err;
   nameLength = 
(UniCharCount)CFStringGetLength(theReply.saveFileName);
   nameBuffer = (UniChar *) NewPtr((long)nameLength);
   CFStringGetCharacters(theReply.saveFileName, 
CFRangeMake(0, (long)nameLength), 
&nameBuffer[0]);
   if(nameBuffer == NULL) return -1; // generic error
   if(theReply.replacing)
   {
      err = FSMakeFSRefUnicode(&fsRefParent, 
nameLength, nameBuffer, 
                        kTextEncodingUnicodeDefault, 
                        &fsRefDelete);
      if(err == noErr) err = FSDeleteObject(&fsRefDelete);
      if(err == fBsyErr)
      {
         DisposePtr((Ptr)nameBuffer);
         return err;
      }
   }
   
   err = FSCreateFileUnicode(&fsRefParent, nameLength, nameBuffer,
                      kFSCatInfoNone, NULL, NULL,
theFileSpec);
   
   err = FSpGetFInfo(theFileSpec, &fileInfo);
   fileInfo.fdType = fileType;
   fileInfo.fdCreator = fileCreator;
   err = FSpSetFInfo(theFileSpec, &fileInfo);
   
   return err;
}   

So now my original scrap of code would become:

long                           byteCounter;
   StandardFileReply      reply;
   FSSpec                     asciFile;
   short                     asciFileNum;
   char                        *textData;
   OSErr                     err = noErr;
err = SimpleNavPutFile('TEXT', 'XCEL' &asciFile);
   if(err == noErr)   // a file was created
   {
      err = FSpOpenDF(&asciFile, fsCurPerm, &asciFileNum);
      if (err != noErr) return err;
      err = FSWrite(asciFileNum, &byteCounter, textData);
      if (err != noErr) return err;
      err = FSClose(asciFileNum);
   }
   return err;

I've sacrificed a little functionality in the dialog, since I can no longer suggest a default file name and prompt the forgetful user about what he is supposed to be doing. This scrap is fewer lines of code than my original, though, and even easier to use. I shall not attempt to explain what all the functions are doing in my SimpleNavPutFile. All I can say is that this works on my machine and is not meant to be anything other than a quick and dirty solution. Note that it includes the call creating the file and deals with the choice to replace an existing file. It may just amount to the same thing as using the now-deprecated NavPutFile, but I believe it lets OS-X put up the latest and greatest file navigation dialog.

I should confess that this solution will fail with OS-9 because it gives up if it cannot coerce the AEDesc to an FSRef. Bricknell's book has a discussion on page 960 of how to derive the FSSpec in OS-9 when this coercion fails. I have not included it, because my immediate concern was getting over the hump in OS-X, and I want to keep this as simple as possible.

The corresponding SimpleNavGetFile is built around

OSStatus NavCreateGetFileDialog (
   const NavDialogCreationOptions * inOptions,   
   NavTypeListHandle inTypeList,      // can be NULL
   NavEventUPP inEventProc,         // can be NULL
   NavPreviewUPP inPreviewProc,      // can be NULL
   NavObjectFilterUPP inFilterProc,   // can be NULL
   void * inClientData,            // can be NULL
   NavDialogRef * outDialog
);

Most of the parameters which can be set to NULL have system defaults that will be used when they are NULL. Setting the NavTypeListHandle to NULL simply results in no file filtering in the dialog. In order to avoid dealing with the NavDialogCreationOptions you can use NavGetDefaultDialogCreationOptions to set everything to a default.

SimpleNavGetFile(FSSpec *theFileSpec)
{   
   OSStatus                        theStatus;
NavDialogRef                     theDialog;
   NavReplyRecord                theReply;
NavDialogCreationOptions    inOptions;
AEKeyword                        theKeyword;
   DescType                      actualType;
   Size                          actualSize;
   OSErr                           err = noErr;
   
   NavGetDefaultDialogCreationOptions(&inOptions);
   theStatus = NavCreateGetFileDialog(&inOptions, 
NULL, NULL, NULL, NULL, NULL, 
&theDialog);
   NavDialogRun(theDialog);
   theStatus = NavDialogGetReply ( theDialog, &theReply); 
   NavDialogDispose(theDialog);
      
   if(!theReply.validRecord)
   {
      return -1;      
// Assuming the user changed his/her mind? 
// No harm; no foul, but need to know 
// not to try to open the file.
   }   
                        
       // Get a pointer to selected file
err = AEGetNthPtr(&(theReply.selection), 1,
                           typeFSS, &theKeyword,
                           &actualType, theFileSpec,
                           sizeof(FSSpec),
                           &actualSize);
      
   return err;
}   

If you want to filter files to limit the options provided the user in the Navigation Dialog, you can either use an 'open' resource created with a resource editor or you can create a NavTypeList. To use an 'open' resource you get the NavTypeListHandle with a GetResoure function.

NavTypeListHandle typeList = 
(NavTypeListHandle)GetResource('open', 128);
   theStatus = NavCreateGetFileDialog(&inOptions, 
typeList, 
NULL, NULL, NULL, NULL, 
&theDialog);

The 128 is just the number of the resource you have created. If no such resource is found typeList will be given a NULL value and you will be doing no file filtering.

To create your own NavTypeList from scratch you need only fill in a few blanks.

   NavTypeList            inTypeList;
   NavTypeListPtr      inTypeListPtr;
   
   inTypeList.componentSignature = kNavGenericSignature;
   inTypeList.osTypeCount = 1;
   inTypeList.osType[0] = 'TEXT';
   
inTypeListPtr = &inTypeList;
   theStatus = NavCreateGetFileDialog(&inOptions, 
&inTypeListPtr, 
NULL, NULL, NULL, NULL, 
&theDialog);

I have used a NavTypeListPtr just to minimize the confusion when it is necessary to pass a NavTypeListHandle to NavCreateGetFileDialog. The kNavGenericSignature is a system constant which tells it not to filter files by their creator. If you wanted to choose from only Excel files you could put 'XCEL' here instead. Since the osType is an array you can list several file types for a given appliction signature, and you can also make NavTypeList itself an array so that the handle tells the dialog to display any number of file types from any number of specifica applications. If you need to do this, though, you are on your own.


Richard Patterson is in charge of digital imaging at Illusion Arts, a visual effects facility in Van Nuys, CA, specializing in matte paintings and bluescreen compositing for movies. You can reach him at richard@illusion-arts.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Ys Chronicles II (Games)
Ys Chronicles II 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: After a hard fight to recover the six sacred books in Ys Chronicles I, Adol is back for a sequel! | Read more »
FINAL FANTASY Ⅸ (Games)
FINAL FANTASY Ⅸ 1.0.4 Device: iOS Universal Category: Games Price: $16.99, Version: 1.0.4 (iTunes) Description: ==========●Special sale price for the FINAL FANTASY IX release! ●20% off from February 10 to February 21, 2016... | Read more »
Tennis Club Story (Games)
Tennis Club Story 1.03 Device: iOS Universal Category: Games Price: $4.99, Version: 1.03 (iTunes) Description: Aim for the ace position of tennis club prestige in this simulation! Your leadership decides if players make it to the big... | Read more »
Juggernaut Wars guide - How to use skill...
Juggernaut Warsis a brand new auto-RPG on iOS and Android that challenges you to build a team of heroes, send them out into various different missions to defeat waves of heroes, and level them up to increase their power. The actual combat itself... | Read more »
Check out the new Pirate Attack update i...
Love pirates and board games? Well, you'll love the new Pirate Attack themed update that just launched in Game of Dice. It adds a bunch of new content themed around pirates, like an all new event map based on a pirate ship which revamps the toll... | Read more »
Splash Cars guide - How to paint the tow...
Splash Cars is an arcade driving game that feels like a hybrid between Dawn of the Plow and Splatoon. In it, you'll need to drive a car around to repaint areas of a town that have lost all of their color. Check out these tips to help you perform... | Read more »
The best video player on mobile
We all know the stock video player on iOS is not particularly convenient, primarily because it asks us to hook a device up to iTunes to sync video in a world that has things like Netflix. [Read more] | Read more »
Four apps to help improve your Super Bow...
Super Bowl Sunday is upon us, and whether you’re a Panthers or a Broncos fan you’re no doubt gearing up for it. [Read more] | Read more »
LooperSonic (Music)
LooperSonic 1.0 Device: iOS Universal Category: Music Price: $4.99, Version: 1.0 (iTunes) Description: LooperSonic is a multi-track audio looper and recorder that will take your loops to the next level. Use it like a loop pedal to... | Read more »
Space Grunts guide - How to survive
Space Grunts is a fast-paced roguelike from popular iOS developer, Orange Pixel. While it taps into many of the typical roguelike sensibilities, you might still find yourself caught out by a few things. We delved further to find you some helpful... | Read more »

Price Scanner via MacPrices.net

What iPad Pro Still Needs To Make It Truly Pr...
I love my iPad Air 2. So much that I’m grudgingly willing to put up with its compromises and limitations as a production tool in order to take advantage of its virtues. However, since a computer for... Read more
21-inch 3.1GHz 4K on sale for $1399, $100 off...
B&H Photo has the 21″ 3.1GHz 4K iMac on sale $1399 for a limited time. Shipping is free, and B&H charges NY sales tax only. Their price is $100 off MSRP: - 21″ 3.1GHz 4K iMac (MK452LL/A): $... Read more
Apple price trackers, updated continuously
Scan our Apple Price Trackers for the latest information on sales, bundles, and availability on systems from Apple’s authorized internet/catalog resellers. We update the trackers continuously: - 15″... Read more
Save up to $240 with Apple Certified Refurbis...
Apple is now offering Certified Refurbished 12″ Retina MacBooks for up to $240 off the cost of new models. Apple will include a standard one-year warranty with each MacBook, and shipping is free. The... Read more
Apple refurbished 13-inch Retina MacBook Pros...
Apple has Certified Refurbished 13″ Retina MacBook Pros available for up to $270 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 13″ 2.7GHz... Read more
Apple refurbished Time Capsules available for...
Apple has certified refurbished Time Capsules available for $120 off MSRP. Apple’s one-year warranty is included with each Time Capsule, and shipping is free: - 2TB Time Capsule: $179, $120 off - 3TB... Read more
13-inch 2.5GHz MacBook Pro (refurbished) avai...
Apple has Certified Refurbished 13″ 2.5GHz MacBook Pros available for $829, or $270 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.5GHz MacBook Pros... Read more
Apple refurbished 15-inch Retina MacBook Pros...
Apple has Certified Refurbished 2015 15″ Retina MacBook Pros available for up to $380 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2... Read more
New Liquid Crystal Technology Prevents Automo...
Researchers at the University of Central Florida have developed three new liquid crystal mixtures which will allow automobile displays to operate at unprecedented high and low temperatures In... Read more
BookBook For iPad Pro Coming Soon
The iPad Pro is a device unlike any other, and with Apple Pencil, it’s the ideal portable sketchpad: all that’s missing is the modern easel and portfolio to go. TwelveSouth’s BookBook for iPad Pro... Read more

Jobs Board

Lead Engineer - *Apple* OSX & Hardware...
Lead Engineer - Apple OSX & Hardware **Job ID:** 3125919 **Full/Part\-Time:** Full\-time **Regular/Temporary:** Regular **Listed:** 2016\-02\-10 **Location:** Cary, Read more
*Apple* System Analyst - ATOS IT Services...
Apple System AnalystReference no.198783CountryUSARegionUS - CALIFORNIACityUS - CALIFORNIA - BURBANKPosition TypeProfessionalJob AreaIT SupportJob TypeFull Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Subject Matter Expert - Experis (Uni...
This position is for an Apple Subject Matter Expert to assist in developing the architecture, support and services for integration of Apple devices into the domain. Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.