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...

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »

Price Scanner via MacPrices.net

13-inch M2 MacBook Airs in stock today at App...
Apple has 13″ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Apple’s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
We’ve updated our iPhone Price Tracker with the latest carrier deals on Apple’s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″ and 14″ MacBook Pros along with 13″ and 15″ MacBook... Read more
Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more

Jobs Board

Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
…relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.