CW CHAPTER 3

From Atari Wiki
Revision as of 16:35, 12 October 2011 by Admin (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search









CHAPTER 3: MENU BARS
---------------------

[NOTE: Words enclosed in asterisks (i.e. *word*) should be 
read as italicized text. This text file is copyright 1993 by 
Clayton Walnum. All rights reserved.]

Now that we've covered the easiest of GEM's forms, it's time 
to move on to something a little more complicated--
specifically, menu bars. Although menu bars are easy to 
implement, they require a fairly large chunk of code to 
handle. In this chapter, we'll learn just about everything 
there is to know about menu bars.

--The Program--

In the CHAP3 folder of your *ST Assembly Language Workshop* 
disk are the files PROG3.S, PROG3.PRG, MENU.RSC, MENU.H and 
MENU.DEF. The first two are the source code and the 
executable file for this chapter's sample program. The 
PROG3.PRG file is ready to run, but if you'd like to 
assemble the program yourself, follow the instructions that 
came with your assembler or see this book's Appendix A. The 
file MENU.RSC is the resource file for this chapter's 
program. It must be in the same directory as PROG3.PRG when 
you run the program. Finally, MENU.H and MENU.DEF are the 
header file and definition file for the menu.
	When you run the program, a menu bar will appear on the 
screen. It has four menu titles: Desk, File, Options, and 
Selects. Click on the Desk menu, and then click on the ALW 
Info selection. An alert box will appear, giving you a brief 
description of the program. Click the alert box's okay 
button to remove the box from the screen.
	Now, click on the file menu title. The file menu 
appears, which contains the Load, Save, and Quit commands. 
Click on Load or Save, and an alert box appears, telling you 
which you clicked. (In a full program, you'd get a file 
selector when you select these commands.) Clicking on the 
Quit selection will take you back to the desktop. Don't do 
this yet, though.
	Click on the Options menu title. Then click on any of 
the options. By clicking them, you turn the checkmarks on or 
off. Now, click on the Select menu title. Then click on 
Select 1, Select 2, or Select 3. An alert box will appear, 
verifying your selection. Finally, click on the Off 
selection of the Selects menu bar. This turns the select 
entries off, so that they may no longer be selected. Also, 
the word "Off" changes to "on."
	When you're finished experimenting with the menu bar, 
click the Quit command in the file menu to return to the 
desktop.

--Resource Files--

In previous chapters, we used forms that GEM provides ready-
made for us. Menu bars, however, must be created by the 
programmer. Once we create a menu bar, the information that 
describes it to GEM is placed into a special file called a 
resource. You've seen plenty of resource files. They're the 
ones with the .RSC extension. In each .RSC file is all the 
data needed to create a program's menu bars, dialog boxes, 
and other GEM objects.
	How do we create a resource file, you ask? We use a 
program called a resource construction program. Two popular 
resource construction programs are RCP.PRG, which comes with 
Laser C, and RCS2.PRG, which comes with the Atari Developer 
Kit. It doesn't really matter which resource construction 
program you use. The resulting .RSC file will be the same, 
because all resource files have a standard format.
	In this book, we'll use RCP.PRG to create our 
resources. If you are using a different resource 
construction program, you'll still be able to follow along. 
You'll just need to modify the steps slightly, since each 
resource construction program operates a little differently 
than the others.

--Creating a Menu Resource--

Your *ST Assembly Language Workshop* disk contains all the 
necessary resource files. However, it's important that you 
understand how to use your resource construction program, so 
below are the steps necessary to create this chapter's menu 
resource. Start RCP.PRG and follow the steps below to create 
the resource file.

   Step 1: Click on the "New" selection from the file menu. 
A window titled NONAME will be opened. This window is where 
we'll work on our menu bar.

   Step 2: Drag the menu icon from the left of the screen 
into the newly created window. A dialog box will appear, 
prompting you for the name of the menu tree. Press Return to 
select the default name of TREE00. The menu tree icon will 
appear in the work window.

   Step 3: Double-click the menu tree icon. The beginnings 
of your menu bar will appear in the work window.

   Step 4: Give the Desk menu selection (on your menu bar, 
not the RCP's) a single click, and then press Control-N. The 
dialog box for naming objects will appear. Name this object 
"DESK."

   Step 5: Repeat Step 4 for the File menu selection, naming 
this object "FILE."

   Step 6: Drag the word TITLE from the parts list and place 
it to the right of the File title. Double-click this new 
menu bar title. A dialog box will appear. Change the text to 
two spaces followed by the word "Options," followed by 
another two spaces.

   Step 7: Place the mouse pointer on the lower right-hand 
corner of the title's shaded area and, holding down the left 
button, expand the shading to the right, centering the title 
within it. Click once on the options title to select it. 
Then press Control-N and name the object "OPTIONS."

   Step 8: Set up another menu title in the same way, 
entering the text as two spaces followed by the word 
"Selections," followed by two more spaces. Name the object 
"SELECTS."

   Step 9: Give the Desk menu selection a single click. Then
double-click the "Your message here" entry. Change the text 
to two spaces followed by "ALW info..." and press Return. 
Press Control-N, and name the entry "INFO."

   Step 10: Give the File menu selection a single click. 
Then place the mouse pointer on the lower-right corner of 
the QUIT object. Holding down the left button, reduce the 
length of the object by dragging the corner to the left. You 
have to do this in order to uncover the menu box beneath.

   Step 11: Place the mouse pointer on the lower-left corner 
of the menu box and, holding down the left mouse button, 
drag the box downward, enlarging it so that it can fit three 
more entries.

   Step 12: Place the mouse pointer on the QUIT object and,
holding the left button down, move the object to the bottom-
most position of the menu box.

   Step 13: Drag the word "ENTRY" from the parts list and 
place it in the top position of the File menu box, making 
sure you place it as far to the left as it'll go. Double-
click it, and change the text to two spaces followed by 
"Load...", followed by two more spaces. Name the object 
"LOAD."

   Step 14: Create another menu entry below LOAD. The text 
should be two spaces followed by "Save...", followed by two 
more spaces. Name this object "SAVE."

   Step 15: Drag the ---------- icon from the parts list and 
place it below the SAVE entry, all the way to the left. Then 
move the QUIT object just below it and name it "QUIT."

   Step 16: Reduce the menu box to its smallest size using 
the same method as when you enlarged it (Step 11). Add 
enough dashes to the ---------- object (by double-clicking 
on it and changing the text) to extend it to the right-hand 
margin of the menu box.

   Step 17: Single-click the Options menu title, and enlarge 
the menu box to fit three entries.

   Step 18: Drag an ENTRY icon to the top of the Options 
menu box. Set the text to two spaces followed by "Option 1," 
followed by two more spaces. Before closing the dialog, set 
the CHECKED option in the attributes list. Name the object 
"OPTION1."

   Step 19: Create two more entries in the options menu box, 
named "OPTION2" and "OPTION3," and place them in order below 
OPTION1. The spacing of the text will be the same as in Step 
18. Do not set the CHECKED attribute for these two objects. 
Reduce the Options menu box to its smallest possible size.

   Step 20: Single-click the Selections title. Then stretch 
the menu box to fit five entries.

   Step 21: Create an entry in the Selections menu named 
"ONOFF," and enter into the text field five spaces followed 
by "On" followed by five more spaces.

   Step 22: Drag the ---------- icon to a position below the
ONOFF entry. Add two dashes to the already existing ten in 
the text field.

   Step 23: Create three entries below the dashed line. The 
entries should be named "SELECT1," "SELECT2," and "SELECT3." 
Their text fields should contain two spaces followed by 
"Selectn" (where n is the entry's number as indicated by the 
names above), followed by two additional spaces.

   Step 24: Reduce the Selections menu box to its smallest
possible size.

--Setting the Mouse Form--

Now that we understand a little about resource files--
specifically what they do for us and how to create them-- 
let's turn our attention to the program listing. The program 
start with the same type of initialization we've been using 
in all the programs: setting up a stack, releasing memory, 
and calling *appl_init*.
	Right after the initialization, you'll see a call to 
the AES function #78, *graf_mouse*. This function allows us 
to manipulate the mouse pointer in several ways. Using this 
function, we can turn the mouse pointer on or off, select a 
mouse form from the system forms, or even set up our own 
custom mouse form. In this chapter's sample program, we're 
using *graf_mouse* to set the mouse form to the arrow. We 
need to do this because when we run the program, GEM 
automatically changes the mouse form into the busy bee, in 
order to inform the user that the program is loading. 
However, once the program is loaded and running, GEM doesn't 
set the pointer back. That's our job.
	The call to *graf_mouse* looks like this:

move    #GRAF_MOUSE,control0
move    #1,control1
move    #1,control2
move    #1,control3
clr     control4
move    #ARROW,int_in0
clr.l   addr_in0
jsr     aes

The first five lines initialize the control array, giving 
the function number and the lengths of the int_in, int_out, 
addr_in, and addr_out arrays, as always. In the sixth line, 
we place the code for the arrow pointer form into *int_in0*. 
The array element *int_in0* must contain a value from Table 
3.1.

Number  Shape
----------------------------
0               Arrow
1               I-beam
2               Bee
3               Pointing hand
4               Flat hand
5               Thin crosshairs
6               Thick crosshairs
7               Outline crosshairs
255             User-defined form
256             Mouse off
257             Mouse on

Table 3.1: Mouse Forms

Right now, you need be concerned only with the values 0 
through 7 in the table. We won't be discussing the others 
until later. Just realize that all you need to do to change 
the mouse form in the sample program is to replace the 
#ARROW (which equals 0) with any other number from 0 to 7 in 
the chart. Try it!
	In the last line of the *graf_mouse* call, we place a 0 
in *addr_in0*. When all we're doing is changing the mouse to 
one of the eight standard forms, this value will always be a 
0. When we learn about user-defined mouse forms, we'll be 
placing the address of our custom forms in *addr_in0*.
	After setting up the required arrays, we call 
*graf_mouse* just like any other AES function, with a call 
to our *aes* subroutine, after which *int_out0* will contain 
an error status code: 0 if an error occurred or a value 
greater than 0 if there was no error. Since it's extremely 
unlikely that we'll get an error when using the system mouse 
forms, we don't bother checking for an error in the sample 
program.

--Loading a Resource File--

Next, we need to load our resource file--the one containing 
our menu--into memory. We do that with a call to the AES 
function #110, *rsrc_load*. The code looks like this:

move    #RSRC_LOAD,control0
clr     control1
move    #1,control2
move    #1,control3
clr     control4
move.l  #rsrc_file,addr_in0
jsr     aes

As always, in the first five lines, we initialize the 
control array. You should be very familiar with this process 
by now, since every call to the AES requires the control 
array. After taking care of the control array, we move the 
address of our resource filename into *addr_in0*. If you 
look in the data section of our program, you'll see that the 
label *rsrc_file* holds the address of the filename 
"MENU.RSC." A call to our *aes* subroutine is all that's 
necessary to get GEM to find the resource file and load it 
into memory--as long as the resource file is in the same 
directory as the program file, where it belongs.
	After the call, *int_out0* will contain a 0 if there 
was an error or a value greater than 0 if no error occurred. 
In the program, we use the instruction *tst int_out0* to 
check for an error. If we discover an error occurred, we 
display an alert box to the user and then exit the program. 
After all, our program won't work without the resource 
loaded. An error here probably means that the resource file 
is missing.
	Note that, to simplify showing error messages, the 
sample program includes a handy *alert* macro. You learned 
about macros in *The ST Assembly Language Workshop, Volume 
1*, and you used alert boxes a few pages ago, so you should 
have little difficulty understanding this simple macro.

--Getting the Resource's Address--

You may think that loading the resource is enough to get us 
into business, but it's not. Next, we must ask GEM for the 
resource's address, since many functions we'll be using in 
the program require this address. We get the address with a 
call to AES function #112, *rsrc_gaddr*. The code looks like 
this:

move    #RSRC_GADDR,control0
move    #2,control1
move    #1,control2
clr     control3
move    #1,control4
move    #R_TREE,int_in0
move    #TREE00,int_in1
jsr     aes

In the first five lines, we initialize the control array. 
Then we place the code number for the type of data in our 
resource. This value must be one of those given in Table 
3.2.

Number  Structure Type
---------------------------
0               Tree
1               OBJECT
2               TEDINFO
3               ICONBLK
4               BITBLK
5               String
6               Image data
7               obspec
8               te_ptext
9               te_ptmplt
10              te_pvalid
11              ib_pmask
12              ib_pdata
13              ib_ptext
14              bi_pdata
15              ad_frstr
16              ad_frimg

Table 3.2: Data Numbers

Yeah, I know how you're feeling. Table 3.2 looks downright 
scary. But don't let it bother you. At this point, you need 
be concerned only with the first code in the table. In fact, 
most resources you'll load in your programs will need only 
this first value.
	Finally, we must place into *int_in1* the index of the 
data structure we need the address for. This can be a bit 
confusing, too, and is something else we'll discuss in more 
detail later. Suffice it to say that a resource file is 
really an array of "objects." In our resource file, we have 
only one object, our menu. But despite the number of objects 
in our resources, we'll usually want the address of the 
entire resource. In the case of our menu, this index is 
represented by the constant TREE00, which was created by the 
resource construction program and placed into our MENU.H 
header file.
	Finally, we call our *aes* subroutine, which in turn 
calls *rsrc_gaddr* for us, using the arrays we set up. After 
the call, *int_out0* will contain a 0 if an error occurred 
or a value greater than 0 if no error occurred. More 
importantly, *addr_out0* will contain the address for which 
we are looking. In the sample program, if we don't get an 
error, we copy this address into the long word *menu_adr*, 
for safe keeping.

--Showing a Menu Bar--

Now that we have our resource file loaded, and we have its 
address, we can put our menu bar on the screen. We do this 
with a call to AES function #30, *menu_bar*, like this:

move    #MENU_BAR,control0
move    #1,control1
move    #1,control2
move    #1,control3
clr     control4
move    #1,int_in0
move.l  menu_,addr_in0  
jsr     aes

To call this function, we first set up the control array 
with the appropriate values. Then we place a 1 into 
*int_in0*. This is a flag that tells *menu_bar* that we want 
to show the menu. Finally, we put the address of our 
resource into *addr_in0* and call our *aes* subroutine. 
After the call, *int_out0* will contain a 0 if an error 
occurred or a value greater than 0 if no error occurred. If 
there was no error, the menu bar will be on the screen, as 
shown in figure 3.1.

[INSERT FIGURE 3.1]

--Getting Event Messages--

Our GEM programs are event driven. This means that, rather 
than force the user to do things in a specific preset order, 
we allow him to do whatever he wants. In other words, we can 
never be sure what the user is going to do. For example, now 
that we have a menu bar on the screen, the user may try to 
select menu functions with his keyboard or he may try to use 
his mouse. If we also had a window on the screen, the user 
may opt to manipulate the window before he uses the menu bar  
We just don't know what he'll do. This means that we must 
keep an eye out for his every action.
	GEM handles this seemingly impossible task by sending 
messages to our program. Whenever the user does something, 
he generates an event that GEM passes on to us. We won't go 
into too much detail about events in this chapter. We'll 
cover only what we need to know in order to handle a menu 
bar. But in upcoming chapters we'll use events a lot.
	Obviously, if GEM is going to be sending us 
information--in this case an event message--we need a place 
to store it. GEM stores this information in an event buffer, 
a 16-byte array that we must supply. If you look at the data 
section of this chapter's program, you'll see our message 
buffer. It looks like this:

msgbuf:
msgbuf0:    ds.w    1
msgbuf1:    ds.w    1
msgbuf2:    ds.w    1
msgbuf3:    ds.w    1
msgbuf4:    ds.w    1
msgbuf5:    ds.w    1
msgbuf6:    ds.w    1
msgbuf7:    ds.w    1

When we ask GEM for event messages, we also tell it where 
our message buffer is. When GEM returns the message, it 
places the type of message in *msgbuf0* and other 
information about the specific message in the rest of the 
buffer. What this other information is depends on the type 
of message being returned. Table 3.3 shows the contents of a 
typical GEM message.

Buffer
Element    Meaning
---------------------------------------
 0          Message ID
 1          ID of sending application
 2          Additional bytes in message
3-7         Changes with message type

Table 3.3: Contents of Message Buffer

	Element 2 of the message buffer requires explanation. 
Although the standard GEM message is 16 bytes long, it is 
possible for us to construct our own messages, which may be 
longer than the 16-byte standard. When constructing this 
type of message, we place the number of extra bytes in 
element 2 of the message buffer. But don't worry about these 
custom messages; we're a long way from discussing them--a 
long way, indeed.
	So, the next thing we must do is tell GEM to start 
sending us messages. That's the only way we'll know that the 
user has done something with our menu bar. In most full-
scale programs, we'd use a function called *evnt_multi*, but 
this function is so unwieldy (it has almost two dozen 
parameters!), that I thought it best if we opted for 
something simpler. For now, we'll start getting messages 
with a call to the AES function #23, *evnt_mesag*. That call 
looks like this:

move    #EVNT_MESAG,control0
clr     control1
move    #1,control2
move    #1,control3
clr     control4
move.l  #msgbuf,addr_in0        
jsr     aes

As you can see, the only information we need to give this 
function is the address of our message buffer, which we 
place in *addr_in0*. After the call, *int_out0* will contain 
a 1, a value that really means nothing, since it's always a 
1. More importantly, when the function returns, our message 
buffer will contain an event message, of which there are 
over a dozen types. In this chapter, we're concerned with 
only one type, the MN_SELECTED message, which has a value of 
10. In other words, if *msgbuf0* is a 10 after the call to 
*evnt_mesag*, we know the user has done something with the 
menu.

--Translating a Message--

When we call *evnt_mesag*, our program stops executing, 
while *evnt_mesag* waits for the user to do something. When 
the user does anything, such as clicking the mouse or typing 
on the keyboard, *evnt_mesag* sends us a message that tells 
us what the user did. As I mentioned previously, there are 
over a dozen different types of messages, but right now 
we're interested only in the MN_SELECTED message. With this 
message, GEM places the menu ID of the selected menu name in 
*msgbuf3* and the ID of the selected menu item in *msgbuf4*.
	To see what type of message our program received, we 
must examine the first element of the message buffer, in our 
case *msgbuf0*. We do this with these instructions:

cmpi    #MN_SELECTED,msgbuf0
bne     event_loop

The above instructions first compare the constant 
MN_SELECTED to the value returned into *msgbuf0* by our call 
to *evnt_mesag*. Then, if we didn't get a MN_SELECTED 
message, the second line sends program execution back to 
grab another message. This loop continues until we find a 
MN_SELECTED message.
	When we finally receive a MN_SELECTED message, we know 
that the user has selected a command from our menu bar. The 
next step is to figure out which command the user selected. 
We do this by checking the menu selection's ID, which is 
returned in *msgbuf4* as part of the message. In the sample 
program, we have set up a table of menu IDs. You can see 
this table in the data area, at the label *menu_ids*. We 
loop through this table, looking for the menu ID that the 
user selected. When we find it, we use its position in the 
table to find the address of the routine to which we need to 
jump in order to handle the user's request.
	In other words, if the user selects the Quit command on 
the menu, our program finds that the QUIT ID is in element 4 
of the *menu_ids* table. So, the program loads into A6 the 
address stored in the fourth element of the *menu_vecs* 
array. If you look at the *menu_vecs* array, you'll see that 
this element is the address of the *quit* section of the 
program. (Remember: by putting labels into a table, we're 
really adding the addresses the labels represent to the 
table. The *menu_ids* table, therefore, contains the 
addresses of all the routines that handle our menu's 
commands.)
	When we have the address that we need in A6, we use 
address register indirect addressing to jump to that 
address, like this:

jmp    (a6)

The *jmp* instruction is used to transfer program execution 
to an effective address. It works a lot like the *bsr* and 
*jsr* instructions, but doesn't load a return address onto 
the stack. In other words, a *jmp* is like a GOTO in BASIC, 
rather than like a GOSUB.
	In the case of the info, load, save, select1, select2, 
and select3 menu selections, all we do is show the user an 
alert box, confirming that his menu selection was received. 
In a real program, of course, we'd need to do a lot more. 
The real fun starts when the user selects one of the other 
menu items.

--Menus Items and Checkmarks--

For example, the entries in the Options menu must be checked 
or unchecked whenever the user clicks on one. To do this, we 
first must know the state of the selected menu item. We keep 
track of each option's state with three flags: *optn1*, 
*optn2*, and *optn3*. A flag value of 0 means that the menu 
item is unchecked; a value of 1 means the menu item is 
checked.
	Let's say the user clicks on Option 1, which, in the 
sample program, starts off with a checkmark, as shown in 
figure 3.2. Here's the code that handles that situation:

option1:
	tst    optn1
	beq    setcheck1
	clr    optn1   
	clr    check_flag
	bra    check
setcheck1:
	move   #1,optn1        
	move   #1,check_flag
	bra    check

[INSERT FIGURE 3.2]

First, we check the flag for the Option 1 menu item with the 
*tst* instruction. If that flag is a 0, we jump to the label 
*setcheck1*, where we change the *optn1* flag to true (1) 
and also initialize *check_flag* to true. We'll use 
*check_flag* in our call to the AES function that displays 
or removes checkmarks.
	If, as is true in our sample program, *optn1* is equal 
to 1, we set both *optn1* and *check_flag* to 0, indicating 
that we're about to turn the checkmark off.
	Finally after setting up our flags, we branch to the 
label *check*, where we actually add or remove the 
checkmark. That code looks like this:

check:
      move    #MENU_ICHECK,control0
      move    #2,control1
      move    #1,control2
      move    #1,control3
      clr     control4
      move    msgbuf4,int_in0 
      move    check_flag,int_in1      
      move.l  menu_adr,addr_in0       
     jsr      aes

This is a call to AES function #31, *menu_icheck*, which 
adds or removes checkmarks to and from menu items. After 
setting up the control array, we place the menu item ID into 
*int_in0*, the flag value into *int_in1*, and the address of 
our resource into *addr_in0*. If the value in *int_in1* is 
0, *menu_icheck* will remove the menu item's checkmark; if 
the value is 1, *menu_icheck* will add a checkmark to the 
item. After the call, *int_out0* will contain a 0 if an 
error occurred or a value greater than 0 if no error 
occurred.

--Unhighlighting a Menu Name--

When GEM receives a menu message, it automatically 
highlights the menu name on the menu bar, as seen in figure 
3.3. What it doesn't do is set the menu name back to normal 
text. This is the programmer's job, since GEM assumes that 
you want the menu name to stay highlighted until you're 
through handling the user's request (a safe assumption).

[INSERT FIGURE 3.3]

	When we're ready to unhighlight the menu's name, we 
need only call AES function #33, *menu_tnormal*. Because we 
need to call this function after every menu message, it 
seems logical to make the call into a subroutine, which 
we've done in the sample program. Here's what that 
subroutine looks like:

move    #MENU_TNORMAL,control0
move    #2,control1
move    #1,control2
move    #1,control3
clr     control4
move    msgbuf3,int_in  0
move    #NORMAL,int_in1
move.l  menu_adr,addr_in0       
jsr     aes
rts

After setting up the control array, we place the ID of the 
menu name to unhighlight in *int_in0*, a flag telling the 
function whether to highlight or unhighlight the menu 
(0=highlight, 1=unhighlight) into *int_in1*, and the address 
of the resource containing the menu into *addr_in0*. After 
the call, *int_out0* will contain a 0 if an error occurred 
or a value greater than 0 if no error occurred.

--Enabling and Disabling Menu Entries--

When the user clicks the Off selection of the Selects menu, 
we have much work to do. First, we must disable all the 
commands in the bottom part of the menu. Then we must change 
the string "Off" in the menu to read "on," since this menu 
selection should tell the user what the command does. These 
changes are shown in figure 3.4.

[INSERT FIGURE 3.4]

	To disable a menu selection, we use a call to AES 
function #32, *menu_ienable*. In our program, that call 
looks like this:

move    #MENU_IENABLE,control0
move    #2,control1
move    #1,control2
move    #1,control3
clr     control4
move    d6,int_in0
move    on,int_in1
move.l  menu_adr,addr_in0       
jsr     aes

As always, we start by setting up the control array. Then, 
we place the ID of the menu item we want to disable into 
*int_in0*, the setting code into *int_in1*, and the address 
of our resource into *addr_in0*. In the example above, D6 
holds the menu item ID, while the flag *on* holds the 
setting code, which should be 0 to disable a menu item or a 
1 to enable it. After this call, *int_out0* will contain a 0 
if an error occurred or a value greater than 0 if no error 
occurred.
	To disable all the menu items below the ONOFF menu 
item, we must call *menu_ienable* once for each. To simplify 
this, we use a loop, with register D6 containing the menu 
item ID we want to disable. Each time through the loop 
(which starts at the label *onoff*), we increment D6. 
Obviously, this technique will work only if the three menu 
item IDs are in consecutive order.

--Changing a Menu String--

After we've disabled the menu entries, we need to change the 
string in the ONOFF menu entry to "On," indicating that, if 
the user clicks on it, the menu items will be turned on. We 
do this change with a call to AES function #34, *menu_text*, 
which looks like this:

move    #MENU_TEXT,control0
move    #1,control1
move    #1,control2
move    #2,control3
clr     control4
move    #ONOFF,int_in0  
move.l  menu_adr,addr_in0       
move.l  a5,addr_in1     
jsr     aes

First, we set up the control array. Then we place the menu 
item ID of the item we want to change into *int_in0*, the 
address of the resource into *addr_in0*, and the address of 
the replacement string into *addr_in1*. Note that the new 
string must not be larger than the original string. If it 
is, you may find that the new string overflows the menu, as 
shown in figure 3.5. The strings we use for the menu in the 
sample program are found in the data section at the labels 
*off_str* and *on_str*.

[INSERT FIGURE 3.5]

--Removing a Menu Bar--

Eventually, the program's user is going to click on the 
menu's Quit selection. When he does, we need to remove the 
menu from the screen in preparation for shutting down the 
program. To remove a menu from the screen, we use another 
call to AES function #30, *menu_bar*. That call looks like 
this:

move    #MENU_BAR,control0
move    #1,control1
move    #1,control2
move    #1,control3
clr     control4
clr     int_in0 
move.l  menu_adr,addr_in0       
jsr     aes

This call is identical to the one we used to display the 
menu, except we're placing a 0 in *int_in0*, which tells the 
function to remove the menu, rather than a 1, which tells 
the function to create the menu.

--Releasing a Resource--

Before we can exit the program, we have one last bit of 
clean-up we need to do. Because we loaded a resource into 
the computer's memory, we have to tell GEM that it's okay to 
use that memory for something else. We do this with a call 
to AES function #111, *rsrc_free*. Our call to this function 
looks like this:

move    #RSRC_FREE,control0
clr     control1
move    #1,control2
clr     control3
clr     control4
jsr     aes

This call requires no parameters from us. All we need do is 
set up the control array and call our *aes* subroutine. 
After the call, *int_out0* will contain a 0 if an error 
occurred or a value greater than 0 if no error occurred.
	After releasing the resource's memory, we're all clear 
to end the program, with the usual call to *pterm0*.

--Conclusion--

While handling a menu isn't too complex in theory, it does 
require a great deal of code, as you can tell from this 
chapter's program. This is typical of GEM, which, in order 
to provide extra convenience for the user, heaps the 
programmer with additional responsibilities. But that's 
really as it should be. The user should always be the first 
consideration.

--Summary--

* GEM menus and dialogs, as well as other types of GEM 
objects, are stored in a resource file.

* The easiest way to create a resource file is to use a 
resource construction program.

* AES function #78, *graf_mouse*, is used to change the 
shape of the mouse pointer, among other things.

* Before GEM resources can be displayed on the screen, their 
resource file must be loaded into memory. This is done with 
AES function #110, *rsrc_load*.

* After a resource is loaded, the program needs to know its 
address. This address is retrieved using AES function #112, 
*rsrc_gaddr*.

* AES function #30, *menu_bar*, displays or removes a menu 
bar.

* GEM tells an application about its user's activities 
through messages, which are 16-byte records stored in a 
message buffer that the programmer supplies.

* AES function #23, *evnt_mesag*, is one way to retrieve 
messages from GEM.

* AES function #31, *menu_icheck*, adds or removes 
checkmarks from a menu item.

* AES function #33, *menu_tnormal*, highlights or 
unhighlights a menu name on the menu bar.

* AES function #32, *menu_ienable*, enables or disables 
(grays out) menu items.

* AES function #34, *menu_text*, changes the string 
displayed in a menu item.

* AES function #111, *rsrc_free*, releases the memory 
occupied by a resource.


CW_MENU.H (Source of the chapiter)
CW_PROG3MAC.S (Source of the chapiter)
CW_PROG3.S (Source of the chapiter)


Back to Assembly_language_tutorials