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

Latest Forum Discussions

See All

Whitethorn Games combines two completely...
If you have ever gone fishing then you know that it is a lesson in patience, sitting around waiting for a bite that may never come. Well, that's because you have been doing it wrong, since as Whitehorn Games now demonstrates in new release Skate... | Read more »
Call of Duty Warzone is a Waiting Simula...
It's always fun when a splashy multiplayer game comes to mobile because they are few and far between, so I was excited to see the notification about Call of Duty: Warzone Mobile (finally) launching last week and wanted to try it out. As someone who... | Read more »
Albion Online introduces some massive ne...
Sandbox Interactive has announced an upcoming update to its flagship MMORPG Albion Online, containing massive updates to its existing guild Vs guild systems. Someone clearly rewatched the Helms Deep battle in Lord of the Rings and spent the next... | Read more »
Chucklefish announces launch date of the...
Chucklefish, the indie London-based team we probably all know from developing Terraria or their stint publishing Stardew Valley, has revealed the mobile release date for roguelike deck-builder Wildfrost. Developed by Gaziter and Deadpan Games, the... | Read more »
Netmarble opens pre-registration for act...
It has been close to three years since Netmarble announced they would be adapting the smash series Solo Leveling into a video game, and at last, they have announced the opening of pre-orders for Solo Leveling: Arise. [Read more] | Read more »
PUBG Mobile celebrates sixth anniversary...
For the past six years, PUBG Mobile has been one of the most popular shooters you can play in the palm of your hand, and Krafton is celebrating this milestone and many years of ups by teaming up with hit music man JVKE to create a special song for... | Read more »
ASTRA: Knights of Veda refuse to pump th...
In perhaps the most recent example of being incredibly eager, ASTRA: Knights of Veda has dropped its second collaboration with South Korean boyband Seventeen, named so as it consists of exactly thirteen members and a video collaboration with Lee... | Read more »
Collect all your cats and caterpillars a...
If you are growing tired of trying to build a town with your phone by using it as a tiny, ineffectual shover then fear no longer, as Independent Arts Software has announced the upcoming release of Construction Simulator 4, from the critically... | Read more »
Backbone complete its lineup of 2nd Gene...
With all the ports of big AAA games that have been coming to mobile, it is becoming more convenient than ever to own a good controller, and to help with this Backbone has announced the completion of their 2nd generation product lineup with their... | Read more »
Zenless Zone Zero opens entries for its...
miHoYo, aka HoYoverse, has become such a big name in mobile gaming that it's hard to believe that arguably their flagship title, Genshin Impact, is only three and a half years old. Now, they continue the road to the next title in their world, with... | Read more »

Price Scanner via MacPrices.net

B&H has Apple’s 13-inch M2 MacBook Airs o...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for up to $150 off Apple’s new MSRP, starting at only $849. Free 1-2 day delivery is available to most US... Read more
M2 Mac minis on sale for $100-$200 off MSRP,...
B&H Photo has Apple’s M2-powered Mac minis back in stock and on sale today for $100-$200 off MSRP. Free 1-2 day shipping is available for most US addresses: – Mac mini M2/256GB SSD: $499, save $... Read more
Mac Studios with M2 Max and M2 Ultra CPUs on...
B&H Photo has standard-configuration Mac Studios with Apple’s M2 Max & Ultra CPUs in stock today and on Easter sale for $200 off MSRP. Their prices are the lowest available for these models... Read more
Deal Alert! B&H Photo has Apple’s 14-inch...
B&H Photo has new Gray and Black 14″ M3, M3 Pro, and M3 Max MacBook Pros on sale for $200-$300 off MSRP, starting at only $1399. B&H offers free 1-2 day delivery to most US addresses: – 14″ 8... Read more
Department Of Justice Sets Sights On Apple In...
NEWS – The ball has finally dropped on the big Apple. The ball (metaphorically speaking) — an antitrust lawsuit filed in the U.S. on March 21 by the Department of Justice (DOJ) — came down following... Read more
New 13-inch M3 MacBook Air on sale for $999,...
Amazon has Apple’s new 13″ M3 MacBook Air on sale for $100 off MSRP for the first time, now just $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD/Space Gray): $999 $100 off MSRP... Read more
Amazon has Apple’s 9th-generation WiFi iPads...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80-$100 off MSRP, starting only $249. Their prices are the lowest available for new iPads anywhere: – 10″ 64GB WiFi iPad (Space Gray or... Read more
Discounted 14-inch M3 MacBook Pros with 16GB...
Apple retailer Expercom has 14″ MacBook Pros with M3 CPUs and 16GB of standard memory discounted by up to $120 off Apple’s MSRP: – 14″ M3 MacBook Pro (16GB RAM/256GB SSD): $1691.06 $108 off MSRP – 14... Read more
Clearance 15-inch M2 MacBook Airs on sale for...
B&H Photo has Apple’s 15″ MacBook Airs with M2 CPUs (8GB RAM/256GB SSD) in stock today and on clearance sale for $999 in all four colors. Free 1-2 delivery is available to most US addresses.... Read more
Clearance 13-inch M1 MacBook Airs drop to onl...
B&H has Apple’s base 13″ M1 MacBook Air (Space Gray, Silver, & Gold) in stock and on clearance sale today for $300 off MSRP, only $699. Free 1-2 day shipping is available to most addresses in... Read more

Jobs Board

Medical Assistant - Surgical Oncology- *Apple...
Medical Assistant - Surgical Oncology- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Business Analyst | *Apple* Pay - Banco Popu...
Business Analyst | Apple PayApply now " Apply now + Apply Now + Start applying with LinkedIn Start + Please wait Date:Mar 19, 2024 Location: San Juan-Cupey, PR Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.