DAVIDLI@SIMVAX.BITNET (Dave Meile) (12/08/87)
This is another OSS Personal Pascal tutorial. You can put it up on BBS
systems, on user disks, etc. I just don't want to see them come out in
a book (without my name on it... :-) )
Length c420 lines:
----------------------------------CUT HERE-------------------------------------
Now What?
OSS Personal Pascal and the beginner - part 5
written by David Meile
[Copyright 1987 David Meile, all rights reserved. Permission is
given to Atari user groups to reprint this article, as long as
this statement is included. OSS Personal Pascal is a product of
Optimized Systems Software, Inc. I am not related to the company,
I simply bought their compiler.]
In this fifth article on programming using OSS Personal Pascal,
we will take a look at the "how-to" of creating GEM menus.
Creating menus is similar in many ways to creating dialog
boxes. First, we allocate space for the menu. Then we describe
the titles that will appear on our menu bar. Then we describe the
items that will appear under each title of the menu bar. After
that, we define the characteristics (if any) for those menu items.
Then we DRAW the menu on the screen.
At this point, we depart from the way dialog boxes are handled.
The reason is that GEM menus require the use of an "event
manager". This manager receives messages from GEM about
keypresses, mouse point-and-clicks, window manipulation, etc. and
responds according to how we want our program to handle these
messages. Since this column is directed towards menus, I won't go
into all of the options for event management -- instead I will
show you the minimum amount of event management required to handle
menus.
Once we are done with a menu, we can erase it from the screen.
If our program has no further need of such a menu, we can delete
the menu from the computer memory, freeing up some space for other
things in our program.
****WARNING****
As released by OSS, both version 1.0 and 2.0 have a "bug". You
cannot run a program which calls MENU_TEXT sucessfully until you
remove the three letters "VAR" from the parameter list for
the procedure Menu_Text (located in GEMSUBS.PAS). Make this a
permanent change to that particular file, and all should go well
when you compile.
AN EXAMPLE ...
Take a moment now to look over the program 'NowWhat5'. I'll be
explaining it at the end of the listing.
PROGRAM NowWhat5;
{ This program demonstrates the creation and manipulation of
GEM menus using OSS Personal Pascal. }
{$I gemsubs.pas}
{NOTE: version 2.0 owners, use this with the new gemsubs.pas.
version 1.0 owners, use the individual 'includes' of
gemconst.pas, gemtypes.pas and the old gemsubs.pas. }
VAR
ap_id : integer;
PROCEDURE Do_Menu;
VAR
le_menu : Menu_Ptr; { menu tree }
le_file : integer; { menu title }
le_select : integer; { menu title }
le_show : integer; { menu title }
load : integer; { menu item }
quit : integer; { menu item }
joke1 : integer; { menu item }
joke2 : integer; { menu item }
joke3 : integer; { menu item }
show : integer; { menu item }
null1 : integer; { menu item }
some1 : integer; { menu item }
some2 : integer; { menu item }
some3 : integer; { menu item }
op1, op2, op3, bye, active : boolean;
junk, event, dummy : integer; { for event management }
msg : Message_Buffer; { for event management }
all_str, some_str, none_str : Str255;
s1_str, s2_str, s3_str : Str255;
astring : Str255;
BEGIN { Do_Menu }
{ Initialize variables and text strings. }
op1 := False;
op2 := False;
op3 := False;
all_str := ' Show all Jokes ';
none_str := ' No jokes selected ';
some_str := ' Show selected Jokes ';
s1_str := 'Why did the chicken cross..';
s2_str := 'How many lightbulbs does...';
s3_str := 'What do you get when you ..';
astring := '[1][ Now What? article 5 | by David Meile ][ Okay ]';
{ Set up the menu. }
le_menu := New_Menu( 20, ' About NowWhat 5... ' );
le_file := Add_MTitle( le_menu, ' Files ' );
le_select := Add_MTitle( le_menu, ' Select ' );
le_show := Add_MTitle( le_menu, ' Show ' );
load := Add_MItem( le_menu, le_file, ' Load ' );
quit := Add_MItem( le_menu, le_file, ' Quit ' );
joke1 := Add_MItem( le_menu, le_select, ' Joke File 1 ' );
joke2 := Add_MItem( le_menu, le_select, ' Joke File 2 ' );
joke3 := Add_MItem( le_menu, le_select, ' Joke File 3 ' );
show := Add_MItem( le_menu, le_show, none_str );
null1 := Add_MItem( le_menu, le_show, '-----------------------' );
some1 := Add_MItem( le_menu, le_show, ' Joke File 1 ' );
some2 := Add_MItem( le_menu, le_show, ' Joke File 2 ' );
some3 := Add_MItem( le_menu, le_show, ' Joke File 3 ' );
Menu_Check( le_menu, joke1, op1 );
Menu_Check( le_menu, joke2, op2 );
Menu_Check( le_menu, joke3, op3 );
Menu_Disable( le_menu, some1 );
Menu_Disable( le_menu, some2 );
Menu_Disable( le_menu, some3 );
Menu_Disable( le_menu, null1 );
{ Allow selection of various menu options. Exit only when the
QUIT options is chosen from the menu. }
Draw_Menu( le_menu );
bye := False;
REPEAT
IF NOT( op1 AND op2 AND op3 ) THEN
BEGIN
active := False;
Menu_Text( le_menu, show, none_str )
END;
IF ( op1 OR op2 OR op3 ) THEN
BEGIN
active := True;
Menu_Text( le_menu, show, some_str )
END;
IF ( op1 AND op2 AND op3 ) THEN
BEGIN
active := True;
Menu_Text( le_menu, show, all_str );
END;
event := Get_Event( E_Message, 0,0,0,0, False, 0,0,0,0,
False, 0,0,0,0, msg, dummy, dummy, dummy,
dummy, dummy, dummy );
{ Erase old text on screen }
Draw_String( 60,150, ' ' );
Draw_String( 60,170, ' ' );
Draw_String( 60,190, ' ' );
{ msg[3] = 3 when the 'About ...' from the DESK menu is chosen}
IF msg[3] = 3 THEN
junk := Do_Alert( astring, 1 );
IF msg[3] = le_file THEN
IF msg[4] = load THEN
BEGIN
IF NOT( op1 OR op2 OR op3 ) THEN
Draw_String( 60,150, 'Select a joke file first...' )
ELSE
Draw_String( 60,150, 'Loading jokes... ' )
END
ELSE IF msg[4] = quit THEN
bye := True;
IF msg[3] = le_select THEN
IF msg[4] = joke1 THEN
BEGIN
op1 := NOT( op1 );
Menu_Check( le_menu, joke1, op1 );
IF op1 THEN
Menu_Enable( le_menu, some1 )
ELSE
Menu_Disable( le_menu, some1 )
END
ELSE IF msg[4] = joke2 THEN
BEGIN
op2 := NOT( op2 );
Menu_Check( le_menu, joke2, op2 );
IF op2 THEN
Menu_Enable( le_menu, some2 )
ELSE
Menu_Disable( le_menu, some2 )
END
ELSE IF msg[4] = joke3 THEN
BEGIN
op3 := NOT( op3 );
Menu_Check( le_menu, joke3, op3 );
IF op3 THEN
Menu_Enable( le_menu, some3 )
ELSE
Menu_Disable( le_menu, some3 )
END; { IF }
IF msg[3] = le_show THEN
BEGIN
IF msg[4] = show THEN
IF active THEN
BEGIN
IF op1 THEN
Draw_String( 60,150, s1_str );
IF op2 THEN
Draw_String( 60,170, s2_str );
IF op3 THEN
Draw_String( 60,190, s3_str );
END;
IF msg[4] = some1 THEN
IF op1 THEN
Draw_String( 60,150, s1_str );
IF msg[4] = some2 THEN
IF op2 THEN
Draw_String( 60,150, s2_str );
IF msg[4] = some3 THEN
IF op3 THEN
Draw_String( 60,150, s3_str );
END;
Menu_Normal( le_menu, msg[3] );
UNTIL bye;
Erase_Menu( le_menu );
Delete_Menu( le_menu );
END; { Do_Menu }
BEGIN { NowWhat5 }
ap_id := Init_Gem;
IF ap_id >= 0 THEN
BEGIN
Init_Mouse;
Hide_Mouse;
Clear_Screen;
Show_Mouse;
Text_color( Red );
Draw_Mode( 1 );
Do_Menu;
Exit_Gem;
END;
END. { NowWhat5 }
AN EXPLANATION
A menu requires a pointer, titles for the desktop menu bar and
items to go under those titles. Locate the section titled { Set
up the menu. }. Note that it is somewhat similar to the way we
set up dialog boxes last column.
First, we identify a specific menu by name, in this case I
called it 'le_menu'. The function that does this is NEW_MENU.
It's parameters are (1) the number of items to be associated with
this menu (including menu titles and items) and (2) the text
string which will go under the DESK menu title -- which is usually
something like "About this program".
Next, we identify the other titles that will appear on the menu
bar at the top of our screen, using the function ADD_MTITLE. The
parameters are the menu pointer (le_menu), and the text string
identifying the menu title. Here, 'le_file' refers to the menu
title "Files". Similarly 'le_show' refers to the menu title
"Show". The variables will be used when adding specific items
under the menu titles.
After adding all of the titles, we add items to go under the
menu titles, using the function ADD_MITEM. It's parameters are
(1) the menu pointer, (2) the menu title and (3) the specific text
string that will refer to the item. We will use the item
variables when we wish to change the appearance of a particular
text string.
MENU_CHECK adds a check-mark to the left of a menu item. For
that reason, you should put at least two spaces to the left of a
text string referring to a menu item, so there is enough room for
a check mark. All of the menu items are defined in the program
with two spaces to the left of the text string. MENU_CHECK has a
boolean variable as the last parameter -- you can tell it to check
or "un"check a menu item depending on whether the variable is True
or False. The menu item to be checked is referred to by both the
menu pointer and the menu item.
MENU_DISABLE has a complementary procedure called MENU_ENABLE.
Both procedures are called in the program NowWhat5. MENU_DISABLE
produces "ghost" writing -- the item is grey instead of black,
showing that the item is unavailable. Also, when an item is
disabled you can click on it all you want, but the event handler
will not recognize it.
WITHIN THE REPEAT LOOP
All of this takes us to the beginning of the actual work within
the program NowWhat5: the REPEAT loop. First we set the variable
that will cause the program to repeat the loop until it is
changed. The variable is 'bye', and it will only be set to a True
value if we select the Quit option from the menu we've created.
The three IF statements test to see whether we have chosen
anything under the Select menu. Depending on whether none, some,
or all items under the Select menu have check marks by them, we
display an appropriate message at the top of the Show menu.
Right after the IF statements is the heart of the repeat loop -
the call to the event manager, GET_EVENT. There are many
variables and options for this function, and I won't go into them
all here, as I'll be doing so in a later column. The important
things to know are:
1) E_Message is a message event. Here, it is used to indicate
that the user has clicked on one of the menu items.
2) msg is the message buffer. There are many sorts of messages
which may be returned, but we are only interested in two
elements of that message buffer:
msg[3] refers to the menu title which has been selected
msg[4] refers to the menu item selected
If there were more than one event possible in this program, we
would first have to test whether msg[0] referred to a MN_SELECTED
state before assuming msg[3] and msg[4] referred to a GEM menu.
Please make note of this if you also are handling other window
events at the same time (more about this will appear in the next
column).
A menu-event where msg[3] = 3 always refers to the 'About' item
under the Desk menu title. In the program, we draw an alert box
with information about the program when we select the 'About...'
item.
What follow are a large number of IF statements. There is an
outer IF statement (IF msg[3] = xxx) and one or more inner IF
statements (IF msg[4] = xxx). They test to see which menu title is
referred to (msg[3]) and then which message item is reffered to
(msg[4]). Then, depending on what the menu item actually is,
several other statements may be executed.
For example, if msg[3] = le_file and msg[4] = quit then we set
the variable 'bye' to True, which enables us to exit the repeat
loop.
For the Select title, we toggle between the checked and
unchecked state using the statement
opn := NOT(opn)
then, depending on the state of 'opn' we enable or disable the
corresponding menu item under the Show title.
For the Show title, we can select the upper default, which will
print out a joke if it is enabled, or we can select an individual
joke if it is enabled. Of course, when all jokes are disabled
nothing will be shown.
The logic of the IF statements may seem to be complex, but if
you actually run the program, you will be able to see what happens
when you select the various menu items. If you have desk
accessories, I would suggest you do not select them from the Desk
menu title as they will have unpredictable results within this
particular program (due to the very minor "event management" going
on.)
Finally, before we repeat the loop we make a call to
Menu_Normal using the menu title (referred to by msg[3]). The
reason this is necessary is that GEM will highlight that menu
title when you select a menu item, and will NOT return it to
normal automatically.
When we are done with the menu, we can erase it from the screen
using ERASE_MENU and then remove it from computer memory using
DELETE_MENU. Of course, if you're going to use a particular menu
more than once in a program, it makes sense not to delete it. You
can issue a call to DRAW_MENU after you've called ERASE_MENU, and
all of the menu items will be in the same state as before.
ANOTHER THING
You will notice that I have not created a normal window in this
program. Instead, I made a call to CLEAR_SCREEN instead. As a
precaution, I first hid the mouse, then cleared the screen, then
showed the mouse again. When you only want a blank page upon
which to draw graphics or menus or dialog boxes, you can use this
call. Clear_Screen will cover ALL WINDOWS, regardless, so don't
use it in a case where you have multiple windows (or where you are
going to create windows). The full-screen window will not go away
until you end your program. Be aware.
FINAL WORDS
I have been entirely too long getting this article out, and I
apologize. I hope that you have found the previous articles
useful. I fully intend to complete this series "real soon", and
then, perhaps, to go onto some more difficult programming
articles.
Feedback is appreciated. If your user group uses this article
in a newsletter, I'd appreciate seeing a copy. Mail will reach me
at the following address:
David Meile
Box 13038
Minneapolis, MN 55414