Rez 102
by: Tim Swihart


This time around, we'll look at Part 2 of the resource tutorial, naming 
conventions, and places to go for more detailed information.  This one
should be fairly short since the differences between Part 1 and Part 2 of
the tutorial are minor and we've already laid a pretty solid ground work
for the basics of resources.

For those of you who are still wondering why the changes from one part of 
the resource tutorial to the next seem to be pretty small, it's on purpose.  
This allows you to see the minimum amount of code needed to move from 
one phase to the next.  It allows you to figure out which parts of the
code are needed to start the tools, which parts are needed to open a
window, which parts are needed to insert a menu bar, etc.  By breaking the
application into smaller pieces, it's a lot easier to digest.  Many of you
may want to just grab the complete app and figure it out for yourself,
others would prefer to do it in increments so that they don't waste time
trying to decipher some one else's thought process.  :-) 

The rest of this file assumes that you've already read (and more or less 
understood) "Rez 101" which I uploaded earlier.  If you're still lost and
this file only makes it worse, then either send email or post a plea for
help.  If I don't know you're lost, I can't help (and neither can any of
the other developers/programmers/hackers/etc that can help).


What's New in Part 2?

Part 1 showed you how to start up the tools using a single tool call 
(StartUpTools) instead of a series of routines (like we used to have to
do before System Disk 5.0 saved us from the drudgery).  In Part 1, the 
application started the tools, beeped three times, and shutdown the tools, 
returning you to wherever you launched the app from.  This time around, 
we'll do all of that, but AFTER the tools are started and BEFORE the three 
calls to SysBeep, we'll insert the application's menu bar.

We'll still shutdown afterwards, so you'll never get a chance to do
anything else in this version of the application.  Part 2.5 adds an Event
Loop to the application so that we can play with Desk Accessories, pick
menu items, etc.


What's on the Menu (Bar)?

Prior to System Disk 5.0, menus were inserted one at a time in a loop.  If 
your menu bar had ten menus, you had to make the tool call "InsertMenu" 
ten times.  Not all that hard, but it could be easier.  Menus also had to be 
described in a weird text format (clearly documented in several outstanding 
books on e subject such as the Addison-Wesley "Programmer's Introduction 
to the Apple IIGS" and the Apple IIGS Toolbox Reference Volume 1).

Wouldn't it be nice if you could just type in your menus, using a tools that 
made it look and feel like you were editing REAL menus, not just a bunch of 
text laying around?  GeneSys (from Simple Software Systems International) 
and several other tools (AppMaker/GS, DesignMaster, etc) allow you to 
create your menus in a format that looks like you're really editing an actual 
menu!  Sure does make it a lot easier to see what your menus will look like 
(and to tweak them before they're really part of your application).

But what if you suddenly wanted to have your menus in French?  Or you 
wanted the text to be in Pig Latin?  Using the "old way" of doing things, 
you'd have to edit your application's source, recompile, re-link, and run
your application to see the end result (hmmm, maybe they looked better in 
English after all).  Using power tools like GeneSys, and taking full
advantage of resources, your menus are NOT part of your application source
code - they're resources (which are discrete units of data, right?
[say, "right!"]).  That means you can simply edit the text in your menu
resources and re-run your application!  No compiling, no linking, no
waiting, and the editor looks like a menu editor should instead of just
another text editor.


Look at the Shiny New Line:

If you compare the last chunk of source code (the routine named "main) in 
the rTutor.CC file from Part 2 to the same chunk from Part 2, you'll notice 
that I added ONE line of code to it.  That line of code simply calls the 
procedure that inserts the menu bar and all of its menus for us.  By "hiding" 
the code to actually insert the menu bar in another area of the source code,
I was able to make the "main" routine far more readable.  Look at the handful 
of lines of source that are in Part 2 and you can immediately tell what's 
going on.  Imagine if you expanded each routine right there instead of using 
separate routines - it would be MUCH less obvious what was going on!

Readability is a BIG concern - especially when you're publishing your source 
for the world to learn from.  If they can't read it, they can't learn very
well.


Naming Conventions are Your Friends!:

Despite what some folks say, it's important that you follow some sort of 
naming conventions in your source code so that it's easier to read.  You may 
know NOW what all of it does, but in a few weeks or months when you come 
back to fix a bug, you'll have forgotten parts of it.  It's also very
important to have easily readable code if it's going to be published (in an
article for example), turned in for a grade (do you really want the
instructor to have to guess what you're doing?), or used by others (who may
have to fix your bugs).

In the source code I write, I follow several conventions to make things 
easier.  The first one is to start all global variables with a lower case
"g" (for "global").  This lets me quickly tell if I'm assigning an important
value to a local variable (where it can quickly get lost) or to a global
variable (where it will live forever).  I also capitalize the "words" within
a global variables name (i.e.:  gToolListRef) - notice how the words are run
together in my global variables?  Local variables have their "words"
separated with an underscore (i.e.: my_mbar_hndl) and are all lower case.
That gives me three different ways to tell at a glance whether a variable
is global or local.  You may want to use only one or two ways instead of all
three - do what ever works for you, just be consistent.

All constants within my source start with a lower case "k" (for "konstant").  
why not a lower case "c"?  Because I picked this up from somebody else and 
they used a "k" (not all conventions make a lot of sense, but they are 
conventions...)  :-)   Constants, like globals variables, have their "words"
run together with each "word" starting with an upper case letter (i.e:  
"kShowClipItem").

Anywhere in my source that I'd normally imbed a hard-coded number (such 
as passing a menu ID to FixAppleMenu), I use a constant.  All of my 
constants are defined at the top of the file so that I can easily find them.  
They all have a comment after them to job my memory about what they're 
for.  I also tend to group them logically - all menu ID constants in one
group, all menu items ID's for the first menu in the next group, all menus
item ID's for the second menu in the next group, etc.  I also tend to
separate major sections of constants from one another with a dividing
line (in a comment).  This lets me group all of the smaller groups of
menu-related constants into one area, window-related constants into
another, etc.  All of these larger groups of constant come one behind
the other in near the top of the file.

I generally add new constants to the end of the constants area so that I can 
tell faster what's different from one version of a source file to the next.
(the new stuff will be at the bottom of the constants section).

I generally start my procedures off with the characters "do_" - it makes the 
source where I call those routines more readable (i.e.:  "do_make_menus" is 
more readable than "menus" or even "make_menus").  Like local variables, I 
name my procedures with all lower case letters and separate the "words" 
within their names by underscores.

Each routine in the source file is separated from the preceding one by a 
dividing line (in a comment) to make it more obvious where one routine 
starts and another ends.

If you see a variable that STARTS with an underscore, then it's a "system" 
variable - i.e.:  it's defined for us by the language author.  In the
rTutor.cc file, you can find a couple such variables ("_ownerid" and
"_toolerr").  Both of these are defined by Start.Root and are given values
by either Start.Root (in the case of "_ownerID" which is simply the app's
Memory Manager user ID) or the library (in the case of "_toolerr" which is
used after all tool calls to save whatever error the call returned).

DO NOT NAME ANY OF YOUR OWN VARIABLES WITH A LEADING UNDERSCORE!!!

You'll confuse yourself into thinking they're "system" variables and you'll
confuse others who have to read your source!  :-)

While we're on the subject, take full advantage of the pre-defined constants
that are in the interface files.  Things like using "refIsResource" instead
of "2" makes a LOT of difference when you're trying to see if the parameters
to a call are right.  One is intuitive ("refIsResource") and one is asking
for trouble (what's "2" for anyways?).  The Toolbox Reference Manuals have
a summary of all constants related to a particular tool Manager at the end
of each chapter.  Each of those constants SHOULD be in the interfaces (if
they're not, report it to Apple via the interfaces bug report form in the
Release Notes for "APW Tools & Interfaces").


Now How Do We Do Menus?

Using System Disk 5.0 and up, we can insert an ENTIRE menu bar, with a 
virtually unlimited number of menus (they really should fit on the screen, 
but that's about the only limit) can be inserted with ONE tool call!  Since 
we're only interested in using resources, I won't describe how this call
could be used with hard-coded menu descriptions (after all, this is the
90's, the old way is passe').  :-)

Once we have our menu bar resource created (using a desktop tool like 
GeneSys or the resource compiler [Rez] from APDA), we can insert it into the 
running application by calling NewMenuBar2 (remember the "2" on the end, 
leave it off and you're making a VERY different tool call!).  NewMenuBar2 
requires three parameters as input and returns a handle to the newly 
created menu bar.  The three parameters are:

1) A reference descriptor (which I defined in Rez 101) to tell whether we're 
using a resource, handle, or pointer to reference the menu bar.  Naturally, 
we're using a resource, so we use the Apple-provided constant 
"refIsResource" (this lets us not worry about what the actual value is that 
goes here - it also makes the code a LOT more readable).  This parameter is
a "word" in size (i.e.:  it takes up two bytes on the stack).

2) The reference to the menu bar.  Parameter #1 described the reference 
and Parameter #2 is the reference - be sure you understand the difference 
(one of them describes something and the other IS something).  Since we're 
using resources, this reference is simply the resource ID of the menu bar 
resource.  There is a standard type defined for menu bar resources and you 
HAVE to use it or this call won't be able to find your menu bar in the 
resource fork.  Remember, ALL resources are identified by the combination 
of BOTH their type and their ID.  This parameter is a "long" in size (i.e.:
it takes up four bytes on the stack).  Pay attention here!  It's easy to 
accidentally pass only a word (especially if you're using C [we are] and
don't have prototyped interfaces [we don't because APW C doesn't support
them]).  If you only pass a word for this parameter, the tool call will
still pull a LONG off the stack and all sorts of weird things can start
happening.

3) A pointer to the PORT of the window that you want the menu bar inserted 
into. Huh?  We don't want our main menu in a window!  We want it at the 
top of the screen!!!!  Relax, if you pass a NIL (four bytes that are zero),
then NewMenuBar2 will insert the new menu bar in the "system menu bar" 
(better known as the top of the screen)  <grin>.  Since you pass four bytes
of zeros for a NIL, this parameter must be a "long" in size (it is).  If you
do try to put a menu bar in a window, be sure you pass a pointer to the
window's PORT - if you don't know what a graf port pointer is, then look it
up in Toolbox Reference Volume 2 (and don't try to stick a menu bar in a
window if you don't understand ports).   :-)


What Else?

Actually, NewMenuBar2 will only read in your menu bar resource (and all 
associated menus and menu items) and insert it.  You have to make a couple 
of other calls to tell the Menu Manager that this newly inserted menu is the 
"system" menu and to draw it on the screen.  (If you don't draw it, the user 
can't see it).  :-)

First, we call "SetSysBar" and pass it the handle that we got back from 
NewMenuBar2.  SetSysBar tells the Menu Manager that the menu handle you 
just gave it is supposed to be treated as the system menu.  SetSysBar
doesn't return anything.

Next, we call "SetMenuBar" and pass it a NIL.  This tells the Menu Manager 
which menu bar is the "current" one.  Confused about system menus and 
current menus?  Don't be - just think of what it takes for the Menu Manager 
to keep track of the situation where you have a menu bar at the top of the 
screen (the "system" menu bar) and a desk accessory (such as C. K. Haun's 
"WriteIt!" NDA) that also have a menu bar in their window.  How does the 
Menu Manager figure out whether the application or the desk accessory 
should be told that a menu item was picked?  That's where "current" and 
"system" menu bar meanings come into play.  If you're still confused, don't 
worry about it.  Just make both of these calls and don't stick any menu bars 
in windows, then you won't have to hassle with it.  :-)

So, we've inserted the menu bar, told the Menu Manager to make it the 
"system" menu bar, and told the Menu Manager to also make it the "current" 
menu bar.  Now do we draw it so the user can see it?

NO!  What kind of application would you have if it didn't support desk 
accessories???!!!  Oh yeah, NDA's...  Forgot about them, didn't you
(and only two paragraphs after I mentioned an NDA by name).  There are a few 
applications out there that don't bother to support NDA's and that's an 
incredible shame because it only takes ONE tool call to insert NDA's into
your Apple menu!  Sure, you have to have certain tool sets started up first,
but so what!  Odds are you'd have those tool sets started anyways, so all
we're REALLY talking about IS just ONE tool call!

That "magic" tool call is "FixAppleMenu" and you pass it the menu ID of your 
Apple menu.  Actually, you can insert NDA's on ANY menu (as my Two 
Apples NDA showed years ago), but tradition (and the STRONG desire to have 
an intuitive user interface) dictates that we only insert NDA's into the 
"Apple" menu.

Does that mean we should just pass a "1" for the menu ID used in this call?  
NO!!!!  There is NOTHING requiring the Apple menu to have an ID of 1!  In 
fact, many very well known applications do NOT use "1" for the ID of the 
Apple menu (I know, I made that rather bogus assumption in an early 
version of Two Apples NDA <blush> and had to fix it later).  When you 
created your menu bar, you had to give each menu in it a unique ID.  That's 
the ID you pass to FixAppleMenu.

One more thing...  Did you ever notice that menus are all different widths 
and lengths?  That they always seem to just "exactly" fit around the text of 
the items within them?  Notice that when you created your menus, you 
didn't do ANYTHING to indicate how wide or how long the menus should be?  
Then how come the menus are all the right size?

Simple, you make a tool call and that tool measures all the text in each menu 
and makes the menus long enough and wide enough to hold the contents of 
each menu.  The tool call I'm referring to is "FixMenuBar" and it requires no 
input parameters.  It does however return a "word" that tells you how tall 
the menu is.  The Rez tutorial application puts the returned menu height in a 
local variable (so it's lost as soon as the "do_make_menus" routine is 
finished).  We never really use this number in the things we're doing, so 
that's why I didn't bother to keep it around.


Now Can I Draw the Menu Bar?

Yes, now that we've inserted the menu bar, told the Menu Manager that it 
was both the "system" and the "current" menu bar, added NDA's to our Apple 
menu, and made sure the heights and widths of the menus were all correct, 
we can finally draw the menu bar!  You do this with....  (you guessed it)  
"DrawMenuBar".  This call takes no parameters as input and returns nothing 
(kind of like SysBeep, only more special purpose in nature).


Menu Bars and Rez:

Notice I mentioned using a desktop tool like GeneSys to lay out your menus?  
Notice that the tutorial source uses Rez?  Confused over which tool to use?  
Don't be!!!  GeneSys is more intuitive than Rez for laying out menus if you 
don't really understand what you're doing.  But, since I uploaded Rez source 
for the menu bars, I'd better explain it here.

Before we jump into all the various types of resource needed to make a 
menu bar work, let's first look at what's in a menu bar from the 
application/user perspective.  The menu bar contains several menus (i.e.:
it's a list of menus).  The menus each contain several menu items (i.e.:
they're lists of items).  Each menu has a name and each item has a name.
Menus and menu items also have some special attributes (whether or not
they're disabled, whether or not the items have a command key equivalent,
etc).

In English, this means we need to have two types of lists (one type is a
list of menus and the other is a list of menu items), text strings for
each menu and each item, and attributes for menus and items.  Remembering
from Rez 101 that each type of resource is different and that Apple defined
a bunch of "standard" resource types already, we can guess that we should
have the following types of resources:

- rMenuBar  (where we list the menus in the menu bar)
- rMenu        (where we list the menu items and attributes for each menu)
- rMenuItem (where we describe each menu item)
- rPString      (where we list the "text" for each menu or item)

(rPString means that we're using a resource [that's what the "r" is for]
and it's a Pascal-style string [that's what the "P" is for] - because
that's what the Menu Manager wants).

There are a couple of ways to lay out the Rez source for a complete set of 
menu bar/menus/menu items.  My preference is to lay out the menu bar 
itself (which is a simple list of the resource ID's of the menus in the menu 
bar), then the first menu, then each item in that menu.  After the menu, but 
BEFORE the first item is described, I list the rPString for the name of that 
menu (just to keep it close to the menu definition).  After each item, but 
BEFORE the next item, I list the "rPString" for that item (again, just to
keep it close to the menu item's definition).

The alternate way is to list the menu bar, then the menus, then the menu 
items, then all of the rPStrings.  I don't like this one because it pushes
the rPStrings too far away from the menus they're with and I like keeping 
things close together (to avoid unnecessary scrolling).  Which method you 
use is up to you (you could even come up with your own).

I also define constants for ALL resource ID's used by the menu bar, menus, 
menu items, and rPStrings!  Why?  Because it's a LOT easier to figure out 
that "resource rMenu (kAppleMID)" and "resource rPString (kAppleMID)" go 
together than it would be if "kAppleMID" were just a number.  It also helps 
you figure out later which item is which number (gee, I want to insert an 
item in the File menu right after "Save", but I'm not sure which one of
these menus is the File menu and which menu item is "Save" and....).
Remember, naming conventions are your friends!

Inside the Rez source file (rTutor.rez), near the beginning of the menu
layout section, are several lines of comments that spell out the strategy
for laying out menu bars, menus, and menu items in Rez using the strategy
I prefer.  Read those six steps if you're still a little fuzzy about how to
do all of this.  It looks like a lot of typing, but most of us just use
cut and paste instead of typing new menus, menu items, etc.  Just be
careful to edit ALL of the fields of the pasted menu item or you'll get
some bizarre results...  :-)

In your C (or Asm or Pascal) source file, the constants for menu bars
should be long's (i.e.:  four bytes) - that's why they have an "L" after
them.  You'll be making toolbox calls using the resource ID's as references
to your menu bar (such as NewMenuBar2).  The constants for menus and menu
items should be WORDS (not longs, despite the fact that I accidently left
"L" on the end of each of them in Part 2's C source <blush>).  Why?
Because you'll be passing these numbers to tool calls such as FixAppleMenu
that expect a word.  We'll cover this in more detail in Part 2.5 once we've
put in the event loop.  If you're in doubt about whether to use a word or
a long, look at the toolbox reference manual's description of the call
you're going to make and pass what it expects.  The only languages that
will check the sizes for you are Pascal and an ANSI C (but only if the
interfaces that you use are "prototyped").  If you use smart enough
assembler macros, it should be able to check for you also.


Review Time:

OK, what did it take to go from Part 1 (start the tools, beep three times,
shut down) to Part 2 (start the tools, insert a menu bar, beep three times,
shut down)?  We had to add one line of source to "main" (right before the
beeps) and we had to write the routine that we wanted to call from "main"
(I'm referring of course to the routine named "do_make_menus").  We could
have slipped the call to "do_make_menus" into the middle of the SysBeeps
or at the end.  I didn't just put them at the beginning for the heck of it.
Later, we'll replace the SysBeeps with real stuff that HAS to come AFTER
the menu bar is inserted, so the SysBeeps sort of mark our place for us.


Need More Info?

You can get a different explanation of how Part 2 works by reading the 
comments in the rTutor.cc source code.  Be sure to read them in the order 
the program runs AFTER it's compiled - i.e.:  read the comments in "main", 
jump to the comments in "do_init_rom" when you hit that line, come back to 
"main" after "do_init_rom" is done, go to the comments in "do_make_menus", 
etc.  That way, you'll read the comments in the same order the program is 
actually running - it should help...

You can also consult the following references for more information:

- Apple IIGS Toolbox Reference, Volume 1 (for details on SetSysBar, 
SetMenuBar, FixAppleMenu, FixMenuBar, and DrawMenuBar).

- Apple IIGS Toolbox Reference, Volume 3 (for details on NewMenuBar2 and 
menu bar resources).


OK, So I Lied:

I told you at the beginning that this would be a short file compared to Rez 
101.  I just counted the number of words in this one and in Rez 101 
(actually, my word processor counted the words, I'm lazy <grin>) and here's 
how Rez 102 stacks up against Rez 101 size-wise:

Version        Num. Words       Num. Characters
-------------------------------------------------
Rez 101               4672                     26698
Rez 102               4483                     24951


Enjoy,
Tim S.

P.S.:  Get Rez from APDA or Developer Tools Express if you don't have it yet 
or if you have the old version that was in "Programming Tools & Interfaces 
for APW v.1.1".  Rez is part of the new "APW Tools & Interfaces v.1.1" 
package.  New customers should order part number "A0240LL/A" (price $50, 
includes manual) and those of you who bought "Programming Tools & 
Interfaces for APW" should order the "update" version by requesting part 
number "A0241LL/A" (price $25, no new manual).  These packages include 
three disks (one of which contains revised interfaces for APW/ORCA Asm 
and APW C).  The other two disks contain:

  Rez (resource compiler)
  DeRez (resource decompiler)

  LinkIIGS (scriptable linker, creates OMF 2.0/2.1, auto-express's)
  Compact (converts OMF 1.0 to OMF 2.0 so files are smaller and faster)

  CrunchIIGS (converts ORCA/Pascal and ORCA/C compiled files for LinkIIGS)
  DiskCheck (checks for block conflicts, bad bitmaps, etc)

  DumpObj (lets you inspect/disassemble OMF files)
  Duplicate (copies files without losing resource forks)

  Equal (compares files byte by byte- can compare resource forks too)
  Express (convert older apps to take advantage of ExpressLoad in System 5.x)

  Files (super fancy catalog utility)
  Join (merge two files together - used by CrunchIIGS)

  MakeBin (convert linked apps to binary image for 8-bit stuff, etc)
  MakeDirect (create custom sized stack segments for your apps)

  MakeLib (library management tool)
  ResEqual (compares two resource forks, resource by resource)

  Search (finds text in files)
  Canon (makes sure you use proper casing for tool calls)
  Canon.dict (dictionary for Canon - supports 5.0.2 tool calls)

  (nice list, eh?)  GET 'EM!   ;-)
