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

How to build a successful civilisation i...
GodFinger 2 grants you godlike powers, leaving you to raise a civilization of followers. In the spirit of games like Black & White, the GodFinger games will see you building bigger and better villages, developing more advanced technology and... | Read more »
How to get all the crabs in Mr Crab 2
Mr. Crab 2 may look like a cutesy platformer for kids, but if you're the kind of person who likes to complete a game 100%, you'll soon realise that it's a tougher than a crustacean's shell. [Read more] | Read more »
How to be a star in Britney Spears: Amer...
If you've ever wanted to be a star, baby, then you've probably already checked out Britney Spears: American Dream and are happily making your way up the charts. But fame doesn't come easy, and everyone needs a helping hand sometimes. So we've got... | Read more »
AppSpy is hiring a part time Staff Write...
| Read more »
How to save lives in ER Surgery Simulato...
A serious earthquake has struck a nearby town in ER Surgery Simulator - Emergency Doctor, and it’s up to you to save the victims. [Read more] | Read more »
Tips and tricks to get a high score in G...
Ketchapp Games loves the endless runner genre. And its newest game, Gravity Switch, is no exception. Gravity Switch takes a fresh approach, though, as you move a block, suspended in zero gravity, safely through a maze of shifting pillars. If the... | Read more »
Tips and tricks to get a high score in S...
Smash Fu is a high-paced tile-tapping game that requires quick reflexes and some practice. You’ll have to smash bricks with the skill of a seasoned black belt to get a high score. To raise the stakes a bit, you’ll also have to avoid tapping any... | Read more »
How to keep the ball rolling in Dropple
If you're new to the minimalist puzzler Dropple, you may find yourself struggling to make it beyond the first couple of steps before your ball falls into the endless abyss below. [Read more] | Read more »
Game Craft releases new Legend of War ti...
Set for release at the end of this month, real time strategy title Legend of War seems sure to delight with a veritable feast of sweet features to get stuck into. Developed by Game Craft, the game is due for release through both the App Store and... | Read more »
How not to die in Traffic Rider
Traffic Rider, an Out Run-esque game in which your ride a motorcycle recklessly into trffic, might not seem particularly complicated. [Read more] | Read more »

Price Scanner via MacPrices.net

Textkraft Professional Becomes A Mobile Produ...
The new update 4.1 of Textkraft Professional for the iPad comes with many new and updated features that will be particularly of interest to self-publishers of e-books. Highlights include import and... Read more
SnipNotes 2.0 – Intelligent note-taking for i...
Indie software developer Felix Lisczyk has announced the release and immediate availability of SnipNotes 2.0, the next major version of his productivity app for iOS devices and Apple Watch.... Read more
Pitch Clock – The Entrepreneur’s Wingman Laun...
Grand Rapids, Michigan based Skunk Tank has announced the release and immediate availability of Pitch Clock – The Entrepreneur’s Wingman 1.1, the company’s new business app available exclusively on... Read more
13-inch 2.9GHz Retina MacBook Pro on sale for...
B&H Photo has the 13″ 2.9GHz Retina MacBook Pro (model #MF841LL/A) on sale for $1599 including free shipping plus NY tax only. Their price is $200 off MSRP. Amazon also has the 13″ 3.9GHz Retina... 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
Clearance 12-inch Retina MacBooks available s...
B&H Photo has dropped prices on leftover 2015 12″ Retina MacBooks with models now available starting at $999. Shipping is free, and B&H charges NY tax only: - 12″ 1.1GHz Gray Retina MacBook... Read more
Check Apple prices on any device with the iTr...
MacPrices is proud to offer readers a free iOS app (iPhones, iPads, & iPod touch) and Android app (Google Play and Amazon App Store) called iTracx, which allows you to glance at today’s lowest... Read more
New 2016 13-inch 256GB MacBook Air on sale fo...
B&H Photo has the new 13″ 1.6GHz/256GB MacBook Air (model MMGG2LL/A) on sale for $1149 including free shipping plus NY sales tax only. Their price is $50 off MSRP. Amazon has the 13″ 1.6GHz/256GB... Read more
Apple refurbished iPad Air 2s available start...
Apple has Certified Refurbished iPad Air 2 available starting at $339. Apple’s one-year warranty is included with each model, and shipping is free: - 128GB Wi-Fi iPad Air 2: $499 - 64GB Wi-Fi iPad... Read more
Accenture and Vatican Opera Romana Pellegrina...
Accenture has announced that the official mobile application for the Extraordinary Jubilee Year of Mercy declared by Pope Francis has been built and launched by Accenture Mobility, part of Accenture... Read more

Jobs Board

*Apple* Nissan Service Technicians - Apple A...
Apple Automotive is one of the fastest growing dealer...and it shows. Consider making the switch to the Apple Automotive Group today! At Apple Automotive , Read more
ISCS *Apple* ID Site Support Engineer - APP...
…position, we are looking for an individual who has experience supporting customers with Apple ID issues and enjoys this area of support. This person should be Read more
Automotive Sales Consultant - Apple Ford Linc...
…you. The best candidates are smart, technologically savvy and are customer focused. Apple Ford Lincoln Apple Valley is different, because: $30,000 annual salary Read more
*Apple* Support Technician II - Worldventure...
…global, fast growing member based travel company, is currently sourcing for an Apple Support Technician II to be based in our Plano headquarters. WorldVentures is Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.