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

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom 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.