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
$567.67
Apple Inc.
+42.92
MSFT
$39.63
Microsoft Corpora
-0.06
GOOG
$524.46
Google Inc.
-2.48

MacTech Search:
Community Search:

Software Updates via MacUpdate

Ember 1.5.1 - Versatile digital scrapboo...
Ember (formerly LittleSnapper) is your digital scrapbook of things that inspire you: websites, photos, apps or other things. Just drag in images that you want to keep, organize them into relevant... Read more
Cyberduck 4.4.4 - FTP and SFTP browser....
Cyberduck is a robust FTP/FTP-TLS/SFTP browser for the Mac whose lack of visual clutter and cleverly intuitive features make it easy to use. Support for external editors and system technologies such... Read more
TechTool Pro 7.0.3 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
MacFamilyTree 7.1.6 - Create and explore...
MacFamilyTree gives genealogy a facelift: it's modern, interactive, incredibly fast, and easy to use. We're convinced that generations of chroniclers would have loved to trade in their genealogy... Read more
EtreCheck 1.9.9 - For troubleshooting yo...
EtreCheck is a simple little app to display the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support... Read more
TeamViewer 9.0.28116 - Establish remote...
TeamViewer gives you remote control of any computer or Mac over the Internet within seconds, or can be used for online meetings. Find out why more than 200 million users trust TeamViewer! Free for... Read more
Viber 4.1.0 - Send messages and make cal...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device,... Read more
Apple iOS 7.1.1 - The latest version of...
The latest version of iOS can be downloaded through iTunes. Apple iOS 7 brings an all-new design and all-new features. Simplicity Simplicity is often equated with minimalism. Yet true simplicity is... Read more
1Password 4.3 - Powerful password manage...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Lens Blur 1.3.0 - True out-of-focus boke...
Let Lens Blur transform your existing photo into true SLR-quality out-of-focus bokeh effect! Everyone needs a gorgeous personalized background for a social profile, blog, Web/UI design, presentation... Read more

Latest Forum Discussions

See All

Soccer Rally 2 Review
Soccer Rally 2 Review By Carter Dotson on April 24th, 2014 Our Rating: :: GOALKEEPINGUniversal App - Designed for iPhone and iPad Soccer Rally 2 is the most serious vehicular soccer game ever created.   | Read more »
Galaxy Conquerors Review
Galaxy Conquerors Review By Jennifer Allen on April 24th, 2014 Our Rating: :: RETRO SHOOTINGUniversal App - Designed for iPhone and iPad Old school shooting is fun but inaccurate in Galaxy Conquerors.   | Read more »
Yomi Review
Yomi Review By Rob Thomas on April 24th, 2014 Our Rating: :: C-C-C-COMBO BREAKERiPad Only App - Designed for the iPad Round One – Fight! No quarters required for this iOS adaptation of a tabletop adaptation of the arcade fighting... | Read more »
Injustice: Gods Among Us Updated with Ne...
Injustice: Gods Among Us Updated with New Characters, Leaderboards, Gear, and Online Multiplayer Posted by Rob Rich on April 24th, 2014 [ | Read more »
Spin It Review
Spin It Review By Jordan Minor on April 24th, 2014 Our Rating: :: SPIN ME RIGHT ROUNDUniversal App - Designed for iPhone and iPad Spin It has a fine puzzle game model, but its execution lacks energy.   | Read more »
Productivity App NoteSuite is Having its...
Productivity App NoteSuite is Having its Biggest Sale Ever, Just for One Week Posted by Rob Rich on April 24th, 2014 [ permalink ] | Read more »
Wayward Souls Review
Wayward Souls Review By Carter Dotson on April 24th, 2014 Our Rating: :: CARRY ON, WAYWARD SONUniversal App - Designed for iPhone and iPad Wayward Souls is a roguelike-inspired action-RPG that sets a high bar for other games to... | Read more »
The Sandbox Gets Update, Receives New Ca...
The Sandbox Gets Update, Receives New Campaign and New Elements Posted by Tre Lawrence on April 24th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Football Management Simulator One For El...
Football Management Simulator One For Eleven Released Worldwide Today for iOS Posted by Simon Reed on April 24th, 2014 [ permalink ] Free-To-Play football management title One For E | Read more »
Leo’s Fortune Review
Leo’s Fortune Review By Jordan Minor on April 24th, 2014 Our Rating: :: FORTUNATE SONUniversal App - Designed for iPhone and iPad Leo’s Fortune delivers a platforming experience as creative and refined as any console game.   | Read more »

Price Scanner via MacPrices.net

Award-Winning NoteSuite Productivity App is $...
Minneapolis based Theory.io has announced an 80-Percent Markdown NoteSuite for iPad. NoteSuite helps users stay organized by capturing their notes, to-dos and documents in one organized place.... Read more
16GB 1st generation iPad mini available for $...
Radio Shack has a select number of refurbished 1st generation 16GB WiFi iPad minis available for $199.99 on their online store. Choose free shipping or free ship-to-store. We expect these to sell out... Read more
13-inch 2.5GHz MacBook Pro on sale for $100 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $1099 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
iPad Sales “Lull” A Reality Correction Of Unm...
I have lots of time for Jean-Louis Gassée, the former Apple Computer executive (1981 to 1990) who succeeded Steve Jobs as head of Macintosh development when the latter was dismissed in 1985. Mr.... Read more
Apple Makes OS X Betas Available To All – Wit...
Apple’s OS X Beta Seed Program, which lets you install the latest pre-release builds, try it out, and submit your feedback, is now open to anyone who wants to sign on rather than to developers and... Read more
Apple Releases iOS 7.1.1 Update
The latest iOS 7.1.1 update contains improvements, bug fixes and security updates, including: • Further improvements to Touch ID fingerprint recognition • Fixes a bug that could impact keyboard... Read more
Logitech Announces Thinner, Lighter, More Fle...
Logitech has announced an update to its Ultrathin for iPad Air, iPad mini and iPad mini with Retina display, improving the flexibility and design of its award-winning predecessor with an even thinner... Read more
Logitech Introduces Hinge, Big Bang and Turna...
Logitech has announced expansion of its tablet product line with three new cases – the Logitech Hinge, the Logitech Big Bang and the Logitech Turnaround – each for the iPad Air, iPad mini and iPad... Read more
WaterField’s Rough Rider Leather Messenger Ba...
WaterField Designs have announced the new 15-inch size of their popular Rough Rider leather messenger bag, a vintage-looking bag that combines Old West charm and ruggedness with distinctly modern... Read more
New Mac Pro on sale, save $100 on the 4-Core...
J&R has the new 4-Core Mac Pro in stock today and on sale for $2899 including free shipping plus NY sales tax only. Their price is $100 off MSRP, and it’s the lowest price available for this... Read more

Jobs Board

*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
*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
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of 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
*Apple* Inc. Research Data Specialist - Appl...
…of Worldwide Market Research & Intelligence. The team is responsible for conducting Apple branded consumer market research. It is also responsible for analyzing data Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.