CW CHAPTER 4

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









CHAPTER 4: OBJECT TREES AND DIALOG BOXES
---------------------------------------- 

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

We now know how to handle the two simplest of GEM's forms, 
the alert box and the file selector. So, it's time to move 
on to the granddaddy form of them all: the dialog box. 
Because the dialog box is so versatile, we could discuss its 
uses endlessly and still not exhaust its possibilities. For 
that reason, this chapter's discussion should not be 
considered as a complete guide to dialog boxes, but only as 
an introduction. Once you understand the way dialogs work, 
the only limit will be your imagination.

--The Program--

In the CHAP4 folder of your *ST Assembly Language Workshop* 
disk are the files PROG4.S, PROG4.PRG, SAMPLE.RSC, SAMPLE.H 
and SAMPLE.DEF. The first two are the source code and the 
executable file for this chapter's sample program. The 
PROG4.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 SAMPLE.RSC is the resource file for this chapter's 
program. It must be in the same directory as PROG4.PRG when 
you run the program. Finally, SAMPLE.H and SAMPLE.DEF are 
the header file and definition file for the sample dialog.
	When you run this chapter's program (make sure the 
SAMPLE.RSC file is on the disk!), you'll see the dialog box 
shown in Figure 4.1. Clicking on the OK or CANCEL buttons 
will exit the dialog. Clicking on the up or down arrows will 
change the value displayed in the NUMBERS object. Clicking 
any of the other buttons brings up an alert box containing 
the name of the object selected. Notice that, with the radio 
buttons, only one may be selected at a time, while the 
OPTION 1 and OPTION 2 buttons can be on or off in any 
combination.

[INSERT FIGURE 4.1.]

	You can enter your name and age (lie if you want to) in 
the text fields. Use the arrow keys on your keyboard to move 
between the two fields (or click on them with the mouse), 
since, due to the OK button being set up as a default, 
pressing Return will exit the dialog box. Try to enter 
something other than upper- or lowercase letters in the name 
field, or something other than a number in the age field. No 
dice, right?

--The Definitions--

Before we get into a detailed discussion of dialog boxes, we 
must first define a couple of terms: objects and trees.
	Objects are used to visually represent each item that 
makes up a dialog box. You've seen them hundreds of times: 
boxes and buttons and text strings. Each object has its own 
set of attributes that tailor it to the programmer's (and 
eventually, the user's) needs. From a programming point of 
view, an object is a data structure, the members of which 
describe the object, storing all the necessary information 
to bring that object up on the screen.
	The objects of a dialog box are connected in an object 
tree. A tree is a way to link items in a hierarchical 
manner. That is, there's one main item (the tree's root), 
which has connected to it other items, which, relative to 
the tree's root are called children, and relative to each 
other are called siblings. The children may also have 
children of their own (and thus become parents), and so on 
down the line, each new group of siblings subordinate to the 
ones that have gone before.
	An object tree is an array of objects, the attributes 
of which are stored in the previously mentioned data 
structure. Three elements of an object's data structure 
determine the way the object fits in with the rest of the 
tree. Specifically, each object contains, among other 
things, a pointer to the next sibling, a pointer to the 
first child (the head) and a pointer to the last child (the 
tail). Because of this complexity, few programmers bother to 
try and design dialog boxes from scratch. They instead use a 
resource construction program.

--An RCP Mini Tutorial--

When we created a menu bar in chapter 3, we used Megamax's 
Resource Construction Program (RCP) to build our resource. 
Now, we'll use it again to create this chapter's sample 
dialog box. If you're using the Atari Developer's Kit, don't 
fret; the Resource Construction Set (RCS) that came with 
your kit will work equally well for our purposes. The only 
difference is the operation of the programs. (Of course, the 
complete resource file is on your *ST Assembly Language 
Workshop* disk. You don't have to create it on your own, but 
this is good practice if you're not familiar with resource 
construction programs.)
	So, everybody load up their resource construction 
programs, and let's get busy. Figure 4.1 is the dialog box 
we'll be building. You should refer to this illustration as 
you construct your version.
	Once you have your resource construction program 
loaded, go to the File option of the menu bar and select 
New. A window titled NONAME will appear. To the left of this 
window are the types of resources we can build, represented 
in icon form. Place the mouse pointer over the dialog icon, 
press and hold down the left button, and drag the icon into 
the window. Release the button, and a dialog box will 
appear, asking you for the name of the new tree. Clear the 
NAME field by pressing Escape. Then type SAMPLE and press 
Return.
	Now, double-click the new dialog icon (the one you 
dragged to the window). This will open the dialog, 
presenting you with a "blank slate." The icons to the left 
will change to a dialog box parts kit. The parts shown are 
icon representations of the types of objects you can use to 
build your dialog box. The types are as follows:
 
  Button................A box containing centered text
  String................................A line of text
  FText.................................Formatted text
  FBoxText.............A box containing formatted text
  IBox........................An invisible graphic box
  Box....................................A graphic box
  Text....................................Graphic text
  BoxChar...A graphic box enclosing a single character
  BoxText.................A graphic box enclosing text
  Icon..........................Description of an icon

	Now let's start filling our dialog box with objects.

	Step 1: Drag the STRING object onto your dialog box, 
and then double-click it. A dialog box containing a number 
of attributes will appear. At the bottom will be a line 
labeled TEXT. Clear the line by pressing the escape key. 
Then enter (without the quotes) "THIS IS A SAMPLE DIALOG 
BOX," and press Return. Drag your new string to the top of 
the dialog box and center it, as shown in Figure 4.1.

	Step 2: Drag an ICON object onto your dialog box, and 
double-click it. Click the EDIT ICON button from the dialog 
box that appears, and draw the ANALOG icon (or any icon you 
like) with your mouse. When complete, click the OK button. 
Drag the icon to the left of the text created in Step 1 and 
position it as shown in Figure 4.1.

	Step 3: If the icon is not shown in inverse (selected), 
give it a single mouse click. When the icon is selected, 
type Control-C (copy), point the mouse to the right-hand 
side of your dialog box, and type Control-V (paste) twice 
(the first keystroke deselected the original icon). You 
should now have a duplicate of the first icon. Drag it into 
position, to the right of the string, as shown in Figure 
4.1.

	Step 4: Drag the BOX object (the empty rectangle) onto 
your dialog box. Place the mouse cursor on the lower-right 
corner of the box and, holding down the left button, stretch 
the box until it's about the same size as the box labeled 
RADIO BUTTONS in Figure 4.1. Position the box, and double-
click it. An attribute dialog box will appear. Use the mouse 
to select the SHADOWED attribute. Then click the OK button.

	Step 5: Using the same method as in Step 1, create a 
string that reads "RADIO BUTTONS," and position it at the 
top of the box created in Step 4.

	Step 6: Drag a BUTTON object into the box created in 
Step 4, and double-click it. When the attribute dialog 
appears, select the following options: SELECTABLE, RADIO 
BUTN, and TOUCHEXIT. (Note that sometimes an attribute--in 
this case, SELECTABLE--has already been activated for you.) 
Modify the text field to read "#1." Then click the OK 
button. While the radio button is still highlighted (shown 
in inverse), type Control-N, and name the object "RADIO1." 
Position this button in the upper left of the "Radio Button" 
box, beneath the string, as shown in Figure 4.1.

	Step 7: Use the copy and paste functions (as in Step 2) 
to place another radio button to the right of the one 
created in Step 6. Change the button's text to read "#2" and 
change the object's name to "RADIO2."

	Step 8: Using the method in Step 7, create four buttons 
labeled "#3," "#4," "#5," and "#6," and name them "RADIO3," 
"RADIO4," "RADIO5," and "RADIO6," respectively. See Figure 
4.1 for placement.

     Step 9: Drag the EDIT:______ (not the one surrounded by 
a box) object into your dialog box, and double click it. If 
it isn't already selected, turn on the EDITABLE option. 
Clear the PTMPLT field with the escape key, and then type 
"NAME:" followed by one space and 10 underline characters. 
Use the down arrow on your keyboard to move the text cursor 
to the PVALID field. Then press Escape to clear the field. 
Type "aaaaaaaaaa." Use the down arrow key to move the text 
cursor to the PTEXT field. Then backspace till you reach a 
tilde (~) character. Now type "@" followed by nine spaces. 
Click the OK button. Then name the object "NAME." Position 
the object as shown in Figure 4.1.

	Step 10: Drag a second EDIT:______ object into your 
dialog box, and double-click it. Make sure the EDITABLE 
option is set. Change the PTMPLT field to "AGE:" followed by 
one space and two underlines. Change the PVALID field to 
"99." Move to the PTEXT field and backspace until you reach 
a tilde character. Then type "@" followed by one space. 
Click the OK button, name the object "AGE," and then 
position it as shown in Figure 4.1.

	Step 11: Drag another BUTTON object into your dialog 
box, and double-click it. Select the attributes SELECTABLE, 
SHADOWED and TOUCHEXIT. Change the button's text to "OPTION 
1." Click the OK button, name the object "OPTION1," and 
position it as shown in Figure 4.1.

	Step 12: Use the copy and paste functions to create a 
duplicate of the button created in Step 11. Change the 
button's text to "OPTION 2," name the object "OPTION2," and 
position it as shown in Figure 4.1.

	Step 13: Drag the BOXTEXT object into your dialog box, 
and double-click it. Select the SHADOWED attribute. Clear 
the PTMPLT and PVALID fields (if necessary). Then change the 
PTEXT field to four spaces followed by four 0s and four more 
spaces. Using the method shown in Step 4, stretch the box 
one segment higher (as you pull down on the mouse, the box 
will automatically "snap" to the next size). Name the object 
"NUMBERS," and position it as shown in Figure 4.1.

	Step 14: Drag another BOXTEXT icon onto your dialog 
box, and double-click it. Set the TOUCHEXIT option. Make 
sure the PTMPLT, PVALID and PTEXT fields are clear. Then, 
when positioned on the PTEXT field, hit space, Control-A, 
space (the keys, not the words). Resize the object as in 
Step 13, and name it "UPARROW." Position it on top of the 
box created in Step 13 as shown in Figure 4.1.

	Step 15: Use the copy and paste functions to create a 
duplicate of the UPARROW object. Then clear the PTEXT field 
and press space, Control-B, space. Name the object 
"DWNARROW." Then position it on top of the NUMBERS object as 
shown in Figure 4.1.

     Step 16: Drag another BUTTON object into your dialog 
box, and double-click it. Set the SELECTABLE, DEFAULT and 
TOUCHEXIT options. Change the text field to "OK." Resize and 
position the object as shown in Figure 4.1. Then name it 
"OK."

	Step 17: Drag yet another button into your dialog box, 
and double-click it. Set the SELECTABLE and TOUCHEXIT 
options. Then change the TEXT field to "CANCEL." Resize and 
position the object as shown in Figure 4.1. Then name it 
"CANCEL."

	And that's it. You've just created your first dialog 
box. Now, to save all your hard work, close the dialog box 
by clicking on the upper-left corner of the window. Then 
select the SAVE AS option from the FILE menu. Name the file 
"SAMPLE," and you're on your way. To leave the RCP, select 
the Quit option from the File menu.
	You should now have three files on your disk: SAMPLE.H, 
SAMPLE.DEF and SAMPLE.RSC. These are the files that the RCP 
created. SAMPLE.H contains all your object and tree names as 
a series of C #defines. If you want to refer to the objects 
by name in your program, you must *include* this file in 
your source code. Unfortunately, because the file is in C 
format, you must first change it to assembly language form, 
by changing each #define line to an *equ* line.
	The SAMPLE.DEF file contains information the RCP uses 
for its own purposes, and the SAMPLE.RSC file is the tree 
data for our dialog box. We'll load this data into memory 
when we run our program.

--Object Attributes--

That was a fast course in the use of a resource construction 
program. You probably have many unanswered questions. For 
example, what do all those attributes do?
	SELECTABLE simply means that the user can select the 
object. When the object is selected, it will be displayed in 
inverse video. If you set the DEFAULT option when editing an 
object, the object will be selectable with the Return key, 
as well as with a mouse click. Obviously, only one object at 
a time can be set as a default.
	The EXIT and TOUCHEXIT attributes are similar: they 
both cause the dialog box to be exited when selected. The 
difference is that, with TOUCHEXIT, the mouse button need 
not be released to exit the dialog box.
	What did you think about the RADIO BUTN option? Radio 
buttons are handy devices, allowing the programmer to set up 
a series of related buttons, only one of which may be 
selected at a time. As soon as a button is selected, the 
previously selected button is turned off. They get their 
name from those old car radio tuners with the push buttons 
to select the channel. An important note: In order for radio 
buttons to operate properly, they must have the same parent 
object; that is, they all must be "on top of" the same 
object.
	The CHECKED, SHADOWED, OUTLINED, CROSSED and DISABLED 
options affect the way the objects will be graphically 
represented on the screen. You can easily see their effect 
by using your RCP to set them for various objects. The 
options' names describe their effect.
	An EDITABLE object may be modified in some manner by 
the user.
 
--Editable Text--

Now, what's the story behind those strange text fields 
PTMPLT, PVALID, and PTEXT? These three strings combine in 
such a way as to tell GEM which part of the text is editable 
and what characters the user is allowed to input.
	PTMPLT is used as an input mask. Any text entered here 
will be displayed on the screen and will be unchangeable 
(except underline characters) by the user. PTMPLT also tells 
GEM where the user can edit the text. We indicate this with 
underline characters. In Step 9 above, the unalterable text 
is "NAME:" and the editable area, where the user will enter 
his or her name, is represented by the 10 underlines.
	The PVALID field tells GEM what type of characters we 
want the input restricted to. Each underline character in 
the PTMPLT field must have an entry in the PVALID field as 
follows:
 
  Code Characters Allowed
  ---- ------------------
  9  0 to 9
  A  A to Z, space
  a  A to Z, a to z, space
  N  0 to 9, A to Z, space
  n  0 to 9, A to Z, a to z, space
  F  DOS filename characters, plus ? * :
  P  DOS filename characters, plus \ ? * :
  p  DOS filename characters, plus \ :
  X  Any character
 
	In Step 9 we entered 10 lowercase A's in the PVALID 
field, limiting the user's input to upper- and lowercase 
letters. A logical choice for a person's name.
	Finally, the PTEXT string will be combined with PTMPLT 
when the latter is printed. Unlike the text in PTMPLT, the 
string stored in PTEXT is editable. This is handy when you 
want an editable text field displayed with a default 
setting. For example, in Step 9, if we had made the PTEXT 
string "FRED," when the dialog box appeared on the screen, 
the text cursor would appear to the right of the string 
"FRED." The user could then just leave the string as it is, 
and thus select FRED as his name, or he could backspace over 
it (or use the escape key to clear it) and type in something 
new. When the user exits the dialog box, the new information 
will be found in PTEXT, replacing what we had stored there 
previously.
	When we set up our editable text objects in Steps 9 and 
10, however, we wanted to end up with the text cursor to the 
left of a blank field, ready for the user's input. To do 
this, we must either enter an "@" or a null as the first 
character of the PTEXT string. To reserve space for any text 
the user may enter, we must fill the rest of the PTEXT 
string with blanks (actually, any character will work; once 
GEM sees the "@: or null, it'll ignore the rest of the 
string and go on its merry way).

--A Look at an Object's Structure--

Now that we've created our dialog box and played with it a 
little, it's time to dig into the actual structure of 
objects.
	We said before that an object is a data structure, the 
members of which describe the object, storing all the 
necessary information to bring that object up on the screen. 
When we load a resource file into memory, each object is 
allocated memory as follows:

word          ob_next
word          ob_head
word          ob_tail
word          ob_type
word          ob_flags
word          ob_state
longword     ob_spec
word          ob_x
word          ob_y
word          ob_w
word          ob_h
 
Here, *ob_next* is the index of the object's next sibling, 
*ob_head* is the index of the object's first child, and 
*ob_tail* is the index of the object's last child. (Remember 
that the objects are stored in an array of structures. The 
indices mentioned above are the location of the object 
within the array.)
	The *ob_type* field is the object type and will contain 
one of the following values:
 
  Object Type     Value
  -----------     -----
  Box....................20
  Text...................21
  BoxText................22
  Image..................23
  ProgDef................24
  IBox...................25
  Button.................26
  BoxChar................27
  String.................28
  FText..................29
  FBoxText...............30
  Icon...................31
  Title..................32
 
	The *ob_flags* field contains the object flags and will 
be one (or a combination of more than one) of the values 
shown below:
 
  NONE...............0x0000
  SELECTABLE.........0x0001
  DEFAULT............0x0002
  EXIT...............0x0004
  EDITABLE...........0x0008
  RBUTTON............0x0010
  LASTOB.............0x0020
  TOUCHEXIT..........0x0040
  HIDETREE...........0x0080
  INDIRECT...........0x0100
 
You should recognize most of these from your work with the 
RCP.
	The *ob_state* field holds the current state of the 
object as follows:
 
  NORMAL.............0x0000
  SELECTED...........0x0001
  CROSSED............0x0002
  CHECKED............0x0004
  DISABLED...........0x0008
  OUTLINED...........0x0010
  SHADOWED...........0x0020
 
You've seen most of these before, right?
 
	The *ob_spec* field contains object-specific 
information and changes depending on the type of object 
that's being described. The possible values of this field 
are as below:

  Object Type       Contents of *ob_spec*
  -----------       ----------------------
  Box.............Object's color and thickness
  Text............Pointer to TEDINFO structure
  BoxText.........Pointer to TEDINFO structure
  Image...........Pointer to BITBLK structure
  ProgDef.........Pointer to APPLBLK structure
  IBox............Border's color and thickness
  Button..........Pointer to text string
  BoxChar.........Object's color and thickness,
	  and the character to display
  String..........Pointer to text string
  FText...........Pointer to TEDINFO structure
  FBoxText........Pointer to TEDINFO structure
  Icon............Pointer to ICONBLK structure
  Title...........Pointer to text string
 
Notice that the value stored in *ob_spec* can be a pointer 
to another structure containing additional information about 
the object. We'll take a look at one of the structures, 
TEDINFO, shortly.
	Finally, *ob_x*, *ob_y*, *ob_w*, and *ob_h* contain the 
object's coordinates, width and height, respectively.
 
--The Mysterious TEDINFO--

The second structure type we need to look is called TEDINFO. 
Whenever our object has an editable text field, we need to 
store information about it in a TEDINFO structure (actually, 
when using an RCP, we don't have to worry about storing 
information in the structure; it's done for us). Here's what 
a TEDINFO structure looks like in memory:
 
longword        te_ptext
longword        te_ptmplt
longword        te_pvalid
word            te_font
word            te_junk1
word            te_just
word            te_color
word            te_junk2
word            te_thickness
word            te_txtlen
word            te_tmplen

Here, *te_ptext* is a pointer to the PTEXT string, 
*te_ptmplt* is a pointer to the PTMPLT string, *te_pvalid* 
is a pointer to the PVALID string, *te_font* is the text 
font (3=system font; 5=small font), *te_just* is the 
justification (0=left; 1=right; 2=centered), *te_color* is 
the color and pattern type, *te_thickness* is the thickness 
in pixels of the border (0=no border; 1 to 128=thickness 
inward from the edge; -1 to -127=thickness outward from the 
edge), *te_txtlen* is the length of the string pointed to by 
*te_ptext*, and *te_tmplen* is the length of the string 
pointed to by *te_ptmplt*. You can ignore *te_junk2*; it's 
reserved for future use.
	Don't let all this technical stuff get you down. In 
most cases, when using the RCP to put together your dialog 
box, you won't have to worry about the contents of the above 
structures. But, in case you want to do something more 
sophisticated, you do need to understand where to find 
information about your dialog box. An example of this is the 
NUMBERS object in the sample dialog. In order to get the up 
and down arrows to change the value shown, we have to be 
able to get at the displayed strings. This is just one 
example of the creative ways you can use a dialog box.

--The Workings--

Now, just to make absolutely sure your head is spinning, 
let's look at the source code for this chapter's program, to 
see how everything we've discussed ties in with the dialog 
box.
	At the top of the listing, we include the file 
SAMPLE.H. This file contains the name of the object tree 
that represents our dialog box, as well as the names of all 
the objects within the tree. Each object is given a number. 
This number is the index used to find the object within the 
array. (A tree is an array of objects, remember?) Listing 2 
at the end of this chapter shows what the SAMPLE.H file 
contains.
	If you created your dialog box from scratch, you may 
find that your objects are numbered differently than those 
in Listing 2. Don't worry about it. That just means we 
constructed our dialog boxes a little differently. For 
instance, the example's object numbers don't run in perfect 
order. Some numbers are missing because, in the course of 
constructing the dialog, I removed a couple of objects from 
the screen without actually deleting them from the tree. 
Those objects were assigned numbers, but since they remain 
unnamed (and unused), they don't appear in the .H file. 
(They are, however, still taking up space in the resource 
file.)
	The equates after the *include* in Listing 1 assign to 
logical names the values of various parameters we'll be 
using when handling the dialog.
	Now, look at the data section of the program. The 
variable *num* will hold the current value of the dialog's 
number control. Following that is our AES parameter block. 
Notice something strange? The first element of this array, 
which should be the address of the control array, is 0. This 
is because, starting with this program, we're going to be 
using a shortcut for initializing the control array for each 
function call.
	If you look below the *apb*, you'll see how we're going 
to do this. Each AES function now has its own control array, 
already initialized with the proper values. All we have to 
do to set up our *apb* for an AES call is place the address 
of the function's control array into the first element of 
the *apb* array. You'll see how this is done a little later, 
when we discuss the main program.
	Following the control arrays is the filename for our 
resource file. Then, the byte array *number_str* is the 
string we'll use to change the displayed value of the 
NUMBERS object, in response to the user's clicking one of 
the arrows.
	Finally, in the *bss* section, we have several 
variables and buffers, as well as our GEM arrays and our 
stack.

--The Main Program--

The main program starts off by performing all the usual 
initializations: calculating the size of the program area, 
releasing unused memory, setting the global array, and 
calling *appl_init*, *rsrc_load*, and *rsrc_gaddr*. The only 
thing different here is the way we're initializing the 
control array for each AES function call. Rather than five 
lines that initialize each control value individually, we're 
just moving the address of the already initialized arrays 
into the first element of the *apb*, like this:

move.l  #appl_init,apb

This method saves us a lot of extra work and shortens our 
source code considerably.
	The process of loading a resource file and finding its 
address is the same for any resource, whether it be a dialog 
box, a menu, or something else. If you need a refresher on 
the *rsrc_load* and *rsrc_gaddr* functions, please refer to 
chapter 3. In this chapter, we'll start our discussion with 
how to display a dialog box.

--Displaying a Dialog Box--

In order to display a dialog box, allow the user to use it, 
and close the dialog, we perform the following seven steps:

1) Center the dialog's coordinates.
2) Reserve screen space for the dialog.
3) Draw an expanding rectangle.
4) Draw the dialog.
5) Activate the dialog.
6) Draw a shrinking rectangle.
7) Erase the dialog.

	A few of these steps are optional. For example, 
although a dialog box usually looks best in the center of 
the screen, there's no reason you have to center it. Also, 
you can skip the expanding and shrinking rectangles, if you 
like. This will slightly speed up the process of displaying 
and removing the dialog, at the expense of losing some of 
the visual effects.
	Although some steps are optional, we will, of course, 
look at them all.

--Dialog Display Step 1--

The first thing we do in the program is modify the 
coordinates of our dialog box so that it'll appear in the 
center of the screen. This is done with a call to AES 
function #54, *form_center*, like this:

move.l  #form_center,apb        
move.l  dialog_addr,addr_in     
bsr     aes     
move    int_out+2,dial_x                
move    int_out+4,dial_y                
move    int_out+6,dial_w                
move    int_out+8,dial_h                

	First, we place the address of the *form_center* 
control array into the first element of the *apb*. Then, we 
place the address of our dialog box into the first element 
of the addr_in array, after which we call the AES. After the 
call, *int_out* will contain the dialog's new coordinates. 
We save these coordinates for later use, by moving them into 
*dial_x*, *dial_y*, *dial_w*, and *dial_h*. These are the 
dialog's x coordinate, y coordinate, width, and height, 
respectively.

--Finding an Object's Coordinates--

Because we'll be doing some work by hand, as it were, on the 
NUMBERS object in order to update the number it displays, we 
must find its eventual position on the screen. We do this 
with a call to AES function #44, *obj_offset*, like this:

move.l  #objc_offset,apb        
move    #NUMBERS,int_in 
move.l  dialog_addr,addr_in     
bsr     aes
move    int_out+2,num_x 
move    int_out+4,num_y 

	In the first line, we move the address of the 
*objc_offset* control array into the first element of our 
*apb*. Then, we place the number of the object for which we 
want the coordinates into the first element of *int_in*. 
Finally, we place the dialog's address in *addr_in* and call 
the AES. After the call, *int_out0* will contain an error 
code: 0 if an error occurred or a value greater than 0 if 
there was no error. Also, *int_out1* and *int_out2* will 
contain the X and Y coordinates of the object. In our 
program, we copy these values into the variables *num_x* and 
*num_y*.

--Dealing with TEDINFO--

After we have these values, we need to tell the dialog to 
use our *number_str* string, instead of the one it's 
currently using. To do this, we must access the NUMBERS 
object's TEDINFO structure. Here are the lines of code that 
do this, the first of which is a macro call:

tedinfo_str  dialog_addr,NUMBERS     
move.l       #number_str,(a5)        

The main body of the macro looks like this:

move.l   \1,a5   
add.l    #(\2*24)+12,a5  
move.l   (a5),a5 

To trick the object into using our string, we have to find 
the pointer that points to the string contained in the 
NUMBERS object. Remember that our tree, which is now pointed 
to by *tree_addr*, is an array of structures, each structure 
describing one of the objects within the tree. Just like any 
other array, we can access a particular element by using an 
index. The object whose structure we wish to locate is 
NUMBERS, and, thanks to our handy RCP, NUMBERS has been 
defined in the SAMPLE.H header file to the value of the 
index we need.
	The *ob_spec* member of the structure that describes 
NUMBERS, contains a pointer to the TEDINFO structure that 
holds the pointer to our string. So, in the first line of 
the macro, we load the address of our resource into A5. Now, 
we need to locate the *ob_spec* field of the numbers object. 
If you look at an object's structure as shown previously, 
you'll see that each object is 24 bytes long. So, because 
NUMBERS is the index of the object in the tree, NUMBERS*24 
gives us the first byte of the NUMBERS object.
	Another look at an object's structure will tell you 
that the *ob_spec* field is 12 bytes from the start of the 
object. So, by adding 12 to NUMBERS*24, we get the location 
of NUMBERS's *ob_spec* field from the beginning of the 
entire resource tree.
	The entire formula, then, is (NUMBERS*24)+12. By adding 
the result of this formula to the address we stored in A5, 
we'll have the address of NUMBER's *ob_spec* field in A5. 
That's what we do in the second line of the macro, as shown 
above. The third macro line above uses address register 
indirect addressing to load the contents of the address 
pointed to by A5 into A5. This address is the address of the 
tedinfo's string, because that address happens to be stored 
in the first four bytes of the tedinfo.

--Dialog Display Step 2--

Now, on to step 2, reserving space on-screen for the dialog. 
We have to do this so that, when we remove the dialog from 
the screen, GEM will be able to restore the display. A call 
to AES function #51, *form_dial*, will take care of this bit 
of prestidigitation. The call looks like this:

move.l  #form_dial,apb  
move    #FMD_START,int_in       
move    #0,int_in+2     
move    #0,int_in+4     
move    #10,int_in+6
move    #10,int_in+8    
move    dial_x,int_in+10        
move    dial_y,int_in+12        
move    dial_w,int_in+14        
move    dial_h,int_in+16        
bsr     aes

	After placing the address of the *form_dial* control 
array into our *apb*, we place the *form_dial* operations 
code into *int_in0*. The code must be one of those listed 
below:

  0 FMD_START......................reserves screen space
  1 FMD_GROW.........................draws expanding box
  2 FMD_SHRINK.......................draws shrinking box
  3 FMD_FINISH...releases screen space and does a redraw

The *form_dial* function does a lot of work for us, as you 
can see. This one function can handle four of our dialog 
steps all by itself. For the first call to *form_dial*, we 
use FMD_START code, since we want to reserve space for our 
dialog.
	Next, we place the X,Y coordinates, width, and height 
of the smallest rectangle (we'll talk about this in a 
moment); and the X,Y coordinates, width, and height of the 
largest rectangle (the actual size of the dialog) into 
*int_in1* through *int_in8*, respectively. Finally, we call 
the AES to perform the function.

--Dialog Display Step 3--

For step 3, drawing an expanding rectangle, we again call 
*form_dial*, but this time with the FMD_GROW code. The call 
looks exactly the same as that for the first call, except 
for the operation code. When the function is called, GEM 
draws the expanding box, starting with the coordinates and 
size of the smallest rectangle, and ending with the 
coordinates and size of the largest rectangle. Again, the 
drawing of both the expanding and shrinking boxes is 
optional. If you wish, you can skip over this step and go 
directly to the call below, which actually draws the dialog.

--Dialog Display Step 4--

Finally, we're ready to draw our dialog, with a call to AES 
function #42, *objc_draw*:

move.l  #objc_draw,apb  
move    #0,int_in
move    #2,int_in+2     
move    dial_x,int_in+4 
move    dial_y,int_in+6 
move    dial_w,int_in+8 
move    dial_h,int_in+10        
move.l  dialog_addr,addr_in     
bsr     aes

	For this call, we place into succeeding elements of 
*int_in* the number of the first object to draw, how many 
objects deep to draw, and the coordinates of the dialog 
(which were returned by *form_center*), respectively. The 
dialog coordinates in this call are called the "clipping 
rectangle." The clipping rectangle is the portion of the 
display to which all our screen output is limited. For 
instance, if we print text that'll extend beyond the 
rectangle's border, the text will be "clipped" to fit; 
anything that would be drawn outside the clipping rectangle 
will be ignored. Thus we can protect the integrity of the 
rest of the display.
	When we set *int_in0* to 0 in the above call, we're 
asking that GEM start drawing the dialog with the first 
object in the tree. The first object in a tree is the root--
in our case, the box containing the rest of the objects that 
make up our dialog. To be sure all the objects contained 
within the dialog are drawn, we must set the depth to the 
proper value. If we had set it to 0, only the main box would 
have been drawn. If we had set it to 1, only the main box 
and its children would have been drawn, meaning that our 
radio button box would be missing its buttons and our number 
box would be missing its arrows. By setting depth to 2 in 
the sample program, the main box plus its children and 
grandchildren (the children of the children) are drawn, thus 
completing our dialog. If you want to be sure you get 
everything, just set depth to its maximum value of 8.

--Dialog Display Step 5--

Now that our dialog is on the screen, how do we give control 
to the user? Simple! We call AES function #50, *form_do*:

move.l  #form_do,apb
move    #NAME,int_in
move.l  dialog_addr,addr_in
bsr     aes

	Here, we load the index of an editable text field (0 if 
there are no editable text fields) into *int_in0* and the 
address of our dialog into *addr_in0*. The value in 
*int_in0* tells *form_do* the number of the editable text 
field we want to be active when the dialog appears.
	Now that we've made our call to *form_do*, GEM will 
handle the dialog for us, highlighting any selectable fields 
clicked on and letting us enter text into any editable text 
field. When an exit button is clicked, GEM terminates the 
dialog and returns the number of the button clicked, in 
*int_out0*. The button number is the only piece of 
information *form_do* returns directly. If we want to see 
what was entered into the strings, we have to hunt.
	Obviously, only EXIT buttons will ever have their 
values returned from the *form_do* call, so if you want to 
know when a button is clicked, make sure, when you design 
your dialog, that one of the button's attributes is EXIT or 
TOUCHEXIT.
	(Actually, that's not entirely true. There is another 
way to get this information. Each time we click a button or 
fiddle with the dialog in some other way, the object's 
status is changed and recorded in the *ob_state* field of 
the OBJECT structure.)
	Because the only time we want to close our sample 
dialog box is when the OK or CANCEL button is clicked, we 
place the *form_do* call within a large loop. In the loop, 
we check to see which button was clicked (by looking at 
*int_out0*), and perform the necessary action. The loop 
repeats, continually activating and deactivating the dialog, 
until the OK or CANCEL button is clicked. Note that the call 
to *form_do* doesn't redraw the dialog; it only notifies GEM 
to accept more input from the form.

--Modifying a TEDINFO string--

In our sample dialog, when a button is clicked, all the 
program does is print the object's name in an alert box. But 
when the user clicks on one of the arrows, we have to find a 
way to change the value shown in the NUMBERS object.
	Let's say the user clicks on the up arrow. At that 
point, program execution finds its way to the label 
*chk_but8*, where *num* is incremented. The *cmpi* 
instruction makes sure the displayed value doesn't exceed 
9999. If *num* gets too big, we jump to the label 
*num_at_max*, which calls the *change_number* subroutine, 
without changing the value of *num*.
	Look at the *change_number* subroutine, where we change 
NUMBERS's string. The first thing we do is copy all zeroes 
into *number_str*. Then, we convert the value in *num* into 
ASCII form and copy it into the correct position of 
*number_str*. Finally, we call *objc_draw* to redraw only 
the NUMBERS object. To do this, we store the object's number 
in *int_in0*, and a drawing depth of 1 into *int_in1*. For 
the clipping rectangle, we use the X,Y coordinates for 
NUMBERS returned to us previously. On a high resolution 
screen, the width and height of this object are 96 and 32, 
respectively. To run the program in another resolution, 
these two numbers must be changed. Use 96 and 16 for medium 
resolution. (Of course, the best way to handle these 
resolution-dependent values is to use variables that you 
initialize according to the current resolution, which you 
check at the beginning of the program.)

--Dialog Display Step 6 & 7--

When the user has finished with the dialog, we must remove 
it from the screen. We do this by performing two more 
*form_dial* calls: one to display the shrinking box 
(FMD_SHRINK) and one to restore the screen (FMD_FINISH). 
These two calls look like this:

move.l  #form_dial,apb
move    #FMD_SHRINK,int_in
move    #0,int_in+2
move    #0,int_in+4
move    #10,int_in+6
move    #10,int_in+8
move    dial_x,int_in+10
move    dial_y,int_in+12
move    dial_w,int_in+14
move    dial_h,int_in+16
bsr     aes
	
move    #FMD_FINISH,int_in
bsr     aes

	Why is the second call (the one using FMD_FINISH) so 
much shorter? Because the only parameter that changes 
between the calls is the operation code, FMD_FINISH. It'd be 
a waste of time to load the arrays with values that are 
already there.

--Conclusion--

This finishes up our introduction to dialog boxes. By now, 
you should have a good idea of how versatile they can be. 
With a little creativity, you could write an entire program 
that used nothing but dialog boxes for all its input and 
output. Don't be afraid to experiment. Get as outrageous as 
you like!

--Summary--

* An object is a data structure, the members of which 
describe the object, storing all the necessary information 
to bring that object up on the screen.

* The objects of a dialog box (or other type of resource) 
are connected to form an object tree.

* An object can be one of several types, including Button, 
String, FText, FBoxText, IBox, Box, Text, BoxChar, BoxText, 
and Icon.

* An object can have several flags that control how the 
object is used, including NONE, SELECTABLE, DEFAULT, EXIT, 
TOUCHEXIT, RBUTTON, LASTOB, EDITABLE, HIDETREE, and 
INDIRECT.

* An object can have various states, including NORMAL, 
SELECTED, CHECKED, SHADOWED, OUTLINED, CROSSED, and 
DISABLED.

* Editable text in a dialog box uses three fields--PTEXT, 
PTMPLT, and PVALID--to tell GEM what text to display, what 
part of the text is editable, and what characters the user 
is allowed to input.

* Editable text fields in an object are described by a 
TEDINFO structure.

* There are seven steps to displaying and handling a dialog 
box. They are 1) center the dialog's coordinates, 2) reserve 
screen space for the dialog, 3) draw an expanding rectangle, 
4) draw the dialog, 5) activate the dialog, 6) draw a 
shrinking rectangle, and 7) erase the dialog.

* The AES function #54, *form_center*, centers a dialog's 
coordinates with respect to the screen.

* AES function #44, *objc_offset*, finds an object's 
coordinates.

* AES function #51, *form_dial*, has four different 
functions, depending on the operation code. A code of 0 
reserves space on the screen for the dialog, a code of 1 
displays an expanding box, a code of 2 displays a shrinking 
box, and a code of 3 removes a dialog from the screen.

* AES function #42, *objc_draw*, draws an object or object 
tree on the screen.

* AES function #50, *form_do*, gives control of a dialog box 
to the user.



CW_PROG4.S (Source of the chapiter)
CW_PROG4MAC.S (Source of the chapiter)
CW_SAMPLE.H (Source of the chapiter)


Back to Assembly_language_tutorials