TweetFollow Us on Twitter

May 99 Getting Started

Volume Number: 15 (1999)
Issue Number: 5
Column Tag: Getting Started

Resource Files

by Dan Parks Sydow

How a Mac program accesses resources stored in external files

Understanding what resources are, and how to work with them, is critical to programming the Mac. After even only the briefest introduction to Macintosh programming, it becomes obvious to a programmer that it's resources that define the interface of a Mac program. But resources can be used for much more than defining the look of a program's menus, windows, and dialog boxes. A resource can be used to store a sound, a few (or a few thousand) characters of stylized text, a program preference (such as the screen placement of a window), or just about any other type of information a clever developer can think of.

The resources that define a program's interface are invariably stored within the application itself - within the program's resource fork. But a resource doesn't have to be a part of a program in order to be used by that program. Instead, a resource can reside in a separate resource file that the program opens and accesses. In this article we take a look at why resources might be stored in a file external to a program, and how a program accesses the resources in such a file. In next month's Getting Started we'll expand on this month's techniques in order to develop a program that creates, maintains, and uses, its own preferences file.

Resource Forks

The contents of a Macintosh file are said to exist in forks. There are two types of file forks - the resource fork and the data fork. A Macintosh file can consist of either or both types of forks (some will say that a file always consists of both forks, though one fork may just happen to be empty). As you'd expect, the resource fork holds resources. The data fork holds other types of information. Exactly what type of information is in a file's data fork is dependent on the program that created the file. If you save a SimpleText document, for instance, the resulting file will have a data fork that holds the text of the saved document. You can see how much information is stored in both forks of any one file by opening that file in ResEdit and choosing Get Info from the File menu. Figure 1 shows that a small SimpleText text file named READ ME has 356 bytes in its resource fork and 4092 bytes in its data fork. Without a program like ResEdit, you wouldn't know the exact byte values of the file's forks. But the general result should be as you'd expect - a text file stores the file's characters in the data fork, and has little need for resources (in this case SimpleText stores a resource that keeps track of any stylized text that appears in the READ ME file).


Figure 1. Using ResEdit to find the size of a file's forks.

When viewed in a hex editor (a type of text editor that displays a file's contents in hexadecimal format), the information in either fork appears to be simply a stream of hexadecimal characters. Consider ResFiles, this month's example program. When a PowerPC version of ResFiles is built, the Metrowerks linker merges the compiled program code with the project resources. The result is a program that holds the compiled code in its data fork and resources in its resource fork. In Figure 2 a special hex editor named BrainHex (shareware available for downloading at <http://www.psyber.com/~brainscn/brainhex.html>) is displaying the hex content of both forks.


Figure 2. Using a hex editor to view the contents of both forks of a file.

In the sense that both forks hold a series of hex characters, the contents of the two forks look similar. But there are differences. Those differences involve formatting - the way in which special formatting characters appear throughout information in the fork. When a program opens a file of its own creation, the program knows how to read in the information from the file's data fork - and it knows how to format that information. When a word processor opens one of its files, it knows which words should be displayed in boldface, the font to use for each section of text, and so forth. When a graphics program opens one of its files, it knows how to interpret the data fork information and display the proper shapes in the appropriate colors, patterns, and so on. Thus no single program can open and make use of the data fork of all types of files - no one program knows how to format the data stored in every type of file. For a file's resource fork, this isn't the case - there is a single program that can make sense of the contents.

The information that makes up a resource follows a clearly defined format. A developer who knows that format can write an editor that can open and display (in a meaningful way) that resource. Regardless of the type of file the resource resides in (the resource fork of an application, a word processor file, a graphics file, and so forth), the resource editor can make sense of that resource. Apple has created such a program - ResEdit. Apple isn't the only company that's created a resource editor, though. Most notable of the third-party resource editors is the very useful Resorcerer by Mathemaesthetics, Inc. (<http://www.mathemaesthetics.com>).

Applications and Resource Files

When a program reads information from a file - whether the information is a resource or some other type of data - the operation is a relatively safe one. When an error does occur, it more typically occurs when a program writes information to a file. If a write operation fails, the result can be a corrupt file. A program is safe in storing its interface-related resources in its own resource fork because the program typically reads the resources from this fork, but seldom or never writes to this fork. When a program opens a window, it may do so be reading WIND resource information into memory. When a program displays a menu bar and menus, it may do so be reading MBAR and MENU resources. In both cases no resource fork information is altered. If a program is to work with resources that it may in fact alter, it is safer to store these resources in a different resource fork. That is, these resources should be kept in the resource fork of a separate file. Corruption of a file is always bad news, but if the writing of a resource corrupts an external file, the damage is less costly than if the writing of the resource corrupts the application itself.

A preferences file is a good example of the use of storing resources external to an application. A preferences file doesn't have to be a resource file (software developers can devise any number of schemes for storing user-configurable options in a file), but it often is. The information in a preferences file can be altered by a program any time the user specifies that some application setting should be changed (often be choices made in a Preferences dialog box accessed through a Preferences item in the program's Edit menu). If the changing of some information in a preferences file corrupts that file, the application is not damaged. If a subsequent attempt to access the preferences file fails, the application typically creates a new preferences file that holds some default settings. The user will need to re-enter his or her preferences, but that's a small price to pay to preserve the integrity of the application.

Avoiding application corruption is one reason to keep a resource in an external file. Other reasons for storing resources outside of an application involve portability and application updating. For example, a program that plays sounds may store those sounds as sound resources in external resource files (refer to Getting Started in this year's February issue of MacTech for the details of sound resource playing). To allow the program to play more, or new, sounds, the user might then need to obtain only the sound files. The user won't have to get a new copy of the entire application and then reinstall the program (which would be the case if the sounds were all stored within the application's resource fork). Carrying on with our sound playing program example, imagine that the application allowed users to supply their own sounds. It's an easy task to get sounds that are stored as resources: one can create, download, buy, or swap files with others. So here again the user benefits - he or she can obtain sounds and make use of them without having to obtain a new version of the sound playing program.

Toggling Resource Forks

Before jumping into the specifics of working with resource files, a little resource-related terminology is in order. When most or all of a file's information is held in the file's resource fork, that file is often referred to as a resource file. When that file's resource fork is being manipulated (opened, written to, read from, or closed) by a program, programmers often refer to the fork itself as a file. When you hear about a Toolbox routine being used to "open the resource file," that routine is in fact opening the resource fork of the file. So in your readings of resource-related material, you'll occasionally encounter the terms resource fork and resource file used interchangeably. In this introductory article we'll make a special effort to stick with fork when discussing the fork and file when discussing the file that owns the fork - but be aware that a looseness of this terminology does exist. Here we'll also talk about an external resource file. Of course any file is external to a program, but for clarity we'll put an emphasis on external. That should help avoid confusion with a project resource file, the contents of which become internal to an application and thus a part of an application's internal resource fork.

When a program executes, its own resource fork is open - by default it's available for reading from and writing to. This same program can open the resource forks from any number of other files as well. When a program opens a resource fork (including its own), the File Manager assigns a reference number to it. This reference number is then used by the application to make one (and only one) resource fork the current fork. Having only one resource fork considered the current fork prevents the application from loading a wrong resource into memory. Keep in mind that different files can hold similarly numbered resources. For instance, two files could each hold a WIND resource with an ID of 128. If a call to GetNewWindow() specified that a WIND with an ID of 128 be used as the basis for the new window, and the resource forks of both of these files were open, the program could load the wrong resource. Specifying which fork to use before calling GetNewWindow() solves this problem.

The Toolbox routine CurResFile() returns the reference number of the resource fork that's considered current. When an application launches we know that the application's resource fork is automatically opened and made current. If an application will be opening other resource forks, it's a good idea to retrieve and save the reference number of the application resource fork just after application start-up:

short      gApplRsrcForkRef;

gApplRsrcForkRef = CurResFile();

When the application opens the resource fork of a different resource file, the reference number of the newly opened file will be returned to the program. At that time the program should store this reference number as well. Then, at any time in the execution of the program either resource fork can be made current by calling the Toolbox function UseResFile(). The only parameter to this routine is the reference number of the file to make current. This more involved (but still incomplete) snippet provides an example:

short      gApplRsrcForkRef;
short      gFileRsrcForkRef;

gApplRsrcForkRef = CurResFile();

[ Here we'd open the resource fork of a resource file ]
[ and save its reference number in gFileRsrcForkRef ]

UseResFile( gFileRsrcForkRef );

[ Now resource-related tasks, such as calls to  ]
[ GetNewWindow(), GetPicture(), and GetResource(), ]
[ use resources from the external resource file ]

UseResFile( gApplRsrcForkRef );

[ Now resource-related tasks use resources from the ]
[ application resource fork       ]

Opening and Closing a Resource Fork

The previous snippet left out an important detail - exactly how a program goes about opening the resource fork of a file. This task is accomplished by calling the Toolbox routine FSpOpenResFile(). The routine name's leading FSp indicates that the function is one that works with a file system specification - a variable of type FSSpec. An FSSpec for a file can be obtained by calling the Toolbox function FSMakeFSSpec(). This function requires four parameters. Together, the first and second parameters define where on disk the file named in the third parameter is located. The fourth parameter is the file system specification, and is filled in by FSMakeFSSpec(). Consider this snippet:

#define   kRsrcFileName   "\pMyResourceFile"

short      volRef = 0;
long       dirID = 0;
FSSpec     rsrcFSSpec;
 
FSMakeFSSpec( volRef, dirID, kRsrcFileName, &rsrcFSSpec );

The first parameter is the reference number for the volume that holds the file in question. Every volume, or drive, connected to the user's machine has a unique volume reference number. The second parameter is an ID for the directory that holds the file. Every directory, or folder, on a drive has a unique directory ID. The third parameter is the file's name. To reference any one file, only these three pieces of information (volume reference number, directory ID, and file name) are ever needed. From this information a FSSpec can always be created.

If a file is located in the same folder as the application that's accessing the file, then a value of 0 can be used in place of both the volume reference number and the directory ID. For simplicity, that's what we've done in the above example. Plugging in a zero for the first two parameters is a simple means of getting an FSSpec for a file, but it also means that the file must be stored in the same folder as the application or the returned FSSpec will be invalid. In a future Getting Started article we'll explore more fully the topic of working with files - including how a program accesses files in other directories and on other disks.

Once the program has the FSSpec of a file, a call to the Toolbox function FSpOpenResFile() is made to open the resource fork of the file. The first of two FSpOpenResFile() parameters is the just-obtained FSSpec. The second parameter is a file permission constant. Using the Apple-defined fsRdPerm allows the application to read from (load resources from) the opened fork, but denies the application the ability to write to (alter resources in) the fork. To give an application the power to both read from and write to a resource fork, instead use fsRdWrPerm as the permission constant:

gFileRsrcForkRef = FSpOpenResFile( &rsrcFSSpec, fsRdWrPerm );

With all the pieces to the fork-opening puzzle in place, we can go back and rewrite the earlier snippet - the one that omitted the details of opening the resource fork. This final snippet opens the resource fork of a file named MyResourceFile and then opens a window based on WIND resource 128 from MyResourceFile. The code then goes on to open a second window. This second window is also based on a WIND with an ID of 128 - but this second window uses a WIND resource from the application's resource fork.

#define   kRsrcFileName   "\pMyResourceFile"

short      gApplRsrcForkRef;
short      gFileRsrcForkRef;

short      volRef = 0;
long         dirID = 0;
FSSpec      rsrcFSSpec;

gApplRsrcForkRef = CurResFile();

FSMakeFSSpec( volRef, dirID, kRsrcFileName, &rsrcFSSpec );
gFileRsrcForkRef = FSpOpenResFile( &rsrcFSSpec, fsRdWrPerm );

UseResFile( gFileRsrcForkRef );
GetNewWindow();

UseResFile( gApplRsrcForkRef );
GetNewWindow();

CloseResFile( gFileRsrcForkRef );

The above snippet ends with a call to CloseResFile() - the Toolbox function that closes an open resource fork. CloseResFile() is called only to close the resource fork of an external file - the application's resource fork remains open as long as the application is running. Also note that if a program will be using the resources in an external file frequently, that file's resource fork can be left open for the duration of a program's running. When the program exits there's then no need to call CloseResFile() - the program automatically closes all open resource forks (including its own) when the program terminates.

ResFiles

This month's program is called ResFiles. Running ResFiles results in the appearance of the menu bar and window shown in Figure 3. The one menu of interest is shown - the Picture menu holds an item named Display All Pictures. When ResFiles launches, an empty window opens. Choosing the Display All Pictures item results in ResFiles opening the resource fork of an external resource file and then displaying one after another every picture that's stored in that file. In this example the external file (named MyResourceFile) holds five PICT resources - each displaying one of the different "flavors" of the iMac computer. The number of pictures in MyResourceFile, and the pictures themselves, can be changed and the Display All Pictures item will still work correctly.


Figure 3. The ResFiles menu bar and window.

Creating the ResFiles Resources

The project begins with the creation of a new folder named ResFiles in your CodeWarrior development folder. Launch ResEdit and create a new resource file named ResFiles.rsrc, making sure to designate the ResFiles folder as the resource file's destination. Figure 4 shows the five types of resources that go into the ResFiles.rsrc file.


Figure 4. The ResFiles resources.

The one ALRT and one DITL resource used by ResFiles are standard to Getting Started - they're used in the display of an error-handling alert posted by the DoError() routine that's a part of each column's example program. The one WIND resource is used in the creation of the program's picture-displaying window. The exact placement and size of the window isn't critical, but it should be large enough to hold whatever pictures will eventually be displayed in it. Figure 4 shows the four MENU resources that the ResFiles program uses. After creating the MENU resources, create a single MBAR resource that references the ID of each of the four menus. That completes the ResFiles.rsrc file. Don't quit ResEdit just yet, though.

Now you need to create a second resource file. This file won't become a part of the project, but will instead serve as the external file that the ResFiles program accesses. Choose New from the ResEdit File menu, then make sure the file will end up in the ResFiles folder - the same folder that holds the project resource file ResFiles.rsrc. Give the file the name MyResourceFile (that's the name the ResFiles code will be expecting to see). Now draw or find a few pictures and paste them into this resource file. The number of pictures you paste into this file isn't critical - the ResFiles code is written to accommodate any number of PICTs. The ResFiles code doesn't adjust the size of the picture-displaying window though, so you'll want to keep the size of the pictures in line with the size of the window defined in the WIND resource in the ResFiles.rsrc file. Figure 5 shows that MyResourceFile consists of nothing more than a number of PICT files. After saving both files, quit ResEdit. You're now ready to create the ResFiles project.


Figure 5. The MyResourceFile resources.

Creating the ResFiles Project

Start up CodeWarrior and choose New Project from the File menu. Base the new project on the MacOS:C_C++:MacOS Toolbox:MacOS Toolbox Multi-Target project stationary. Uncheck the Create Folder check box before clicking the OK button. Give the project the name ResFiles.mcp, and make sure the ResFiles folder will be the project's destination.

Add the ResFiles.rsrc file to the new project and then remove the SillyBalls.rsrc file. No standard ANSI libraries are used by the project, so you can optionally remove the ANSI Libraries folder from the project window. Don't add the second resource file - the one you've named MyResourceFile. The ResFiles program will eventually use this file - the file won't become a part of the program.

Now choose New from the File menu to create a new, empty source code window. Save the new file, giving it the name ResFiles.c. Choose Add Window from the Project menu to add this file to the project. Next, remove the SillyBalls.c placeholder file from the project window. Now you're all set to type in the source code.

If you want to save some work, you can skip all the above steps_ and instead download the entire ResFiles project (including the resource file MyResourceFile) from MacTech's ftp site at <ftp://ftp.mactech.com/src/>.

Walking Through the Source Code

ResFiles starts with the standard list of constant definitions, most of which define resource IDs. One exception is kRsrcFileName, which defines the name of the external file that holds the picture resources that ResFiles will make use of.

/********************* constants *********************/

#define      kWINDResID        128
#define      kMBARResID        128
#define      kALRTResID        128
#define      kPICTiMacID       128
#define      kRsrcFileName     "\pMyResourceFile"

#define      kSleep            7
#define      kMoveToFront      (WindowPtr)-1L

#define      mApple            128
#define      iAbout            1

#define      mFile             129
#define      iQuit             1

#define      mPicture          131
#define      iDisplayPicts     1

ResFiles declares three global variables. The Boolean flag gDone tells the program that it's time to quit. The two short variables, gApplRsrcForkRef and gFileRsrcForkRef, are used to hold the reference number of the application's resource fork and the reference number of the resource fork of the external resource file that ResFiles opens. gFileRsrcForkRef is set to 0 - this is a part of the program's simple scheme of keeping track of whether an external resource fork is currently open. A gFileRsrcForkRef value of 0 indicates that no external resource fork is initially open.

/****************** global variables *****************/

Boolean      gDone;
short        gApplRsrcForkRef;
short        gFileRsrcForkRef = 0;

Next come the program's function prototypes.

/********************* functions *********************/

void      ToolBoxInit( void );
void      MenuBarInit( void );
void      InitWindow( void );
void      OpenResourceFork( void );
void      CloseResourceFork( void );
void      DrawOnePicture( short );
void      DrawAllPictures( void );
void      EventLoop( void );
void      DoEvent( EventRecord *eventPtr );
void      HandleMouseDown( EventRecord *eventPtr );
void      HandleMenuChoice( long menuChoice );
void      HandleAppleChoice( short item );
void      HandleFileChoice( short item );
void      HandlePictureChoice( short item );
void      DoError( Str255 errorString );

The main() function begins by initializing the Toolbox and the menu bar. Because the launching of a program automatically opens the program's resource fork and makes that fork current, and because ResFiles hasn't explicitly opened the resource fork of an external resource file yet, we know that the application resource fork is now the current resource fork. So we also know we're justified in calling CurResFile() to take hold of the reference number of the application resource fork for later use by the program. After that an empty window is opened (for the eventual display of pictures), and the program goes into an event loop.

void   main( void )
{  
   ToolBoxInit();

   MenuBarInit();
   gApplRsrcForkRef = CurResFile();
   InitWindow();

   EventLoop();
}

ToolBoxInit() and MenuBarInit() are the same as prior versions.

/******************** ToolBoxInit ********************/

void      ToolBoxInit( void )
{
   InitGraf( &qd.thePort );
   InitFonts();
   InitWindows();
   InitMenus();
   TEInit();
   InitDialogs( nil );
   InitCursor();
}

/******************** MenuBarInit ********************/

void      MenuBarInit( void )
{
   Handle          menuBar;
   MenuHandle      menu;
   
   menuBar = GetNewMBar( kMBARResID );
   SetMenuBar( menuBar );
   menu = GetMenuHandle( mApple );
   AppendResMenu( menu, 'DRVR' );
   
   DrawMenuBar();
}

InitWindow() consists of some very basic code that opens a window. The resource fork of the external file hasn't been opened yet, so ResFiles looks to the application resource fork for the WIND resource specified in the call to GetNewWindow(). As an added precaution, a call to UseResFile() with an argument of gApplRsrcForkRef could precede the call to GetNewWindow(). Such a use of UseResFile() might be overkill here, but it could be practical in a large program where it is harder to keep track of just when external resource forks are open.

/******************** InitWindow *********************/

void      InitWindow( void )
{
   WindowPtr   window;
   window = GetNewWindow( kWINDResID, nil, (WindowPtr)-1L );
   ShowWindow( window );
   SetPort( window );
}

When the user chooses Display All Pictures from the Picture menu, the application-defined function DrawAllPictures() is invoked. This function begins by invoking the program's OpenResourceFork() function to open the resource fork of the picture-holding external resource file. OpenResourceFork() is discussed ahead, though by now you can guess that its code relies on calls to FSMakeFSSpec() to create an FSSpec for the resource file and FSpOpenResFile() to then open the file's resource fork. After opening the resource fork, DrawAllPictures() enters a loop that draws one picture at each pass through the loop. The Toolbox function Count1Resources() returns the number of resources of a particular type in the current resource fork. After all the resource file's pictures have been drawn, the application-defined function CloseResourceFork() (discussed ahead) is invoked to close the fork.

/****************** DrawAllPictures ******************/

void      DrawAllPictures( void )
{
   short      i;
   short      numPicts;
   short      pictID = kPICTiMacID;

   OpenResourceFork();
   numPicts = Count1Resources( 'PICT' );

   for ( i = 0; i < numPicts; i++ )
   {
      DrawOnePicture( pictID );
      pictID++;
   }

   CloseResourceFork();
}

Pass DrawOnePicture() the resource ID of a PICT resource and the function loads that resource's picture data into memory and draws the picture. The picFrame field of a picture holds the boundaries of the picture. After ensuring that the boundaries have an origin of (0, 0), a call to the Toolbox function DrawPicture() does the work of drawing the picture. A call to the Toolbox function Delay() creates a delay of a second and a half (the first parameter being the number of sixtieths of a second to pause). The second Delay() parameter gets filled with the length of time the user's Mac has been on, in sixtieth seconds (a value you'll almost always ignore). Without a delay the picture would be overwritten almost immediately by the next picture (because DrawOnePicture() is being called from within a loop in the DrawAllPictures() routine).

/****************** DrawOnePicture *******************/

void   DrawOnePicture( short pictID )
{
   PicHandle       picture;
   Rect            r;
   short           width;
   short           height;
   unsigned long   theLong;

   picture = GetPicture( pictID );
   if ( picture == nil )
      DoError( "\pLoading picture failed" );
  
   r = (**picture).picFrame;
   
   width = r.right - r.left;
   height = r.bottom - r.top;
 
   SetRect( &r, 0, 0, width, height );

   DrawPicture( picture, &r ); 

   Delay( 90, &theLong );

   EraseRect( &r );
   
   ReleaseResource( (Handle)picture );
}

OpenResourceFork() requires little explanation - almost all of its code was introduced earlier in this article. The only new twist is the call to the Toolbox function ResError(). After calling a Resource Manager function, you can call ResError() in order to determine whether a resource-related error occurred. ResError() checks the value that's kept in the ResErr system global variable and returns an OSErr value that describes the type of error that occurred. If there was no error, ResError() returns a value of noErr. The More Macintosh Toolbox volume of Inside Macintosh provides more information on FSpOpenResFile() and error values returned by ResError().

/***************** OpenResourceFork ******************/

void      OpenResourceFork( void )
{
   short      volRef = 0;
   long       dirID = 0;
   FSSpec     rsrcFSSpec;
   OSErr      err;

   FSMakeFSSpec( volRef, dirID, kRsrcFileName, &rsrcFSSpec );

   gFileRsrcForkRef = FSpOpenResFile( &rsrcFSSpec, fsRdPerm );
   err = ResError();
   if ( err != noErr )
      DoError( "\pOpening resource file failed" );

   UseResFile( gFileRsrcForkRef ); 
}

To close the external resource fork, ResFiles calls the application-defined CloseResourceFork() function. This short routine relies on the Toolbox function CloseResFile() to close the fork. After that, gFileRsrcForkRef is set to 0 to indicate that no external resource fork is open. Recall that when gFileRsrcForkRef was declared, it was assigned a value of 0. If our program ever needs to check to see if an external resource fork is open, it can take a look at the value of gFileRsrcForkRef. If this global variable has a value of 0, an external fork isn't open. If gFileRsrcForkRef has any value other than 0, the program knows an external resource fork is open. Before exiting, CloseResourceFork() calls UseResFile() to ensure that the application's resource fork is now the current application fork.

/**************** CloseResourceFork ******************/

void   CloseResourceFork( void )
{
   CloseResFile( gFileRsrcForkRef );
 
   gFileRsrcForkRef = 0;
 
   UseResFile( gApplRsrcForkRef );
}

The remainder of the ResFiles code is the event-handling code with which you should be familiar - it's included in all of our Getting Started programs.

/********************** EventLoop ********************/

void      EventLoop( void )
{      
   EventRecord      event;
   
   gDone = false;
   while ( gDone == false )
   {
      if ( WaitNextEvent( everyEvent, &event, kSleep, nil ) )
         DoEvent( &event );
   }
}

/*********************** DoEvent *********************/

void      DoEvent( EventRecord *eventPtr )
{
   char   theChar;
   
   switch ( eventPtr->what )
   {
      case mouseDown: 
         HandleMouseDown( eventPtr );
         break;
      case keyDown:
      case autoKey:
         theChar = eventPtr->message & charCodeMask;
         if ( (eventPtr->modifiers & cmdKey) != 0 ) 
            HandleMenuChoice( MenuKey( theChar ) );
         break;
      case updateEvt:
         BeginUpdate( (WindowPtr)(eventPtr->message) );
         EndUpdate( (WindowPtr)(eventPtr->message) );
         break;
   }
}

/******************* HandleMouseDown *****************/

void      HandleMouseDown( EventRecord *eventPtr )
{
   WindowPtr   window;
   short       thePart;
   long        menuChoice;
   
   thePart = FindWindow( eventPtr->where, &window );
   
   switch ( thePart )
   {
      case inMenuBar:
         menuChoice = MenuSelect( eventPtr->where );
         HandleMenuChoice( menuChoice );
         break;
      case inSysWindow : 
         SystemClick( eventPtr, window );
         break;
   }
}

/******************* HandleMenuChoice ****************/

void      HandleMenuChoice( long menuChoice )
{
   short   menu;
   short   item;
   
   if ( menuChoice != 0 )
   {
      menu = HiWord( menuChoice );
      item = LoWord( menuChoice );
      
      switch ( menu )
      {
         case mApple:
            HandleAppleChoice( item );
            break;
         case mFile:
            HandleFileChoice( item );
            break;
         case mPicture:
            HandlePictureChoice( item );
            break;
      }
      HiliteMenu( 0 );
   }
}

/****************** HandleAppleChoice ****************/

void      HandleAppleChoice( short item )
{
   MenuHandle      appleMenu;
   Str255          accName;
   short           accNumber;
   
   switch ( item )
   {
      case iAbout:
         SysBeep( 10 );
         break;
      default:
         appleMenu = GetMenuHandle( mApple );
         GetMenuItemText( appleMenu, item, accName );
         accNumber = OpenDeskAcc( accName );
         break;
   }
}

/******************* HandleFileChoice ****************/

void      HandleFileChoice( short item )
{
   switch ( item )
   {
      case iQuit:
         gDone = true;
         break;
   }
}

When the user chooses Display All Pictures from the Picture menu, HandlePictureChoice() gets called. HandlePictureChoice() then invokes DrawAllPictures() to open the external resource file and display all of its pictures in the program's window.

/***************** HandlePictureChoice ***************/

void      HandlePictureChoice( short item )
{
   switch ( item )
   {
      case iDisplayPicts:
         DrawAllPictures();
         break;
   }
}

/*********************** DoError *********************/

void      DoError( Str255 errorString )
{
   ParamText( errorString, "\p", "\p", "\p" );
   
   StopAlert( kALRTResID, nil );
   
   ExitToShell();
}

Running ResFiles

Run ResFiles by selecting Run from CodeWarrior's Project menu. After compiling the code and building a program, CodeWarrior runs the program. The menu bar and a small empty window appear. Choose Display All Pictures from the Picture menu to display each of the PICT resources that are a part of the MyResourceFile resource file. Note that if you move this resource file out of the application's folder, the program will invoke it's DoError() routine and quit. Go ahead and try forcing that error to verify that DoError() does its work!

The program can be changed without doing any of the following: altering source code, recompiling, or making changes to the program's resources. To see how that's accomplished, launch ResEdit and open the MyResourceFile resource file. Remove or add any number of pictures. The only requirement is that the PICT resource IDs are numbered sequentially starting with a value of 128. Then save and close the file. Now again run ResFiles (if ResFiles was already running, that's fine too). Choose Display All Pictures from the Picture menu and the program will successfully display all of the pictures in MyResourceFile - regardless of the changes you made.

Till Next Month...

Will the use of an external resource file benefit your own Mac program? The need for an external file may not be readily apparent, but indeed your program can make use of at least one such file. A program's preferences file is often nothing more than a resource file. Now that you've read a primer on resource files, you're part of the way to giving your own program the ability to store preferences. Next month you'll go the rest of the way to that end. In next month's Getting Started column we'll carry on with resource files, with the emphasis on describing how a program creates a resource file on the fly and then writes resources to, and reads resources from, that file. Until next month you can dig deeper into resources by reading the Resource Manager chapter of the More Macintosh Toolbox volume of Inside Macintosh...

 
AAPL
$98.15
Apple Inc.
-0.23
MSFT
$43.58
Microsoft Corpora
-0.31
GOOG
$587.42
Google Inc.
+1.81

MacTech Search:
Community Search:

Software Updates via MacUpdate

Knock 1.1.7 - Unlock your Mac by knockin...
Knock is a faster, safer way to sign in. You keep your iPhone with you all the time. Now you can use it as a password. You never have to open the app -- just knock on your phone twice, even when it's... Read more
Mellel 3.3.6 - Powerful word processor w...
Mellel is the leading word processor for OS X and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
LibreOffice 4.3.0.4 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
Freeway Pro 7.0 - Drag-and-drop Web desi...
Freeway Pro lets you build websites with speed and precision... without writing a line of code! With it's user-oriented drag-and-drop interface, Freeway Pro helps you piece together the website of... Read more
Drive Genius 3.2.4 - Powerful system uti...
Drive Genius is an OS X utility designed to provide unsurpassed storage management. Featuring an easy-to-use interface, Drive Genius is packed with powerful tools such as a drive optimizer, a... Read more
Vitamin-R 2.15 - Personal productivity t...
Vitamin-R creates the optimal conditions for your brain to work at its best by structuring your work into short bursts of distraction-free, highly focused activity alternating with opportunities for... Read more
Toast Titanium 12.0 - The ultimate media...
Toast Titanium goes way beyond the very basic burning in the Mac OS and iLife software, and sets the standard for burning CDs, DVDs, and now Blu-ray discs on the Mac. Create superior sounding audio... Read more
OS X Yosemite Wallpaper 1.0 - Desktop im...
OS X Yosemite Wallpaper is the gorgeous new background image for Apple's upcoming OS X 10.10 Yosemite. This wallpaper is available for all screen resolutions with a source file that measures 5,418... Read more
Acorn 4.4 - Bitmap image editor. (Demo)
Acorn is a new image editor built with one goal in mind - simplicity. Fast, easy, and fluid, Acorn provides the options you'll need without any overhead. Acorn feels right, and won't drain your bank... Read more
Bartender 1.2.20 - Organize your menu ba...
Bartender lets you organize your menu bar apps. Features: Lets you tidy your menu bar apps how you want. See your menu bar apps when you want. Hide the apps you need to run, but do not need to... Read more

Latest Forum Discussions

See All

Murl the Squirrel (Games)
Murl the Squirrel 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Meet Murl. He is teased by a group of flying squirrels because he can't fly. Determined to show them he's can fly, he meets... | Read more »
Celleste (Games)
Celleste 0.1 Device: iOS Universal Category: Games Price: $2.99, Version: 0.1 (iTunes) Description: Lots of cute action with amazing 3D graphics and a new type of gameplay! Take control over the forces of the universe to help a group... | Read more »
Super Heavy Sword (Games)
Super Heavy Sword 0.0.1 Device: iOS Universal Category: Games Price: $.99, Version: 0.0.1 (iTunes) Description: Get Ready to Get HEAVY! Monster Robot Studios presents SUPER Heavy Sword! The sequel to the smash hit HEAVY sword which... | Read more »
Angels In The Sky (Games)
Angels In The Sky 1.00 Device: iOS Universal Category: Games Price: $6.99, Version: 1.00 (iTunes) Description: - This game is only for the iPhone 5s. please do not use the iPad, iPhone 5 or earlier devices.- Just touch or holding... | Read more »
80 Days (Games)
80 Days 1.0.2 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.2 (iTunes) Description: 1872, with a steampunk twist. Phileas Fogg has wagered he can circumnavigate the world in just eighty days. Choose your own route... | Read more »
Micromon (Games)
Micromon 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: 130+ Animated Monsters to Catch & Battle! No waiting, play at your own pace! Embark on an epic monster capture RPG like none... | Read more »
Empire Manager (Games)
Empire Manager 1.0 Device: iOS iPhone Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: Become ruler of an empire. Manage your economy, develop technology, hire an army and conquer the world in this addictive turn-... | Read more »
Empire Manager HD (Games)
Empire Manager HD 1.0 Device: iOS Universal Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: Become ruler of an empire. Manage your economy, develop technology, hire an army and conquer the world in this addictive... | Read more »
Star Admiral Review
Star Admiral Review By Rob Thomas on July 30th, 2014 Our Rating: :: ADMIRABLE ADMIRALSUniversal App - Designed for iPhone and iPad While this new digital CCG may feel a bit familiar, Star Admiral offers a sci-fi twist and galaxy’s... | Read more »
Zap! Pow! Become a Badass Wizard in Phan...
Zap! Pow! | Read more »

Price Scanner via MacPrices.net

iPad Cannibalization Threat “Overblown”
Seeking Alpha’s Kevin Greenhalgh observes that while many commentators think Apple’s forthcoming 5.5-inch panel iPhone 6 will cannibalize iPad sales, in his estimation, these concerns are being... Read more
Primate Labs Releases July 2014 MacBook Pro P...
Primate Labs’ John Poole has posted Geekbench 3 results for most of the new MacBook Pro models that Apple released on Tuesday. Poole observes that overall performance improvements for the new MacBook... Read more
Apple Re-Releases Bugfixed MacBook Air EFI Fi...
Apple has posted a bugfixed version EFI Firmware Update 2.9 a for MacBook Air (Mid 2011) models. The update addresses an issue where systems may take longer to wake from sleep than expected, and... Read more
Save $50 on the 2.5GHz Mac mini, plus free sh...
B&H Photo has the 2.5GHz Mac mini on sale for $549.99 including free shipping. That’s $50 off MSRP, and B&H will also include a free copy of Parallels Desktop software. NY sales tax only. Read more
Save up to $140 on an iPad Air with Apple ref...
Apple is offering Certified Refurbished iPad Airs for up to $140 off MSRP. Apple’s one-year warranty is included with each model, and shipping is free. Stock tends to come and go with some of these... Read more
$250 price drop on leftover 15-inch Retina Ma...
B&H Photo has dropped prices on 2013 15″ Retina MacBook Pros by $250 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.3GHz Retina MacBook Pro: $2249, $250 off... Read more
More iPad Upgrade Musings – The ‘Book Mystiqu...
Much discussed recently, what with Apple reporting iPad sales shrinkage over two consecutive quarters, is that it had apparently been widely assumed that tablet users would follow a two-year hardware... Read more
13-inch 2.5GHz MacBook Pro on sale for $999,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $999.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $100 off MSRP. Price is... Read more
Save up to $300 on an iMac with Apple refurbi...
The Apple Store has Apple Certified Refurbished iMacs available for up to $300 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. These are the best prices on... Read more
WaterField Unveils 15″ Outback Solo & 13″...
Hard on the heels of Apple’s refreshed MacBook Pro Retina laptops announcement, WaterField Designs has unveiled a 15-inch version of the waxed-canvas and leather Outback Solo and a 13-inch version of... Read more

Jobs Board

Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full Read more
Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**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
Sr. Product Leader, *Apple* Store Apps - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.