[comp.sys.atari.st] OSS Personal Pascal tutorial #4

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 c600 lines:
----------------------------------CUT HERE-------------------------------------


                                 Now What?
               OSS Personal Pascal and the beginner - part 4
                           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.]

        Welcome back for a fourth round of Pascal programming.  Over
     the next three columns I will be looking at creating dialog boxes,
     then creating menus.  Once the basic application is understood, I
     will explore the myriad intricacies of Event Management.

        Both dialog boxes and menus follow the same patterns within
     Personal Pascal.  First, we allocate space for the dialog
     box/menu.  Next, we describe the kinds of things we want to go
     into the dialog box/menu.  Then, we describe the ACTUAL things we
     want to put within the dialog box/menu.  After all of that, we can
     actually call procedures to DRAW the dialog box/menu.  And
     finally, we can erase the dialog box/menu.  Of course, when we end
     the program, we should always release the space we allocated.  (A
     clean workspace doesn't contain surprises!)

        Aside from alert boxes, dialog boxes are the only GEM routines
     that don't require us to know anything about "event management".
     Even so, there are a LOT of different things that can be done with
     a dialog box.  If necessary, I suppose that you could write an
     entire program using nothing but dialog and alert boxes.  (This
     month's program is a fair example ...)

        Take a moment now to look over the program 'NowWhat4'.  Then
     shift to the explanation at the end.

     PROGRAM NowWhat4;

     { The purpose of this program is to demonstrate some of the many
       ways dialog boxes can be created and used in a typical program,
       indeed, dialog boxes can be used EXCLUSIVELY in a program --
       though it's not recommended! }

     VAR
       Location      : STRING[255];
       Name          : STRING[255];
       Choice        : integer;
       TellIt        : STRING[255];
       done          : boolean;

     {$I gemsubs.pas}
     {NOTE:  OSS Personal Pascal 2.0.  For 1.0, use the familiar
             'includes' of gemconst.pas, gemtypes.pas, and gemsubs.pas }

     PROCEDURE Obj_Draw( BOX : Dialog_Ptr; ITEM : Tree_Index;
                         DEPTH, x,y,w,h : integer);
     EXTERNAL;

     PROCEDURE Get_Direction( VAR Direction : Str255 );

     { A dialog box consisting of four 'direction' arrows is presented.
       By clicking on one of the arrows, a direction is chosen ...  }

     VAR
       DirBox        : Dialog_Ptr;
       North,South,
       East,West     : integer;
       N,S,E,W       : char;
       Explain       : integer;
       GotIt         : integer;

     BEGIN { Get_Direction }

       DirBox := New_Dialog( 6, 0,0, 22,10 );

       North := Add_Ditem( DirBox, G_Button, Selectable | Radio_Btn
                              | Touch_Exit,  9,3,  3,1, 1,0 );
       South := Add_Ditem( DirBox, G_Button, Selectable | Radio_Btn
                              | Touch_Exit,  9,7,  3,1, 1,0 );
       East  := Add_Ditem( DirBox, G_Button, Selectable | Radio_Btn
                              | Touch_Exit, 14,5,  3,1, 1,0 );
       West  := Add_Ditem( DirBox, G_Button, Selectable | Radio_Btn
                              | Touch_Exit,  4,5,  3,1, 1,0 );
       Explain := Add_Ditem( DirBox, G_String, None,
                                             2,1, 18,1, 1,0 );

       N := chr( 1 ); S := chr( 2 ); E := chr( 3 ); W := chr( 4 );

       Set_DText( DirBox, North, N, System_Font, TE_Center );
       Set_DText( DirBox, South, S, System_Font, TE_Center );
       Set_DText( DirBox, East,  E, System_Font, TE_Center );
       Set_DText( DirBox, West,  W, System_Font, TE_Center );
       Set_DText( DirBox, Explain, 'Choose a direction', System_Font,
                    TE_Center);

       Init_Mouse;
       Set_Mouse( M_Arrow );
       Center_Dialog( DirBox );
       GotIt := Do_dialog( DirBox, 0);
       End_Dialog( DirBox);

       IF GotIt = North THEN
         Direction := 'North'
       ELSE IF GotIt = South THEN
         Direction := 'South'
       ELSE IF GotIt = East THEN
         Direction := 'East'
       ELSE IF GotIt = West THEN
         Direction := 'West'
       ELSE Direction := '>>ERROR<<';

     END; { Get_Direction }

     PROCEDURE Get_Text( VAR Edit_Line : Str255 );

     { A dialog box consisting of three text lines, an OK and a Cancel
       button are presented with an editable text field asking for a
       first name... }

     VAR
       DirBox        : Dialog_Ptr;
       Explain1,
       Explain2,
       Explain3,
       Editted,
       OK, Cancel    : integer;
       GotLine       : integer;

     BEGIN { Get_Text }

       Edit_Line := '';

       DirBox := New_Dialog( 10, 0,0, 22,13 );

       Explain1 := Add_Ditem( DirBox, G_String,  None, 2,2, 18,1, 0,0 );
       Explain2 := Add_Ditem( DirBox, G_String,  None, 2,3, 18,1, 0,0 );
       Explain3 := Add_Ditem( DirBox, G_String,  None, 2,4, 18,1, 0,0 );

       OK       := Add_Ditem( Dirbox, G_Button, Selectable | Default
                                   | Exit_Btn,  2,10, 8,2, 1,0 );
       Cancel   := Add_Ditem( Dirbox, G_Button, Selectable | Touch_Exit,
                                               12,10, 8,2, 1,0 );

       Editted  := Add_Ditem( Dirbox, G_FText, Editable, 2,7, 18,1,
                      0, (black*256) | (black*128) | (black*16) | black);

       Set_DText( DirBox, Explain1, 'This DIALOG box is', System_Font,
                    TE_Center );
       Set_DText( DirBox, Explain2, 'set to accept your', System_Font,
                    TE_Center );
       Set_DText( DirBox, Explain3, 'first name .......', System_Font,
                    TE_Center );

       Set_DText( DirBox, OK, 'OK', System_Font, TE_Center );
       Set_DText( DirBox, Cancel, 'Cancel', System_Font, TE_Center );

       Set_DEdit( DirBox, Editted, 'Name: __________','aaaaaaaaaa','',
                     System_Font, TE_Left );

       Init_Mouse;
       Set_Mouse( M_Point_Hand );
       Center_Dialog( DirBox );
       GotLine := Do_dialog( DirBox, Editted );

       IF GotLine = OK THEN
         Get_DEdit( DirBox, Editted, Edit_Line )
       ELSE IF GotLine = Cancel THEN
         Edit_Line := '..No name..';

       End_Dialog( DirBox);

     END; { Get_Text }


     PROCEDURE Do_Things;

     { This dialog box demonstrates how to create a scaler - simple, but
       direct.  Instructions are included in a small boxed off area which
       has been shaded.  There are no 'radio-buttons' in this procedure,
       it is all done  with boxed text and the 'Touch_Exit' flag. }

     VAR

       fini          : boolean;
       DirBox        : Dialog_Ptr;
       DoIt          : integer;
       Box1          : integer;
       Line1         : integer;
       Line2         : integer;
       Track         : integer;      { Track for our slider }
       Slider        : integer;
       Slider_Num    : char;
       Incr,Decr     : integer;      { Arrows for slider }
       OK            : integer;
       x,y,w,h       : integer;      { parameters for Obj_Draw }

     BEGIN { Do_Things }

       DirBox := New_Dialog( 15, 0,0, 30,15 );

       Box1   := Add_DItem( DirBox, G_Box, None, 2,1, 26,5,
                            1, (Red*4096) | (1*16) | Green );
       Line1  := Add_DItem( DirBox, G_Text, None, 3,2, 24,1,
                            0, (Black*256) | (7*16) );
       Line2  := Add_DItem( DirBox, G_Text, None, 3,4, 24,1,
                            0, (Black*256) | (7*16) );
       Track  := Add_Ditem( DirBox, G_Box, None, 10,8, 10,1,
                           -1, (Black*4096) | (4*16) | Green );
       Slider := Add_Ditem( DirBox, G_BoxText, None, 14,8, 2,1,
                           -1, (Black*4096) | (Black*256) | (Black*128)
                                   | (7*16) );
       Decr   := Add_Ditem( DirBox, G_BoxText, Touch_Exit, 8,8, 2,1,
                           -1, (Black*4096) | (Black*256) | (Black*128)
                                   | (7*16) );
       Incr   := Add_Ditem( DirBox, G_BoxText, Touch_Exit, 20,8, 2,1,
                           -1, (Black*4096) | (Black*256) | (Black*128)
                                   | (7*16) );
       OK    := Add_Ditem( DirBox, G_BoxText, Selectable | Touch_Exit,
                              13,13, 4,1,-1,(Black*4096) | (Black*256)
                                   | (7*16));

       {NOTE:  For monochrome users, the colors "Green" and "Red" have
               no meaning ... 'Line1' and 'Line2' will be obscured by
               the filled black background.  To rectify this, you would
               leave out references to other colors and minimize the
               fill (as done here) }

       Slider_Num := '4';

       Set_DText( DirBox, Line1, 'Click on the', System_Font,
                    TE_Center );
       Set_DText( DirBox, Line2, 'slider arrows.', System_Font,
                    TE_Center );
       Set_DText( DirBox, Decr, chr(4), System_Font, TE_Center );
       Set_DText( DirBox, Incr, chr(3), System_Font, TE_Center );
       Set_DText( DirBox, Slider, Slider_Num, System_Font, TE_Center );
       Set_DText( DirBox, OK, 'DONE', System_Font, TE_Center );

       Center_Dialog( DirBox );
       DoIt := Do_dialog( DirBox, 0 );

       fini := FALSE;

       REPEAT
         IF DoIt = Incr THEN
           Slider_Num := Succ( Slider_Num )
         ELSE IF DoIt = Decr THEN
           Slider_Num := Pred( Slider_Num )
         ELSE IF DoIt = OK THEN
           fini := TRUE;

         IF ( Slider_Num < '0') THEN
           Slider_Num := '0';
         IF ( Slider_Num > '9' ) THEN
           Slider_Num := '9';

         IF NOT( fini) THEN
           BEGIN
             Set_DText( DirBox, Slider, Slider_Num, System_Font, TE_Center );
             work_rect(0,x,y,w,h);
             obj_draw( DirBox, Slider, 1, x,y,w,h );
             DoIt := Redo_Dialog( DirBox, 0);
           END;
       UNTIL fini;

       End_Dialog( DirBox );
       Delete_Dialog( DirBox );

     END; { Do_Things }

     BEGIN { Main }

       IF Init_Gem >= 0 THEN
       BEGIN
         Location := '';

         Get_Direction( Location );
         TellIt  := concat( '[0][The direction|chosen was|',
                             Location, '| ][ OK ]' );
         TellIt  := concat( '[0][The direction|chosen was|',
                             Location, '| ][ OK ]' );
         Choice := Do_Alert( TellIt, 0 );

         Name := '';
         Get_Text( Name );
         TellIt := concat( '[0][ Your first name | is ', Name,
                     '| ][ Yes/No ]' );
         Choice := Do_Alert( TellIt, 0 );

         done := FALSE;
         Init_Mouse;
         Set_Mouse( M_Arrow );
         Do_Things;

         Exit_Gem;
       END;

     END. { Main }

                        AND NOW ... WHAT'S GOING ON

        The program listing for NOWWHAT4.PAS is about four pages long.
     There are three procedures and a main program.  Each procedure
     creates a dialog box and lets you do something, like point-and-
     click the mouse or typing your first name.

        The first thing each procedure does is set up a dialog box with
     a call to the function "New_Dialog".  The call looks like this:

               DirBox := New_Dialog( num_item, x,y, w,h );

     where "num_item" is the number of items you're going to put into
     the dialog box.  "x" and "y" indicate where the top left corner of
     the dialog box should be.  If both are 0, then be sure to make a
     call to "Center_Dialog" later.  "w" is the box width, and "h" is
     the box height (in characters).  "DirBox" is the variable we use
     to tell GEM which dialog box we are interested in.

        The next thing each procedure does is indicate what items
     should go INTO the dialog box.  You should NEVER have more
     "Add_DItem"s than the "num_item" you indicated in the call to
     New_Dialog.  The call looks like this:

               Box1 := Add_DItem( DirBox, G_Box, none, 2,1, 26,5,
                                   1, (Red*4096) | (5*16) | Green );

     The call to Add_DItem can have *lots* of basic parameters, but
     they all follow simple rules.

        1) Indicate WHICH dialog box the item is associated with
        2) Indicate the TYPE of item is being added
        3) Indicate any FLAGS to be associated with this item
        4) Indicate the POSITION of the item in relation to the
               top, left part of the dialog box
        5) Indicate the DIMENSIONS of the item
        6) Indicate a BORDER for the item
        7) Indicate the COLOR(s) to be associated with the item.

     Let's take, for example, the declaration of BOX1 in the procedure
     Do_Things:

        Box1 := Add_DItem( DirBox, G_Box, None, 2,1, 26,5,
                           1, (Red*4096) | (1*16) | Green );

        "DirBox" is the dialog box where we want to put "Box1".

        "G_Box" is the item type.  G_BOX is an outline of a box which
     can be  filled with some color and pattern.  G_IBOX is another
     kind of box, which cannot be filled.  Other item types used in the
     program NowWhat4 include G_STRING, G_BUTTON, G_FTEX, G_TEXT,
     G_BOXTEXT.  Look at the program to see how each is used.

        There are no flags to be associated with "Box1".  However, you
     can see examples of flags in the procedure "Get_Text" in the
     program above.  The OK box has flags SELECTABLE and DEFAULT
     assigned to it, while the cancel box has flags SELECTABLE and
     TOUCH_EXIT assigned to it.  You assign more than one flag using
     the "|" in Personal Pascal:

                            SELECTABLE | DEFAULT
                          SELECTABLE | TOUCH_EXIT

        The DEFAULT flag indicates that this item is the default when
     you press the return key (or click on it with the mouse).  The
     TOUCH_EXIT flag indicates that the item is selected as soon as you
     click the mouse while pointing at it.

        All items have a POSITION.  "Box1" is to be located two
     characters to the right and one character down from the top left
     of the dialog box.

        Likewise, "Box1" has DIMENSIONS.  Box1 will be 26 characters
     wide and 5 characters high.

        The border value indicates how "thick" the item's border (if
     any) should be.  Positive numbers thicken toward the center of the
     item, while negative numbers thicken away from the center of the
     item.  For all practical purposes, the value should be between -4
     and +4.  For example, Alert Boxes have a border around the default
     button of 3.  A value of 0 specifies "no border", which can be
     useful in creating "hidden" boxes.

        Most item types can be assigned colors.  The exceptions are
     buttons and strings.  For example, in the procedure GET_TEXT, the
     items with G_STRING type have no color assigned.  Likewise the OK
     button has no color assigned.  You still have to assign colors to
     these items (using 0 for example), but they will be ignored.
        The color(s) of an item can be indicated in two ways if you
     have version 2.0 of OSS Personal Pascal.  If you are still using
     version 1.0, then color characteristics are indicated in this
     manner:

        Border color    (color*4096)
        Text color      (color*256)
        Text mode       (128) {for "replace" mode}
                        {otherwise, "transparent" mode}
        Fill pattern    ([0..7]*16} {where [0..7] is the "density" of
                         the color fill}
        Fill color      color

     Colors are created the same way flags were above, by combining one
     or more of the 5 characteristics above.  For example, "Box1" has
     the following:

                        (Red*4096) | (1*16) | Green

     indicating a red BORDER, a very light density fill, and a fill
     color of green.
        Of course, if you are programming for both color and monochrome
     monitors, you will want to limit you color choices! For example:

                 (Black*4096) | (Black*256) | (7*16) | Red

     will produce a solid-red-filled item with black text.  On a
     monochrome monitor the text will be obscured by the solid-fill; it
     will be a black box for all intents and purposes.  Remember -- you
     should be programming for the greatest variety of users,
     regardless of monitor type.

        With the release of version 2.0 of OSS Personal Pascal, the
     color selection above has been simplified with a function called
     D_COLOR.  It is called as follows:

               Box1_Color := D_Color( Red, No_Color, False, 1, Green);

     This is equivalent to      (Red*4096) | (1*16) | Green

     All of the parameters for this function call are Short_Integers,
     with the exception of the third, which is a boolean indicating
     transparent (False) or replace (True) modes for text.

     NOTE:  You want to use TRANSPARENT text mode if you have a fill
            pattern other than 0!


                             MOVING RIGHT ALONG

        Whew!  That was a lot of stuff for only one function call!

        Of course, once you've added a text item, you will want to
     indicate what sort of text you want.  This is what the procedure
     SET_DTEXT does.

     Calls to Set_DText look like this:

        Set_DText( DirBox, Explain1, 'This DIALOG box is', System_Font,
                    TE_Center );

     DirBox is the dialog box.  Explain1 is the item we want to set the
     text for.  'This DIALOG box...' (or anything between single quotes
     in this position) is the text we want to assign to Explain1.  The
     next parameter indicates the font size and type.  System_Font and
     Small_Font are the only recognized fonts at this time.
     System_Font is the regular font size.  TE_xxxxx indicates the
     justification to be used.  XXXXX can be LEFT, CENTER or RIGHT.
     The justification parameter may be ignored by certain item types,
     but you still need to include it in the call to Set_DText.

        Editable text fields use the procedure SET_DEDIT.  In our
     programming example above, this is called once in procedure
     GET_TEXT:

     Set_DEdit( DirBox, Editted, 'Name: __________','aaaaaaaaaa','',
                    System_Font, TE_Left );

     You will notice that it is very similar to Set_Edit, but there are
     three string parameters instead of one.  The first ('Name...') is
     the "input template".  The '_' characters indicate where the user
     can type in text.
        The second ('aaa...') parameter is the "validation template".
     You need as many characters here as you have underscore '_'
     characters.  In this example the 'a' character indicates you can
     type upper/lower case alphabetic characters only.  '9' would
     indicated decimal numbers only, etc.  Check your manual for a full
     list of validation characters.
        The third ('') parameter is the "initial" set of characters to
     be displayed where the '_' is.  You can use this to put a default
     value on the edit line, so the user only has to press the return
     key if no changes are necessary.

     NOTE:  Use Set_DEdit only ONCE.  If you want to change the text at
            all in future calls, use Set_DText.

        With the initial setup complete, we can finally produce a
     dialog box on the screen.  You set it up as follows:

        Center_Dialog( DirBox );   { used if the New_Dialog call has
                                     0,0 as the position parameters,
                                     otherwise you don't need it }

        GotLine := Do_dialog( DirBox, Editted );

     The function DO_DIALOG has two parameters.  The first is, of
     course, the dialog box we are dealing with currently.  The second
     indicates the item number of an editable text field.  The cursor
     will be put on the edit line.  If you don't have an editable text
     field, or don't want the cursor on the edit line, you must use 0
     as the value.

     Do_Dialog returns the index for the item which caused a return
     from the dialog box.  Return keys, mouse clicks on button items,
     etc. will cause the return

     We assign the value returned by the call to function Do_dialog to
     the integer variable GotLine.  We now test for what was done while
     the dialog box was on the screen.

        IF GotLine = OK THEN
          Get_DEdit( DirBox, Editted, Edit_Line )
        ELSE IF GotLine = Cancel THEN
          Edit_Line := '..No name..';

     This statement tests whether the OK button was clicked on or the
     return key was pressed (OK, being SELECTABLE | DEFAULT, will
     return the "OK" value either way).  If so, then we assign whatever
     text was typed in to the string variable Edit_Line.
        If the Cancel box was clicked on, then it doesn't matter what
     was typed in, we ignore it and assign a default value to Edit_Line
     indicating no name was typed in.

        If we are finished with the dialog box, we can call END_DIALOG.
     Otherwise, we can present the dialog box again using REDO_DIALOG.
     An example of this is in the procedure DO_THINGS.

        If we plan to reuse the dialog box in our program, we don't
     have to do anything else.  However, if we need to free up space
     for other things, we can delete the dialog box using
     DELETE_DIALOG.  An example of this is in the procedure DO_THINGS
     as well.

        Finally, in the procedure DO_THINGS, there are lines to change
     a number within a "slider box".  The procedure "OBJ_DRAW" is
     available in version 1.0 of Personal Pascal when declared as I
     have done in this program.  You must use the lines as I have given
     them to change text within a dialog box:

        Set_DText...
        work_rect...
        obj_draw...

     In version 2.0 of Personal Pascal OBJ_REDRAW is supposed to be
     there, it's listed in the manual, but it is not.  You can use the
     procedure declared in NowWhat4.pas, or you can use the procedure
     SHOW_DIALOG (which has the same parameters as OBJ_REDRAW):

        Set_DText...
        show_dialog...

        That's all you should need to get started building your own
     dialog boxes into your programs.  There are several other
     functions and procedures which I haven't mentioned so far.  Look
     in your manual to discover some of the more "esoteric" ways of
     working with dialog boxes.


                         SOMETHING A BIT DIFFERENT

        You may have noticed the following line in the above program:

               TellIt  := concat( '[0][The direction|chosen was|',
                             Location, '| ][ OK ]' );

     When you have to insert text within an Alert Box, you can do so
     easily using the CONCAT function.  Concat takes two or more
     strings and makes them into one big string.  If LOCATION is
     'North', then TellIt becomes:

                '[0][The direction|chosen was|North|][ OK ]'

     You can custom-build an Alert Box using this little trick!

        You may have also noticed the line:

        N := chr( 1 ); S := chr( 2 ); E := chr( 3 ); W := chr( 4 );

     This assigns "arrows" pointing in the directions of up, down,
     right and left respectively.

                                FINAL WORDS

        I have been entirely too long getting this article out, and I
     apologize.  I hope that you have found the previous three 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