Classic Computer Magazine Archive COMPUTE! ISSUE 89 / OCTOBER 1987 / PAGE 71

A BUTTON Command

For Amiga Basic

Robert Katz

Take advantage of the mouse and icon interface with this subprogram that you can use in your own Amiga Basic programs. A demo that lets you change the speed and gender of the Amiga's voice is included.


Perhaps more than any other BASIC, Amiga Basic is extensible. New commands are easily added to the language. "BUTTON" takes advantage of this flexibility to add a BUTTON command similar to the one in Microsft BASIC for the Macintosh.

Buttons allow a menu-like choice without forcing the user to hold down the right mouse button and perform a menu action. In general, you use a button when you want the choice to be intrusive; you use menus when you want the choice to be available only when the user looks for it.

Buttons are useful when you want several choices to be displayed and alterable at once. As an example, if you were writing a telecommunications terminal program, you would probably want all the options to be displayed on the screen simultaneously. (See the accompanying figure for an example.) All at once, the user can see that the speed of communication varies between 110 and 9600; parity may be even, odd, or not selected; and the data and stop bits may be selected in a given number of ways. All of the current settings are clearly shown.

The BUTTON command is implemented as a subprogram. Subprograms differ from subroutines in that subprograms allow local varibles that cannot be accessed by the main program or other subprograms. When you use a subprogram, you don't have to worry about using a variable that is already being used by the main program—no side effects are possible (unless you specify that certain variables are to be shared among program and subprograms). Although the variables are independent, it is possible to pass values (called parameters) back and forth between the subprogram and its calling program.

Example: Telecommunications Parameters

A subprogram must begin with a SUB statement containing the name of the subprogram and a list of parameters. The END SUB statement marks the end of a subprogram. All statements between SUB and END SUB are considered part of the subprogram rather than part of the main program.

Program 1 is the subprogram to implement the BUTTON command. Type it in and save a copy to disk, but don't try to run it yet. This isn't a complete program, only a subprogram. Since you will want to merge this with your own programs, be sure to save it in ASCII format. To do this, you must use a command of the form SAVE "filename", A

Calling All Subprograms

There are two ways for the main program to access, or call, a subprogram. The formal method is with a statement of the form CALL subprogram name (parameter list)

However, Amiga Basic also permits you to omit the CALL and the parentheses surrounding the parameter list. This format makes the subprogram call look just like any other BASIC statement, so the subprogram essentially becomes a new BASIC command.

Here is the syntax for the BUTTON command:

BUTTON ID, state, title, x position, y position, type

The ID and state parameters are two-way values. Setting them before you call the subprogram provides instructions to the subprogram, and reading their values after the subprogram is called, returns information on the buttons. The other parameters are one-way—they provide information to the subprogram, but nothing is reported back through them. One difference between subprograms and actual commands is that you must supply some value for all the parameters in a subprogram call. None of the parameters are optional, even in cases where the parameter values aren't meaningful.

The ID value is a unique number by which you can identify each button. You may have any number of buttons on the screen at once. Allowable button numbers range from 1 up to the maximum number you specify in Program 1.

An ID value of 0 has a special meaning. When the subprogram is called with this value in the first parameter position, it checks whether the mouse is currently positioned over any previously defined button. (In this case, the values of the other parameters are not important.) If the mouse is over a button, then the ID value of that button is returned in the ID parameter, the state of the button is inverted (from 1 to 2, or vice versa), and the new state of the button is returned in the state parameter. If the mouse isn't over any of the buttons, the ID parameter will still be 0 upon return.

Note that this function compares the current mouse pointer position against the coordinates for all defined buttons, without regard for whether the buttons are actually being displayed on the screen at the time of the test. To prevent spurious readings, it is important to clear button definitions when the corresponding buttons are erased from the screen.

The state parameter determines the state of the button specified by the ID parameter. If this parameter is set to 0 when the subprogram is called, then the current definition for the specified button is erased. This makes the button inactive, but does not remove it from the screen. If this parameter is set to 1, then the specified button is deselected (turned off). Setting this parameter to 2 causes the corresponding button to be selected (turned on).

The remaining parameters are only meaningful the first time the subprogram is called for a particular ID value. Title is the name you assign to a button, x position and y position define the screen coordinates of the button, and type identifies the shape of the button as described below. Once a button is defined, these features cannot be changed.

The subprogram provides three different types of buttons. Type 1 is a rectangle with the name of the button inside it. When a type 1 button is selected, the button name appears in reverse video. Type 2 buttons are squares with their names beside them. When a type 2 button is selected, it is marked with an X. Type 3 buttons are circles with their names beside them. When a type 3 button is selected, a smaller filled circle is placed inside the circle. The figure shows all three types of buttons.

Two preparatory steps are required when using BUTTON in your own programs. First, your main program should begin with the statement DEFINT a-z to force all variables to default to short integer type. Secondly, because the subprogram makes calls to operating sustem graphics routines, you must place a copy of the graphics.bmap file into the disk directory that holds your program. The graphics.bmap file can be found in the BasicDemo folder on the Extras disk that came with your Amiga.

Buttoned Speech

The best way to learn how to use BUTTON is by looking at an example. Program 2 illustrates all three types of buttons provided by the BUTTON subprogram. It sets up a control panel for a speech synthesis demonstration. Buttons allow you to change the voice between male and female, and between fast and slow. There are also buttons to start or stop the voice. To try Program 2, type it in as listed and save a copy, but don't try to run it yet. First you must merge in the button subprogram. Use a command of the form

MERGE "filename"

where filename is the name you used for the ASCII file containing the subprogram. After the subprogram is merged in, the combined program is ready to run. If you want, you can save a copy of the completed program to avoid having to merge the subprogram again.

The first BUTTON command in Program 2, in the FOR-NEXT loop, provides the initial definitions of the six buttons. The second BUTTON command, following the WEND, has the ID parameter set to 0 to check whether the mouse pointer was over a button when the mouse button was clicked. If so, the variable ID will have a nonzero value upon return, and array element State(ID) will hold the new state of that variable. (Although Program 2 uses a variable named ID for the ID parameter, that's not mandatory. Any other variable name would work just as well.) The final BUTTON command is used to set the other button in the panel pair to the complementary state. Note that dummy values are supplied for the last three parameters in the latter two BUTTON commands. These values are necessary to satisfy the syntax of the subprogram call.

Since Program 2 defines only six buttons, the default value for the maxbut variable in the subprogram is sufficient. Should your application require more than ten buttons, simply increase maxbut to whatever value you need.

After examining Program 2, try using the BUTTON subprogram in your own programs. Remember that you must place a copy of the graphics.bmap file into the disk folder that holds your BASIC programs for the program to function properly.

Technical Details

Because there is no easy way for Amiga Basic to print text at an arbitrary pixel location, the BUTTON subprogram uses an Amiga operating system graphic routine called Move. Now, characters can be placed outside of normal character boundaries. The Move routine is called like an Amiga Basic subprogram. The routine has three parameters. The first is the address of the current window's RastPort, a structure used by the operating system. We can find this address with the WINDOW(8) function. The other two parameters are the X and Y locations (in pixels) that specify where the text should begin. Here's an example of a call to Move:

CALL Move& (WINDOW(8), X%, Y%)

Note that X and Y are long integers (as the suffix % shows).

One other system routine is used in BUTTON. This routine, SetDrMd, changes the default drawing mode. This is used to invert the colors when a type 1 button is selected. From BASIC, it's called like this:

CALL SetDrMd&(WINDOW(8),value)

Value represents one of the four available drawing modes. The following list shows the modes and their corresponding values:

ModeValue
Jam10
Jam21
Complement2
Inversvid3

The default mode is Jam2. It "jams" the foreground and background colors (as an 8 × 8 rectangle) onto the screen memory. Jam1 jams only the foreground color onto the screen, allowing you to overlay characters. Complement reverses the colors, changing all ones to zeros and all zeros to ones. For instance, if you are using a four-color screen, color 0 would switch places with color 3, and color 1 would switch places with color 2 (it makes more sense in binary). Inversvid exchanges the foreground color with the background color to make inverse characters.

For instructions on entering these programs, please refer to "COMPUTE!'s Guide to Typing In Programs" elsewhere in this issue.

Program 1. BUTTON

‘BUTTON subprogram ‘ copyright 1987, COMPUTE! Publications, Inc.
‘ ALL rights reserved.

SUB BUTTON (ID, State, TitLe$, X, Y, Type) STATIC
IF ID = 0 THEN GOTO CheckButton
IF numbut > 0 THEN GOTO Skipped
LIBRARY "graphics.library"
rastport& = WINDOW(8)
CALL SetDrMd&astport&, 0)
maxbut = 10
DIM ID(maxbut), State(maxbut), Tit
Le$(maxbut), Type(maxbut)
DIM X1 (maxbut), Y1(maxbut), X2(max but), Y2(maxbut)
Le$(maxbut), Type(maxbut)
DIM X1(maxbut), Y1(maxbut), X2(max but), Y2(maxbut)
AREA(10, 10): AREAFILL

Skipped:
IF (ID < 0 OR ID > maxbut OR State < 0 OR State > 3 OR Type > 3) THEN EXIT SUB
IF State = 0 THEN GOTO EraseButton

IF ID(ID) < > ID THEN
ID(ID) = ID
IF ID > numbut THEN numbut = ID
TitLe$(ID) = " " + TitLe$ + " "
X1 (ID) = X : Y1(ID) = Y
Type(ID) = Type
END IF
IF Type(ID) < 1 THEN GOTO EraseButton
State(ID) = State
ON Type(ID) GOTO One, Two, Three

One:
X2(ID) = X1(ID) + LEN(TitLe$(ID))*10 + 1 : Y2(ID) = Y1(ID) + 11
LINE(X1(ID), Y1(ID)) - (X2(ID), Y2(ID)), 0, bf
LINE(X1(ID), Y1(ID)) - (X2(ID), Y2(ID)), 1, b
IF State = 2 THEN
LINE(X1(ID), Y1(ID)) - (X2(ID), Y2(ID)), 1, bf
CALL SetDrMd&(rastport&, 5)
END IF
CALL Move&(rastport&, X1(ID) + 1, Y1(ID) + 8)
PRINT TitLe$(ID) : CALL SetDrMd&(rastport&, 1)
EXIT SUB

Two:
X2(ID) = X1(ID) + 20 : Y2(ID) = Y1(ID) + 10
LINE (X1(ID), Y1(ID)) - (X2(ID), Y2(ID)), l + (State = 1)
LINE (X1(ID), Y2(ID)) - (X2(ID), Y1(ID)), 1 + (State = 1)
LINE (X1(ID), Y1(ID)) - (X2(ID), Y2(ID)), 1, b
CALL Move&(rastport&, X1(ID) + 23, Y1(ID) + 8)
PRINT TitLe$(ID)
EXIT SUB

Three:
X2(ID) = X1(ID) + 12 : Y2(ID) = Y1(ID) + 12
Xc = X1(ID) + 5 : Yc = Y1(ID) + 5
CIRCLE (Xc, Yc), 12
IF State = 2 THEN CIRCLE (Xc, Yc), 6

PAINT (Xc, Yc), 1 + (State = 1)
CALL Move&(rastport&,X1(ID) + 19,Y1(ID) + 8)
PRINT TitLe$(ID)
EXIT SUB

CheckButton:
Xm = MOUSE(1) : Ym = MOUSE(2)
Checkloop:
ID = ID + 1
IF ID > numbut THEN
ID = 0
EXIT SUB
END IF
IF ID(ID) = 0 THEN GOTO Checkloop
IF (Xm > X1(ID) AND Xm < X2(ID) AND
Ym > Y1(ID) AND Ym < Y2(ID)) THEN
State=1 - (State(ID) = 1)
GOTO Skipped
END IF
GOTO Checkloop

EraseButton:
ID(ID) = 0 : State(ID) = 0
TitLe$(ID) = " "
X1(ID) = 0 : Y1(ID) = 0
Type(ID) = 0
EXIT SUB

END SUB

Program 2. A BUTTON Demonstration

‘BUTTON Demo
‘Copyright 1987, COMPUTER! Publications, Inc.
‘ALL rights reserved.

DEFINT a - z
DIM voice(8), word$(9),State(6),TitLe$(6),X(6),Y(6),Type(6)
FOR i = 0 TO 8 : READ voice(i) : NEXT
DATA 110, 0, 150, 1, 22200, 64, 10, 0, 0

FOR i=1 TO 9 : READ word$(i) : NEXT
DATA one, two, three, four, five, six, seven, eight, nine
LOCATE 1,10 : PRINT "Copyright 1987, COMPUTE! Publications, Inc."
LOCATE 2,21 : PRINT "All rights reserved."
‘Initialize buttons
FOR ID = 1 TO 6
READ State(ID), TitLe$(ID), X(ID), Y(ID), Type(ID)
BUTTON ID, State(ID), TitLe$(ID), X(ID), Y(ID), Type(ID)
NEXT
DATA 1, Male, 150, 25, 1
DATA 2, Female, 300, 25, 1
DATA 1, Fast, 300, 50, 2
DATA 1, Start, 155, 75, 3
DATA 2, Stop, 305, 75, 3
‘Initialize voice parameters
GOSUB Gender
GOSUB Speed
GOSUB Status
Count = 1

here:
WHILE MOUSE(0) < 1
IF speak THEN
SAY TRANSLATE$(word$(count)),voice
Count = count + 1
IF count > 9 THEN count = 1
END IF
WEND : ID = 0
BUTTON ID, State, " ", 0, 0, 0
IF ID = 0 THEN GOTO here ‘mouse pressed when not on a button
State(ID) = State
‘Reverse state of complementary button
IF (ID MOD 2) THEN ID2 = ID + 1 ELSE ID2 = ID - 1
State(ID2) = 1 - (State = 1)
BUTTON ID2, State(ID2), " ", 0, 0, 0
‘Change operating conditions according to button selection
ON INT((ID/2) + .5) GOSUB Gender, Speed, Status
GOTO here
END

Gender:
IF State(1) = 2 THEN voice(0) = 110 : voice(3) = 0 ELSE voice(0) = 200 : voice(3) = 1
RETURN
Speed:
IF State(3) = 2 THEN voice(2) = 150
ELSE voice(2) = 250
RETURN
Status:
Speak = (State(5) = 2)
RETURN
‘Merge BUTTON subprogram at this point