WINDOWS AND MENUS, FOR TURBO PASCAL REFERENCE GUIDE


.PO 0
.PL 66
        WINDOWS AND MENUS, FOR TURBO PASCAL
      ------------------------------------------------------------------------
              REFERENCE GUIDE



           VERSION 1.5
           BY G.R.DYKE
               (c)1990
.PA 2
      TABLE OF CONTENTS

      DESCRIPTION OF TOPIC                                             PAGE NO
      ------------------------------------------------------------------------

      PART 1: PROGRAMMER'S REFERENCE...................................... 7
      ------------------------------

      INTRODUCTION........................................................ 8
      What's on Your Discs................................................ 8
  Installing Windows and Menus on Your System..................... 10
      About This Manual.................................................. 10
      How to Contact us.................................................. 10

      CHAPTER 1: THE WINDOWS AND MENUS UNIT, WINMEN.TPU.................. 11
      Unit Dependantcies................................................. 11
      Winmen declared types (Additional to Turbo Pascal Types)........... 11
  B1_4, B0_15, B1_23.............................................. 13
  B1_24, B1_25, B1_78............................................. 13
  B1_80, B2_25, B3_25, B3_80...................................... 13
  W0_400.......................................................... 13
  mtext........................................................... 13
  mmenu........................................................... 13
  Colour Chart.................................................... 15
  Ascii Chart..................................................... 15
  screen.......................................................... 16
  wmrec........................................................... 17
     Fields used by windows, menus & user screens only............ 17
        .typ...................................................... 17
        .op_cl.................................................... 18
        .l, .r, .t, .b............................................ 18
        .tb, .tf.................................................. 18
        .horc..................................................... 18
     Fields used by windows & menus only.......................... 19
        .bar...................................................... 19
        .bb, .bf.................................................. 19
        .tlc, .trc, .blc, .brc.................................... 19
     Fields used by menus only.................................... 19
        .comnt.................................................... 19
        .tbc, .tfc................................................ 19
        .pc....................................................... 20
        .nrow, .ncol.............................................. 20
        .sumopt................................................... 21
        .row...................................................... 21
        .bar_start, .bar_end...................................... 21
        .first.................................................... 21
     Fields used by pop & list menus only......................... 22
        .trow..................................................... 22
        .rows_vis................................................. 22
     Fields used by bar menus only................................ 22
        .ncolc.................................................... 22
        .gap...................................................... 22
        .cols_vis................................................. 23
.PA 3
     Fields used by windows only.................................. 23
        .x, .y.................................................... 23
        .lt, .rt, .tt, .bt........................................ 23
        .max...................................................... 23
        .cp....................................................... 23
  maprec.......................................................... 24
     .ID.......................................................... 25
     .sc_inx...................................................... 25
     .next........................................................ 25
  pt_maprec....................................................... 24
  pt_mtext........................................................ 30
  pt_mmenu........................................................ 30
  pt_wmrec........................................................ 30
  pt_scr.......................................................... 31
      Winmen declared Global Variables 
  (Additional to Turbo Pascal Variables).......................... 31
  wt.............................................................. 31
  VIDEO........................................................... 31
  scr............................................................. 32
  log............................................................. 32
  comment......................................................... 32
  IDG............................................................. 32
  MENU_EXIT_BY_CURSOR............................................. 32
  MENU_ACT_ON_PC.................................................. 33
  CLOSE_WITH_REMOVE............................................... 33
  IDW_active, IDM_active.......................................... 34
  gdriver, gmode.................................................. 34
  ID_WM[]......................................................... 35
  ID_MM[]......................................................... 35
  ID_SC[]......................................................... 35
  ID_MC[]......................................................... 35
  MAP[]........................................................... 25
      Winmen Initialisation.............................................. 37
      Winmen Quick Reference Lookup...................................... 38
  Windows Functions High Level.................................... 38
  Windows Procedures High Level................................... 38
  Windows Procedures Low Level.................................... 38
  Menus Functions High Level...................................... 38
  Menus Procedures High Level..................................... 39
  Menus Procedures Low Level...................................... 39
  Common Functions Low Level...................................... 40
  Common Procedures High Level.................................... 40
  Common Procedures Low Level..................................... 41

      CHAPTER 2: THE WINDOWS AND MENUS COMPLETE REFERENCE LOOKUP......... 42
      Sample Procedure................................................... 42
      Windows Functions.................................................. 44
  new_win()....................................................... 45
      Windows Procedures................................................. 48
  wclrscr()....................................................... 49
  wcolour()....................................................... 51
  wexplode()...................................................... 53
  wgotoxy()....................................................... 55
  wopen()......................................................... 57
  wselect()....................................................... 59
  wwrite()........................................................ 61
.PA 4
  wwriteln()...................................................... 64
  _wbox_on()...................................................... 67
  _wchk_min_size()................................................ 71
  _wscroll_up()................................................... 73
      Menus Functions.................................................... 76
  bar_menu()...................................................... 77
  list_menu()..................................................... 81
  new_menu()...................................................... 85
  pop_menu()...................................................... 90
      Menus Procedures................................................... 94
  mbar_comment().................................................. 95
  mchange_char().................................................. 98
  mpic_col()..................................................... 101
  _mbbar_end()................................................... 103
  _mbbar_home().................................................. 105
  _mbbar_left().................................................. 107
  _mbbar_off()................................................... 109
  _mbbar_on().................................................... 111
  _mbbar_right()................................................. 113
  _mbdisplay()................................................... 115
  _mchk_on_screen().............................................. 118
  _mdisplay().................................................... 120
  _mlbar_blst().................................................. 122
  _mlbar_end()................................................... 124
  _mlbar_home().................................................. 126
  _mlbar_tlst().................................................. 128
  _mlmov_do().................................................... 130
  _mlmov_up().................................................... 134
  _mpbar_end()................................................... 136
  _mpbar_home().................................................. 138
  _mplbar_do()................................................... 140
  _mplbar_off().................................................. 142
  _mplbar_on()................................................... 144
  _mplbar_up()................................................... 146
      Common Functions.................................................. 148
  _wmchk_for_mrec().............................................. 149
  _wmsave_scr().................................................. 151
      Common Procedures................................................. 153
  wmadd_mrec_all()............................................... 154
  wmattribs().................................................... 157
  wmclose()...................................................... 160
  wmdelete()..................................................... 162
  wmdel_mrec_all()............................................... 165
  wmfloat()...................................................... 168
  wmmem_stat()................................................... 171
  wmmove()....................................................... 183
  wmremove()..................................................... 186
  wmrest_scr()................................................... 189
  wmsave_scr()................................................... 192
  wmslide()...................................................... 204
  wmtitle()...................................................... 211
.PA 5
  _wmadd_mrec().................................................. 217
  _wmbox_attribs()............................................... 219
  _wmchk_exist()................................................. 223
  _wmchk_mem()................................................... 225
  _wmchk_type().................................................. 228
  _wmcopy_scr().................................................. 230
  _wmdel_mrec().................................................. 232
  _wmrest_scr().................................................. 234
  _wmslide_d()................................................... 236
  _wmslide_l()................................................... 240
  _wmslide_r()................................................... 243
  _wmslide_u()................................................... 246

      PART 2: APPENDICIES............................................... 249
      -------------------

      APPENDIX A: ERROR MESSAGES........................................ 250
      Windows Error Messages............................................ 250
      Menus Error Messages.............................................. 251
      Common Error Messages............................................. 251
.PA 6





        THIS PAGE LEFT INTENTIONALLY BLANK






.PA 7
       P                      A                       R                      T 
      ------------------------------------------------------------------------
              1





      PROGRAMMER'S REFERENCE
.PA 8
      I      N      T      R      O      D     U     C     T     I     O     N
      ------------------------------------------------------------------------
      
      Welcome to Windows and Menus for Turbo Pascal, a complete library of
      powerfull functions and procedures packed into one Turbo Pascal Unit
      Winmen.Tpu, in fact 67 in all, each one designed to make the programmers
      life a little easier and his or her applications just that little bit
      more professional looking, not to mention functional and flexible. 
      Windows and Menus makes creating, good looking, practical programmes 
      a joy rather than a chore and of course the end result, which is what
      we all want, comes up in a fraction of the time that would be required
      to produce the same application programme without the use of Windows and
      Menus. One other very important aspect of using the Windows and menus
      Unit, which may not have occured to everyone, is that the actual source
      code, which you write, becomes automatically more manageable, because
      you do not have to worry about any of the code associated with any of 
      the Windows and Menus functions and procedures, its all taken care of
      in the Unit, so for example, if the code for one of the Windows and 
      Menus routines is changed, all the user would have to do, on recipt of 
      an update, would be recompile all the modules of his code that used that
      particular function or procedure, rather than having to change reams
      and reams of code in dozens of modules. These benefits are of course
      automatic to all users of Turbo Pascal Units and are synonimus with the
      modular approach to programming and not just common to the Windows and
      Menus Unit.

      What's On Your Discs
      ------------------------------------------------------------------------

      Windows and Menus comes on two 5.25, or one 3.5 inch distribution
      disc(s). For 5.25" disc users:-
  Disc 1 contains all the example programmes from the manual, two
      demonstration programmes, one application programme, all with source and
      object code and the Windows and Menus Reference Guide.
  Disc 2 containes the Windows and Menus interface section and the
      actual TP Unit file.
  All files have been archived using the LHARC.EXE utility, which
      is also on disc 1, to enable us to pack them onto as few disc's as
      possible, see the LHARC.DOC file, or just type LHARC at the prompt for
      info on how to use this very simple programme.
  Below is a complete listing of all the files contained on disc and in
      the archive .LZH files, so you can choose which ones to load on to your
      system, you will probably only need the WINMEN.TPU (library) file, about
      1,100,000 bytes will be required to unarchive all the files.

      README       To see any last-minute notes and corrections, Type README
      (If Present) at the system prompt. (If you have a printer you can print
     it out.) Once you have reviewed this material keep it
     around for future reference.

      EXAMPLES.LZH These are all the example programmes exactly as you will
      *.PAS Files  find them in the Windows and Menus Reference Guide, to
     save you hours of typing and they have all been tested
     fully, so there should'nt be any frustrating moments.
.PA 9
      DEMOS.LZH    This is the Windows and Menus basic demonstration programme,
      WINDEMO.EXE  to give you a taste of what can be done using this units,
     basic features, available in V1.0 and V1.5.

      DEMOS.LZH    Here's the source code for the above demonstration, so
      WINDEMO.PAS  you can see how it's been done.

      DEMOS.LZH    This is the Windows and menus advanced demonstration
      ADVANCED.EXE programme and covers all of the units more advanced
     features, available from V1.5 onwards.

      DEMOS.LZH    Here's the source code for the above demonstration, so
      ADVANCED.PAS you can see how it's been done.

      DEMOS.LZH    This is a fully working application programme, that makes
      PAGEPRNT.EXE full use of the Winmen unit, to prove what is demonstrated
     in the above demo programmes.
        It is a fully featured Epson/IBM compatible printer set
     up utility, complete with print spooler facility, it
     should work fine with most printers. Not only does it
     show off the Winmen unit, but also many advanced features
     of Turbo Pascal as well.
        The main purpose of including this programme and its
     source code, is to demonstrate exactly how to get a list
     of dos filenames into a Winmen Menu, using a filename
     template, which can include the wild cards '?' and '*' or
     just single filenames. It also demonstrates how to use
     them once you have got them into the Menu.
        There is no manual supplied, as we don't think you
     will require one, it is very easy to use, just experiment.

      DEMOS.LZH    Here's the source code for the above application, so you
      PAGEPRNT.*   can see how it's been done.

      MANUAL.LZH   This is the complete Windows and Menus Programmers
      WINMEN.DOC   Reference Guide, on a disc, ready for you to print out,
     you will need about 260 pages of paper. It is a plain
     ascii file so should be compatible with all editors and
     page break numbers have been put in to give you a guide.
        If you specifically want a Wordstar format version, to
     save you the nightmare of editing the ascii file version,
     which also contains all the appropriate embolden control
     characters etc, then we would be more than happy to supply
     you with one, just sent a blank disc, size of your choice,
     along with your name, address, and postcode to the address
     supplied on page 10 of this manual.  Please also include œ1
     for postage and packing, checks payable to G.R.Dyke, please
     allow a couple of weeks for delivery.
        Alternatively see the order form at the end of the
     WINDEMO.EXE demonstration programme.

      WINMEN.PAS   This is the complete interface section listing from the
     Windows and Menus Unit, with nothing missing at all, so
     that you can inspect all the new variable types, the
.PA 10
     actual global variables defined for the unit, both of
     which of course become available to the user whenever the
     WINMEN.TPU unit is used in one of your programmes and
     lastly the initialisation section, so you can see what
     certain global variables are initialised to, each time an
     application programme, that uses WINMEN.TPU, is run.

      WINMEN.TPU   This is Windows and Menus unit library and contains all
     the code for every function and procedure in the unit.
     This is probably the only file you will need to copy on
     to your system disc.

      Installing Windows And Menus On Your System Disc.
      ------------------------------------------------------------------------

      This is the easy part, simply copy the WINMEN.TPU library file onto your
      system, into the subdirectory that contains all the other .TPU files,
      this will ususly be called EXE or UNIT, if in doubt copy it into each.
      This file must be accessable to the compiler at compile time.

      About This Manual
      ------------------------------------------------------------------------
      This manual is split into two parts: a programmers reference section and
      appendices. The first part of this manual, "The Programmers Reference"
      offers technical information on the following subjects:

      >Chapter 1: The Windows and Menus unit, WINMEN.TPU                       

      >Chapter 2: The Windows and Menus complete reference lookup              

      Part 2 of this manual "Appendices" contains listings of all runtime
      error messages that could be generated by the Windows and Menus code
      in your programme and also offers advice on what to do about it.

      >Appendix A: Windows Error Messages
     Menus Error Messages
     Common Error Messages                                                   

      How To Contact Us
      ------------------------------------------------------------------------

      If, after reading this manual and using Windows and Menus, you would
      like to contact us with comments, suggestions or a technical querie,
      we suggest that you write a letter detailing your problem and send it
      to:- 

      G.R.Dyke,               Also include the following:-
      8, Middle Road,         >Product name and version number.
      North Baddesley,        >Product serial number.
      Southampton.            >Computer make and model number.
      Hants SO52 9JE.         >Operating system and version number.
      England.

      Unfortunatly we cannot offer any telephone support at this time.
.PA 11

      C           H           A           P           T           E          R
      ------------------------------------------------------------------------
              1

       



      THE WINDOWS AND MENUS UNIT, WINMEN.TPU


      Units are the basis of modular programming in Turbo Pascal. They are
      used to create libraries that you can include in various programmes
      without making the source code available, and to devide large programmes
      into logically related modules.

      UNIT-->INTERFACE PART-->IMPLEMENTATION PART-->INITIALIZATION PART

      Further information on units in general can be obtained from the 
      Turbo Pascal Reference Guide.

      Unit Dependantcies
      ------------------------------------------------------------------------

      The Windows and Menus unit is dependant upon the following Turbo Pascal
      standard units:- Crt, Dos and Graph, These three units are called in the
      Uses clause of the Winmen unit, so any programme or unit that uses the
      Windows and Menus unit "Winmen" in its Uses clause need not include the
      Crt, Dos or Graph units in that clause as they are already called.
      None of the routines in the Winmen unit are defined by standard pascal
      thus they are placed in there own unit.

      Winmen Declared Types, (Additional To Turbo Pascal Types)
      ------------------------------------------------------------------------
  
      Each of the types defined by the Winmen unit are briefly discussed in
      this section. For more detailed information, see the descriptions of the
      functions and procedures that depend on these objects in Chapter 2 "The
      Windows And Menus Complete Reference Lookup". Where this is not
      applicable, a full description is given in this section.

      type                     {*ALL BYTE SETS BETWEEN THE VALUES SHOWN*}
  B1_4    = 1..4;       {*WINDOW/MENU BORDER BOX TYPE*}
  B0_15   = 0..15;      {*ALL BACKGROUND & FOREGROUND COLOUR VALUES*}
  B1_23   = 1..23;      {*>                                     <*}
  B1_24   = 1..24;      {* >                                   < *}
  B1_25   = 1..25;      {*  >                                 <  *}
  B1_78   = 1..78;      {*   > ALL MENU/WINDOW LIMITS VALUES <   *}
  B1_80   = 1..80;      {*   >                               <   *}
  B2_25   = 2..25;      {*  >                                 <  *}
  B3_25   = 3..25;      {* >                                   < *}
  B3_80   = 3..80;      {*>                                     <*}
  W0_400  = 0..400;     {*WORD VAL SENT TO NEW_MENU() & RETURNED FROM*}
          {*LIST_MENU() (MENU OPTIONS)                 *}
.PA 12
  mtext  = array[1..400] of string[80]; {*USER MENU OPTION TEXT ARRAY*}
  mmenu  = array[0..402,0..80] of word; {*MENUS SCREEN RAM FORMAT OF*}
            {*MTEXT                     *}
  screen = array[0..2000] of word;      {*SAVED SCREEN AREA BEHIND*}
            {*WINDOW OR MENU IN SCREEN*}
            {*RAM FORMAT              *}

  wmrec = record               {*WINDOW/MENU/USER SCREEN, RECORD*}
     typ : char;               {*RECORD TYPE I.E. WINDOW OR MENU*}
     op_cl : boolean;          {*FLAG FOR 1=OPEN/0=CLOSED*}
     bar   : boolean;          {*FLAG FOR A MENU OPEN AS A BAR MENU*}
     comnt : boolean;          {*FLAG 1=BAR MENU COMMENTS 0=NONE*}
     first : boolean;          {*FLAG 1=MENU NEVER OPENED, 0=OPENED*}
     x,y : byte;               {*X,Y CURSOR POSITION IN A WINDOW ONLY*}
     l : B1_80;                {*>                            <*}
     r : B1_80;                {* >CURRENT BOUNDS OF A WINDOW< *}
     t : B1_25;                {* >MENU OR USER SCREEN AREA  < *}
     b : B1_25;                {*>                            <*}
     lt,rt   : B1_80;          {*WINDOW TEMPARARY LEFT,RIGHT BOUNDS*}
     tt,bt   : B1_25;          {*WINDOW TEMPARARY TOP,BOTTOM BOUNDS*}
     max     : B1_80;          {*WINDOW MAXIMUM OF (R-L) & (B-T)*}
     bb,bf   : B0_15;          {*WIN/MEN BORDER BACK & FORE COLOURS*}
     tb,tf   : B0_15;          {*WIN/MEN TEXT BACK & FORE COLOURS*}
     tbc,tfc : B0_15;          {*MENU COMPLEMENT OF TEXT COLOURS*}
     pc      : B0_15;          {*MENU FIRST (PICK) CHAR COLOUR*}
     nrow     : word;          {*TOTAL NUMBER OF ROWS IN WIN/MEN*}
     ncol     : word;          {*TOTAL NUMBER OF COLUMNS IN WIN/MEN*}
     ncolc    : byte;          {*NUMBER COLS OF COMNT IN BAR OPTION*}
     sumopt   : word;          {*SUM OF ALL THE BAR MENU OPTION TXT*}
     gap      : B1_80;         {*GAP SIZE BETWEEN BAR MENU OPTIONS*}
     row,trow : word;          {*CURRENT & TOP ROW OF LIST/POP MENU*}
     rows_vis : B1_23;         {*ACTUAL ROWS VISIBLE LIST/POP MENU*}
     cols_vis : word;          {*ACTUAL COLUMNS VISIBLE-BAR MENU*}
     tlc,trc,blc,brc   : word; {*WIN/MEN CORNERS IN SCR RAM FORMAT*}
     horc,verc         : word; {*WIN/MEN EDGES IN SCREEN RAM FORMAT*}
     cp                : word; {*CENTRE POINT OF WIN SCR RAM FORMAT*}
     bar_start,bar_end : word; {*MENU BAR START & END SCRN ADDRESS*}
  end;

  pt_mtext  = ^mtext;          {*POINTER TO USERS MENU TEXT ARRAYS*}
  pt_mmenu  = ^mmenu;          {*POINTER TO SCR RAM FORMAT OF ABOVE*}
  pt_wmrec  = ^wmrec;          {*POINTER TO WIN/MEN/SCR RECORD*}
  pt_scr    = ^screen;         {*POINTER TO SAVED SCREEN AREA ARRAY*}
  pt_maprec = ^maprec;         {*POINTER TO 3D SCREEN MAP RECORD*}

  maprec = record              {*3D SCREEN MAP RECORD*}
     ID : byte;                {*ID OF OBJECT THAT OWNS THIS MAPREC*}
     sc_inx : word;            {*ID_SC[] INDEX FOR OBJECTS BACKGROUND*}
     next : pt_maprec;         {*POINTER TO NEXT MAPREC IN LINKED LST*}
  end;
.PA 13
      >>B1_4, B0_15, B1_23, B1_24, B1_25,
      B1_78, B1_80, B2_25, B3_25, B3_80 = byte;
      These types are all defined to be values of type byte, between the
      ranges shown, ie. B1_23 would give a byte type variable with an
      allowable range of 1 to 23, anything outside this range would of
      course produce a range check error (provided the {$R+} compiler
      directive is set).
  B1_4  is used for the window and menu border box type.
  B0_15 is used for all the window/menu background and foreground
        text colours.
  The remainder of types in this group are used for all window
  menu and user saved screen limits, ie. left top right and bottom.

      >>W0_400 = word;
      This is a word type, which is used in the unit to define variables, to 
      send values to, and receive from, functions and procedures, namely the 
      number of options in a menu for NEW_MENU(), and the number of the option
      chosen on a menu for POP_MENU(), LIST_MENU() and BAR_MENU().

      >>mtext = array[1..400] of string[80];
      This string type defines an array of 400 strings of length 80, this is  
      designed to make it easier, and when used in conjunction with a pointer
      to this type (pt_mtext, see below), more memory efficient, for the user 
      when he/she is defining text strings for all menus and bar menu comment 
      strings for bar menus, although you can of course define your own 
      similar type if you wish. For full details of this type and how to use 
      it to best effect see the NEW_MENU() and MBAR_COMMENT() sections in the 
      Complete Reference Lookup.

      >>mmenu = array[0..402,0..80] of word;
      This word type will be explained in more detail here as it is not
      actually used, directly, as a parameter for any of the Windows or Menus
      functions and procedures, but is a very important basic building block
      for all menus.
 Firstly the type defines a 2D word array of 402x80 words, that is each 
      element in the 402 array has access to 80 words. Now by coincidence the
      screen is 80 Ascii characters wide, But you say, the array is 80 words,
      correct, although the screen is 80 characters wide, that is in Ascii 
      terms, or how it actualy appears to the user, in reality each screen
      cell or character in screen ram is one word, hense the 80 words for
      each of the 402 array elements will give us enough memory to hold one 
      screen line for each one, giving us a possible maximum of 402 screen
      lines. 
 Each screen ram character word is organised as described below:-
      CHARACTER BYTE, COLOUR BYTE (but DOS requires the low byte first!).
      LOW BYTE FIRST : COLOUR BYTE, HIGH BYTE LAST : CHARACTER BYTE.
.PA 14
      The colour byte is made up in the following way:-
      High Bit 7 : blink flag 0=off 1=on --------------[0 100 1001]
    Bit 6 : -|                                      |   |
    Bit 5 :  -> Character background colour 0-7 ----|   |
    Bit 4 : -|                                          |
    Bit 3 : -|                                          |
    Bit 2 :  -> Character foreground colour 0-15 -------|
    Bit 1 : -|
      Low  Bit 0 : -|

      Full colour chart
      CGA                      MDA 
      Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink                     

      Chart of usefull box type Ascii values
      Dec   | 169 170 179 180 181 182 183 184 185 186 187 188 189 190 191
      Hex   |  A9  AA  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF
      ASCII |  ©   ª   ³   ´   µ   ¶   ·   ¸   ¹   º   »   ¼   ½   ¾   ¿

      Dec   | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
      Hex   |  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE
      ASCII |  À   Á   Â   Ã   Ä   Å   Æ   Ç   È   É   Ê   Ë   Ì   Í   Î

      Dec   | 207 208 209 210 211 212 213 214 215 216 217 218
      Hex   |  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA
      ASCII |  Ï   Ð   Ñ   Ò   Ó   Ô   Õ   Ö   ×   Ø   Ù   Ú                  

      So a screen ram word value of $49B9 would give the character ¹ with
      a background colour of red and forground colour of light blue. The
      value $49 for the colour byte (low byte) converts to a binary value of
      01001001 which is exactly as described above. The $B9 value for the  
      character byte (high byte) can be referenced in the table above.
 It should be clear by now that the 80 words for each of the 402 array
      elements is wide enough for a menu the entire width of the screen, the
      limitation on the maximum number of lines in a menu will of course be
      400 as 2 lines are required for the top and bottom borders of the menu.
      We cannot define a type with any more lines in at this width as this now
.PA 15
      represents the largest structure we can have in Turbo Pascal, which is
      65535 bytes. But dont fear the Windows and Menus unit never actualy
      defines a structure of this type directly, only an array of pointers, 
      (see ID_MM below) each one of which, could eventualy point to a 
      structure of this type. By using getmem() in conjunction with a pointer 
      defined as type pt_mmenu (see pt_mmenu below), we can allocate the 
      minimum amount of memory required for a particular length of menu, in 
      much the same way as for mtext and pt_mtext (see sections on mtext, 
      pt_mtext and NEW_MENU() function). We are unable to save ram on the 
      width as the offset calculated for the start of each 80 word array is
      fixed from the begining of the block of ram allocated when getmem() is
      used, so we could not just say getmem(ptr,LxWx2), if L=2 and W=25 that 
      only gives us 50 words, line/array 1 ptr^[1] starts at offset 0 but 
      line/array 2 ptr^[2] starts at offset 80, this is because the type 
      definition sayes that each of the 402 array elements accesses an array 
      of 80 words, as you can see we have exceeded the 50 word block allocated 
      to us in getmem() possibly overwriting other data. There is no error 
      either as everything is within range, there would also be no error if 
      we tried to fill up a 3rd line/array, again disasterous. But the Windows 
      and Menus unit takes care of all this for you, although you do need to 
      be carefull when using getmem() and freemem() when used in conjunction 
      with arrays and pointers to arrays. 
 The diagram below will give you a better idea of how the mmenu array 
      is used and how to access it correctly for a particular menu, assuming
      that menu has been set up using the NEW_MENU() function:- 

      IDM^[0..402][0..80]               WORDS
          [0..80]
  0123456789............................................80
        0ÉÍÍÍÍÍÍÍÍÍÍÍÍÍ»   = IDM^[0][0..15]
        1ºOPTION TEXT 1º
       ARRAYS  2ºOPTION TEXT 2º   --------------------->
      [0..402] 3ºOPTION TEXT 3º
        4ºOPTION TEXT 4º   = IDM^[4][0..15]
        5ÈÍÍÍÍÍÍÍÍÍÍÍÍͼ
        6       |
        7       |
        8       |
        9       V

      The above diagram assumes that you have defined a pointer somewhere in
      your programme, like this:- 
      var
  IDM : pt_mmenu; (see pt_mmenu below)
      This then gives us a nil pointer variable to a structure of type mmenu 
      which is array[0..402][0..80] of word.
      To enable us access the actual data for the above menu, assuming the 
      menu has been set up using NEW_MENU() you would then do the following:- 
      IDM := ID_MM[ID]; (see ID_MM below)
.PA 16
      Where ID is the reference byte returned from NEW_MENU() when the menu
      in question was set up, and ID_MM[] is the array of pointers, mentioned
      above, each one of which could point to a separarte mmenu structure.
      This assignment gives the IDM pointer the starting address for the above
      menu structure.
 You could of course not bother with defining the IDM pointer and just
      use the array of pointers to reference the above menu, like this:-
      ID_MM[ID]^[4][0..15], this is equivalent to IDM^[4][0..15], but I think
      it's neater to define the pointer. 
 The [0..15] range is only there to indicate the width of the above 
      menu, you would only normaly use one number for the index thus:- 
      IDM^[4][1], this would access word 1 in line 4 of the above menu, which 
      is the word describing the character 'O'.
 You will see all the above techniques used from time to time in the
      example programmes supplied with the Windows and Menus unit, it is a
      very important aspect of the unit and the descriptions here, of all the
      new types and variables, will enable you to extract the most benefit
      from it, but it will require a little studying.

      >>screen : array[0..2000] of word;
      This is another array of type word, this time it is just a single
      array[0..2000] of word, this type gives us sufficient space to save
      all 25 screen lines, of 80 characters into, in other words we may
      save a complete screen full of information into this array type.
 Each word represents a character in screen ram format exactly as
      above in the mmenu array.
 The purpose of a structure of this type is to save, or copy into it
      all the screen ram data beneath a window or menu before it is opened
      so that it can be copied back to screen ram when the window or menu
      is finished with, effectively closing it.
 As with the mmenu and mtext arrays the Windows and Menus unit never
      actualy defines a variable of this type directly, but does so by means
      of pointers, all the pointers being of type pt_screen (see pt_screen
      below) which are held in a pointer array called ID_SC (see ID_SC
      below). This time when the unit uses getmem() to allocate memory for
      the structure, of type screen[0..2000], it only needs to reserve
      exactly enough for the actual size of the window or menu, that is
      Length x Width, unlike the mmenu and mtext type structures above,
      which needed to allocate all 80 words width x length, regardless of
      the actual width of the menu, this is because the screen data only
      needs to be saved into the screen array in a linear fashion, the
      number of rows and columns variables, from the wmrec (see wmrec below)
      are used to calculate how to copy the data back to screen ram from
      the linear screen array when the window or menu is closed.
 To access a screen array for a particular window or menu, assuming
      it has already been set up using NEW_WIN() or NEW_MENU(), you would
      need to do the following:-
      var
  IDS : pt_screen; (see pt_screen below)
      This defines the pointer variable that you need to point to the screen
      array, next you need to give the nil pointer a valid address to point
.PA 17
      to, ie. the start address for that particulr windows or menus screen
      array data, like this:-
      IDS := ID_SC[ID]; (see ID_SC below)
      Where ID is the byte value reference for the window or menu in question
      as returned from NEW_WIN() or NEW_MENU(), as explained in mmenu above.
      Then to access the screen array data an assignment like this would be
      used:- some_word_var := IDS^[index]; or IDS^[index] := some_word_var;
      Again as for mmenu you could just use the predefined pointer array
      ID_SC[] to access the relevant screen array for that particular window

      >>wmrec = record;
      This is the main Windows and Menus unit record structure, every window
      menu or user saved screen that has been created with NEW_WIN(), 
      NEW_MENU() or WMSAVE_SCR() will have one of these records, but which
      fields are filled in depends on weather the object is a window, menu or
      a user saved screen, this is given below.
 As for mmenu and screen, wmrec is always used in conjunction with a
      pointer of type pt_wmrec (see pt_wmrec below), and again all the
      pointers are stored in a pointer array ID_WM[] (see ID_WM[] below), so
      the procedure for accessing a particular record structure, assuming
      that the window, menu or user saved screen has already been set up 
      using either NEW_WIN(), NEW_MENU() or WMSAVE_SCR(), is as follows:-

      var
  IDR : pt_wmrec; (see pt_wmrec below);
      This defines our pointer to a record structure of type wmrec, next we
      need to give the nil pointer an address to point to, this will be the
      base address for that particular record structure:-
      IDR := ID_WM[ID]
      The above assignment gives the pointer the address of that record
      whoose reference is ID, where ID is the byte value returned from the
      NEW_WIN() or NEW_MENU() functions or the user saved screen number
      sent to WMSAVE_SCR(). Once this has been done you an access  all the
      individual record fields in the normal way:- IDR^.rec_var, where 
      rec_var is any of the record fields, as described below.
 You could of course access the record in question without defining
      a pointer, using just the predefined pointer array:- ID_WM[ID]^.rec_var;
      although this is not as tidy as defining a pointer it is equivalent to
      it.

      N.B: A window, menu or user saved screen is refered to below as the
    'object' for convienience only.
      
      Fields Used By Windows, Menus and User Saved Screens:-
  >>.typ : char;
  This field describes the object, and will be 'W' for Window and user
  saved screen or 'M' for a menu. This is defined when either NEW_WIN()
  NEW_MENU() or WMSAVE_SCR() is called.
.PA 18
  >>.op_cl : boolean;
  This is a flag which sayes weather the object is open or closed and
  will be true for open and false for closed. This is set true when
  the window is opened using WOPEN(), when the menu is activated with
  POP_MENU(), LIST_MENU() or BAR_MENU() and for a user saved screen
  when WMSAVE_SCR() is called. It is reset to false only when WMCLOSE()
  is called and when NEW_MENU() and NEW_WIN() are called.

  >>.l, .r : B1_80; .t, .b : B1_25;
  These fields are the current bounds or extents of an object and give
  its exact position on the screen. For windows these are available as
  soon as NEW_WIN() has been called, for a user saved screen as soon
  as WMSAVE_SCR() has been called, but for a menu they are obviously
  not available until one of either POP_MENU(), LIST_MENU() or
  BAR_MENU() has been called, as the position on the screen is not
  defined in the call to NEW_MENU().

  >>.tb, .tf : B0_15;
  These are the textbackground and textforegroung colours for the
  object. For a user saved screen these values are taken from the
  Turbo Pascal TEXTATTR system variable at the time that WMSAVE_SCR()
  was called. For all other objects they are taken from the appropriate
  function or procedure parameter (see 'The Complete Reference Lookup'
  for details).
     Each screen ram character word is organised as described below:-
  CHARACTER BYTE, COLOUR BYTE (but DOS requires the low byte first!).
  LOW BYTE FIRST : COLOUR BYTE, HIGH BYTE LAST : CHARACTER BYTE.

  The colour byte is made up in the following way:-
  High Bit 7 : blink flag 0=off 1=on --------------[0 100 1001]
       Bit 6 : -|                                      |   |
       Bit 5 :  -> Character background colour 0-7 ----|   |
       Bit 4 : -|                                          |
       Bit 3 : -|                                          |
       Bit 2 :  -> Character foreground colour 0-15 -------|
       Bit 1 : -|
  Low  Bit 0 : -|

  >>.horc : word;
  This is the horizontal border character for the object, and will be
  in screen ram format, that is low byte first, colour, high byte last 
  character. For a user saved screen the colour byte is taken from the
  TEXTATTR system variable when WMSAVE_SCR() is called, the character
  will always be 'space', and is only used for wiping out a title  
  string, if one existed, when WMTITLE() is used for a subsequent time. 
  For all other objects the word is calculated from the border type and 
  colour parameters, in the call to either NEW_WIN() or NEW_MENU(), as 
  these both call _WMBOX_ATTRIBS().  
.PA 19
      Fields Used By Windows and Menus Only:-
  >>.bar : boolean;
  This field is used to describe weather a menu is open as a bar menu
  or as a pop/list menu. If it is open as a bar menu then it will be
  true otherwise it is false. It is set during the call to either
  POP_MENU(), LIST_MENU() or BAR_MENU() as these, in turn call, 
  _MDISPLAY() and _MBDISPLAY() respectively, which is where the actual
  assignment takes plase. It is always initialised to false, in the
  calls to NEW_WIN() and NEW_MENU(). It will determine weather certain 
  operations can be carried out on the menu or not, namely the 
  displaying of comment lines, which are only allowed when you are 
  displaying a menu as a bar menu.                                     
  
  >>.bb, .bf : B0_15;
  These fields are the border background and foreground colours and
  are taken from the appropriate function or procedure parameter (see
  the complete reference lookup for details).
     Each screen ram character word is organised as described below:-
  CHARACTER BYTE, COLOUR BYTE (but DOS requires the low byte first!).
  LOW BYTE FIRST : COLOUR BYTE, HIGH BYTE LAST : CHARACTER BYTE.

  The colour byte is made up in the following way:-
  High Bit 7 : blink flag 0=off 1=on --------------[0 100 1001]
       Bit 6 : -|                                      |   |
       Bit 5 :  -> Character background colour 0-7 ----|   |
       Bit 4 : -|                                          |
       Bit 3 : -|                                          |
       Bit 2 :  -> Character foreground colour 0-15 -------|
       Bit 1 : -|
  Low  Bit 0 : -|

  >>.tlc, .trc, .blc, .brc : word;
  These are the four corner characters of the objects border, and will 
  be in screen ram format, that is low byte first, colour, high byte 
  last character. For all objects the word is calculated from the 
  border type and colour parameters, in the call to either NEW_WIN() or  
  NEW_MENU(), as these both call _WMBOX_ATTRIBS(). 
  
      Fields Used By Menus Only:-
  >>.comnt : boolean;
  This field will be true, for a particular menu record, if the  
  MBAR_COMMENT() procedure has been used on that menu, regardless
  of the menus eventual type. It is set to false in the call to
  NEW_MENU() and to true in the call to MBAR_COMMENT(). Comments
  cannot be erased from a menu record, but by setting this field to
  false you can prevent them from being displayed, they can of course
  be overwritten in each subsequent call to MBAR_COMMENT().

  >>.tbc, .tfc : B0_15;
  These two fields are the textbackground and foreground colours that
  were originally defined, for a particular menu, in the call to
  NEW_MENU(), but they have been complemented. The .tbc field is 
  defined as '.tbc := .tb xor 7;' as the background colour can only
.PA 20
  be a value 0-7 (00000000-00000111 binary), this ensures that each 
  result bit in .tbc will be the opposite of the same bit in .tb, the 
  blink flag (bit 3 .tb > 7) will remain unnafected, so that the new 
  complemented colour will also blink if the flag was set in .tb. The 
  .tfc field is defined as '.tfc := tf xor 15;' again this makes sure
  that each bit in .tfc is the opposite of the same bit in .tf, as the
  foreground colours are values 0-15 (00000000-00001111 binary).
  These two complemented colour fields are used to draw the menu's
  highlighted selection bar, and makes sure they are always in a 
  colour that can be seen against the option text it's self.
     Each screen ram character word is organised as described below:-
  CHARACTER BYTE, COLOUR BYTE (but DOS requires the low byte first!).
  LOW BYTE FIRST : COLOUR BYTE, HIGH BYTE LAST : CHARACTER BYTE.

  The colour byte is made up in the following way:-
  High Bit 7 : blink flag 0=off 1=on --------------[0 100 1001]
       Bit 6 : -|                                      |   |
       Bit 5 :  -> Character background colour 0-7 ----|   |
       Bit 4 : -|                                          |
       Bit 3 : -|                                          |
       Bit 2 :  -> Character foreground colour 0-15 -------|
       Bit 1 : -|
  Low  Bit 0 : -|
  
  >>.pc : B0_15;
  The menu option text's first character colour is stored in this field
  its best to have the first character a different colour, so that it 
  stands out, as this character on the keyboard can be used to select
  that option. The field is taken from the appropriate function or 
  procedure parameter (see the complete reference lookup for details).
     Each screen ram character word is organised as described below:-
  CHARACTER BYTE, COLOUR BYTE (but DOS requires the low byte first!).
  LOW BYTE FIRST : COLOUR BYTE, HIGH BYTE LAST : CHARACTER BYTE.

  The colour byte is made up in the following way:-
  High Bit 7 : blink flag 0=off 1=on --------------[0 100 1001]
       Bit 6 : -|                                      |   |
       Bit 5 :  -> Character background colour 0-7 ----|   |
       Bit 4 : -|                                          |
       Bit 3 : -|                                          |
       Bit 2 :  -> Character foreground colour 0-15 -------|
       Bit 1 : -|
  Low  Bit 0 : -|
  
  >>.nrow, .ncol : word;
  These two fields give the length, in rows (lines) and width, in 
  columns of the actual menu, as it is seen on the screen. The two
  fields are calculated and filled in during the call to NEW_MENU().
  For more detailed information refer to The Complete Reference Lookup.
.PA 21
  >>.sumopt : word;
  This field contains the total numeric length of all the option text
  strings in that particular menu added together, it is calculated in 
  the call to NEW_MENU(), it is used mainly for bar menus, to help
  calculate the required '.gap' between options (see also .gap below).
  
  >>.row : word;
  This is one of the more important fields and contains the current
  row number of the menu's highlighted option bar, 1 being the top
  most, or for a bar menu, leftmost option. This field is available
  to the user as soon as a menu has been opened, each subsequent 
  time that menu is used, the highlighted bar will appear on the 
  option on which it was left, thus reflecting the value of .row.
  The list in a list menu will also appear in the same position as
  it was left, when the menu was closed. (see .trow below).
  
  >>.bar_start, .bar_end : word;
  These two word fields give the offset address in screen ram to the
  start and end of the highlighted menu selection bar, respectively.
  only the .bar_start field is used for bar menus, .bar_end is not
  defined. As mentioned above these two fields are the offset part
  of the segment:offset address in screen ram, the segment part of
  the address can be obtained from the predefined Winmen system
  variable called 'VIDEO'. The actual screen ram contents at that

  particular address, ie. the highlighted selection bar, for that
  particular menu, as seen on the screen, can be accessed in the
  following ways:- memw[VIDEO:ID_WM[ID]^.bar_start] := word_var;
     memw[VIDEO:IDR^.bar_start] := word_var;
     The reverse assignments are of course also valid.
  
  Where memw[:] is the Turbo pascal Direct memory access word array;
        VIDEO is the Winmen system variable containing the segment
        base address for screen ram.
        ID_WM[] is the Winmen pt_wmrec pointer array (see ID_WM[],
        pt_wmrec and wmrec);
        ID is the menu identifier byte as returned from NEW_MENU();
        IDR is a pointer variable of type pt_wmrec (see pt_wmrec and
        wmrec).
  
  >>.first : boolean;
  This flag tells various functions and procedures in the Winmen unit,
  weather or not that particular menu has ever been opened, so that the
  positioning of the highlighted menu bar can be determined, the first
  time a menu is opened the bar is positioned at the top of the menu
  over option one, on subsequent opens the bar is put back in the same
  position as when the menu was closed, this is also the case for a
  list menus option text, where it exceeds the visible options. The
  flag is set to 'true' for menu never opened (during the call to
  NEW_MENU()) and 'false' if it has been opened (during the first call
  to _MDISPLAY() or _MBDISPLAY, which are called by POP/LIST_MENU(),
  and BAR_MENU() respectively).
.PA 22
      Fields Used By Pop and List Menus Only:-   
  >>.trow : word;
  This field is used to hold the number, of the menu option, which is 
  currently at the top of the visible menu. For a Pop menu this will
  always be row one, and for a List menu it will depend upon how far
  the list has been scrolled, towards the bottom of the list. This 
  field is initialised in the calls to POP_MENU() and LIST_MENU()
  which both call _MDISPLAY(), it is altered and used by almost all
  of the functions and procedures that support POP/LIST_MENU(). The
  first time a menu is opened as a List menu .trow and the actual
  visible top menu option, will be one. Each subsequent time the menu 
  is opened as a List menu, .trow and the actual visible top menu 
  option, will be the same as when the menu was closed the last time.
  (See also .row above).
  
  >>.rows_vis : B1_23;
  This gives the actual number of options, that are visible on the 
  screen for a particular menu. For a menu used as a Pop menu this 
  will always be equal to the total number of options that were 
  specified, in the call to NEW_MENU(). For a menu used as a List menu 
  this will always be equal to the number of visible options parameter
  specified in the call to LIST_MENU(). The .rows_vis field becomes
  available after the first call to either POP_MENU() or LIST_MENU() 
  fotr that particular menu and once again is used by many of the 
  functions and procedures that support POP/LIST_MENU().

      Fields Used By Bar Menus Only:-
  >>.ncolc : byte;
  This field becomes available after the call to MBAR_COMMENT(), it
  contains the length of the longest string of comment text that was
  sent to MBAR_COMMENT(). All the comment strings are padded out to
  make them the same length as the longest one, in the call to
  MBAR_COMMENT(). The comment text is displayed, and the remainder up
  to the end of the bar menu is again padded, unless of course the
  comment text strings are longer than the bar menu, in which case
  the comment text will be truncated at the end of the menu. The 
  comment text has to be padded, to the end of the bar menu, to enable
  the menu to slide properly, during a call to WMSLIDE(). You may also
  find it usefull to read the section on MBAR_COMMENT() in the 
  'Complete Reference Lookup' and the ID_MC[] description in the
  section 'Winmen Declared Variables'.
  
  >>.gap : B1_80;
  This field contains the minimum gap, given in screen characters, 
  between each, before the first and after the last option on a bar 
  menu. If the remainder of (blen-.sumopt) is not equaly divisable 
  (in integer terms) by the (number of options+1) then the quotiant is
  used for the .gap field and the integer remainder is added to the
  gap after the last option, when the bar menu is displayed. The blen           
  variable mentioned above is not actualy a record field, it is just
  there to represent the length of a particular bar menu, it is also
  the bar menu length parameter as passed to the BAR_MENU() function 
  by the user, the .sumopt field is given above. This field becomes 
  available for a particular bar menu after the call to BAR_MENU().
.PA 23
  >>.cols_vis : word;
  The .cols_vis field will always be equal to the bar menu length, 
  which was passed to the BAR_MENU() function as one of its parameters.
  
      Fields Used By Windows Only:-
  >>.x, .y : byte;
  These two fields give the column and row position of the text cursor
  that is used with the WWRITE(); WWRITELN(); and WGOTOXY() procedures
  and is relative to the top left corner of the window or user screen.
  Do'nt forget that position 1,1 for a user screen is the absolute top
  left corner, whereas for a window it's the top left character inside
  the border. These two fields have no effect or relationship with the
  Turbo Pascal text window cursor, which is positioned using the
  gotoxy() procedure. Both fields are set to 1,1 as soon as that
  window or user screen is defined in the call to either NEW_WIN(); or
  WMSAVE_SCR(); and are available thereafter, untill that object is
  destroyed with WMDELETE();

  >>.lt, .rt : B1_80; .tt, .bt : B1_25;
  These four fields are temporary values used by the WOPEN() procedure
  and are the starting positions for when the window is exploded onto
  the screen. The four values will always be as follows:-
     The .lt field will always be equal to the horizontal centre point 
     of the window - 1.
     The .rt field will always be equal to the horizontal centre point 
     of the window + 1.                                                
     The .tt field will always be equal to the vertical centre point 
     of the window - 1. 
     The .bt field will always be equal to the vertical centre point 
     of the window + 1. 
  These four basic values are passed on to the _WBOX_ON() procedure
  but are only altered when a window is moved or slid using WMMOVE()
  or WMSLIDE() respectively.

  >>.max : B1_80;
  This field contains either the total length or height of a window
  in screen characters, depending upon which is the larger, hense max. 
  It is used in the WOPEN() procedure as a loop count, while calls to 
  the _WBOX_ON() procedure are made, so that the window can be exploded 
  to the correct size.
  
  >>.cp : word;
  This field holds the offset part, of the segment:offset address, 
  which is the exact centre of the window in screen ram. The segment
  part of the address is held in the Winmen system variable 'VIDEO'
  and is dependant upon the graphics card being used in the PC. It is
  used again in the WOPEN() procedure, to enable the centre character
  of the window to be filled with the windows textbackground colour,
  prior to the window being exploded onto the screen, this is because
  the smallest rectangle/box that we can put on the screen using the
  _WBOX_ON() procedure, and wipe off with the windows background colour
  again using the _WBOX_ON() procedure, is 3x3 characters, this leaves 
  the centre character the wrong colour, thus the need for .cp.
.PA 24
      This ends the discription of the wmrec record fields.

      >>maprec = record;
      Firstly, before we start to go into the maprec type, I think we need to
      explain the basic principles and ideas behind the whole concept of a 3D
      screen map.
  For the purposes of simplicity a window, user screen or menu will
      all be refered to as objects, from now on, unless otherwise stated.
  To enable the Winmen Unit to manipulate objects on the text screen by
      moving and sliding, is a fairly simple task, even opening and closing,
      overlapping objects, as long as it's in a logical, "last open first
      shut" sequence, is reletively straight forward. When we start floating
      obscured objects to the surface, closing objects that are covered by
      other objects, "first open first shut", or removing partially or fully
      obscured objects from the display, while still leaving the screen
      intact, we need to keep track of exactly where each open object is on
      the screen and what open objects there are, above and below, each open
      object and weather or not they overlap, above and below in this sense is
      meant logically, as the screen has no phisical depth.
  The type of structure needed for this task has to be able to view
      the text screen as a three dimensional system. The basis for this
      structure is the array of pointers:-

  >>pt_maprec = ^maprec;
  >>MAP : array[0..2000] of pt_maprec;
  The 2000 pointers defined in this array are each capeable of pointing
  to a "maprec" type record variable, which is the subject of this
  section.  The 2000 pointers are needed to cover the screen completely
  that is 80 columns x 25 rows = 2000 pointers, this array forms the
  base or platform on which our three dimensional structure is built.
     If no objects are open on the screen, then all the pointers in the
  MAP[] array will be "nil". If an object is opened and say it's the
  only object on the screen, then all of the objects surface will be
  logicaly next to the MAP[] array, this will always be the case as
  long as none of the open objects overlap each other.
     Each time an object is opened onto the screen, in the maner
  described above, a "maprec" will be created for each character cell
  on the objects surface area. Each of these "maprecs" is linked into
  the MAP[] pointer array, by passing the address of the newly created
  "maprec" to the MAP[] pointer variable, whoose index, corresponds to
  the screen position of that particular character cell. For example,
  if an object were positioned on the screen at say x5,y5 the address
  of the "maprec", for the top left most character in the object, would
  be passed to the pointer variable at MAP[((y-1)*80)+x-1] which is,
  MAP[324], we have to take off the 1 at the end as the MAP[] array
  starts at MAP[0] not MAP[1], the 80 represents the columns accross
  the screen.
     After the "maprec" is linked in, as explained above, the three
  fields are then filled in as follows:-
.PA 25
     >>.ID : byte;
     This is the objects ID byte, sent back from the call to NEW_WIN();
     or NEW_MENU(); or sent to WMSAVE_SCR(); if the object is a user
     screen (ID < 20), when the object was defined. This tells us
     which object owns that particular "maprec".

     >>.sc_inx : word;
     This field is an index into the owning objects ID_SC[] background
     screen array and tells the system which screen ram format word
     to display at the 3D screen MAP[] position that this particular
     "maprec" is linked into, either directly, if the object is
     logically touching the MAP[] array (first open), or indirectly,
     if the object is an overlaping one, in which case its "maprec"
     will be further along the linked list of "maprecs", whilst still
     being in the same MAP[map_inx] position, how far along the list a
     "maprec" is will depend on how many objects there are directly
     underneath it, where the character cells actually overlap. The
     screen ram format word at this particular index is not forced to
     be displeyed, it may just be passed to another objects ID_SC[]
     background screen array, depending on the type of operation
     being carried out.
        The ID_SC[] background screen array, for any object, contains a
     copy of the screen image that is directly underneath that
     particular object. For more details on this array and the pointer
     types associated with it refer to the sections on "screen",
     "pt_screen" and "ID_SC[]".

     >>.next : pt_maprec;
     This field is a pointer to the record type "maprec", just as the
     2000 pointers in the MAP[] array.
        As discused above, each time an object is opened onto the
     screen, a "maprec" will be created for each character cell on the
     objects surface area, initialy this ".next" pointer field is set
     to nil, this indicates that this "maprec" is the last one in the
     linked list, at that particular 3D screen MAP[] position. This
     also tells us that the object that owns this particular "maprec"
     must be the topmost one at that particular 3D screen MAP[]
     position, i.e. the character cell that corresponds to that 3D
     screen MAP[] position is visible on the screen, as opposed to
     being hidden by an overlaping object.
        The ".next" field will only be passed the address of another
     "maprec" under the following circumstances:-

        1) If an object is opened onto the screen and it overlaps any
    other objects, a new "maprec" will, as before, be created
    for for each character cell location on the objects surface
    area, having done this, the linked list at each appropriate
    3D screen MAP[] position, corresponding to each character
    cell location on the objects surface area, will in turn, be
    searched to find the last "maprec" in that particular linked
    list, once this has been done, the ".next" field of this
    last "maprec" is passed the address of the new "maprec" that
.PA 26
    corresponds to that same 3D screen MAP[] position. This
    makes the linked lists grow outwards from the 3D screen
    MAP[], thus giving us our three dimensional system, which
    allows us to see what is above or below an object at any
    particular character cell or 3D screen MAP[] position.

        2) If an object is closed or removed from the screen, then
    every "maprec" owned by that object will be deleted. this
    will of course leave gaps in the affected linked lists,
    unless that object was logicaly next to the MAP[] array
    and with no other objects overlaping it (first open), in
    which case the linked list at that particular 3D screen
    MAP[] position will no longer exist and the appropriate
    MAP[] array pointer at that position will be given a nil
    value. If the "maprec" deleted was in the middle of a
    linked list then the address contained in its ".next"
    field (address of the next "maprec" in that list) will be
    passed to the ".next" field of the "maprec" that was
    immediatly before it in that linked list, thus rejoining the
    list. If on the other hand the "maprec" deleted was on the
    end of a list then all we need to do is give the ".next"
    field of the new last "maprec" in the list a nil value, to
    indicate that it is now the end of the list.

        3) The final case that involves the manipulation of the ".next"
    pointer field is when objects are floated to the surface or
    moved or slid around the screen. This time we wont actualy
    be creating or destroying any "maprecs", just moving them
    around to new positions on the END of existing linked lists
    on the 3D screen MAP[] array.
       All of these object manipulation operations demand that
    the object is on the top of the pile i.e. completely
    visible on the screen and not obscured at any character cell
    location by any other object, the exceptions to this rule
    are the WMMOVE(); procedure and of course the WMFLOAT();
    procedure, which both bring the object to the surface
    themselves, the WMSLIDE(); procedure does not float the
    object to the surface or carry out any operations on the
    objects "maprecs" at all, the user has to do this himself,
    this is for speed considerations in certain circumstances,
    unlike the WMFLOAT() and WMMOVE(); procedures, which do
    carry out all the required "maprec" manipulation operations
    for you, the WMMOVE(); procedure can also be used when
    objects are closed, this will not affect the 3D screen
    MAP[], or any "maprecs", as none exist while the object is
    closed. For more detailed information on the WMSLIDE();
    WMFLOAT(); and WMMOVE(); procedures, see the relevant
    sections in the "Complete Reference Lookup".
       Briefly, what actualy happens when you float an object to
    the surface is, the "maprecs", that are owened by that
    particular object, in the linked lists, at the affected 3D
.PA 27
    screen MAP[] positions, are also floated to the surface,
    or rather to the END of each affected list, this is done
    by transfering the address contained in the ".next" field of
    the "maprec" below the one being floated, to the ".next"
    field of the "maprec" that is currently at the end of the
    list, replacing the nil value, we then take the address
    contained in the ".next" field of the "maprec" being
    floated, which is now on the end of the list, and put it
    in the ".next" field of the "maprec" that was below it in
    the list, lastly the ".next" field of the "maprec" that
    was floated, which is now at the end of the list, is given a
    nil value, to end the list, this is done for each affected
    3D screen MAP[] position.  The "maprecs" are not the only
    things that have to be manipulated, the ID_SC[] background
    screen array that is associated with each affected object,
    also has to be updated, with new information, at each
    character cell location affected by the float, this is
    done at the same time, in a similar maner.
       Moving an object does not cause as many problems as you
    might imagine, firstly we just float the object to the
    surface, as above, then instead of manipulating the objects
    "maprecs" further, we delete them, move the object, and
    create a new "maprec", on the end of each appropriate linked
    list, at the relevant 3D screen MAP[] position, one for each
    character cell location on the objects surface, doing it
    this way means we can use existing routines within the
    Winmen Unit and seems logical, as the object starts off on
    the top layer and finishes on the top layer, if we had
    chosen to move all the objects "maprecs" to the new
    positions directly and done away with the float, delete and
    create, we would have had to look at transfering the object
    along with its "maprecs" and ID_SC[] background screen array
    words, from the level it was on, to the same relative level
    in its new position, a much more complex and time consuming
    task. The WMSLIDE(); procedure also demands the same sort
    of considerations as the WMMOVE(); procedure i.e. the
    object will need to be brought to the surface, before it
    is slid.

     This ends the description of the "maprec" fields.

      Hopefully the above text will give you a fairly clear idea of how the
      Winmen Unit sees the text screen as a three dimensional system, with
      the aid of the 3D screen MAP[] pointer array as its base and linked
      lists of "maprecs" growing outwards from the MAP[] array, linked using
      the "pt_maprec" pointers, to form its logical depth.
  The diagram below may help to clear up any doubts, following that
      are examples on how to access the 3D screen MAP[] pointer array and the
      linked lists of "maprecs" adjoining it.
.PA 28
      ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
      ³                                                                      ³
      ³              THIS REPRESENTS THE TEXT SCREEN                         ³
      ³              ===============================                         ³
      ³                                                                      ³
      ³                                                                      ³
      ³          ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»                     ³
      ³          º                                     º                     ³
      ³          º  THIS IS A TEXT WINDOW, WITH TEXT   º                     ³
      ³          º  IN IT.                             º                     ³
      ³          º                                     º                     ³
      ³          º          ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸          ³
      ³          º          ³                                     ³          ³
      ³          º          ³                                     ³          ³
      ³          º     ÚÄÄÄÄÁÄÄÄÄÄÄÄÄÄ¿ ANOTHER TEXT WINDOW WITH  ³          ³
      ³          º     ³MENU OPTION 01³ TEXT IN IT.               ³          ³
      ³          º     ³MENU OPTION 02³                           ³          ³
      ³          º     ³MENU OPTION 03³                           ³          ³
      ³          ÈÍÍÍÍͳMENU OPTION 04³                           ³          ³
      ³                ³MENU OPTION 05³                           ³          ³
      ³                ³MENU OPTION 06³                           ³          ³
      ³                ³MENU OPTION 07³                           ³          ³
      ³                ³MENU OPTION 08³                           ³          ³
      ³                ³MENU OPTION 09³ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ;          ³
      ³                ³MENU OPTION 10³                                      ³
      ³                ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ                                      ³
      ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
      /\
     /ºº\
      ºº
      ºº A

      VIEW ON ARROW 'A'
      =================
       (Three Level)
      MENU             ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ
         ################
         ################
         êêêêêêêêêêêêêêêê (two Level)     (Single Level)
      TEXT WINDOW      êêêêêÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
         êêêêê²²²²²²²²²²²ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ
         êêêêê#######################################
         êêêêê#######################################
         êêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêê
      TEXT WINDOWÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄêêêêêêêêêêê
   ÝÝÝÝÝݲ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²êêêêêêêêêêê
   #######################################êêêêêêêêêêê
   #######################################êêêêêêêêêêê
   êêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêêê
      ÝÝÝÝÝÝÝÝÝÝݲ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÝÝÝÝÝÝÝÝÝÝÝ
      ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
      TEXT SCREEN
.PA 29
      LEGEND FOR VIEW ON ARROW 'A':-

  ##Ý = A "maprec" with the ".next" field = nil;

  ##² = A "maprec" with the ".next" field = address of next "maprec"
        in the linked list;

  #   = The "maprec" ".ID" field;

  #   = The "maprec" ".sc_inx" field;

  Ý   = The "maprec" ".next" field set to nil;

  ²   = The "maprec" ".next" field set to address of next "maprec"
        in the linked list;

      Using the 3D screen MAP[] array and the associated "maprecs" and
      "pt_maprecs".

  MAP[] array:-
  The 3D screen MAP[] array pointers can be accessed in the following
  maner:-
     var
        map_inx : word;
        ptr_to_maprec : pt_maprec;

     begin
        ptr_to_maprec := MAP[map_inx];
        MAP[map_inx] := ptr_to_maprec;
     end.
  where map_inx is a value in the range 0..2000.

  maprecs:-
  Any maprecs that have already been created for open objects may be
  accessed in the following maner:-
     var
        pt_rec : pt_maprec;
        map_inx,scr_inx : word;
        IDB : byte;

     begin
        IDB := MAP[map_inx]^.ID;         \
        MAP[map_inx]^.ID := IDB;          \
   OR                        \
        pt_rec := MAP[map_inx];             \ FOR A SINGLE MAPREC LEVEL
        IDB := pt_rec^.ID;                  / (SEE DIAGRAM ABOVE)
        pt_rec^.ID := IDB;                 /
       /
        scr_inx := MAP[map_inx]^.sc_inx; /
        etc....                         /
     end;                              /
.PA 30
     begin
        IDB := MAP[map_inx]^.next^.ID;    \
   OR                        \
        pt_rec := MAP[map_inx];             \ FOR A DOUBLE MAPREC LEVEL
        IDB := pt_rec^.next^.ID;             \(SEE DIAGRAM ABOVE)
   OR                           \
        pt_rec := MAP[map_inx]^.next;          \
        IDB := pt_rec^.ID;                      \
             /
        scr_inx := MAP[map_inx]^.next^.sc_inx; /
        etc....                               /
     end;                                    /

     begin
        IDB := MAP[map_inx]^.next^.next^.ID;\
   OR                          \
        pt_rec := MAP[map_inx];               \ FOR A TRIPLE MAPREC LEV
        IDB := pt_rec^.next^.next^.ID;         \(SEE DIAGRAM ABOVE)
   OR                             \
        pt_rec := MAP[map_inx]^.next^.next;      \
        IDB := pt_rec^.ID;                        \
         \
        scr_inx := MAP[map_inx]^.next^.next^.sc_inx;\
        etc....                                     /
     end;                                          /

      This should give you a good working knowlege of how to access the
      three dimensional text screen system, its arrays, maprecs and pointers.
      It would pay dividends to study the above sections throughly, so that
      you can get the maximum from this very powerful set of Winmen types
      and variables.

      >>pt_mtext = ^mtext;
      This is a pointer type, when a variable of this type is defined it will
      point to a variable of type mtext. The mtext type along with a full
      discription of how to use a pointer of type pt_mtext, is given in the
      description of 'mtext' above.  More detail can be obtained by reading
      the descriptions of NEW_MENU() and MBAR_COMMENT() in the section 'The
      Complete Reference Lookup'.

      >>pt_mmenu = ^mmenu;
      This is a pointer type, when a variable of this type is defined it will
      point to a variable of type mmenu. The mmenu type along with a full
      discription of how to use a pointer of type pt_mmenu, is given in the
      description of 'mmenu' above.

      >>pt_wmrec = ^wmrec;
      This is a pointer type, when a variable of this type is defined it will
      point to a variable of type wmrec. The wmrec type along with a full
      discription of how to use a pointer of type pt_wmrec, is given in the
      description of 'wmrec' above.
.PA 31
      >>pt_scr = ^screen;
      This is a pointer type, when a variable of this type is defined it will
      point to a variable of type screen. The screen type along with a full
      discription of how to use a pointer of type pt_scr, is given in the
      description of 'screen' above.

      >>pt_maprec = ^maprec;
      This is a pointer type, when a variable of this type is defined it will
      point to a variable of type maprec. The maprec type along with a full
      discription of how to use a pointer of type pt_maprec, is given in the
      description of 'maprec' above.

      Winmen Declared Global Variables (Additional to Turbo Pascal Variables)
      ------------------------------------------------------------------------

      Each of the variables defined by the Winmen unit are briefly discussed
      in this section. For more detailed information, see the descriptions of
      the functions and procedures that depend on these objects in Chapter 2
      "The Windows And Menus Complete Reference Lookup". Where this is not
      applicable, a full description is given in this section.

      var
  wt      : array[0..100] of word;  {*TEMP ARRAY LEAD/TRL EDGE DATA*}
  VIDEO   : word;                   {*SEG BASE ADDRESS OF VIDEO RAM*}
  scr     : text;                   {*FHANDLE FOR SCREEN*}
  log     : text;                   {*FHANDLE MEM LOG FILE (DEBUG)*}
  comment : string;                 {*STRING FOR WMMEM_STAT() COMNTS*}
  IDG     : byte;                   {*PTR ARRAY INDEX USED IN INIT*}
  MENU_EXIT_BY_CURSOR : boolean;    {*FALSE=NO TRUE=YES DEFAULT=TRUE*}
  MENU_ACT_ON_PC : boolean;         {*FALSE=NO TRUE=YES DEFAULT=TRUE*}
  CLOSE_WITH_REMOVE : boolean;      {*FALSE=NO TRUE=YES DEFAULT=FALSE*}
  IDW_active,IDM_active,            {*HOLDS ACTIVE WINDOW/MENU IDB*}
  gdriver,gmode : integer;          {*GRAPH DRIVER & MODE DETECTED*}
  ID_WM : array[0..255] of pt_wmrec;{*ARRAY OF RECORD PTRS*}
  ID_MM : array[0..255] of pt_mmenu;{*ARRAY OF PTRS TO MENU OPT TEXT*}
  ID_SC : array[0..255] of pt_scr;  {*ARRAY OF PTRS TO SAVED SCR DATA*}
  ID_MC : array[0..255] of pt_mmenu;{*ARRAY PTRS TO BAR MENU COMNTS*}
  MAP : aray[0..2000] of pt_maprec; {*3D SCREEN MAP ARRAY OF POINTERS*}

      >>wt : array[0..100] of word; 
      This array is used as temporary storage for the screen ram format data
      that is just about to dissapear underneath the leading edge of a window
      menu or user saved screen area, during a WMSLIDE() operation, after
      which, this data is merged with that objects proper ID_SC[] 'screen'
      array. This is explained in more detail in the description of the
      WMSLIDE() and associated procedures, in 'The Complete Reference Lookup'. 

      >>VIDEO : word;
      This word variable contains the segment base address of video ram and 
      is dependant upon what type of graphics addapter you have in your PC.
      It will be one of two values:-
  $B000 : for a mono type addapter.
  $B800 : for a colour or any other type addapter.
.PA 32
      This variable is set during the initialisation of the Winmen unit, so
      dont tamper with it during a programme, unless you are shure that what
      you are doing is correct.

      A word of warning, the Amstrad PPC portable machines come under the 
      mono type addapter, and for the most part, programmes that use the 
      Winmen unit to display windows and menus, function as they should, 
      on these machines, until one uses the Turbo Pascal readln procedure
      in a Winmen window, this for some reason, unknown to the world outside
      of Amstrad, completely scrambles the video ram, all suggestions will
      be gratefully received. Every other aspect works 100%
 
      >>scr : text;
      This text type file variable is assigned to crt, again in the initiali-
      sation section of the Winmen unit. By using this file variable in all
      your write procedures, thus:- writeln(scr,'quick text'); you will
      greatly increase the speed of writing text to the screen.

      >>log : text;
      This text type file variable is assigned to a file called 'Memory.Log'
      during the initialisation section of Winmen. The Winmen unit will then
      automatically write memory and other information to this file if at
      any time a memory allocation or similar error occures during the runtime
      of a programme that uses the Winmen unit. Full details of this file
      variable and how and when to use it are given in the description of the
      WMMEM_STAT() procedure, in the section 'The Complete Reference Lookup'.

      >>comment : string;
      this string variable has been predefined for use with the WMMEM_STAT()
      procedure, and is used by the Winmen unit for this purpose. Refer to
      the WMMEM_STAT() procedure in 'The Complete Reference Lookup' for full
      details.

      >>IDG : byte;
      This variable has been defined purely for use in the Winmen units
      initialisation section, and is used as an index when the pointer arrays
      are initialised to nil, see below.

      >>MENU_EXIT_BY_CURSOR : boolean;
      This switch gives the user the option of being able to exit list and pop
      menus using the cursor left and right keys, as well as the escape key,
      in fact this is the default.  It is sometimes more useful to be able to
      turn this feature off, especialy if you are using a keyboard type mouse,
      like the Amstrad one, as using a mouse of this type makes using the
      menus very dificult with this feature turned on, because each time the
      mouse is moved left or right, the menu will close, as the mouse returns
      the same ascii characters as the keyboard for left and right cursor
      movement.
  MENU_EXIT_BY_CURSOR := true; Menus will exit using cursor or mouse
  left/right keys.
  MENU_EXIT_BY_CURSOR := false; Menus will not exit when cursor/mouse
  left/right keys are used.
  MENU_EXIT_BY_CURSOR := true; default.
.PA 33
      >>MENU_ACT_ON_PC : boolean;
      This switch gives the user control over weather the menus will return
      the option number, when a menu option is chosen using the first
      character method. Normaly a users routine that calls a menu function
      will act on the option returned immediatly, this is quite correct, but
      can sometimes be a bit disconserting, especially if the users next
      operation is to remove the menu in question. By turning this feature
      off the menu functions will not return the option number of an option
      chosen with the first character method, therefore the users routine
      cannot act on it, this effectively gives the user a chance to verify
      that the option the highlighted menu bar has moved to is the one he
      actualy wants. The highlighted menu bar will always move to the option
      that was chosen with the first character method, regardless of how
      this switch is set.
  MENU_ACT_ON_PC := true; Menu functions will return the number of
  the option chosen using the first character method. Don't forget if
  there is more than one option with the same first character, it is
  the number of the first one in the list that will be returned.
  MENU_ACT_ON_PC := false; Menu functions will not return the number
  of the option chosen using the first character method.
  MENU_ACT_ON_PC := true; Default.

      >>CLOSE_WITH_REMOVE : boolean;
      This switch gives the user two diferent methods of closing objects and
      removing them from the screen, they are:-
  CLOSE_WITH_REMOVE := false; All objects will be closed by firstly
  floating them to the surface with WMFLOAT() and then restoring the
  objects ID_SC[] background screen array using WMREST_SCR(); to
  effectively remove them.
  CLOSE_WITH_REMOVE := true; All objects will be closed by using the
  WMREMOVE(); procedure, this makes it appear as if the object is
  being removed from the back of the screen, instead of from the
  front, as the other method does.
  CLOSE_WITH_REMOVE := false; Default.
      Any object, that for some reason, does not have any "maprecs" linking it
      into the Winmen 3D screen map system, can not be WMFLOATed or WMREMOVEd,
      depending on the system switch above, as both these routines will exit,
      with no action, if no "maprecs" exist for the object in question. The
      object will however still be closed, by WMCLOSE(); but using just the
      WMREST_SCR(); procedure as its main effort.
  WARNING... If the WMCLOSE() procedure reveals that no "maprecs"
      exist, for the object, at the appropriate 3D screen map locations, then
      it is forced to make the assumption that the object is not obscured by
      any other object, as no other facts are available to it, so a close by
      using WMREST_SCR(); to restore the objects ID_SC[] background screen
      array should be sufficient. If the object is obscured by others, either
      partially or fully, then you can expect some display corruption at some
      stage, possibly sooner rather than later.
.PA 34
      >>IDW_active, IDM_active : integer;
      These two integer variables will, after initialisation, always contain
      the ID reference byte value of the currently active window and menu
      respectively, if none are active they are set to -1, else they will be
      a value in the range 0-255. They are both initialised to -1 by the
      Winmen unit. The ID reference byte for a window, menu or user saved 
      screen area is the value returned from NEW_WIN() or NEW_MENU() or the  
      value sent to WMSAVE_SCR(), for a user screen (range 0-19). By active
      we mean open and in use.

      >>gdriver, gmode : integer;
      These two variables are used to recieve the graphics driver and its
      current mode values, in the call to detectgraph() in the Winmen units
      initialisation section, and are used to determine a suitable value for
      the 'VIDEO' segment base address variable (see above).

      >>ID_WM : array[0..255] of pt_wmrec;
      This is an array of pointer variables, each one, once dynamically
      allocated, will point to a structure of type wmrec. This is all done
      by the Winmen unit and need not concern the user, although detailed
      information on this array and how to use it, with its pointers is
      given in the description of the 'wmrec' type, above.

      >>ID_MM : array[0..255] of pt_mmenu;
      This is an array of pointer variables, each one, once dynamically
      allocated, will point to a structure of type mmenu. This is all done
      by the Winmen unit and need not concern the user, although detailed
      information on this array and how to use it, with its pointers is
      given in the description of the 'mmenu' type, above.                     

      >>ID_SC : array[0..255] of pt_scr;
      This is an array of pointer variables, each one, once dynamically
      allocated, will point to a structure of type screen. This is all done
      by the Winmen unit and need not concern the user, although detailed
      information on this array and how to use it, with its pointers is
      given in the description of the 'screen' type, above.                     

      >>ID_MC : array[0..255] of pt_mmenu;                                    
      This is an array of pointer variables, each one, once dynamically
      allocated, will point to a structure of type mmenu. This array and the
      pointers associated with it, work in exactly the same way as those for
      the ID_MM[] pointer array. Full information on how to use this array
      and its pointers can be found in the description of the 'mmenu' type
      in the 'Winmen Declared Types' section above. All the examples given
      in the 'memnu' description are equaly valid for this array, just swap
      ID_MM[] for ID_MC[], and IDM for IDMC each time. The only diference
      being, that the pt_mmenu pointer variables will point to menu comment
      lines rather than actual menus. In view of this the structure layout
      and access technique will differ slightly.
.PA 35
 The diagram below will give you a better idea of how the mmenu array
      is used and how to access it correctly for a particular bar menu
      comments structure, assuming that menu has been set up using the
      NEW_MENU() function and that bar menu comments have also been defined
      using the MBAR_COMMENT() procedure:-

    OPTION, STRING
      IDMC^[0..402][0..80]             WORDS
          [0..80]
  0123456789............................................80
        0menu comment line = IDMC^[0][0..16]
        1another comment
       ARRAYS  2and another line  --------------------------->
      [0..402] 3penultimate line
      (MENUS   4last comment line = IDMC^[4][0..16]
       OPTION  5       |
       NUM-1)  6       |
        7       |
        8       |
        9       V

      The above diagram assumes that you have defined a pointer somewhere in
      your programme, like this:-
      var
  IDMC : pt_mmenu; (see pt_mmenu above)
      This then gives us a nil pointer variable to a structure of type mmenu
      which is array[0..402][0..80] of word.
      To enable us access the actual data for the above bar menu comments,
      assuming the menu has been set up using NEW_MENU(), and the comments
      with MBAR_COMMENT(), you would then do the following:-
      IDMC := ID_MC[ID];

      Where ID is the reference byte returned from NEW_MENU() when the menu
      in question was set up, and ID_MC[] is this array of pointers, each one
      of which could point to a separarte bar menu comments structure.
      This assignment gives the IDMC pointer the start address for the above
      bar menu comments structure.
 You could of course not bother with defining the IDMC pointer and just
      use the array of pointers to reference the above bar menu comments
      structure, like this:-
      ID_MC[ID]^[0][0..16], this is equivalent to IDM^[0][0..16], but I think
      it's neater to define the pointer.
 The [0..16] range is only there to indicate the width of the above
      bar menu comments structure, all comment lines are padded to the same
      length as the longest one (see the .ncolc wmrec field in the 'Winmen
      Defined Types' section, for more info), you would only normaly use one
      number for the index thus:-
      IDM^[0][0], this would access word 0 in line 0 of the above menu, which
      is the word describing the character 'm' in 'menu comment line'. As can
      be seen the majour difference is that the bar menu comment words begin
      at IDMC^[0][0] or array [0] word [0], whereas the menu options, which
      use the ID_MM[] pointer array begin at IDM^[1][1] or array [1] word [1],
.PA 36
      see mmenu above, so you will have to remember that the index for
      accessing bar menu comment strings in the IDMC^[index1][index2]
      structure will always be one less (-1) than the actual menu option that
      it pertains to, ie. IDMC^[option-1][character-1], if you remember this
      idea you will not go far wrong.
 You will see all the above techniques used from time to time in the
      example programmes supplied with the Windows and Menus unit, it is a
      very important aspect of the unit and the descriptions here, of all the
      new types and variables, will enable you to extract the most benefit
      from it, but it will require a little studying.

      >>MAP : array[0..2000] of pt_maprec;
      This array of pointers is the base for the complete three dimensional
      screen maping system operated by the Winmen Unit, for full details on
      this array and the associated "maprec" record structure and "pt_maprec"
      pointers, see the sub-section on the >>MAP[] array under the main
      section on the >>maprec type, all these are in the "Winmen Declared
      Types" section.
.PA 37
      Winmen Initialisation
      ------------------------------------------------------------------------

      Below is an exact copy of the Winmen Unit Initialisation section, by
      studying it you can see exactly how and which of the Winmen global type
      variables are initialised, and what they are initialised to:-

      var
  rec_pt : word;

      begin
  assign(log,'MEMORY.LOG');      {*OPEN A MEMORY LOG FILE FOR DEBUGING ONLY*}
  comment := 'TURBO PASCAL WINDOWS & MENUS MEMORY LOG';
  comment := comment + chr(13) + chr(10);
  comment := comment + 'Memory Condition At Initialization:-';
  WMMEM_STAT(comment,log,'R');

  assigncrt(scr);                {*OPEN A FAST TEXT FILE FOR THE SCREEN*}
  rewrite(scr);
  IDW_active := -1;              {*SET NO WINDOWS ACTIVE*}
  IDM_active := -1;              {*SET NO MENUS ACTIVE*}
  for IDG := 1 to 255 do
  begin
     ID_WM[IDG-1] := nil;        {*INITIALISE ALL POINTER ARRAY ELEMENTS*}
     ID_MM[IDG-1] := nil;        {*TO NIL                               *}
     ID_SC[IDG-1] := nil;
     ID_MC[IDG-1] := nil;
  end;
  ID_WM[255] := nil;
  ID_MM[255] := nil;
  ID_SC[255] := nil;
  ID_MC[255] := nil;

  rec_pt := 0;
  while(rec_pt < 2000) do        {*INITIALISE 3D SCREEN MAP BASE POINTERS*}
  begin                          {*TO NIL                                *}
     MAP[rec_pt] := nil;
     inc(rec_pt);
  end;

  detectgraph(gdriver,gmode);    {*GET INSTALLED GRAPHICS CARD DRIVER & MODE*}
  case gdriver of                {*CHECK DRIVER AGAINST MONO LIST*}
     MCGA,HercMono,
     EGAMono,-2 : VIDEO := $B000;{*SET VIDEO ADDRESS FOR A MONO TYPE ADAPTER*}
     else
        VIDEO := $B800;          {*SET VIDEO ADDRESS FOR COLOUR TYPE ADAPTER*}
  end;

  MENU_EXIT_BY_CURSOR := true;   {*SET SO POP/LIST MENUS EXITED WITH L/RCURS*}
  MENU_ACT_ON_PC := true;        {*SET SO IF PIC CHAR SELECT, TAKE ACTION*}
  CLOSE_WITH_REMOVE := false;    {*SET SO WMCLOSE USES WMFLOAT & REST_SCR*}
      end {end unit}.
.PA 38
      Winmen Quick Reference Lookup
      ------------------------------------------------------------------------

      Each of the subsections below are in alphabetical order, the function or
      procedure name is given along with its parameters and syntax.


      Windows Functions

  NEW_WIN(l : B1_78; t : B1_23; r : B3_80; b : B3_25;
   btype : B1_4;
   bb,bf,tb,tf : B0_15) : byte;


      Windows Procedures High Level

  WCLRSCR(IDB : byte);

  WCOLOUR(IDB : byte;tb,tf : B0_15);

  WEXPLODE(l : B1_78; t : B1_23; r : B3_80; b : B3_25;
    btype : B1_4;
    bb,bf,tb : B0_15);

  WGOTOXY(IDB,x,y : byte);

  WOPEN(IDB : byte);

  WSELECT(IDB : byte);

  WWRITE(IDB : byte; stg : string);

  WWRITELN(IDB : byte; stg : string);


      Windows Procedures Low Level

  _WBOX_ON(lt : B1_80; tt : B1_25; rt : B1_80; bt : B1_25;
    tlc,trc,blc,brc,horc,verc : word);

  _WCHK_MIN_SIZE(proc : string;
   l : B1_78; t : B1_23; r : B3_80; b : B3_25);

  _WSCROLL_UP(IDB : byte);


      Menus Functions

  BAR_MENU(IDB : byte;l : B1_78;t : B1_23;blen : B1_80) : W0_400;

  LIST_MENU(IDB : byte;l : B1_78;t,rows_vis : B1_23) : W0_400;
.PA 39
  NEW_MENU(btype : B1_4;
    bb,bf,tb,tf,pc : B0_15;
    mt : pt_mtext;
    rowt : W0_400) : byte;

  POP_MENU(IDB : byte;l : B1_78;t : B1_23) : W0_400;


      Menus Procedures High Level

  MBAR_COMMENT(IDB : byte;mtc : pt_mtext);

  MCHANGE_CHAR(IDB : byte;
        row : word; col : byte;
        new_char : char;
        tb,tf : B0_15);

  MPIC_COL(IDB : byte;pc : B0_15);


      Menus Procedures Low Level

  _MBBAR_END(IDB : byte);

  _MBBAR_HOME(IDB : byte);

  _MBBAR_LEFT(IDB : byte);

  _MBBAR_OFF(IDB : byte);

  _MBBAR_ON(IDB : byte);

  _MBBAR_RIGHT(IDB : byte);

  _MBDISPLAY(IDB : byte);

  _MCHK_ON_SCREEN(IDB : byte;proc : string;
    l : B1_78;t : B1_23;nrow : B1_25);

  _MDISPLAY(IDB : byte);

  _MLBAR_BLST(IDB : byte);

  _MLBAR_END(IDB : byte);

  _MLBAR_HOME(IDB : byte);

  _MLBAR_TLST(IDB : byte);

  _MLMOV_DO(IDB : byte;move : char);

  _MLMOV_UP(IDB : byte;move : char);
.PA 40
  _MPBAR_END(IDB : byte);

  _MPBAR_HOME(IDB : byte);

  _MPLBAR_DO(IDB : byte);

  _MPLBAR_OFF(IDB : byte);

  _MPLBAR_ON(IDB : byte);

  _MPLBAR_UP(IDB : byte);


      Common Functions Low Level

  _WMCHK_FOR_MREC(IDB : byte) : boolean;

  _WMSAVE_SCR(l : B1_80; t : B1_25 r : B1_80; b : B1_25) : pt_scr;


      Common Procedures High Level

  WMADD_MREC_ALL(IDB : byte);

  WMATTRIBS(IDB : byte;
     btype : B1_4;
     bb,bf,tb,tf : B0_15);

  WMCLOSE(IDB : byte);

  WMDELETE(IDB : byte);

  WMDEL_MREC_ALL(IDB :byte);

  WMFLOAT(IDB : byte);

  WMMEM_STAT(comment : string; var handle : text; r_a : char);

  WMMOVE(IDB : byte; l : B1_78; t : B1_23);

  WMREMOVE(IDB : byte);

  WMREST_SCR(IDB : byte;l : B1_80; t : B1_25; r : B1_80; b : B1_25);

  WMSAVE_SCR(IDB : byte; l : B1_80; t : B1_25; r : B1_80; b : B1_25);

  WMSLIDE(IDB : byte; dir : char; nchar : byte);

  WMTITLE(IDB : byte; bb,bf : B0_15;title : string);
.PA 41
      Common Proocedures Low Level

  _WMADD_MREC(IDB : byte; sc_inx,map_inx : word);

  _WMBOX_ATTRIBS(btype : B1_4;
   bb,bf : B0_15;
   var tlc,trc,blc,brc,horc,verc : word);

  _WMCHK_EXIST(IDB : byte;proc : string);

  _WMCHK_MEM(proc : string;blk_reqd : word);

  _WMCHK_TYPE(IDB : byte;proc : string;typ : char);

  _WMCOPY_SCR(l1 : B1_80; t1 : B1_25; r : B1_80; b : B1_25;
       l2 : B1_80; t2 : B1_25);

  _WMDEL_MREC(IDB : byte; map_inx : word);

  _WMREST_SCR(pt_sc : pt_scr; 
       l : B1_80; t : B1_25; r : B1_80; b : B1_25);

  _WMSLIDE_D(IDB,nchar,l,t,r,b,ncol,nrow : byte);

  _WMSLIDE_L(IDB,nchar,l,t,r,b,ncol,nrow : byte);

  _WMSLIDE_R(IDB,nchar,l,t,r,b,ncol,nrow : byte);

  _WMSLIDE_U(IDB,nchar,l,t,r,b,ncol,nrow : byte);
.PA 42
      C           H           A           P           T           E          R
      ------------------------------------------------------------------------
              2





      THE WINDOWS AND MENUS, COMPLETE REFERENCE LOOKUP



      This chapter describes all the procedures and functions of Windows and
      Menus V1.0. For your convenience they are arranged alphabetically. Here
      is a sample layout so you can easily understand the format of the lookup
      Note that only the relevant sections are listed in each entry.


      ------------------------------------------------------------------------
      SAMPLE PROCEDURE()                                    SAMPLE PROCEDURE()
      ------------------------------------------------------------------------

      type and    : Sayes weather it is a Function or Procedure, and if it is
      level         high or low level; 
      High Level : All the high level functions and procedures
     carry out full type and range checking on 
     all parameters passed to them, they also
     carry out other error checking and display
     messages where required (see appendix A).
     The user can use these without fear or favor.

      Low Level  : In general the low level functions and procs
     do not carry out any type or range checking
     on any parameters passed to them, nor do they 
     initiate any other error checking or display
     any messages to the user, this is because all
     these low level routines are used within the 
     Windows and Menus unit, by the high level 
     routines, so there is no need, so if you do
     want to dabble with a little more power and
     get your hands dirty, you'll need to take a
     little more care,or you may burn your fingers

      This section also gives a brief description of the funct-
      ion or procedure.

      Syntax      : This is how it should be used.

      Variables   : This tells you about each parameter in the function or
      procedure call in detail, and how it should be declared
      along with its type. It also tells you what that parameter 
      does, what it cannot do and any checking that is carried
      out on it. A functions return value is also described. 
.PA 43
      Tables      : This section, if present, will give some form of tabulated
      information, such as a colour table.

      Example     : This section provides the user with a full working example
      using the function or procedure in question. Alternatively
      it will, where appropriate, just give a code segment.

      Rules Notes
      Tips & Traps: As the section title suggests, this is where you will find
      any rules to be observed and also offers additional info
      on the function or procedure in the form of tips and traps
      This section is most important and should always be read 
      in detail. The example is also explained in this section.
.PA 44












       WINDOWS AND MENUS FOR TURBO PASCAL
       ----------------||----------------
       WINDOWS FUNCTIONS REFERENCE SECTION
.PA 45
      ------------------------------------------------------------------------
      NEW_WIN()                                                      NEW_WIN()
      ------------------------------------------------------------------------

      Function    : Set up a new window structure and return the user a unique
      High Level    ID number for future reference to that particular window.
         The windows text cursor, used by the WWRITE(); and
      WWRITELN(); procedures only, is positioned at x1,y1 which
      is the top left corner of the window excluding the border
      the Winmen window text cursor can be positioned using the
      WGOTOXY(); procedure, it has no effect at all on the Turbo
      Pascal text cursor, which is used with the write(); and
      read(); procedures and positioned with gotoxy().
         This function is not called by any of the functions or
      procedures in the Winmen Unit.
         The windows record is created and updated to reflect
      any changes.

      Syntax      : ID := NEW_WIN(l,t,r,b,ty,bb,bf,tb,tf);

      Variables   : ID : byte;
      Receives the returned unique window identifier.

      l : byte; (B1_78)
      Leftmost edge of the window including border, must be a
      value between 1-78 or a range check error will occure.

      t : byte; (B1_23)
      Topmost edge of the window including border, must be a
      value between 1-23 or a range check error will occure.

      r : byte; (B3_80)
      Rightmost edge of the window including border, must be a
      value between 3-80 or a range check error will occure.

      b : byte; (B3_25)
      Bottommost edge of the window including border, must be a
      value between 3-25 or a range check error will occure.

      ty : byte; (B1_4)
      Type of border required around the window edge, must be a
      value between 1-4 or a range check error will occure.
      The possible border types are as follows:-
         1 = Double horizontal and double vertical bars.
         2 = Single horizontal and single vertical bars.
         3 = Double horizontal and single vertical bars.
         4 = Single horizontal and double vertical bars.

      bb : byte; (B0_15)
      Border background colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      bf : byte; (B0_15)
      Border foregroung colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      tb : byte; (B0_15)
      Text background colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).
.PA 46
      tf : byte; (B0_15)
      Text foreground colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink

      Example     : program NEW_WIND;
      uses dos,crt,winmen;
      var
         ID : byte;
      begin
         ID := NEW_WIN(5,5,50,20,1,1,14,7,0);
         WOPEN(ID);
         writeln(scr,'A window 46 cols x 16 rows');
         write(scr,'Press Return');
         readln;
         WMDELETE(ID);
         normvideo;
      end.

      Notes Rules
      Tips & Traps: A maximum of 255 unique identifiers can be allocated
      the first 20 (0-19) of these are used only for user
      saved screen areas using WMSAVE_SCR(), the rest 236
      (20-255) of these are to be shared between NEW_WIN()
      and NEW_MENU(), if all possible identifiers have been
      allocated or there is insuficient memory left for the
      window the programme will abort with an error message
      (see apendix for list), although you will most likely
      run out of memory before identifiers, depending upon
      the size of existing windows and menus.
      You can of course free up memory and unique identifiers
      by using WMDELETE() to delete unwanted windows or menus.
.PA 47
      Take care not to overwrite or lose the returned unique
      identifier ID as this is your only link with the
      window record structure and means of using/deleting the
      window.
      The window must be of a minimum size of 3 columns by 3
      rows else there would not be any display area, if it
      is less than this the programme will abort with an error
      message (see apendix for list).
      The function will always allocate a fixed amount of memory
      for the window record, but the amount of memory allocated
      for the screen under the window, of course depends upon
      the size of the window.

.PA 48












       WINDOWS AND MENUS FOR TURBO PASCAL 
       ----------------||----------------
      WINDOWS PROCEDURES REFERENCE SECTION
.PA 49
      ------------------------------------------------------------------------
      WCLRSCR()                                                      WCLRSCR()
      ------------------------------------------------------------------------

      Procedure   : Clears the window or user screen area, (created with
      High Level    NEW_WIN() or WMSAVE_SCR()) associated with the unique
      identifier ID, to the text background colour defined in
      its record.
         This procedure interrogates the Winmen 3D screen MAP[]
      while it is operating, this allows it to clear the screen
      of windows and user screen that are partialy or even fully
      obscured by other objects, while still leaving the
      foreground intact, the ID_SC[] background screen array,
      of any obscuring objects, will be updated automatically.
      Windows and user screens not obscured by other objects
      can, of course, also be cleared.
         It will also clear windows and user screen that are not
      linked into the 3D screen MAP[] system, this could occure
      if an object has just been slid using the WMSLIDE();
      procedure and the user has not relinked the object into
      the 3D screen MAP[] using the WMADD_MREC_ALL(); procedure,
      refer to the WMSLIDE(); procedure for further details. If
      an unlinked object, that is obscured by other objects, is
      cleared, beware! the parts of the objects that are
      covering the window or user screen being cleared will be
      wiped from the screen, not being linked into the 3D screen
      MAP[] means the system does not know what is above or
      below the object in question.
         The window record is not affected by this command.

      Syntax      : WCLRSCR(ID);

      Variables   : ID : byte;
      The unique identifier for the specified window as returned
      from NEW_WIN() or a user screen area ID.
      If the ID does not exist or the ID belongs to a menu the
      programme will abort with an error message. (see apendix
      for list).  

      Example     : program WCLRSCRN;
      uses dos,crt,winmen;
      var
         ID : byte;
      begin
         ID := NEW_WIN(1,1,80,25,1,1,14,7,0);
         WOPEN(ID);
         writeln(scr,'A window 80 cols x 25 rows');
         write(scr,'Press Rerturn To Clear Window');
         readln;
.PA 50
         WCLRSCR(ID);   {*CLEARS TO COLOUR 7-WHITE*}
         write(scr,'Window Cleared, Press Return');
         readln;
         WDELETE(ID);
         normvideo;
      end.

      Rules Notes
      Tips & Traps: The text background colour, for a window, was originaly
      defined in the call to NEW_WIN(), unless it has since
      been changed using WCOLOUR() or WMATTRIBS().
         A user screen area when cleared using WCLRSCR() is
      cleared to the limits of l,t,r,b ie.  it is active right
      to its edges, unlike a window which is cleared up to its
      border ie. its only active to l+1,t+1,r-1,b-1 1 character
      less all round, this is to allow for the window border.
         When the user saves an area with WMSAVE_SCR()
      information put in the screen record apart from its bounds
      are, the text background and foreground colour values,
      which are taken from the TEXTATTR system variable at the
      time of saving, so whatever colours were active when
      WMSAVE_SCR() was called will be used each time the user
      saved screen area is cleared using WCLRSCR() or written to
      after selecting, using WSELECT().
         The colour values held in the window or user screen
      record can be changed using WCOLOUR() or WMATTRIBS(),
      these will update the record.
         WCLRSCR() has no effect on a closed window.
         Any open window or user screen area active or not
      active can be cleared.
         A window or user screen area when cleared will become
      the active one.
.PA 51
      ------------------------------------------------------------------------
      WCOLOUR()                                                      WCOLOUR()
      ------------------------------------------------------------------------

      Procedure   : Changes the text foreground and background colours of
      high Level    the window or user screen area, (created with NEW_WIN()
      or WMSAVE_SCR()) associated with the unique identifier
      ID, by updating its record.
      The window record is updated to reflect the changes.

      Syntax      : WCOLOUR(ID,tb,tf);

      Variables   : ID : byte;
      The unique identifier for the specified window as returned
      from NEW_WIN() or a user screen area ID.
      If the ID does not exist or the ID belongs to a menu the
      programme will abort with an error message. (see apendix
      for list).                                                

      tb : byte; (B0_15)
      Text background colour, must be a value in the range 0-15
      or a range check error will occure. (see colour table
      below).

      tf : byte; (B0_15)
      Text foreground colour, must be a value in the range 0-15
      or a range check error will occure. (see colour table
      below).

       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink

      Example     : program WIN_COLOUR;
      uses dos,crt,winmen;
      var
         ID : byte;
.PA 52
      begin
         ID := NEW_WIN(1,1,80,25,1,1,14,7,0);
         WOPEN(ID);
         writeln(scr,'A window 80 cols x 25 rows');
         write(scr,'Press Return To Change Colours');
         readln;
         WCOLOUR(ID,4,14);
         writeln(scr,'Colours Now Changed From Black on White');
         writeln(scr,'To Yellow on Red');
         write(scr,'Press Return To Clear To New Bkground');
         readln;
         WCLRSCR(ID);   {*CLEARS TO COLOUR 4-RED*}
         write(scr,'Press Return');
         readln;
         WMDELETE(ID);
         normvideo;
      end.

      Rules Notes
      Tips & Traps: The colour changes will take effect the next time the
      window or user saved screen is:-
      A) Written to, if it is already open and is the active
         one.
      B) Selected for use, with WSELECT() and written to
         regardless of weather it is open or closed as
         WSELECT() will open it and make it active.
      C) Opened for use, with WOPEN() and written to, as
         WOPEN() also selects it for use.
      Any window or user screen area, open, closed, active or
      not active can have its colours changed ready for the
      next time it is used, This can be most effective when
      used with WCLRSCR().
      If the window is closed or not active only the window
      record is updated.
      This procedure does not affect window/user screen area
      currency.
.PA 53
      ------------------------------------------------------------------------
      WEXPLODE()                                                    WEXPLODE()
      ------------------------------------------------------------------------

      Procedure   : Explode a box of the specified size, type and colour on to
      High Level    the screen at the same time clearing the background to
      the specified colour.
      No window records are affected by this command.

      Syntax      : WEXPLODE(l,t,r,b,btype,bb,bf,tb);

      Variables   : l : byte; (B1_78)
      Left edge of the box must be a value between 1-78 or a
      range check error will occure.

      t : byte; (B1_23)
      Top edge of the box must be a value between 1-23 or a
      range check error will occure.

      r : byte; (B3_80)
      Right edge of the box must be a value between 3-80 or a
      range check error will occure.

      b : byte; (B3_25)
      Bottom edge of the box must be a value between 3-25 or a
      range check error will occure.

      btype : byte; (B1_4)
      Type of box required must be a value between 1-4 or a
      range check error will occure. The possible border types
      are as follows:-
         1 = Double horizontal and double vertical bars.
         2 = Single horizontal and single vertical bars.
         3 = Double horizontal and single vertical bars.
         4 = Single horizontal and double vertical bars.

      bb : byte; (B0_15)
      Border background colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      bf : byte; (B0_15)
      Border foregroung colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      tb : byte; (B0_15)
      Colour to clear background to (text background colour),
      must be a value in the range 0-15 or a range check error
      will occure. (see colour table below).
.PA 54
       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink

      Example     : program EXPLODE;
      uses dos,crt,winmen;
      begin
         clrscr;
         WEXPLODE(1,1,80,25,3,2,14,0);
         gotoxy(25,10);
         write(scr,'Press Return For Another Explode');
         readln;
         WEXPLODE(5,5,75,20,3,1,2,0);
         gotoxy(25,10);
         write(scr,'Press Return');
         readln;
         normvideo;
         clrscr;
      end.

      Rules Notes
      Tips & Traps: The above example will explode a box to fill the complete
      screen, the box type will be Double horizontal and single
      vertical bars, with a border background colour of green
      and foreground colour of yellow, it will be cleared to
      black.
      The resulting box must be a minimum size of 3x3 chars
      else a box cannot be drawn and the programme will abort
      with an error message (see apendix for list).
      The second example would just clear the screen from the
      centre outwards (explode), as the border background,
      foreground and clearing colour values are all the same.
.PA 55
      ------------------------------------------------------------------------
      WGOTOXY()                                                      WGOTOXY()
      ------------------------------------------------------------------------ 

      Procedure   : This procedure positions the text cursor, for the window 
      High Level    or user screen (ID < 20) specified by ID, at the 
      location given in the x,y parameters.
         Location 1,1 in any window or user screen is the top 
      left corner of the object, excluding the border, if its 
      a window.   
         This procedure only positions the cursor that is used
      when the WWRITELN(); and WWRITE(); procedures are used, it
      does this by setting the wmrec fields ".x" and ".y" to the
      values supplied in the WGOTOXY(); procedure.
         This procedure does not affect the normal text cursor
      position, associated with the writeln(); write();
      readln(); read(); and gotoxy() standard procedures.
         The objects ID must have been previously created using
      NEW_WIN() or NEW_MENU().
         The objects wmrec is updated by this procedure.
         This procedure is not called by any functions or
      procedures in the Winmen Unit.
         The ".x" and ".y" cursor position, for the specified
      window, will be set and updated in wmrec, regardless of
      weather the window, or user screen is active, open or
      closed.
      
      Syntax      : WGOTOXY(ID,x,y);

      Variables   : ID : byte;
      The unique identifier for the specified window or user 
      saved screen area, as returned from NEW_WIN() and as sent 
      to WMSAVE_SCR(0-19), if it is a user saved screen area.
         If the ID does not yet exist the programme will abort
      with an error message (see appendix for list).

      x : byte;
      This is the column at which to position the cursor in
      the window or user screen specified by ID.
         This value must be in the range 1 - (".r"-".l"-1) for a
      window or 1 - (".r"-".l"+1) for a user screen (ID < 20),
      i.e. it must not be greater than the total number of
      columns, less 2 for the boarder, in a window and not
      greater than the total number of columns in a user screen,
      as there is no boarder.
         If the x value supplied is outside the ranges given 
      above, then the cursor position will be left unaltered. 
.PA 56
      y : byte;
      This is the column at which to position the cursor in
      the window or user screen specified by ID.
         This value must be in the range 1 - (".b"-".t"-1) for a
      window or 1 - (".b"-".t"+1) for a user screen (ID < 20),
      i.e. it must not be greater than the total number of
      columns, less 2 for the boarder, in a window and not
      greater than the total number of columns in a user screen,
      as there is no boarder.                                    
         If the y value supplied is outside the ranges given 
      above, then the cursor position will be left unaltered.    

      Example     : program WGOTO_XY;

      uses crt,dos,winmen;

      var
         w1 : byte;

      begin                             {*CREATE WINDOW*}
         clrscr;
         textbackground(0);
         textcolor(7);
         w1 := NEW_WIN(8,8,50,20,1,4,14,1,14);      
         WOPEN(w1);                     {*OPEN IT*}            
         WWRITE(w1,'Text Positioned At 1,1 <RET> To Cont....');
         readln;                        {*WRITE & WAIT RETURN*}  
         WGOTOXY(w1,10,4);              {*REPOSITION CURSOR*}    
         WWRITE(w1,'Text At 10,4 <RET>....');         
         readln;                        {*WRITE & WAIT RETURN*}                 
         WGOTOXY(w1,19,10);             {*REPOSITION CURSOR*}    
         WWRITE(w1,'Text At 19,10 <RET>....');        
         readln;                        {*WRITE & WAIT RETURN*}
      end.

      Rules Notes
      Tips & Traps: There are no special rules to be observed when using 
      this procedure, other than those outlined above.
         The example being demonstrative and straightforward 
      requires no explanation and is left to the user to 
      examine.   
.PA 57
      ------------------------------------------------------------------------
      WOPEN()                                                          WOPEN()
      ------------------------------------------------------------------------

      Procedure   : Opens the window, or user screen (ID < 20), associated
      High Level    with the unique identifier ID, previously created using
      NEW_WIN(); for windows or WMSAVE_SCR(); for user screens,
      user screens are open by default as soon as the
      WMSAVE_SCR(); procedure is called with an ID < 20, but may
      be closed with the WMCLOSE(); procedure.
         Windows are opened by exploding them on to the screen,
      while user screens are opened by placing them on the
      screen, this is because user screens have no border.
         Windows are opened with a completely clear screen area,
      but when user screens are opened, whatever is in there
      ID_SC[] background screen array will be displayed on the
      screen. This old ID_SC[] background image is then
      overwritten with the new area under the user screen, which
      was saved into a temporary area automaticaly, this is
      another reason why user screens cannot be exploded onto
      the screen.
         The area of screen under the window or user screen is
      saved away in the objects ID_SC[] background screen
      array and the object is linked into the 3D screen MAP[]
      system, see the WMSAVE_SCR(); procedure and the "maprec"
      type definition, which is in the section "Winmen
      Declared Types", for full information on this subject.
         This routine is also called by WSELECT(); it may also
      be called by one of the following routines, through
      WSELECT(); depending on certain factors, see individual
      routines for full details:- WWRITE(); WWRITELN();
      WMFLOAT(); WMMOVE(); WMSLIDE(); and WMTITLE();
         The window or user screen (ID < 20) record is updated
      to reflect anye changes.

      Syntax      : WOPEN(ID);

      Variables   : ID : byte;
      The unique identifier for the specified window or user
      screen as returned from NEW_WIN() or sent to WMSAVE_SCR();
      if its a user screen.
         If the ID does not exist or the ID belongs to a menu
      the programme will abort with an error message.  (see
      apendix for list).

      Example     : program WOPEN_WIN;
      uses dos,crt,winmen;
      var
         ID : byte;
.PA 58
      begin
         clrscr;
         ID := NEW_WIN(10,5,55,20,1,1,2,3,4);
         gotoxy(10,15);
         write(scr,'Press Return To Open A Window');
         readln;
         WOPEN(ID);
         write(scr,'Press return To Reveal Previous Message');
         readln;
         WMDELETE(ID);
         gotoxy(39,15);
         readln;
         normvideo;
         clrscr;
      end.

      Rules Notes
      Tips & Traps: The bounds and attributes of the window were set in the
      call to NEW_WIN()
      The call to WOPEN() will be ignored if the window is
      already open or the ID belongs to a user screen area.
      (user screen areas effectively have nothing to open
      as they have no border).
      The screen area under the window is preserved and restored
      when the window is closed.
      The window ID is now the active one and can be written to
      cleared read from etc.
.PA 59
      ------------------------------------------------------------------------
      WSELECT()                                                      WSELECT()
      ------------------------------------------------------------------------

      Procedure   : Select the window or user screen area, created with
      High Level    NEW_WIN() or WMSAVE_SCR() and associated with the unique
      identifier ID for use, if it is closed it will be opened,
      refer to the WOPEN(); procedure for more information.
         The window or user screen (ID<20) record is updated to
      reflect any changes.
         This routine is also called by the following routines,
      depending on certain factors, see individual routines for
      full details:- WWRITE(); WWRITELN(); WMFLOAT(); WMMOVE();
      WMSLIDE(); and WMTITLE().
         The WSELECT(); procedure is one of the only routines
      that does not call WMFLOAT(); out of all the routines that
      may need to do so.
         The reasons for this are as follows:-
         You may want to write to more than one window at once,
      or at least, make it appear as if you are.  This can only
      be done by first selecting the window to write to, using
      WSELECT(); then actualy writing to it.  If the WSELECT();
      procedure had to call WMFLOAT() each time it was called,
      this would slow things down considerably and spoil our
      illusion, besides the windows that you are writing to may
      all be fully visible, in which case there is no need to
      float them to the surface before writing to them.  if they
      are obscured and you dont want to make use of the
      WWRITE(); or WWRITELN(); procedures, to write text to a
      window in the background, then you will have to call
      WMFLOAT(); either before or after calling WSELECT(); and
      prior to writing to each window.  So as you can see the
      choice is best left up to the user, for maximum
      flexibility.

      Syntax      : WSELECT(ID);

      Variables   : ID : byte;
      The unique identifier for the specified window or user
      screen as returned from NEW_WIN() or sent to WMSAVE_SCR();
      if its a user screen.
         If the ID does not exist or the ID belongs to a menu
      the programme will abort with an error message.  (see
      apendix for list).
.PA 60
      Example     : program WSELECT;
      uses dos,crt,winmen;
      var
         ID : byte;
      begin
         clrscr;
         ID := NEW_WIN(5,6,60,10,4,1,2,3,4);
         gotoxy(5,5);
         write(scr,'Press return To Open & Select The Window');
         readln;
         WSELECT(ID);     *{OPEN THE NEWLY CREATED WINDOW}*
     *{AND SELECT IT FOR USE        }*
         writeln(scr,'Theres No Need To Use Wopen & Wselect');
         write(scr,'One Or the Other Will Do. Press Return');
         readln;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: Makes the chosen window or user screen area ID the active
      one. (selects it for use, for writing to etc).
         If the window or user screen is closed it will be
      opened, refer to the WOPEN(); procedure for further info.
         For a user screen area the background and forground
      text colours used for writing/clearing will be the ones
      saved from the TEXTATTR system variable when the user
      screen was created, unless previously changed via
      WCOLOUR() or WMATTRIBS(), as opposed to the preselected
      colours chosen via NEW_WIN() for a window.
         A user screen area will always require selecting if it
      is to be used directly after creating with WMSAVE_SCR(),
      as that procedure does not change the currency of a user
      screen area or window, ie. WMSAVE_SCR() does not make an
      ID the active one.
         The procedure will halt the programme if an attempt is
      made to select a window or user screen area which does not
      yet exist, an error message will be displayed (see apendix
      for list).
.PA 61
      ------------------------------------------------------------------------
      WWRITE()                                                        WWRITE()
      ------------------------------------------------------------------------ 

      Procedure   : This procedure enables the user to write a text string 
      High Level    "stg" to any window, or user screen (ID < 20). 
         The window or user screen can be written to even if it 
      is partialy or even totaly obscured by other objects (in 
      background).
         The background screen arrays of any obscuring objects 
      are automaticaly updated with the new or changed 
      information, so that when any obscuring objects are 
      removed from view the contents of the window or user 
      screen, that was written to, is visualy correct.
         Any text written to a part of a window or user screen 
      that is obscured, by other object(s), will not become 
      visible until the obscuring object(s) are either removed 
      from the display, moved, or slid away, or the window or 
      user screen that was written to is floated to the 
      surface.   
         If the window or user screen, specified by ID, is found 
      to be closed, it is opened for use, and will be the active 
      one after writing is completed. This is most important, 
      as the WOPEN() procedure, which is called if it is closed, 
      will ensure that the window or user screen in question is 
      properly linked into the 3D screen map, on which the 
      WWRITE() procedure relies for correct operation. (see 
      also the _WSCROLL_UP() procedure for more detailed 
      information on this subject).   
         If it is already open for use, its currency is left 
      unaltered, i.e. if it was open but not the active one, it 
      will remain non-active and if it was the active one, it 
      will remain the active one.
         The objects ID must have been previously created using
      NEW_WIN() or NEW_MENU().
         The objects "wmrec" fields ".x" and ".y" are updated by
      this procedure, to reflect the continually changing
      position of the cursor in the window, specified by ID, as
      the text string "stg" is written. This also allows the
      user to determine the position of the Winmen text cursor
      within any window or user screen, prior to writing, for
      more detail on the "wmrec" type and its fields, refer to
      the section "Winmen Declared Types".
         Text output can be directed to any location within the
      window or user screen specified by ID, by repositioning
      the Winmen text cursor with the WGOTOXY() procedure, this
      has no effect at all on the Turbo Pascal text cursor,
      which is used with the standard write(); writeln()
      read() ; readln() and gotoxy() procedures.
         The WWRITE() procedure has no connection with the
      standard Turbo Pascal write() procedure.
         This procedure is called by:- WWRITELN();
.PA 62
      Syntax      : WWRITE(ID,stg);

      Variables   : ID : byte;
      The unique identifier for the specified window or user 
      saved screen area, as returned from NEW_WIN() and as sent 
      to WMSAVE_SCR(0-19), if it is a user saved screen area.
         If the ID does not yet exist the programme will abort
      with an error message (see appendix for list).             

      stg : string;
                    This is the ascii text string that will be printed in the
      window or user screen, specified by ID.
         The string can be up to 255 characters in length, any
      longer and Turbo Pascal will issue a range check error,
      which will termminate the programe.
         The string can contain any valid ascii character in the
      range 0-255, although any control characters present in
      the "stg" string arguement will be printed, unlike the
      standard write() procedure, which acts on them, i.e.
      chr(7) would be printed as '#' by WWRITE(); but write()
      would sound the bell. WWRITE() will print all characters.
                       The WWRITE() procedure only takes a single string as
                    its arguement, rather than a complete arguement list, as
                    in the standard write() procedure. What this means is
                    that the user will have to convert any non-ascii text
                    variables to a text string first and then consatenate
                    all the required arguements in to one string, prior to
                    using the WWRITE() procedure.
                       The WWRITE() procedure will automaticaly wrap the text
      onto the next line when the cursor reaches the righmost
      edge of the current line, for the window or user screen
      specified by ID. 
         If the last character to be written is the last 
      character on a line, then the next time that window or 
      user screen is written to using WWRITE(); or WWRITELN(); 
      writing will automaticaly start at the begining of the 
      next line.
         If the last character to be written is the last 
      character on the last line, then the next time that window 
      or user screen is written to the contents of it will 
      automaticaly be scrolled up one line, leaving the new 
      bottom line blank.  Writting will then commence at the 
      start of this new bottom line.
         The WWRITE() procedure will automaticaly scroll the 
      entire contents, of the window or user screen, up by one 
      line, when the text exceeds the rightmost character on the 
      bottommost line.
         The WWRITE() procedure always leaves the Winmen text
      cursor at the next available blank character within the
      current window or user screen, ready for the next write.
.PA 63
      Example     : program W_WRITE;                                            
        
      uses dos,crt,winmen;                                                      
        
      var                                                                       
         w1,w2,win : byte;                                                      
         ch : char;                                                             
         c : word;                                                              
        
      begin                             {*DEFINE WINDOWS*}                      
         w1 := NEW_WIN(5,5,40,15,1,1,2,0,14);                                   
         w2 := NEW_WIN(20,8,65,18,2,5,6,7,0);                                   
         WOPEN(w1);                     {*OPEN THEM*}                           
         WOPEN(w2);                                                             
         for c := 1 to 3000 do          {*WRITE CHARACTERS*}                    
         begin                          {*RANDOMLY TO WINDOWS*}                 
     win := 0;                                                                
     while not(win in[w1,w2]) do                                              
     win := random(w2+2);                                                     
     ch := chr(random(255));                                                  
     WWRITE(win,ch);                                                          
         end;                                                                   
         WMFLOAT(w1);                   {*FLOAT 'EM ABOUT*}                     
         delay(1500);                                                           
         WMFLOAT(w2);                                                           
         delay(1500);                                                           
         WMDELETE(w1);                                                          
         delay(1000);                                                           
         WMDELETE(w2);                  {*DELETE 'EM*}                          
      end.                                                                      

      Rules Notes
      Tips & Traps: There are no additional, special rules or tips to follow 
      for this procedure, other than those outlined in the 
      preceeding paragraphs above. 
         The example being demonstrative and straightforward 
      requires no explanation and is left to the user to 
      examine.  
.PA 64
      ------------------------------------------------------------------------
      WWRITELN()                                                    WWRITELN() 
      ------------------------------------------------------------------------ 

      Procedure   : This procedure enables the user to write a text string 
      High Level    "stg" to any window, or user screen (ID < 20). 
         The window or user screen can be written to even if it 
      is partialy or even totaly obscured by other objects (in 
      background).
         The background screen arrays of any obscuring objects 
      are automaticaly updated with the new or changed 
      information, so that when any obscuring objects are 
      removed from view the contents of the window or user 
      screen, that was written to, is visualy correct.
         Any text written to a part of a window or user screen 
      that is obscured, by other object(s), will not become 
      visible until the obscuring object(s) are either removed 
      from the display, moved, or slid away, or the window or 
      user screen that was written to is floated to the 
      surface.   
         If the window or user screen, specified by ID, is found 
      to be closed, it is opened for use, and will be the active 
      one after writing is completed. This is most important, 
      as the WOPEN() procedure, which is called if it is closed, 
      will ensure that the window or user screen in question is 
      properly linked into the 3D screen map, on which the 
      WWRITELN() procedure relies for correct operation. (see 
      also the _WSCROLL_UP() procedure for more detailed 
      information on this subject).   
         If it is already open for use, its currency is left 
      unaltered, i.e. if it was open but not the active one, it 
      will remain non-active and if it was the active one, it 
      will remain the active one.
         The objects ID must have been previously created using
      NEW_WIN() or NEW_MENU().
         The objects "wmrec" fields ".x" and ".y" are updated by
      this procedure, to reflect the continually changing
      position of the cursor in the window, specified by ID, as
      the text string "stg" is written. This also allows the
      user to determine the position of the Winmen text cursor
      within any window or user screen, prior to writing, for
      more detail on the "wmrec" type and its fields, refer to
      the section "Winmen Declared Types".
         Text output can be directed to any location within the
      window or user screen specified by ID, by repositioning
      the Winmen text cursor with the WGOTOXY() procedure, this
      has no effect at all on the Turbo Pascal text cursor,
      which is used with the standard write(); writeln()
      read() ; readln() and gotoxy() procedures.
         The WWRITELN() procedure has no connection with the
      standard Turbo Pascal writeln() procedure.
         This procedure is not called by ony of the functions 
      or procedures in the Winmen Unit. 
.PA 65
      Syntax      : WWRITELN(ID,stg);

      Variables   : ID : byte;
      The unique identifier for the specified window or user 
      saved screen area, as returned from NEW_WIN() and as sent 
      to WMSAVE_SCR(0-19), if it is a user saved screen area.
         If the ID does not yet exist the programme will abort
      with an error message (see appendix for list).             

      stg : string;
      This is the ascii text string that will be printed in the
      window or user screen, specified by ID.
         The string can be up to 255 characters in length, any
      longer and Turbo Pascal will issue a range check error,
      which will termminate the programe.
         The string can contain any valid ascii character in the 
      range 0-255, although any control characters present in 
      the "stg" string arguement will be printed, unlike the 
      standard writeln() procedure, which acts on them, i.e.  
      chr(7) would be printed as '#' by WWRITELN(); but 
      writeln() would sound the bell.  WWRITELN() will print all 
      characters.
         The WWRITELN() procedure only takes a single string as 
      its arguement, rather than a complete arguement list, as 
      in the standard writeln() procedure.  What this means is 
      that the user will have to convert any non-ascii text 
      variables to a text string first and then consatenate all 
      the required arguements in to one string, prior to using 
      the WWRITELN() procedure.
         The WWRITELN() procedure will automaticaly wrap the 
      text onto the next line when the cursor reaches the 
      righmost edge of the current line, for the window or user 
      screen specified by ID.
         The WWRITE() procedure will automaticaly scroll the 
      entire contents, of the window or user screen, up by one 
      line, when the text exceeds the rightmost character on the 
      bottommost line.
         Regardless of where, on a line, writing ends, the
      Winmen text cursor will always be advanced to the start of
      the next line, so that the next time that window or user
      screen is written to using WWRITELN(); or WWRITE();
      writing will automaticaly start at the begining of that
      line, in otherwords a carriage return and line feed are
      always the last operations to be carried out by
      WWRITELN();

      Example     : program W_WRITELN;                                          
        
      uses dos,crt,winmen;                                                      
.PA 66
      var
         w1,w2,win,n,max : byte;                                                
         ch : string[60];                                                       
         c : word;                                                              

      begin                                                                     
         textbackground(0);                                                     
         clrscr;                        {*DEFINE WINDOWS*}                      
         w1 := NEW_WIN(5,5,40,15,1,1,2,0,14);                                   
         w2 := NEW_WIN(20,8,65,18,2,5,6,7,0);                                   
         WOPEN(w1);                     {*OPEN THEM*}                           
         WOPEN(w2);                                                             
         delay(1500);                                                           
         for c := 1 to 400 do           {*WRITE CHARACTERS*}                    
         begin                          {*RANDOMLY TO WINDOWS*}                 
     win := 0;                                                                
     while not(win in[w1,w2]) do                                              
        win := random(w2+2);                                                  
     max := 0;                                                                
     while not(max in[1..50]) do                                              
        max := random(55);                                                    
     ch := '';                                                                
     for n := 1 to max do                                                     
        ch := ch+chr(random(255));                                            
     WWRITELN(win,ch);                                                        
         end;                                                                   
         WMFLOAT(w1);                   {*FLOAT 'EM ABOUT*}                     
         delay(1500);                                                           
         WMFLOAT(w2);                                                           
         delay(1500);                                                           
         WMDELETE(w1);                                                          
         delay(1000);                                                           
         WMDELETE(w2);                  {*DELETE 'EM*}                          
      end.                                                                      

      Rules Notes
      Tips & Traps: There are no additional, special rules or tips to follow 
      for this procedure, other than those outlined in the 
      preceeding paragraphs above. 
         The example being demonstrative and straightforward 
      requires no explanation and is left to the user to 
      examine.                                                   
.PA 67
      ------------------------------------------------------------------------
      _WBOX_ON()                                                    _WBOX_ON()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by WOPEN() and WEXPLODE(), used
      Low Level     to display a box on the screen at the specified position,
      using the combined byte attributes for character type and
      colour, to form a screen ram word value, which specifies
      the character and colour to be put directly into screen
      ram for the horizontal, vertical and four box corner
      characters.
      No window records are affected by this command.
      No checks are carried out on the validity of data supplied 
      to this low level procedure, so extra care is required.

      Syntax      : _WBOX_ON(l,t,r,b,tlc,trc,blc,brc,horc,verc);

      Variables   : l : byte; (B1_80)
      Leftmost edge of the box including border, must be a
      value between 1-80 or a range check error will occure.

      t : byte; (B1_25)
      Topmost edge of the box including border, must be a
      value between 1-25 or a range check error will occure.

      r : byte; (B1_80)
      Rightmost edge of the box including border, must be a
      value between 1-80 or a range check error will occure.

      b : byte; (B1_25)
      Bottommost edge of the box including border, must be a
      value between 1-25 or a range check error will occure.

      tlc : word;
      Word attribute value for the top left corner character
      of the box.

      trc : word;
      Word attribute value for the top right corner character
      of the box.

      blc : word;
      Word attribute value for the bottom left corner character
      of the box.

      brc : word;
      Word attribute value for the bottom right corner character
      of the box.

      horc : word;
      Word attribute value for all the horizontal characters of
      the box.
.PA 68
      verc: word;
      Word attribute value for all the vertical characters of
      the box.

      All the values used for the character/colour attribute word are made up
      in the following way:-
      CHARACTER,COLOUR (but DOS requires the low byte first!).
      Low byte first : Colour value. High byte last : ASCII character value.

      The colour byte is made up in the following way:-
      Bit 7 : blink flag 0=off 1=on --------------[0 100 1000]
      Bit 6 : -|                                      |    |
      Bit 5 :  -> Character background colour 0-7 ----|    |
      Bit 4 : -|                                           |
      Bit 3 : -|                                           |
      Bit 2 :  -> Character foreground colour 0-15 --------|
      Bit 1 : -|
      Bit 0 : -|

      A xref of useful box type ASCII char values and a colour value chart
      follows:-

       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink

      Dec   | 169 170 179 180 181 182 183 184 185 186 187 188 189 190 191
      Hex   |  A9  AA  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF
      ASCII |  ©   ª   ³   ´   µ   ¶   ·   ¸   ¹   º   »   ¼   ½   ¾   ¿

      Dec   | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
      Hex   |  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE
      ASCII |  À   Á   Â   Ã   Ä   Å   Æ   Ç   È   É   Ê   Ë   Ì   Í   Î

      Dec   | 207 208 209 210 211 212 213 214 215 216 217 218
      Hex   |  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA
      ASCII |  Ï   Ð   Ñ   Ò   Ó   Ô   Õ   Ö   ×   Ø   Ù   Ú  
.PA 69
      Example     : program _WBOX_ON;
      uses dos,crt,winmen;
      var
         tlc,trc,blc,brc,horc,verc,colour : word;
      begin
         colour := (7 shl 4) + 12;     {*BK WHITE + FG LRED*}
         tlc := (colour shl 8) + 201   {*I*}
         trc := (colour shl 8) + 187;       {*;*}
         blc := (colour shl 8) + 200;  {*H*}
         brc := (colour shl 8) + 188;       {*<*}
         horc := (colour shl 8) + 205; {*M*}
         verc := (colour shl 8) + 186;      {*:*}
         clrscr;
         _WBOX_ON(1,1,80,25,tlc,trc,blc,brc,horc,verc);
         gotoxy(25,10);
         write(scr,'Press Return To Draw Box Off');
         readln;
         _WBOX_ON(1,1,80,25,0,0,0,0,0,0);
         gotoxy(25,11);
         write(scr,'Press Return');
         readln;
         normvideo;
         clrscr;
      end.

      Rules Notes
      Tips & Traps: This procedure calculates the screen ram offsets using
      the values supplied for l,t,r,b of the box and places
      the given screen ram character/colour attribute words,
      directly in the calculated screen addresses, This makes
      this procedure extremely fast at drawing boxes, which it
      needs to be, to enable it to be used during the WOPEN()
      and WEXPLODE() procedures.
      The example will draw a box to outline the screen, the
      border will be double horizontal and double vertical
      lines, the background colour will be white while the
      foreground colour will be light red.
      Notice the technique for constructing the colour value,
      although the actual colour only occupies a byte, in this
      case the colour variable must be a word value, this is
      because it is shifted left 8 bits when we come to make
      up the character/colour screen attribute word,
      tlc := (colour shl 8) + 201, if a byte were used in this
      instance, shifting it left 8 bits would effectively zero
      the colour variable, as the brackets are done first and
      this operation is done using the variable colour as
      opposed to variable tbc which is a word.
      So the screen ram attribute word is made by shifting the
      colour attribute value into the high order byte and
      adding to it the ASCII character value (low byte),
      not forgetting that this is actualy the reverse of how
      it would be found in screen ram because of DOS.
.PA 70
      The example then effectively draws the box off, by
      sending a zero values (bkground colour black, fground
      colour not care, character value null) to the _WBOX_ON()
      procedure, this of course assumes that the screen was
      black to start with, if it were not then the appropriate
      bkground colour along with any value (0-15) for fground
      colour and null for the character value must be combined
      as before and sent as the screen attribute word to have
      this effect.
.PA 71
      ------------------------------------------------------------------------
      _WCHK_MIN_SIZE()                                        _WCHK_MIN_SIZE()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by NEW_WIN() and WEXPLODE()
      Low Level     used to check that the box/window size is a minimum of
      3x3 characters
      No window records are affected by this command.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.

      Syntax      : _WCHK_MIN_SIZE(proc,l,t,r,b);

      Variables   : proc : string;
      This string should be the name of the function or procedure
      that is calling the _WCHK_MIN_SIZE() procedure, i.e. if say
      a procedure called MYPR() were to call _WCHK_MIN_SIZE()
      the format should be as follows _WCHK_MIN_SIZE(IDB,'MYPR');
      Although of course in reality you could use any string you
      want up to a maximum of 255 characters.
      If the string is longer than 255 characters a range check
      error will occure.                                        
      MYPR will be the routine name displayed in the error 
      message should the check fail. 

      l : byte; (B1_78)
      Leftmost edge of the window/box including border, must be
      a value between 1-78 or a range check error will occure.

      t : byte; (B1_23)
      Topmost edge of the window/box including border, must be
      a value between 1-23 or a range check error will occure.

      r : byte; (B3_80)
      Rightmost edge of the window/box including border, must be
      a value between 3-80 or a range check error will occure.

      b : byte; (B3_25)
      Bottommost edge of the window/box including border, must
      be a value between 3-25 or a range check error will
      occure.
.PA 72
      Example     : procedure SOME_WINDOW_PROC(l,t,r,b,x,y,z : byte);
      var
        .......etc;
      begin
         _WCHK_MIN_SIZE('SOME_WINDOW_PROC',l,t,r,b);
         remainder
     of code executed
        if box size was >= 3x3 chars
         else the _WCHK_MIN_SIZE() proc
     aborts programme with
        an error message
      end;

      Rules Notes
      Tips & Traps: A box of less than 3x3 characters cannot be displayed
      as there needs to be at laest 1 char in the middle of
      the box, for the box to have some purpose.
.PA 73
      ------------------------------------------------------------------------
      _WSCROLL_UP()                                              _WSCROLL_UP()
      ------------------------------------------------------------------------ 

      Procedure   : This procedure scrolls the entire contents of a window
      Low Level     or user screen (ID < 20) upwards by one line, the window 
      or user screen can be in the foreground or background.  
         The contents can be scrolled even if the window is 
      partialy or even totaly obscured by other objects (in 
      background). 
         The background screen arrays of any obscuring objects 
      are automaticaly updated with the new or changed 
      information, so that when any obscuring objects are 
      removed from view the contents of the scrolled window 
      is visualy correct.  
         The contents is scrolled up regardless of where in the 
      window the cursor is positioned, prior to calling the 
      _WSCROLL_UP() procedure.
         When the scroll is complete the cursor will be 
      positioned at column 1 on the last row of the window, 
      this last row will be blank, ready for writing to.
         This routine assumes that a maprec will be present in 
      the 3D screen map, for the given object and any obscuring 
      objects, at each character cell location covered by the 
      window being scrolled, if one does not the routine will 
      hang the system.  See the section on "Winmen Declared 
      Types" for information on the "maprec" record type.
         The 3D screen map is fundamental to the correct 
      operation of this very complex routine. 
         The user must make sure that the window or user 
      screen is actualy open, as no checks are carried out by 
      this low level procedure. If it is not open, then the 
      object is not linked into the 3D screen map, 
      consequently this procedure will cause the system to 
      hang or at best you will get a very messy screen.          
         The objects ID must have been previously created using 
      NEW_WIN() or NEW_MENU().
         In keeping with all low level routines in the Windows 
      and Menus Unit, no checks are carried out on the validity 
      of data supplied to, or used by, this low level procedure, 
      so extra care is needed.  This is to prevent all the 
      checking and safety routines present in the Unit being 
      called more than once, as they are already called by all 
      the high level routines, there is no need, from the 
      Units point of view, to call them again in the low level 
      routines, as all the Units low level routines are always 
      called from a high level routine. A low level routine is 
      never called on its own, by the Unit, but the user may 
      of course do this, thats why the extra care is required. 
      For the Unit to carry out all its checking more than once 
      would be time consuming and unnecassary.
         The objects wmrec is not affected by this procedure.        
         This procedure is called by:- WWRITELN(); WWRITE();       
.PA 74
      Syntax      : _WSCROLL_UP(ID);

      Variables   : ID : byte;
      The unique identifier for the specified window or user 
      saved screen area, as returned from NEW_WIN() and as sent 
      to WMSAVE_SCR(0-19), if it is a user saved screen area.
         If the ID does not yet exist the results will be 
      unpredictable, as the ID is not checked. 

      Example     : program _WSCROLL;                                           
        
      uses dos,crt,winmen;                                                      
        
      var                                                                       
         w1,w2,w3,w,t : byte;                                                   
        
      begin                                                                     
         clrscr;                        {*CREATE WINDOWS*}                      
         w1 := NEW_WIN(5,5,40,15,1,1,2,0,14);                                   
         w2 := NEW_WIN(10,8,55,18,2,5,6,7,0);                                   
         w3 := NEW_WIN(1,12,60,21,3,3,4,1,14);                                  
   
         for w := w1 to w3 do           {*PUT SOME TEXT IN*}                    
         begin                          {*THE WINDOWS     *}                    
     WOPEN(w);                                                                  
     for t := 1 to 5 do                                                         
     begin                                                                      
        writeln('Some text to scroll up the window');                           
        writeln('Window the up scroll to text some');                           
        delay(350);                                                             
     end;                                                                       
         end;                                                                   
     
         window(1,1,80,25);             {*PUT UP MESSAGE*}                      
         gotoxy(26,23);                                                         
         textbackground(black);                                                 
         textcolor(yellow+blink);         
         write(chr(7),'*****SCROLLING TEXT NOW*****',chr(7));                   
         delay(1000);                                                           
     
         for w := 1 to 5 do             {*SCROLL UP TEXT IN*}                   
         begin                          {*WINDOWS          *}                   
     for t := w1 to w3 do                                                       
        _WSCROLL_UP(t);                                                         
     delay(500);                                                                
         end;                                                                   
     
         for w := w1 to w3 do           {*DELETE ALL WINDOWS*}                  
         begin                                                                  
     delay(750);                                                              
     WMDELETE(w);                                                             
         end;                                                                   
      end.                                                                      
.PA 75
      Rules Notes
      Tips & Traps: As you can see from the example the _WSCROLL_UP(); 
      procedure is very fast, we have had to put in some 
      delay() procedures to slow things down, so that you can 
      see what is happening. In fact its so fast that when you 
      scroll the contents of several windows in sucession, it 
      appears as if they are being scrolled simultainiously.  
         You could conceivably use this procedure to scroll up 
      the contents only, of a menu as well, although I can not 
      think of a reason why any one would want to do this. 
      Perhaps you may want to scroll the contents of the menu 
      untill it is completly clear and then write to it, using 
      the WWRITELN() or WWRITE() procedures, to temporarily 
      turn it into a window! now there's a thought.     
.PA 76












       WINDOWS AND MENUS FOR TURBO PASCAL
       ----------------||----------------
        MENUS FUNCTIONS REFERENCE SECTION
.PA 77
      ------------------------------------------------------------------------
      BAR_MENU()                                                    BAR_MENU()
      ------------------------------------------------------------------------

      Function    : Display a menu, previously created using NEW_MENU(), in
      High Level    Bar Menu format, whose unique identifier is ID, at the
      specified row and column position, the menu will be "len"
      columns wide.
         The area of screen under the menu is saved away in the
      objects ID_SC[] background screen array and the menu is
      linked into the Winmen 3D screen MAP[] system automaticaly
      for full details on this subject refer to the
      WMSAVE_SCR(); procedure and the "maprec" record type,
      which is described in the section "Winmen Declared Types".
         There is also a system switch that affects the way in
      which the BAR_MENU(); function behaves:-

      1) MENU_ACT_ON_PC := true; Menu functions will return the
         number of the option chosen using the first character
         method.  Don't forget if there is more than one option
         with the same first character, it is the number of the
         first one in the list that will be returned.
         MENU_ACT_ON_PC := false; Menu functions will not return
         the number of the option chosen using the first
         character method.
         MENU_ACT_ON_PC := true; Default.

         The highlighted menu bar will always go to the option
      chosen, when the first character methos of option
      selection is used, regardles of how the switch is set.
         For more detail on this switch refer to the relevant
      section in the section "Winmen Declared Global Variables".
         A menu, when opened for the first time, will have its
      highlighted option bar placed on option 1, for each
      subsequent open, of that particular menu, the highlighted
      menu bar will appear on whatever menu option it was on,
      the last time the menu was closed. This feature applies
      to all menu types and also to the WMSLIDE(); WMMOVE(); and
      WMFLOAT(); procedures.
         The menu record is continually updated to reflect
      changes while the menu is active.
         This function is not called by any of the functions
      or procedures in the Winmen Unit.

      Syntax      : mc1 := BAR_MENU(ID,l,t,len);

      Variables   : mc1 : word (W1_400);
      This variable will receive the number of the option
      chosen on the Bar Menu. One is returned if the leftmost
      option is chosen, two for the next option and so on.
         A zero is returned if the Bar Menu is exited from, by
      pressing the escape key, without an option being chosen.
         A particular Bar Menu has to be the active one, ie.
      open and currently being used, to return an option number.
.PA 78
      ID : byte;
      The uniqueidentifier for the specified menu as returned
      from NEW_MENU().
         If the ID does not exist or the ID belongs to a window
      the programme will abort with an error message. (see
      apendix for list).

      l : byte; (B1_78)
      The column in which the leftmost edge of the Bar Menu is
      to be placed. Must be a value in the range 1-78 or a range
      check error will occure.

      t : byte; (B1_23)
      The row in which the top of the Bar Menu is to be placed.
      Must be a value in the range 1_23 or a range check error
      will occure.

      blen : byte; (B1_80)
      This is the length that the Bar Menu is to be drawn on the
      screen in columns. Must be a value in the range 1-80 or a
      range check error will occure.

      Example     : program BAR_MEN;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;              {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;

      begin
         getmem(mt,3*81);            {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Menu Option 1';  {*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';  {*OPTION STRINGS        *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);           {*RECLAIM MEMORY AND  *}
         m1c := 10;                  {*DELETE POINTER TO IT*}
         while (m1c <> 0) do         {*USE THE MENU*}
         begin
     m1c := BAR_MENU(m1,5,5,51);
     case m1c of
        1..3 : begin
           gotoxy(5,8);
           write('Menu Choice ',m1c,' Selected');
        end;
     end;
         end;
         normvideo;
      end.
.PA 79
      Rules Notes
      Tips & Traps: This function not only positions and displays the Bar
      Menu on the screen, but also handles all the highlighting
      and selection process and returns the number of the
      option chosen to the user, one being the leftmost option.
         If the menu is already open, only the selection process
      is carried out, ie. if the Bar Menu was used previously
      and exited without closing, the next time that particular
      open but non active Bar Menu is called, the function
      knows not to bother reopening the menu, but to go straight
      into the selection process, this saves a great deal of
      time and looks much nicer.
         The example programme will display a Bar Menu with
      three options, each option text is 13 characters in
      length, the length chosen for the Bar Menu is 51 columns,
      this leaves enough for a gap of 3 characters between
      options and at the start and end, (51-(13*3)) div 4 = 3,
      This formula can be used for calculating the Bar Menu
      length:- blen := sum of all option texts+(gap*(number
      options+1)); If the remainder of (blen-sum_opt) is not
      equaly divisable (in integer terms) by the (num_opt+1)
      then the quotiant is used for the gap and the integer
      remainder is added to the end gap.
         A Bar Menu is closed and exited by pressing the escape
      key while that menu is the active one, this forces a call
      to WMCLOSE(); thus returning a zero to the calling
      routine, or by using the WMCLOSE() procedure at any time.
      Either of these two methods will cause the menu to close
      in one of two ways, depending on how the following system
      switch is set:-

         CLOSE_WITH_REMOVE := false; All objects will be closed
         by firstly floating them to the surface with WMFLOAT();
         and then restoring the objects ID_SC[] background
         screen array, using WMREST_SCR(); to effectively remove
         them.
         CLOSE_WITH_REMOVE := true; All objects will be closed
         by using the WMREMOVE(); procedure, this makes it
         appear as if the object is being removed from the back
         of the screen, instead of from the front, as the other
         method does.
         CLOSE_WITH_REMOVE := false; Default.

         For more information on this system switch refer to
      the WMCLOSE(); procedure and the section on "Winmen
      Declared Types".
         The procedure also checks that no part of the Bar Menu
      will be displayed off the screen, if it would then again
      the programme will abort with an error message. The
      Highlighted menu bar is moved around using the cursor left
      and right keys to move one option at a time or the home
      and end keys to go to the leftmost and rightmost options
      respectively.
.PA 80
         Option Selection can be made with:-
      1) The return key, when the highlighted bar is on the
         required option.
      2) The cursor down key, when the highlighted bar is on the
         required option.
      3) The key that corresponds to the first character of an
         option, regardless of where the highlighted menu bar is
         (Note, this will not move the highlighted bar to that
         option). If there is two or more menu options with the
         same first character, the number of the first one in
         the list is returned.
         For an example of how to set up a Bar Menu with each
      option, when chosen, displaying a drop down type menu, see
      the demonstration programmes source code, Each one of the
      case statements options, for the Bar Menu, could of course
      be replaced with a call to another routine which handled
      another menu of any type, doing it that way as opposed to
      having all the options for all the menus in one routine,
      would keep things much more tidy and manageable.
         When a menu option is highlighted with the menu option
      bar all characters will appear in there original tbc and
      tfc complemented colours, which were calculated from the
      specified tb and tf colours in the call to NEW_MENU(),
      this is true even if they are changed at any time using
      MCHANGE_CHAR(), provided the original tb and tf colours
      were well chosen, this will ensure that the highlighted
      option will always be visible.
         The pick (first) character will always appear in its
      originally defined colour "pc", even when highlighted,
      unless changed with MCHANGE_CHAR().
.PA 81
      ------------------------------------------------------------------------
      LIST_MENU()                                                  LIST_MENU()
      ------------------------------------------------------------------------

      Function    : Display a menu, previously created using NEW_MENU(), in
      High Level    List Menu format, whose unique identifier is ID, at the
      specified row and column position.
         The area of screen under the menu is saved away in the
      objects ID_SC[] background screen array and the menu is
      linked into the Winmen 3D screen MAP[] system automaticaly
      for full details on this subject refer to the
      WMSAVE_SCR(); procedure and the "maprec" record type,
      which is described in the section "Winmen Declared Types".
         This function will also be called by POP_MENU() if it
      is found that the number of options in the menu exceeds
      23.
         There are also two system switches that affects the way
      in which the LIST_MENU(); function behaves:-

      1) MENU_EXIT_BY_CURSOR := true; Menus will exit using
         cursor or mouse left/right keys/action.
         MENU_EXIT_BY_CURSOR := false; Menus will not exit when
         cursor/mouse Mleft/right keys/action are used.
         MENU_EXIT_BY_CURSOR := true; default.

      2) MENU_ACT_ON_PC := true; Menu functions will return the
         number of the option chosen using the first character
         method.  Don't forget if there is more than one option
         with the same first character, it is the number of the
         first one in the list that will be returned.
         MENU_ACT_ON_PC := false; Menu functions will not return
         the number of the option chosen using the first
         character method.
         MENU_ACT_ON_PC := true; Default.

         The highlighted menu bar will always go to the option
      chosen, when the first character methos of option
      selection is used, regardles of how the switch is set.
         For more detail on these switches refer to the relevant
      sections in the section "Winmen Declared Global
      Variables".
         A menu, when opened for the first time, will have its
      highlighted option bar placed on option 1, for each
      subsequent open, of that particular menu, the highlighted
      menu bar will appear on whatever menu option it was on,
      the last time the menu was closed. This feature applies
      to all menu types and also to the WMSLIDE(); WMMOVE(); and
      WMFLOAT(); procedures.
         The menu record is continually updated to reflect
      changes while the menu is active.

      Syntax      : mc1 := LIST_MENU(ID,l,t,rows_vis);
.PA 82
      Variables   : mc1 : word (W1_400);
      This variable will receive the number of the option
      chosen on the List Menu. One is returned if the topmost
      option is chosen, two for the next option and so on, the
      topmost option is the one at the start of the list, as
      defined for NEW_MENU(), not necassarily the one currently
      at the top of the visable menu on the screen, the topmost
      one may have been scrolled of under the top of the menu.
         A zero is returned if the List Menu is exited from, by
      pressing the escape key, without an option being chosen.
         A particular List Menu has to be the active one, ie.
      open and currently being used, to return an option number.

      ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
         If the ID does not exist or the ID belongs to a window
      the programme will abort with an error message.  (see
      apendix for list).

      l : byte; (B1_78)
      The column in which the leftmost edge of the List Menu is
      to be placed. Must be a value in the range 1-78 or a range
      check error will occure.

      t : byte; (B1_23)
      The row in which the top of the List Menu is to be placed.
      Must be a value in the range 1_23 or a range check error
      will occure.

      rows_vis : byte; (B1_23)
      This is the number of menu options to be displayed on the
      screen when the List Menu is activated. Must be a value in
      the range 1-23 or a range check error will occure.
         The value can be less than the total number of options
      available, if so the other options, not visable can be
      scrolled into and out of the menu as required using the
      cursor keys, as explained in the Tips section below, if
      the number of rows_vis is greater than or equal to the
      number of options available and less than or equal to
      23 (max per screen), then a POP_MENU will automaticaly
      be used, this will be transparent to the user.

      Example     : program LIST_MEN;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;              {*DEFINE OPTION TEXT PTR*}
         m1,n : byte;
         m1c : word;
.PA 83
      begin
         getmem(mt,50*81);           {*DYNAMICALY ALLOCATE   *}
         for n := 1 to 50 do         {*MEMORY AND DEFINE MENU*}
         begin                       {*OPTION STRINGS        *}
     str(n,mt^[n]);
     mt^[n] := concat('Menu Option ',mt^[n]);
         end;
         m1 := NEW_MENU(1,1,2,3,4,15,mt,50); {*CREATE MENU*}
         freemem(mt,50*81);          {*RECLAIM MEMORY AND  *}
         m1c := 255;                 {*DELETE POINTER TO IT*}
         while (m1c <> 0) do         {*USE THE MENU*}
         begin
     m1c := LIST_MENU(m1,5,5,5);
     case m1c of
        1..50 : begin
     gotoxy(5,13);
     write('Menu Choice ',m1c,' Selected');
         end;
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This function not only positions and displays the List
      Menu on the screen, but also handles all the highlighting
      and selection process and returns the number of the
      option chosen to the user, one being the topmost option
      of the complete list.
         If the menu is already open, only the selection process
      is carried out, ie.  if the List Menu was used previously
      and exited without closing, the next time that particular
      open but non active List Menu is called, the function
      knows not to bother reopening the menu, but to go straight
      into the selection process, this saves a great deal of
      time and looks much nicer.
         The example programme will display a List Menu with 50
      options, but only 5 of them will be visible at any one
      time on screen, the other options being brought into view
      by using the cursor keys as described below.
         A List Menu is closed and exited by pressing either the
      escape key or the cursor left or right keys (See
      MENU_EXIT_BY_CURSOR above), while that menu is the active
      one, this forces a call to WMCLOSE(); thus returning a
      zero to the calling routine, or by using the WMCLOSE()
      procedure at any time.  Either of these three methods will
      cause the menu to close in one of two ways, depending on
      how the following system switch is set:-
.PA 84
         CLOSE_WITH_REMOVE := false; All objects will be closed
         by firstly floating them to the surface with WMFLOAT();
         and then restoring the objects ID_SC[] background
         screen array, using WMREST_SCR(); to effectively remove
         them.
         CLOSE_WITH_REMOVE := true; All objects will be closed
         by using the WMREMOVE(); procedure, this makes it
         appear as if the object is being removed from the back
         of the screen, instead of from the front, as the other
         method does.
         CLOSE_WITH_REMOVE := false; Default.

         For more information on this system switch refer to
      the WMCLOSE(); procedure and the section on "Winmen
      Declared Types".
         The procedure also checks that no part of the List Menu
      will be displayed off the screen, if it would then again
      the programme will abort with an error message.
         The Highlighted menu bar is moved using the cursor up and
      down keys, to move one option at a time, or the home and
      end keys, to go to the topmost and bottommost visable
      options respectively, when the highlighted menu bar is at
      the top or bottom of the menu, further use of the cursor
      up or down keys will result in list being scrolled, thus
      bringing more options in from the top of the menu while
      those at the bottom are put out of view and vice-versa.
         The list may also be paged while the highlighted menu
      bar is in any position, by using the Pgup and Pgdn keys.
         The absolute top or bottom of the list can be brought
      into view from any position in the menu, by pressing the
      Pgup or Pgdn keys while holding down the Ctrl key
      respectively.
         Option Selection can be made with:-
      1) The return key, when the highlighted bar is on the
         required option.
      2) The key that corresponds to the first character of a
         visable option, regardless of where the highlighted
         menu bar is (Note, this will not move the highlighted
         bar to that option). If there is two or more visable
         menu options with the same first character, the number
         of the first visable one in the list is returned.
         When a menu option is highlighted with the menu option
      bar all characters will appear in there original tbc and
      tfc complemented colours, which were calculated from the
      specified tb and tf colours in the call to NEW_MENU(),
      this is true even if they are changed at any time using
      MCHANGE_CHAR(), provided the original tb and tf colours
      were well chosen, this will ensure that the highlighted
      option will always be visible.
         The pick (first) character will always appear in its
      originally defined colour "pc", even when highlighted,
      unless changed with MCHANGE_CHAR().
.PA 85
      ------------------------------------------------------------------------
      NEW_MENU()                                                    NEW_MENU()
      ------------------------------------------------------------------------

      Function    : Set up a new menu structure and return the user a unique
      High Level    ID number for future reference to that particular menu.
      The menu structure is independant of the type of menu
      required, so the new menu definition can be used for all
      three menu types, POP_MENU(), LIST_MENU() and BAR_MENU().
      The menu record is created and updated to reflect the
      changes.

      Syntax      : ID := NEW_MENU(btype,bb,bf,tb,tf,pc,mt,rt);

      Variables   : ID : byte;
      Receives the returned unique window identifier.

      btype : byte; (B1_4)
      Type of border required around the menu edge, must be a
      value between 1-4 or a range check error will occure.
      The possible border types are as follows:-
         1 = Double horizontal and double vertical bars.
         2 = Single horizontal and single vertical bars.
         3 = Double horizontal and single vertical bars.
         4 = Single horizontal and double vertical bars.

      bb : byte; (B0_15)
      Border background colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      bf : byte; (B0_15)
      Border foregroung colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      tb : byte; (B0_15)
      Text background colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      tf : byte; (B0_15)
      Text foreground colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).
.PA 86
      pc : byte; (B0_15)
      This is the text foreground colour for the first
      character of each menu item, its a good idea to have the
      Pick Character a diferent colour from the rest of the text
      in the menu option so that it stands out, as this
      character can be used to select the menu item if desired.
      Must be a value in the range 0-15 or a range check error
      will occure. (see colour table below).

      mt : pt_mtext; (type
           mtext    = array[1..400] of string[80]
           pt_mtext = ^mtext;)
      This is a pointer variable, which should be used to
      dynamically allocate the above data type, just prior to
      defining the menu option strings, once the menu option
      strings have been defined and the menu created, the
      allocated memory may be reclaimed and the pointer deleted,
      as it will no longer be required by the unit. The example
      shows how it is used to best effect. The maximum number of
      menu options is 400, if this value is exceeded a range
      check error will occure, aborting the programme.

      rt : word (W1_400);
      This is the total number of options in the menu, which must
      be in the range 1..400 or a range check error will occure.

       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink

      Example     : program NEW_MENU;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;              {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
.PA 87
      begin
         getmem(mt,3*81);            {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Menu Option 1';  {*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';  {*OPTION STRINGS        *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);           {*RECLAIM MEMORY AND  *}
         m1c := 10;                  {*DELETE POINTER TO IT*}
         while (m1c <> 0) do         {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,5,5);
     case m1c of
        1..3 : begin
           gotoxy(5,8);
           write('Menu Choice ',m1c,' Selected');
        end;
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: A maximum of 255 unique identifiers can be allocated
      the first 20 (0-19) of these are used only for user
      saved screen areas using WMSAVE_SCR(), the rest 236
      (20-255) of these are to be shared between NEW_MENU()
      and NEW_WIN(), if all possible identifiers have been
      allocated or there is insuficient memory left for the
      menu the programme will abort with an error message
      (see apendix for list), although you will most likely
      run out of memory before identifiers, depending upon
      the size of existing menus and windows.
      You can of course free up memory and unique identifiers
      by using WMDELETE() to delete unwanted menus or windows.
      Take care not to overwrite or lose the returned unique
      identifier ID as this is your only link with the
      menu record structure and means of using/deleting the
      menu.
      As stated earlier NEW_MENU() is used to set up the menu
      structure regardless of which type of menu will eventualy
      be used.
      The example shows quite clearly how to define the menu
      options in the most memory efficient way, using the units
      predefined data type "pt_mtext". When a variable of this
      type is declared, as in the example, only enough static
      data space is reserved, at compile time, for the pointer
      variable "mt", no space is allocated for the array of
      strings to which it must eventually point (ie the pointer
      variable "mt" points to nowhere, its null). This space is
      allocated at run time (dynamically) using either the
      new() or getmem() procedures, I have chosen to use the
      later as it enables me to define exactly how much memory
.PA 88
      I need to set asside for the array of strings to which
      "mt" will point, unlike new() which, if used, would have
      set asside enough memory for the complete array of
      strings, or 400*81=32400 bytes, this is because "pt_mtext"
      is actually a pointer to a variable of type array[1..400]
      of string[80] so "mt" would be of the same type, getmem()
      only reserves the number of bytes that you actually tell it
      to, in our case 3*81=243 bytes, the 3 being the number of
      options and the 81 being the number of bytes required by a
      string of string[80], A considerable saving, especialy
      if you were to define several menus of smalish size
      one after the other, without reclaiming any of the
      previous ones, Not forgeting of course that getmem(), like
      new(), still gives you the ability to access your
      dynamically allocated array of strings, in the same way
      as if it were allocated statically ie. mt^[n] := etc....
      The user must exersise caution though when accessing the
      "mt^[n]" strings, if using the example above the user were
      to try and access or fill up the string "mt^[4]" he would
      be in deep trouble, as only enough space was allocated by
      the call to getmem() for 3 strings "mt^[1]", "mt^[2]" and
      "mt^[3]", thus "mt^[4]" would most certainly overwrite
      something else, possibly some other data or even code,
      no run time errors would be generated, strictly speaking
      there is no error, the "mt^[n]" string pointer is well
      within range (array[1..400] of string[80]) and pascal does
      not carry out checks to see if the memory allocated by
      getmem() has been exceeded, this could be a very difficult
      error to spot. if new() had been used instead of getmem()
      this problem would not occure, as all the "array[1.400] of
      string[80]" space would have been allocated, although I
      still think this is a high price to pay for a little extra
      care.
      Once the call to NEW_MENU() has been made, the space
      occupied by the array of strings which "mt" points to
      may be deallocated or reclaimed, by using either the
      freemem() or dispose() procedures, In our case we use
      freemem() as we used getmem() to allocate, it is used in
      the exact same way as getmem(), with exactly the same
      arguements and values, ie. we tell it to free up or                  
      return to the heep 3*81=243 bytes starting at the address 
      pointed to by "mt". WARNING do not use dispose() to
      reclaim memory if you allocated it using getmem(),
      conversly do not use freemem() to reclaim memory if you
      allocated it using new(), as either too much or too little
      memory will be returned to the heep, possibly with
      disasterous concequences.
      Refer to the section in Chapter 1 called "Winmen Declared
      Types" for information on all the record fields plus mtext
      and pt_mtext types. See also the MBAR_COMMENT() procedure 
      for more information on the mtext and pt_mtext types.         
      The example then goes on to use the newly defined menu.
.PA 89
      The function will always allocate a fixed amount of memory
      for the menu record. The amount of memory allocated for
      the option text and border is based upon the number of
      options. The amount of memory allocated for the screen
      under the menu, depends upon the area the menu covers,
      down to a minimum of 324 bytes, this happens to be the
      maximum amount of memory required to save the screen under
      a BAR_MENU, we have to assume the worst case for a BAR_
      MENU (2 lines) as the NEW_MENU() function does not know
      how wide the user wants the BAR_MENU, or weather there is
      to be a BAR_MENU comment line, but this is a very small
      overhead, when compared with savings in code and extra
      comonality afforded.
      When a menu option is highlighted with the menu option bar
      all characters will appear in there original tbc and tfc
      complemented colours, which are calculated from the
      specified tb and tf colours in the call to NEW_MENU(), this
      is true even if they are changed at any time using
      MCHANGE_CHAR(), provided the original tb and tf colours are
      well chosen, this will ensure that the highlighted option
      will always be visible.
      The pick (first) character will always appear in its
      originally defined colour "pc", even when highlighted,
      unless changed with MCHANGE_CHAR().                       
.PA 90
      ------------------------------------------------------------------------
      POP_MENU()                                                    POP_MENU()
      ------------------------------------------------------------------------

      Function    : Display a menu, previously created using NEW_MENU(), in
      High Level    Pop Menu format, whose unique identifier is ID, at the
      specified row and column position.
         The area of screen under the menu is saved away in the
      objects ID_SC[] background screen array and the menu is
      linked into the Winmen 3D screen MAP[] system automaticaly
      for full details on this subject refer to the
      WMSAVE_SCR(); procedure and the "maprec" record type,
      which is described in the section "Winmen Declared Types".
         This function will also be called from LIST_MENU() if
      it is found that the number of rows_vis is greater than or
      equal to the number of options available and less than or
      equal to 23 (max per screen).
         There are also two system switches that affects the way
      in which the POP_MENU(); function behaves:-

      1) MENU_EXIT_BY_CURSOR := true; Menus will exit using
         cursor or mouse left/right keys/action.
         MENU_EXIT_BY_CURSOR := false; Menus will not exit when
         cursor/mouse Mleft/right keys/action are used.
         MENU_EXIT_BY_CURSOR := true; default.

      2) MENU_ACT_ON_PC := true; Menu functions will return the
         number of the option chosen using the first character
         method.  Don't forget if there is more than one option
         with the same first character, it is the number of the
         first one in the list that will be returned.
         MENU_ACT_ON_PC := false; Menu functions will not return
         the number of the option chosen using the first
         character method.
         MENU_ACT_ON_PC := true; Default.

         The highlighted menu bar will always go to the option
      chosen, when the first character methos of option
      selection is used, regardles of how the switch is set.
      For more detail on these switches refer to the relevant
      sections in the section "Winmen Declared Global
      Variables".
         A menu, when opened for the first time, will have its
      highlighted option bar placed on option 1, for each
      subsequent open, of that particular menu, the highlighted
      menu bar will appear on whatever menu option it was on,
      the last time the menu was closed. This feature applies
      to all menu types and also to the WMSLIDE(); WMMOVE(); and
      WMFLOAT(); procedures.
         The menu record is continually updated to reflect
      changes while the menu is active.

      Syntax      : mc1 := POP_MENU(ID,l,t);
.PA 91
      Variables   : mc1 : word (W1_400);
      This variable will receive the number of the option
      chosen on the Pop Menu. One is returned if the topmost
      option is chosen, two for the next option and so on.
         The topmost option is the one at the start of the list
      as defined for NEW_MENU() and will always be the topmost
      visable option on the screen, as the menu does not scroll.
         A zero is returned if the Pop Menu is exited from, by
      pressing the escape key, without an option being chosen.
      A particular Pop Menu has to be the active one, ie. open
      and currently being used, to return an option number.

      ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
         If the ID does not exist or the ID belongs to a window
      the programme will abort with an error message.  (see
      apendix for list).

      l : byte; (B1_78)
      The column in which the leftmost edge of the List Menu is
      to be placed. Must be a value in the range 1-78 or a range
      check error will occure.

      t : byte; (B1_23)
      The row in which the top of the List Menu is to be placed.
      Must be a value in the range 1_23 or a range check error
      will occure.

      Example     : program POP_MENU;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;              {*DEFINE OPTION TEXT PTR*}
         m1,n : byte;
         m1c : word;

      begin
         getmem(mt,20*81);           {*DYNAMICALY ALLOCATE   *}
         for n := 1 to 20 do         {*MEMORY AND DEFINE MENU*}
         begin                       {*OPTION STRINGS        *}
     str(n,mt^[n]);
     mt^[n] := concat('Menu Option ',mt^[n]);
         end;
         m1 := NEW_MENU(1,1,2,3,4,15,mt,20); {*CREATE MENU*}
         freemem(mt,20*81);          {*RECLAIM MEMORY AND  *}
         m1c := 255;                 {*DELETE POINTER TO IT*}
.PA 92
         while (m1c <> 0) do         {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,5,1);
     case m1c of
        1..20 : begin
           gotoxy(5,24);
           write('Menu Choice ',m1c,' Selected');
         end;
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This function not only positions and displays the Pop
      Menu on the screen, but also handles all the highlighting
      and selection process and returns the number of the
      option chosen to the user, one being the topmost option.
         If the menu is already open, only the selection process
      is carried out, ie.  if the Pop Menu was used previously
      and exited without closing, the next time that particular
      open but non active Pop Menu is called, the function knows
      not to bother reopening the menu, but to go straight into
      the selection process, this saves a great deal of time and
      looks much nicer.
         The example programme will display a Pop Menu with 20
      options, all of them being visable.
         If the chosen menu ID has too many options to be
      displayed as a Pop Menu ie.  greater than 23 options, then
      it will automaticaly be displayed as a List Menu, with a
      rows_vis value of 23.
         A Pop Menu is closed and exited by pressing either the
      escape key or the cursor left or right keys (See
      MENU_EXIT_BY_CURSOR above), while that menu is the active
      one, this forces a call to WMCLOSE(); thus returning a
      zero to the calling routine, or by using the WMCLOSE();
      procedure at any time.  Either of these three methods will
      cause the menu to close in one of two ways, depending on
      how the following system switch is set:-

         CLOSE_WITH_REMOVE := false; All objects will be closed
         by firstly floating them to the surface with WMFLOAT();
         and then restoring the objects ID_SC[] background
         screen array, using WMREST_SCR(); to effectively remove
         them.
         CLOSE_WITH_REMOVE := true; All objects will be closed
         by using the WMREMOVE(); procedure, this makes it
         appear as if the object is being removed from the back
         of the screen, instead of from the front, as the other
         method does.
         CLOSE_WITH_REMOVE := false; Default.
.PA 93
         For more information on this system switch refer to
      the WMCLOSE(); procedure and the section on "Winmen
      Declared Types".
        The procedure also checks that no part of the Pop Menu
      will be displayed off the screen, if it would then again
      the programme will abort with an error message.
         The Highlighted menu bar is moved using the cursor up
      and down keys, to move one option at a time, or the home
      and end keys, to go to the topmost and bottommost options
      respectively, when the highlighted menu bar is at the top
      or bottom of the menu, further use of the cursor up or
      down keys will result in the bar looping to the bottom
      most or topmost options respectively.
         Option Selection can be made with:-
      1) The return key, when the highlighted bar is on the
         required option.
      2) The key that corresponds to the first character of an
         option, regardless of where the highlighted menu bar is
         (Note, this will not move the highlighted bar to that
         option). If there is two or more menu options with the
         same first character, the number of the first one is
         returned.
         When a menu option is highlighted with the menu option
      bar all characters will appear in there original tbc and
      tfc complemented colours, which were calculated from the
      specified tb and tf colours in the call to NEW_MENU(),
      this is true even if they are changed at any time using
      MCHANGE_CHAR(), provided the original tb and tf colours
      were well chosen, this will ensure that the highlighted
      option will always be visible.
         The pick (first) character will always appear in its
      originally defined colour "pc", even when highlighted,
      unless changed with MCHANGE_CHAR().
.PA 94












       WINDOWS AND MENUS FOR TURBO PASCAL
       ----------------||----------------
       MENUS PROCEDURES REFERENCE SECTION
.PA 95
      ------------------------------------------------------------------------
      MBAR_COMMENT()                                            MBAR_COMMENT()
      ------------------------------------------------------------------------

      Procedure   : Assign a list of option comments to a particular menu ID,
      High Level    the menu having previously been created using NEW_MENU().
         The comments will only be displayed when that menu ID
      is used as a bar menu, they will become effictive the next
      time the menu is opened as a bar menu, using BAR_MENU();
      or if the menu is already open as a bar menu, but is not
      active, they will be displayed if the menu is moved using
      WMMOVE(); they will not however be displayed if the menu
      is floated with WMFLOAT(); or slid using WMSLIDE(); this
      is because the WMFLOAT(); and WMSLIDE(); procedures do
      not need to call the _MBDISPLAY(); procedure, WMMOVE();
      does, this in turn calls the _MBBAR_ON(); routine which
      actualy displays the comment lines if they exist.
         The comment lines will automaticaly become linked into
      the 3D screen MAP[] system each time a menu that posseses
      them is opened for use as a bar menu, or if already open
      as a bar menu, the next time it is opened as a bar menu,
      or if it is moved with WMMOVE(); as discused above they
      will not be displayed or linked into the 3D screen MAP[]
      if the menu is already open as a bar menu and floated
      with WMFLOAT(); or slid using WMSLIDE();
         Any open and active menu cannot be commented until it
      is closed or becomes non active, as while it is active
      the programme flow in within the menu function its self.
         For more detailed information on the 3D screen MAP[]
      system and its associated data types, records and arrays
      refer to the section on "maprec" in the "Winmen Declared
      Types" section.
         This procedure is not called by any of the functions
      or procedures in the Winmen Unit.
         The menu record is updated to reflect any changes.

      Syntax      : MBAR_COMMENT(ID,mtc);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
         If the ID does not exist or the ID belongs to a window
      the programme will abort with an error message.  (see
      apendix for list).

      mtc: pt_mtext; (type
           mtext    = array[1..400] of string[80]
           pt_mtext = ^mtext;)
      This is a pointer variable, which should be used to
      dynamically allocate the above data type, just prior to
      defining the bar menu comment strings, once the comment 
      strings have been defined and the MBAR_COMMENT() procedure 
      called, the allocated memory may be reclaimed and the 
      pointer deleted, as it will no longer be required by the
.PA 96
      unit. The example shows how it is used to best effect.
         The maximum number of comment strings that can be
      defined is 400, the same as the maximum number of menu
      options. If this number is exceeded, very unlikely, in
      fact impossible the programme will abort with a range
      check error.

      Example     : program COMMENTS;
      uses dos,crt,winmen;
      var
         menu : byte;
         m1c : word;
         mt,mtc : pt_mtext;          {*DEFINE OPTION TEXT &*}
      begin                          {*COMMENT TEXT POINTERS*}
         getmem(mt,3*81);
         mt^[1] := 'Item 1';         {*DEFINE OPTION TEXT*}
         mt^[2] := 'Item 2';
         mt^[3] := 'Item 3';
         menu := NEW_MENU(1,7,9,1,14,15,mt,3);
         freemem(mt,3*81);
         getmem(mtc,3*81);           {*DEFINE COMMENT TEXT*}
         mtc^[1] := 'This Is The Long Comment For Item 1';
         mtc^[2] := 'Shorter Comment For Item 2';
         mtc^[3] := '';              {*MENU ITEM WITH NO*}
         MBAR_COMMENT(menu,mtc);     {*COMMENT TEXT     *}
         freemem(mtc,3*81);

         m1c := 10;
         while (m1c <> 0) do         {*USE THE MENU*}
         begin
     m1c := BAR_MENU(menu,21,5,38);
     case m1c of
        1..3 : begin
           gotoxy(30,10);
           write('Menu Choice ',m1c,' Selected');
        end;
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure associates a list of bar menu item comments
      with a particular ID.
         There can be one comment for each bar menu option and
      each comment can be up to 80 characters in length.
         Not every bar menu option has to have a comment only
      those required, A bar menu option not requiring a comment
      must have a null comment string assigned to it, as in the
      example above, if this is not done and that particular
      comment text assignment is left out the comment string
      will consist of whatever was in that area of memory at
      that time the length of comment text will also be
      unpredictable, this
.PA 97
      is because Pascal does not automatically clear strings or
      initialise the string length byte to zero.
         The comment will appear on the next line underneath the
      bar menu.
         The colour for the comment textbackground will be taken
      from that IDs record tbc value (Defined in NEW_MENU())
      ie.  the same as the highlighted menu bar background
      colour.
         The colour for the comment text will be taken from that
      IDs record pc value (Defined in NEW_MENU()) i.e the same
      colour as each options first character or pick character.
         The difference between the end of each comment text and
      the end of the menu is filled with the background colour.
         The example shows quite clearly how to define the menu
      options comment text strings in the most memory efficient
      way, using the units predefined data type "pt_mtext", for
      a full discussion of this technique please refer to the
      NEW_MENU() section.
         All the bar menus comments may be changed at any time
      by re-use of the MBAR_COMMENT() procedure.
         You cannot keep some of the old strings by just
      assigning a null string to those particular comments the
      next time you use the MBAR_COMMENT() procedure, as the
      null string effectively overwrites any existing comment
      text, you will need to re-assign the ones you want to keep
      as well.
         Re-use of the MBAR_COMMENT() procedure for particular
      menu ID does not use any extra memory, the existing area
      is used.
         If a particular comment text exceeds the length of the
      menu it will be truncated at the menus edge.
         The programme will abort with an error message, if an
      attempt is made to use MBAR_COMMENT() on a menu ID that
      does not exist, or if the ID refers to a window.
         The comment text strings, for a particular menu record
      once set up, using MBAR_COMMENT(), cannot be erased, only
      overwritten by reuese of the MBAR_COMMENT() procedure, if
      however you do not wish to have the comments displayed
      once thay have been set up, but still want to retain them
      and display them at some other time, then you can do so by
      setting the '.comnt' field of that particular menus record
      to 'false', or back to 'true' when you want them displayed
      again, like this:- ID_WM[ID]^.comnt := false; Where ID is
      the menu identifier (see above), refer to the section in
      Chapter 1 called 'Winmen Declared Types' for further
      information on all the record fields plus mtext and
      pt_mtext types.  See also the NEW_MENU() function for more
      information on the mtext and pt_mtext types.  You may also
      find it usefull to read the description of ID_MC[] in the
      section 'Winmen Declared Variables' as this is the actual
      structure used to hold the bar menu comment strings.
.PA 98
      ------------------------------------------------------------------------
      MCHANGE_CHAR()                                            MCHANGE_CHAR()
      ------------------------------------------------------------------------

      Procedure   : Change a character in menu "ID", option "row", position
      High Level    "col" to the character in "new_char", also assigning the
      new textbackground and foreground colours "tb" and "tf"
      respectively. The ID can only refer to a list or pop menu
      when open or closed or a bar menu when closed. The nemu
      must have previously been created using NEW_MENU().
      The menu record is not affected by this command.

      Syntax      : MCHANGE_CHAR(ID,row,col,new_char,tb,tf);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window the
      programme will abort with an error message. (see apendix
      for list). 

      row : byte;
      The "row" number corresponds to the menu option as defined
      in the option text list passed to NEW_MENU(), "row" 1 will
      be the first option in that list, "row" 2 the second etc..

      col : byte; (B1_79)
      The "col" number corresponds to the menu options column
      as defined in the option text list passed to NEW_MENU(),
      "col" 1 will be the first character of the option, from
      the left hand side. Col must be in the range 1-79 or a 
      range check error will occure

      new_char : char;
      This is the ascii character/value with which to replace
      the existing ascii character/value at position "row",
      "col". The character/value can be any valid ascii value
      in the range 0-255.

      tb : byte; (B0_15)
      Text background colour, for the "new_char" must be a value
      in the range 0-15 or a range check error will occure. (see
      colour table below).

      tf : byte; (B0_15)
      Text foreground colour, for the "new_char" must be a value
      in the range 0-15 or a range check error will occure. (see
      colour table below).
.PA 99
       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink

      Example     : program CHANGE_CHAR;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;              {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);            {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Change a Char';  {*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Open & Close';   {*OPTION STRINGS        *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);           {*RECLAIM MEMORY AND  *}
         m1c := 10;                  {*DELETE POINTER TO IT*}
         while (m1c <> 0) do         {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,31,8);
     case m1c of
        1 : MCHANGE_CHAR(m1,2,1,chr(random(255)),8,15);
    end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure will change any character and its text-
      background and foreground colours in a menu option array
      permenantly, or untill changed again using this procedure.
      The corresponding screen word/character is also updated
      provided the menu is currently open and in the case of a
      list menu, the character at "row","col" is visible on the
      screen.
.PA 100
      As mentioned above list and pop menus can be open or closed
      when this procedure is used, however a bar menu must be
      closed for this procedure to operate correctly, the change
      will be effective the next time the bar menu is opened. If
      this procedure is used on an open bar menu the new char
      with its textforeground and background colours will most
      probably appear somewhere else on the screen, not in its
      correct position, the change will be correct after the bar
      menu has been closed and re-opened, as the mmenu screen
      format arrays are in a common format for all menus
      regardless of there type, its only there respective display
      procedures that differ.
      This procedure may be updated at a future date, so that bar
      menus can updated correctly while still open.
      The new textbackground and foreground colours specified
      only have effect on the new character, and will stay in
      effect untill changed again, even if the menu is closed and
      re-opened, this is because the mmenu word array, which is
      in screen format, is never rebuilt again as it was in the
      call to NEW_MENU(), so the originally defined tb and tf
      values have no effect on the changed character(s).
      If it is found that the character to be changed is on the
      same row as the highlighted menu bar, then the screen is
      updated using the original tbc and tfc complemented colours
      otherwise a changed character with poorly chosen colours
      may become invisable when highlighted.
      When a menu option containing changed characters is high-
      lighted with the menu option bar, all characters will still
      appear in there original tbc and tfc complemented colours,
      which were calculated from the specified tb and tf colours
      in the call to NEW_MENU(), provided the original tb and tf
      colours were well chosen, this will ensure that the high-
      lighted option will always be visible, regardles of how
      individual characters are changed.
      The pick (first) character will always appear in its
      originally defined colour "pc", even when highlighted,
      unless changed with MCHANGE_CHAR().
      The example above changes the second character of menu
      option 2 each time option 1 is chosen. The character value
      will be random, the colours will always be tb=8 & tf=15.
.PA 101
      ------------------------------------------------------------------------
      MPIC_COL()                                                    MPIC_COL()
      ------------------------------------------------------------------------

      Procedure   : Change the pick (first) characters foreground colour to
      High Level    that of "pc" for each menu option in the menu "ID", which
      was previously created using NEW_MENU().
      The menu record is updated to reflect any changes. 

      Syntax      : MPIC_COL(ID,pc);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window the
      programme will abort with an error message. (see apendix
      for list). 

      pc : byte; (B0_15)
      This is the text foreground colour for the first
      character of each menu item, its a good idea to have the
      Pick Character a diferent colour from the rest of the text
      in the menu option so that it stands out, as this
      character can be used to select the menu item if desired.
      Must be a value in the range 0-15 or a range check error
      will occure. (see colour table below).

       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink

      Example     : program PIC_COL;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                  {*DEF OPTION TXT PTR*}
         m1 : byte;
         m1c : word;
.PA 102
      begin
         getmem(mt,3*81);                {*DYNAMICALY ALLOCATE*}
         mt^[1] := 'Change Pick Colour'; {*MEM AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';      {*OPTION STRINGS     *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3);{*CREATE MENU*}
         freemem(mt,3*81);               {*RECLAIM MEMORY AND  *}
         m1c := 10;                      {*DELETE POINTER TO IT*}
         while (m1c <> 0) do             {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,31,8);
     case m1c of
        1 : begin           {*RANDOMLY CHANGE PIC COLOUR*}
        MPIC_COL(m1,random(15));
        WMCLOSE(m1);
     end;
     end;
         end;
         normvideo;
      end.

      Notes Rules
      Tips & Traps: This procedure changes all the menu options first char-
      acters colour to the value sent in "pc".
      This procedure can be used regardless of the state of a
      particular menu.
      The change will be permenant or untill changed again
      using MPIC_COL() or MCHANGE_CHAR() and will only become
      effective when the menu is next opened for use.
      Can be used on all menu types.
.PA 103
      ------------------------------------------------------------------------
      _MBBAR_END()                                                _MBBAR_END()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure which is called by BAR_MENU() to move
      Low Level     the selection bar of bar menu ID to the rightmost option,
      regardless of where it is.
      if the highlighted menu bar is already at the rightmost 
      position, no action is taken. 
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MBBAR_END(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than bar menu the results could be
      unpredictable.

      Example     : program MBBAR_END;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);              {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Bar To End Option';{*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';    {*OPTION STRINGS        *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);             {*RECLAIM MEMORY AND  *}
         m1c := 10;                    {*DELETE POINTER TO IT*}
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := BAR_MENU(m1,5,5,60);
     case m1c of
        1 : _MBBAR_END(m1);     {*MOVE OPTION BAR TO END*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure moves a bar menu, option bar, to the 
      rightmost option in the menu, regardless of where it is,
      or if the highlighted bar is already at the rightmost 
      position, no action is taken. 
.PA 104
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on bar
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open bar menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.                           
      The example will move the highlighted menu option bar to
      the rightmost option each time option 1 is chosen.          
.PA 105
      ------------------------------------------------------------------------
      _MBBAR_HOME()                                              _MBBAR_HOME()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure which is called by BAR_MENU() to move
      Low Level     the selection bar of bar menu ID to the leftmost option in 
      menu, regardless of where it is.
      if the highlighted menu bar is already at the leftmost 
      position, no action is taken. 
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MBBAR_HOME(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than bar menu the results could be
      unpredictable.

      Example     : program MBBAR_HOME;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);              {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Menu Option 1';    {*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Go To Home Option';{*OPTION STRINGS        *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);             {*RECLAIM MEMORY AND  *}
         m1c := 10;                    {*DELETE POINTER TO IT*}
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := BAR_MENU(m1,5,5,60);
     case m1c of
        2 : _MBBAR_HOME(m1);    {*MOVE OPTION BAR LEFT*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure moves a bar menu option bar to the leftmost
      position, regardless of where it is, or if the highlighted
      bar is already at the leftmost position, no action is 
      taken. 
.PA 106
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on bar
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open bar menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.                           
      The example will move the highlighted menu option bar to
      the leftmost option each time option 2 is chosen.          
.PA 107
      ------------------------------------------------------------------------
      _MBBAR_LEFT()                                              _MBBAR_LEFT()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure which is called by BAR_MENU() to move
      Low Level     the selection bar of bar menu ID one option to the left,
      looping to the last option if the highlighted menu bar is
      already at the leftmost position.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MBBAR_LEFT(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than bar menu the results could be
      unpredictable.

      Example     : program MBBAR_LEFT;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);              {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Bar Left 1 Option';{*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';    {*OPTION STRINGS        *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);             {*RECLAIM MEMORY AND  *}
         m1c := 10;                    {*DELETE POINTER TO IT*}
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := BAR_MENU(m1,5,5,60);
     case m1c of
        1 : _MBBAR_LEFT(m1);    {*MOVE OPTION BAR LEFT*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure moves a bar menu option bar one option to
      the left, or if the highlighted bar is already at the
      leftmost position it is looped to the rightmost option.
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
.PA 108
      The procedure will only work correctly when used on bar
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open bar menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.                           
      The example will move the highlighted menu option bar one
      option to the left each time option 1 is chosen.                          
.PA 109
      ------------------------------------------------------------------------
      _MBBAR_OFF()                                                _MBBAR_OFF()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by _MBBAR_LEFT(), _MBBAR_RIGHT()
      Low Level     _MBBAR_HOME() and _MBBAR_END() to turn off the highlighted
      menu bar in a bar menu.
      The menu ID must have been previously created by using
      NEW_MENU().                                               
      The menus record is not affected by this command.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MBBAR_OFF(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().                                          
      If the ID does not exist or the ID belongs to a window or
      a menu type other than bar menu the results could be
      unpredictable.

      Example     : program MBBAR_OFF;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                 {*DEF OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);               {*DYNAMICALY ALLOC   *}
         mt^[1] := 'Turn Bar Off';      {*MEM AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';     {*OPTION STRINGS     *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);              {*RECLAIM MEMORY AND  *}
         m1c := 10;                     {*DELETE POINTER TO IT*}
         while (m1c <> 0) do            {*USE THE MENU*}
         begin
     m1c := BAR_MENU(m1,5,5,60);
     case m1c of
        1 : _MBBAR_OFF(m1);      {*TURN OPTION BAR OFF*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure turns off a highlighted menu bar on a bar
      menu, by redrawing it in its normal attributes, using the
      originally defined "tb" and "tf" colour values sent to 
      NEW_MENU().
.PA 110
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on bar
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open bar menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.                           
      The example will turn off the highlighted menu option bar
      each time option 1 is chosen.                             
.PA 111
      ------------------------------------------------------------------------
      _MBBAR_ON()                                                  _MBBAR_ON()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by _MBBAR_LEFT(), _MBBAR_RIGHT()
      Low Level     _MBBAR_HOME(), _MBBAR_END() and _MBDISPLAY() to turn on the 
      highlighted menu bar in a bar menu, plus any associated 
      comment text for this menu ID, which was created using 
      MBAR_COMMENT().  
      The menu ID must have been previously created by using
      NEW_MENU().                                               
      The menus record is not affected by this command.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MBBAR_ON(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().                                          
      If the ID does not exist or the ID belongs to a window or
      a menu type other than bar menu the results could be
      unpredictable.

      Example     : program MBBAR_ON;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                 {*DEF OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);               {*DYNAMICALY ALLOC   *}
         mt^[1] := 'Turn Bar On';       {*MEM AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';     {*OPTION STRINGS     *}
         mt^[3] := 'Bar Off';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);              {*RECLAIM MEMORY AND  *}
         m1c := 10;                     {*DELETE POINTER TO IT*}
         while (m1c <> 0) do            {*USE THE MENU*}
         begin
     m1c := BAR_MENU(m1,5,5,60);
     case m1c of
        1 : _MBBAR_ON(m1);       {*TURN OPTION BAR ON*}
        3 : _MBBAR_OFF(m1);      {*TURN OPTION BAR OFF*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure turns on a highlighted menu bar on a bar
      menu, this is achieved by changing the screen ram character 
      word's colour byte, using this ID's original "tbc" and 
.PA 112
      "tfc" complemented colours, which were calculated from the 
      specified "tb" and "tf" colours in the call to NEW_MENU(), 
      rather than using the colour values already in the mmenu 
      character word array and complementing them, as they may be 
      poor colours for complementing, provided the original "tb" 
      and "tf" colours were well chosen, this will ensure that 
      the highlighted option will always be visible, regardles of 
      how individual characters may have been changed using the 
      MCHANGE_CHAR() procedure. 
      The pick (first) character will always appear in its
      originally defined colour "pc", even when highlighted,
      unless changed with MCHANGE_CHAR(). 
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      Any comment text associated with the menu is also drawn
      on, on the line under the actual bar menu. The comment
      text, if too long for the menu, will be truncated at the
      end of the menu, or if too short, the remainder, between 
      the end of the comment text and the end of the menu will 
      be filled with complemented textbackground colour only.
      The colour for the comment textbackground will be taken
      from that IDs record "tbc" value (Defined in NEW_MENU())
      ie. the same as the highlighted menu bar background colour.
      The colour for the comment text will be taken from that
      IDs record "pc" value (Defined in NEW_MENU()) i.e the same
      colour as each options first character or pick character. 
      The procedure will only work correctly when used on bar
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open bar menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.                           
      The example will turn on the highlighted menu option bar
      each time option 1 is chosen, and off when option 2 is                    
      chosen.
.PA 113
      ------------------------------------------------------------------------
      _MBBAR_RIGHT()                                            _MBBAR_RIGHT()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure which is called by BAR_MENU() to move
      Low Level     the selection bar of bar menu ID one option to the right,
      looping to the first option if the highlighted menu bar is
      already at the rightmost position.
      The menu ID must have been previously created by using
      NEW_MENU().                                               
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MBBAR_RIGHT(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than bar menu the results could be
      unpredictable.

      Example     : program MBBAR_RIGHT;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                 {*DEF OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);               {*DYNAMICALY ALLOC   *}
         mt^[1] := 'Bar Right 1 Option';{*MEM AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';     {*OPTION STRINGS     *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);              {*RECLAIM MEMORY AND  *}
         m1c := 10;                     {*DELETE POINTER TO IT*}
         while (m1c <> 0) do            {*USE THE MENU*}
         begin
     m1c := BAR_MENU(m1,5,5,60);
     case m1c of
        1 : _MBBAR_RIGHT(m1);    {*MOVE OPTION BAR RIGHT*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure moves a bar menu option bar one option to
      the right, or if the highlighted bar is already at the
      rightmost position it is looped to the leftmost option.
.PA 114
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on bar
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open bar menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.                    
      The example will move the highlighted menu option bar one
      option to the right each time option 1 is chosen.          
.PA 115
      ------------------------------------------------------------------------
      _MBDISPLAY()                                                _MBDISPLAY()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by BAR_MENU() to actually display
      Low Level     the bar menu on the screen at the position requested in the
      call to BAR_MENU().
         This procedure does not save away the screen under the
      bar menu being displayed, niether does it link the bar
      menu into the 3D screen MAP[] system, for details on both
      of these subjects refer to the WMSAVE_SCR(); procedure and
      the sub-section on "maprecs" in the "Winmen Declared
      Types" section.
         The menu ID must have been previously created by using
      NEW_MENU().
         The menus record is updated to reflect the changes.
         No checks are carried out on the validity of data
      supplied to this low level procedure, so extra care is
      needed.

      Syntax      : _MBDISPLAY(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than bar menu the results could be
      unpredictable.                                            

      Example     : program MBDISPLAY;

      uses dos,crt,winmen;

      var
         ID : pt_wmrec;                  {*DEF POINTER TO WMREC*}
         mt : pt_mtext;                  {*DEF OPTION TEXT PTR*}
         blen,nrow,m1,key,l,t,ncol_old : byte;{*DEF OTHER VARBS*}

      begin
         getmem(mt,3*81);             {*DYNAMICALY ALLOC   *}
         mt^[1] := 'Key Pad 6';       {*MEM AND DEFINE MENU*}
         mt^[2] := 'Will Move Bar';   {*OPTION STRINGS     *}
         mt^[3] := '1 Option Right';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);            {*RECLAIM TXT STRINGS MEM*}
         {**************************} {*AND DELETE POINTER*}
         ID := ID_WM[m1];             {*POINT ID TO m1 MENU REC*}
         blen := 60; l := 7; t := 7;  {*INT BAR LEN & TR OF MEN*}
         ID^.gap := (blen-ID^.sumopt) div (ID^.nrow-1); {*GAP*}
         ID^.cols_vis := blen;        {*BAR COLUMNS VISABLE*}
         ncol_old := ID^.ncol;        {*SAVE POP/LIST COLS*}
         ID^.ncol := blen;            {*USE BAR COLS VALUE*}
.PA 116
         if(ID^.comnt = true) then    {*CHECK FOR COMMENT TEXT*}
     nrow := 2                 {*SET NROW=2 IF CMT TXT=Y*}
         else
     nrow := 1;                {*SET NROW=1 IF CMT TXT=N*}
         _MCHK_ON_SCREEN(m1,'MY_MENU',l,t,nrow); {*CHECK VALID*}
         WMSAVE_SCR(m1,ID^.l,ID^.t,ID^.r,ID^.b); {*SAVE SCREEN*}
         _MBDISPLAY(m1);              {*DISPLAY THE BAR MENU*}
         ID^.ncol := ncol_old;        {*REPLACE POP/LIST COLS*}
         {**************************}
         key := 0;                    {*INITIALISE KEY*}
         while (key <> 27) do         {*USE THE MENU*}
         begin                        {*WHILE ESC NOT PRESSED*}
     key := ord(readkey);      {*WAIT FOR A KEYPRESS*}
     case key of               {*CHECK SCAN CODE*}
        77 : _MBBAR_RIGHT(m1); {*MOVE OPTION BAR RIGHT*}
     end;
         end;
         WMREST_SCR(m1,ID^.l,ID^.t,ID^.r,ID^.b); {*RESTO SCREEN*}
         normvideo;
      end.                                                                      

      Rules Notes
      Tips & Traps: This procedure will actually display the bar menu on the
      screen, in the position requested in the call to BAR_MENU()
      and turn on the highlighted menu bar over option one, the
      leftmost option, it will do this each time it is called.
      But as can be seen from the above example a fair amount of
      work has to be done in firstly setting up that particular
      menus record variables, the area of code in the example 
      between the {*********} markings shows the minimum amount 
      of setting up required if the call to _MBDISPLAY() is to
      be sucessful, this includes the calls to _MCHK_ON_SCREEN()
      and WMSAVE_SCR(), as these procedures also contribute
      towards setting record variables for the menu in question.
      All the record variables in the above example begin with
      the identifier "ID^." and are appended with the appropriate
      record variable identifier.
         A full list of all the global wmrec record variables,
      all other global variables and constants, along with all
      the units declared types, can be found, with an
      explanation of each elsewhere in this reference guide.
         The example will actually put up a three option bar
      menu and allow you to move the highlighted option bar one
      option to the right each time the nemeric pad "6" key is
      pressed, the "esc" key will exit the menu.  The procedure
      achieves its speed by using direct screen ram access
      techniques, to place characters directly into screen ram.
         The procedure will only work correctly when used on bar
      menus.  Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
.PA 117
         Being a low level procedure no checks are carried out
      on any data passed to it by the user, so make sure the ID
      is for a valid open or closed bar menu only, not any other
      menu type or a window, the result of doing this will
      possibly corrupt the record or be unpredictable.
.PA 118
      ------------------------------------------------------------------------
      _MCHK_ON_SCREEN()                                      _MCHK_ON_SCREEN()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by the following functions:-
      BAR_MENU(), LIST_MENU() and POP_MENU().
      This procedure checks to make sure that no part of the 
      menu will go off the screen when it is displayed, it is
      an error to do so and the procedure will halt the current
      programme with an error message if it is found it will,
      (see appendix for list).
      If the check is sucessfull the procedure goes on to update
      the menus record with the supplied "ID^.l" and "ID^.t" 
      values and also calculated values for "ID^.r" and "ID^.b"
      as these values are needed by all the calling functions
      in order to actually display the menu.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MCHK_ON_SCREEN(ID,proc,l,t,nrow);              

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window the
      results could be unpredictable.                                           

      proc : string;
      This string should be the name of the function or procedure
      that is calling the _MCHK_ON_SCREEN() procedure, if say a 
      procedure called MYPROC() were to call _MCHK_ON_SCREEN()
      it would look like this _MCHK_ON_SCREEN(IDB,'MYPROC');
      Although of course in reality you could use any string you
      want up to a maximum of 255 characters.
      If the string is longer than 255 characters a range check
      error will occure.                                        

      l : byte; (B1_78)
      Leftmost edge of the menu including border, must be a
      value between 1-78, to be consistant with the way the unit
      works, if not then the results may be unpredictable. 

      t : byte; (B1_23)
      Topmost edge of the menu including border, must be a value
      between 1-23, to be consistant with the way the unit works
      if not then the results may be unpredictable.

      nrow : byte;
      This value is the absolute number of rows that the
      menu area covers or its depth (b-t+1). Must be a value in 
      the range 3-25 to be consistant with the way the unit works 
      if not then the results may be unpredictable.                             
.PA 119
      Example     : procedure MENU_PROC(ID : byte; a,b,c : sometype);
      var
         variables.....
      begin
         _MCHK_ON_SCREEN(ID,'MENU_PROC',10,5,8);
         remainder
     of
       procedure
    statements
      end;                                                      

      Rules Notes
      Tips & Traps: No special instructions to be observed.                   
.PA 120
      ------------------------------------------------------------------------
      _MDISPLAY()                                                  _MDISPLAY()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by POP_MENU() and LIST_MENU() to 
      Low Level     actually display the pop/list menu on the screen at the 
      position requested in the call to POP_MENU() or LIST_MENU()  
      The menu ID must have been previously created by using 
      NEW_MENU().
         This procedure does not save away the screen under the
      bar menu being displayed, niether does it link the bar
      menu into the 3D screen MAP[] system, for details on both
      of these subjects refer to the WMSAVE_SCR(); procedure and
      the sub-section on "maprecs" in the "Winmen Declared
      Types" section.
         The menus record is updated to reflect the changes.
         No checks are carried out on the validity of data
      supplied to this low level procedure, so extra care is
      needed.

      Syntax :      _MDISPLAY(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than pop or list the results could be
      unpredictable.                                            

      Example     : program MDISPLAY;

      uses dos,crt,winmen;

      var
         ID : pt_wmrec;               {*DEF POINTER TO WMREC*}
         mt : pt_mtext;               {*DEF OPTION TEXT PTR*}
         m1,key : byte;               {*DEF SUNDRY VARBS*}

      begin
         getmem(mt,3*81);             {*DYNAMICALY ALLOC   *}
         mt^[1] := 'Key Pad 2';       {*MEM AND DEFINE MENU*}
         mt^[2] := 'Will Move Bar';   {*OPTION STRINGS     *}
         mt^[3] := '1 Option Down';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);            {*RECLAIM TXT STRINGS MEM*}
         {**************************} {*AND DELETE POINTER*}
         ID := ID_WM[m1];             {*POINT ID TO m1 MENU REC*}
         ID^.rows_vis := ID^.nrow-2   {*VISIBLE=TOTAL-BORDER*}
         _MCHK_ON_SCREEN(m1,'MY_MENU',20,10,ID^.nrow);{*CHK POS*}
         WMSAVE_SCR(m1,ID^.l,ID^.t,ID^.r,ID^.b); {*SAVE SCREEN*}
         _MDISPLAY(m1);               {*DISPLAY THE POP MENU*}
         {**************************}
.PA 121
         key := 0;                    {*INITIALISE KEY*}
         while (key <> 27) do         {*USE THE MENU*}
         begin                        {*WHILE ESC NOT PRESSED*}
     key := ord(readkey);      {*WAIT FOR A KEYPRESS*}
     case key of               {*CHECK SCAN CODE*}
        80 : _MPLBAR_DO(m1);   {*MOVE OPTION BAR DOWN*}
     end;
         end;
         WMREST_SCR(m1,ID^.l,ID^.t,ID^.r,ID^.b); {*RESTO SCREEN*}
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure will actually display the pop or list menu
      on the screen, in the position requested in the call to 
      BAR_MENU() and turn on the highlighted menu bar over option 
      one, the topmost option, it will do this each time it is 
      called. The ID^.rows_vis value must be set to the number 
      of option rows you actually want displayed on the screen,
      for a pop menu this will be ID^.nrow-2 (all rows less the
      border), but for a list menu of course this value can be
      anything between 1..23 as long as the value is less than
      the total number of opion rows in the menu.
         As can be seen from the above example a fair amount of
      work has to be done in firstly setting up that particular
      menus record variables, the area of code in the example
      between the {*********} markings shows the minimum amount
      of setting up required if the call to _MDISPLAY() is to be
      sucessful, this includes the calls to _MCHK_ON_SCREEN()
      and WMSAVE_SCR(), as these procedures also contribute
      towards setting record variables for the menu in question.
         All the record variables in the above example begin
      with the identifier "ID^." and are appended with the
      appropriate record variable identifier.
         A full list of all the global wmrec record variables,
      all other global variables and constants, along with all
      the units declared types, can be found, with an
      explanation of each elsewhere in this reference guide.
         The example will actually put up a three option pop
      menu and allow you to move the highlighted option bar one
      option downwards each time the nemeric pad "2" key is
      pressed, the "esc" key will exit the menu.  The procedure
      achieves its speed by using direct screen ram access
      techniques, to place characters directly into screen ram.
         The procedure will only work correctly when used on pop
      and list menus.  Using it on other menu types will trash
      the screen and put incorrect values into that menus record
      possibly rendering it useless.
         Being a low level procedure no checks are carried out
      on any data passed to it by the user, so make sure the ID
      is for a valid open or closed pop or list menu only, not
      any other menu type or a window, the result of doing this
      will possibly corrupt the record or be unpredictable.
.PA 122
      ------------------------------------------------------------------------
      _MLBAR_BLST()                                              _MLBAR_BLST()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by LIST_MENU() to move the high-
      Low Level     lighted menu bar to the very last phisical option in the 
      list, in a list menu only. This last phisical option may or 
      may not be one of the visible options currently on the 
      screen, it will be the last visible option in the list menu
      after execution has completed.
      No action is taken if the highlighted menu bar is already
      at the last phisical option in the menu.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MLBAR_BLST(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than list menu the results could be
      unpredictable.

      Example     : program MLBAR_BLST;

      uses dos,crt,winmen;

      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
         cs : string[5];

      begin
         getmem(mt,15*81);             {*DYNAMICALY ALLOCATE   *}
         for c := 1 to 15 do           {*MEMORY AND DEFINE MENU*}
         begin                         {*OPTION STRINGS*}
     str(c,cs);
     mt^[c] := 'Menu Option '+cs;
         end;
         mt^[1] := 'Move Bar To End Of List';
         mt^[3] := 'Move Bar To End Of List';
         mt^[5] := 'Move Bar To End Of List';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,15);    {*CREATE MENU*}
         freemem(mt,15*81);            {*RECLAIM MEMORY AND  *}
           {*DELETE POINTER TO IT*}
.PA 123
         m1c := 20;
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := LIST_MENU(m1,15,10,5);
     case m1c of
        1,3,5 : _MLBAR_BLST(m1);{*MOVE OPT BAR TO LAST*}
     end;                       {*PHISICAL OPT IN LIST*}
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: Internal procedure called by LIST_MENU() to move the high-
      lighted menu bar to the very last phisical option in the 
      list, in a list menu only. This last phisical option may or 
      may not be one of the visible options currently on the 
      screen, it will be the last visible option in the list menu
      after execution has completed,with as many previous options
      above it as was specified by rows_vis in the call to
      LIST_MENU().
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on list
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open list menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.                    
      The example will move the highlighted menu option bar to
      the very last phisical option in the list each time options 
      1, 3 or 5 are chosen.
.PA 124
      ------------------------------------------------------------------------
      _MLBAR_END()                                                _MLBAR_END()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by LIST_MENU() to move the high-
      Low Level     lighted menu bar to the last/bottommost visible option in a
      list menu only.
      No action is taken if the highlighted menu bar is already
      at the last/bottommost visible option of the menu.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MLBAR_END(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than list menu the results could be
      unpredictable.

      Example     : program MLBAR_END;

      uses dos,crt,winmen;

      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1,c : byte;
         m1c : word;
         cs : string[5];

      begin
         getmem(mt,15*81);             {*DYNAMICALY ALLOCATE   *}
         for c := 1 to 15 do           {*MEMORY AND DEFINE MENU*}
         begin                         {*OPTION STRINGS        *}
     str(c,cs);
     mt^[c] :='Menu Option '+cs;
         end;
         mt^[1] := 'Move Bar To Last Visible Option';
         mt^[7] := 'Move Bar To Last Visible Option';
         mt^[12] := 'Move Bar To Last Visible Option';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,15);    {*CREATE MENU*}
         freemem(mt,15*81);            {*RECLAIM MEMORY AND  *}
.PA 125
         m1c := 20;                    {*DELETE POINTER TO IT*}
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := LIST_MENU(m1,15,10,5);
     case m1c of
        1,7,12 : _MLBAR_END(m1);{*MOVE OPTION BAR TO END*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure will move the highlighted menu option bar
      on a list menu only, to the last/bottommost visible option.
      If the bar is found to be at the bottommost visible option
      already, no action is taken.
      The last or bottommost visible option may not of course be
      the last phisical option in the list.
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on list
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open list menu only, not any other menu type or
      a window, the result of doing this will possibly corrupt
      the record or be unpredictable.
      The example will move the highlighted menu option bar to
      the last/bottommost visible option each time options 1, 7
      or 12 are chosen.
.PA 126
      ------------------------------------------------------------------------
      _MLBAR_HOME()                                              _MLBAR_HOME()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by LIST_MENU() to move the high-
      Low Level     lighted menu bar to the first/topmost visible option in a
      list menu only.
      No action is taken if the highlighted menu bar is already
      at the first/topmost visible option of the menu.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MLBAR_HOME(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than list menu the results could be
      unpredictable.

      Example     : program MLBAR_HOME;

      uses dos,crt,winmen;

      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1,c : byte;
         m1c : word;
         cs : string[5];

      begin
         getmem(mt,15*81);             {*DYNAMICALY ALLOCATE   *}
         for c := 1 to 15 do           {*MEMORY AND DEFINE MENU*}
         begin                         {*OPTION STRINGS        *}
     str(c,cs);
      mt^[c] :='Menu Option '+cs;
         end;
         mt^[4] := 'Move Bar To First Visible Option';
         mt^[8] := 'Move Bar To First Visible Option';
         mt^[15] := 'Move Bar To First Visible Option';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,15);    {*CREATE MENU*}
         freemem(mt,15*81);            {*RECLAIM MEMORY AND  *}
           {*DELETE POINTER TO IT*}
.PA 127
         m1c := 20;                    {*USE THE MENU*}
         while (m1c <> 0) do
         begin
     m1c := LIST_MENU(m1,15,10,5);
     case m1c of
        4,8,15 : _MLBAR_HOME(m1);{*MOVE OPT BAR TO TOP*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure will move the highlighted menu option bar
      on a list menu only, to the first/topmost visible option.
      If the bar is found to be at the topmost visible option
      already, no action is taken.
      The first or topmost visible option may not of course be
      the first phisical option in the list.
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on list
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open list menu only, not any other menu type or
      a window, the result of doing this will possibly corrupt
      the record or be unpredictable.
      The example will move the highlighted menu option bar to
      the first/topmost visible option each time options 4, 8
      or 15 are chosen.
.PA 128
      ------------------------------------------------------------------------
      _MLBAR_TLST()                                              _MLBAR_TLST()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by LIST_MENU() to move the high-
      Low Level     lighted menu bar to the very first phisical option in the
      list, in a list menu only. This first phisical option may
      or may not be one of the visible options currently on the
      screen, it will be the first visible option in the list
      menu after execution has completed.
      No action is taken if the highlighted menu bar is already
      at the first phisical option in the menu.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MLBAR_TLST(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than list menu the results could be
      unpredictable.

 Example   : program MLBAR_TLST;

      uses dos,crt,winmen;

      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
         cs : string[5];

      begin
         getmem(mt,15*81);             {*DYNAMICALY ALLOCATE   *}
         for c := 1 to 15 do           {*MEMORY AND DEFINE MENU*}
         begin                         {*OPTION STRINGS*}
     str(c,cs);
     mt^[c] := 'Menu Option '+cs;
         end;
         mt^[5] := 'Move Bar To Top Of List';
         mt^[8] := 'Move Bar To Top Of List';
         mt^[15] := 'Move Bar To Top Of List';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,15);    {*CREATE MENU*}
         freemem(mt,15*81);            {*RECLAIM MEMORY AND  *}
           {*DELETE POINTER TO IT*}
.PA 129
         m1c := 20;
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := LIST_MENU(m1,15,10,5);
     case m1c of
        5,8,15  : _MLBAR_TLST(m1);
     end;                       {*MOVE OPT BAR TO FIRST*}
         end;                          {*PHISICAL OPT IN LIST*}
         normvideo;
      end.

      Rules Notes
      Tips & Traps: Internal procedure called by LIST_MENU() to move the high-
      lighted menu bar to the very first phisical option in the
      list, in a list menu only. This first phisical option may
      or may not be one of the visible options currently on the
      screen, it will be the first visible option in the list
      menu after execution has completed, with as many subsequent
      options below it as was specified by rows_vis in the call
      to LIST_MENU().
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on list
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open list menu only, not any other menu type or
      a window, the result of doing this will possibly corrupt
      the record or be unpredictable.
      The example will move the highlighted menu option bar to
      the very first phisical option in the list each time 
      options 5, 8 or 15 are chosen.
.PA 130
      ------------------------------------------------------------------------
      _MLMOV_DO()                                                  _MLMOV_DO()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by LIST_MENU() to scroll or page
      Low Level     down through the options in a list menu, towards the bottom
      of the list, this procedure does not move the highlighted
      menu bar, it will remain in the same position.
      The scrolling can be either by a single menu option at a
      time or by a page at a time.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect any changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MLMOV_DO(ID,move);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than list menu the results could be
      unpredictable.

      move : char;
      This move character must be one of the four following
      characters 'S','s' or 'P','p' or its ascii value equivalent
      If 'S' is sent then the list will be scrolled upwards by
      one option only, moving us towards the end of the list,
      if the bottommost visible option is already the bottommost
      phisical option (end of list) then no action is taken.
      If 'P' is sent then the list will be paged upwards by the
      number of options visible on the screen, or the rows_vis
      value used in the call to LIST_MENU(), moving us towards
      the end of the list, if there is insufficient options left
      for a full page (rows left < rows_vis) then the remainder
      will be paged into view with the bottommost phisical option
      in the list becoming the bottommost visible option on the
      screen. if the bottommost visible option is already the
      bottommost phisical option (end of list) then no action is
      taken.
      If any character other than 'S' or 's' is recieved then it
      is assumed to be a 'P' or 'p', no checks are actually
      carried out to see if it is a 'P' or a 'p' so take care.

      Example     : program MLMOV_DU;

      uses dos,crt,winmen;
.PA 131
      var
         ID : pt_wmrec;               {*DEF POINTER TO WMREC*}
         mt : pt_mtext;               {*DEF OPTION TEXT PTR*}
         m1,key,c : byte;             {*DEF SUNDRY VARBS*}
         mid_row : word;
         cs : string[5];

      begin
         getmem(mt,25*81);            {*DYNAMICALY ALLOC   *}
         for c := 1 to 15 do          {*MEMORY AND DEFINE MENU*}
         begin                        {*OPTION STRINGS*}
     str(c,cs);
     mt^[c] := 'Menu Option '+cs;
         end;
         mt^[1] :=  'Key Pad 2 = Down';{*MEM AND DEFINE MENU*}
         mt^[25] := 'Key Pad 8 = Up'; {*OPTION STRINGS     *}
         m1 := NEW_MENU(1,1,2,3,4,15,mt,25); {*CREATE MENU*}
         freemem(mt,25*81);           {*RECLAIM TXT STRINGS MEM*}
         {**************************} {*AND DELETE POINTER*}
         ID := ID_WM[m1];             {*POINT ID TO m1 MENU REC*}
         ID^.rows_vis := 9            {*VISIBLE ROWS=9*}
         _MCHK_ON_SCREEN(m1,'MY_MENU',20,8,ID^.rows_vis-2);
         WMSAVE_SCR(m1,ID^.l,ID^.t,ID^.r,ID^.b); {*SAVE SCREEN*}
         _MDISPLAY(m1);               {*DISPLAY THE LIST MENU*}

         key := 0;                    {*INITIALISE KEY*}
         while (key <> 27) do         {*USE THE MENU*}
         begin                        {*WHILE ESC NOT PRESSED*}
     key := ord(readkey);      {*WAIT FOR A KEYPRESS*}
     mid_row := ID^.trow+ID^.rows_vis-1;
     mid_row := mid_row - (ID^.rows_vis div 2);
     case key of               {*CHECK SCAN CODE*}
        80 : begin
         if(ID^.row < mid_row) then
     _MPLBAR_DO(m1);       {*MOVE BAR DOWN*}
         else
     if(ID^.trow+ID^.rows_vis-1; <
              ID^.nrow-2) then
        _MLMOV_DO(m1,'S'); {*SCROLL LIST*}
     else
        if(ID^.row < ID^.nrow-2) then
           _MPLBAR_DO(m1); {*MOVE BAR DOWN*}
      end;
.PA 132
        72 : begin
         if(ID^.row > mid_row) then
     _MPLBAR_UP(m1);       {*MOVE BAR UP*}
         else
     if(ID^.trow > 1) then
        _MLMOV_UP(m1,'S'); {*SCROLL LIST*}
     else
        if(ID^.row > 1) then
           _MPLBAR_UP(m1); {*MOVE BAR UP*}
      end;
     end;
         end;
         WMREST_SCR(m1,ID^.l,ID^.t,ID^.r,ID^.b); {*RESTO SCREEN*}
         normvideo;
      end.
      {**************************}

      Rules Notes
      Tips & Traps: This procedure scrolls the options of a list menu up under
      the menu border, so that we are moving down towards the end
      of the phisical list.
      Move can be 'S' or 's' for scroll up one option, this does
      not preassume that the current row, ID^.row, is the bottom
      most option, thus allowing the options to scroll up with
      the highlighted bar anywhere on the menu, the menu bar is
      left in its original position.
      Move can be 'P' or 'p' for page up, ID^.rows_vis, scrolls
      up a page at a time, again no assumptions are made, thus
      allowing the options to scroll up with the highlighted bar
      anywhere on the menu, the menu bar is left in its original
      position.
      The above example code between the areas marked with {****}
      represents what would be the core of a new menu function
      possibly called LIST_MENU1(), as the above code is very
      similar in layout to the actual code used in the construc-
      tion of LIST_MENU(), POP_MENU() and BAR_MENU() functions.
      The only additional sections required would be, additions
      to the CASE statement constants to pick up perhaps pgup,
      pgdn, <ctrl>+pgup and <ctrl>+pgdn keystrokes, a section
      to detect if the character key corresponding to the first
      character in a menu option has been pressed, and a further
      combined section to detect Return, Esc and any other keys
      you may require, and to send back the number of the option
      chosen via the "LIST_MENU1 := option number" Equation, to
      the user.
      As can be seen extensive use must be made of the newly
      defined menus record variables, this is true in many of
      the cases where the low level procedures are used.
      It would be well worth your while to take time to study
      the above example, along with all the new predefined
      data types, constants, records and variables, all of which
      are described in another section of this reference guide.
.PA 133
      This particular example serves to demonstrate the following
      procedures:- _MPLBAR_DO(), _MPLBAR_UP(), _MLMOV_DO() and
      _MLMOV_UP(). A 25 option list type menu will be displayed
      the user will be able to scroll the highlighted menu option
      bar up and down, using the keypad 8 and 2 keys respectively
      when the bar reaches the middle visible option, from either
      direction, the list will be scrolled in the opposite
      direction, the menu bar will always appear on the middle
      visible option, untill the topmost visible option is the
      topmost phisical option, moving up, or the bottommost
      visible option is the bottommost phisical opotion, moving
      down, when this condition is reached the menu bar will once
      again move over the options towards either the top or
      bottom of the menu.
.PA 134
      ------------------------------------------------------------------------
      _MLMOV_UP()                                                  _MLMOV_UP()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by LIST_MENU() to scroll or page
      Low Level     up through the options in a list menu, towards the top of
      the list, this procedure does not move the highlighted menu
      bar, it will remain in the same position.
      The scrolling can be either by a single menu option at a
      time or by a page at a time.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect any changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MLMOV_UP(ID,move);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than list menu the results could be
      unpredictable.

      move : char;
      This move character must be one of the four following
      characters 'S','s' or 'P','p' or its ascii value equivalent
      If 'S' is sent then the list will be scrolled downwards by
      one option only, moving us towards the top of the list,
      if the topmost visible option is already the topmost
      phisical option (top of list) then no action is taken.
      If 'P' is sent then the list will be paged downwards by the
      number of options visible on the screen, or the rows_vis
      value used in the call to LIST_MENU(), moving us towards
      the top of the list, if there is insufficient options left
      for a full page (rows left < rows_vis) then the remainder
      will be paged into view with the topmost phisical option
      in the list becoming the topmost visible option on the
      screen. if the topmost visible option is already the
      topmost phisical option (top of list) then no action is
      taken.
      If any character other than 'S' or 's' is recieved then it
      is assumed to be a 'P' or 'p', no checks are actually
      carried out to see if it is a 'P' or a 'p' so take care.

      Example     : Please refer to the _MLMOV_DO() procedure for the example.

      Rules Notes
      Tips & Traps: This procedure scrolls the options of a list menu up under
      the menu border, so that we are moving down towards the end
      of the phisical list.
.PA 135
      Move can be 'S' or 's' for scroll up one option, this does
      not preassume that the current row, ID^.row, is the bottom
      most option, thus allowing the options to scroll up with
      the highlighted bar anywhere on the menu, the menu bar is
      left in its original position.
      Move can be 'P' or 'p' for page up, ID^.rows_vis, scrolls
      up a page at a time, again no assumptions are made, thus
      allowing the options to scroll up with the highlighted bar
      anywhere on the menu, the menu bar is left in its original
      position.
      Please also refer to the _MLMOV_DO() procedure for further
      rules, notes, tips and traps.
.PA 136
      ------------------------------------------------------------------------
      _MPBAR_END()                                                _MPBAR_END()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by POP_MENU() to move the high-
      Low Level     lighted menu bar to the last/bottom option in a pop menu.
      No action is taken if the highlighted menu bar is already
      at the bottom of the menu.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MPBAR_END(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than pop menu the results could be
      unpredictable.

      Example     : program MPBAR_END;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);              {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Move Bar To End';  {*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Move Bar To End';  {*OPTION STRINGS        *}
         mt^[3] := 'Move Bar To End';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);             {*RECLAIM MEMORY AND  *}
         m1c := 10;                    {*DELETE POINTER TO IT*}
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,15,10);
     case m1c of
        1..3 : _MPBAR_END(m1);  {*MOVE OPTION BAR TO END*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure will move the highlighted menu option bar
      on a pop menu only, to the bottom/last option. If the bar
      is found to be at the bottom already, no action is taken.
.PA 137
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on pop
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open pop menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.                    
      The example will move the highlighted menu option bar to
      the end option each time options 1..3 are chosen.
.PA 138
      ------------------------------------------------------------------------
      _MPBAR_HOME()                                              _MPBAR_HOME()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by POP_MENU() to move the high-
      Low Level     lighted menu bar to the top/first option in a pop menu.
      No action is taken if the highlighted menu bar is already
      at the top of the menu.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MPBAR_HOME(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than pop menu the results could be
      unpredictable.

      Example     : program MPBAR_HOME;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);              {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Move Bar To Home'; {*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Move Bar To Home'; {*OPTION STRINGS        *}
         mt^[3] := 'Move Bar To Home';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);             {*RECLAIM MEMORY AND  *}
         m1c := 10;                    {*DELETE POINTER TO IT*}
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,15,10);
     case m1c of
        1..3 : _MPBAR_HOME(m1); {*MOVE OPTION BAR HOME*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure will move the highlighted menu option bar
      on a pop menu only, to the top/first option. If the bar
      is found to be at the top already, no action is taken.
.PA 139
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on pop
      menus. Using it on other menu types will trash the screen
      and put incorrect values into that menus record, possibly
      rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open pop menu only, not any other menu type or 
      a window, the result of doing this will possibly corrupt 
      the record or be unpredictable.  
      The example will move the highlighted menu option bar to
      the home option each time options 1..3 are chosen.        
.PA 140
      ------------------------------------------------------------------------
      _MPLBAR_DO()                                                _MPLBAR_DO()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by POP_MENU() to move the high-
      Low Level     lighted menu bar down by one option in a pop menu.
      If the highlighted menu bar is at the bottom of the menu
      the highlighted menu bar will be looped to the topmost
      option.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MPLBAR_DO(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than pop or list the results could be
      unpredictable.

      Example     : program MPLBAR_DO;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);              {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Go Down 1 Option'; {*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Menu option 2';    {*OPTION STRINGS        *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);             {*RECLAIM MEMORY AND  *}
         m1c := 10;                    {*DELETE POINTER TO IT*}
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,15,10);
     case m1c of
        1..3 : _MPLBAR_DO(m1);  {*MOVE OPTION BAR DOWN 1*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure will move the highlighted menu option bar
      on a pop or list menu only, down by one option. If the bar
      is found to be at the bottomost option already, the bar 
      will be looped back up to the topmost option.
.PA 141
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on pop
      and list menus. Using it on other menu types will trash the 
      screen and put incorrect values into that menus record, 
      possibly rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open pop or list menu only, not any other menu 
      type or a window, the result of doing this will possibly
      corrupt the record or be unpredictable. 
      The example will move the highlighted menu option bar one
      option downwards each time options 1..3 are chosen.         
.PA 142
      ------------------------------------------------------------------------
      _MPLBAR_OFF()                                              _MPLBAR_OFF()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by _MPLBAR_UP(), _MPLBAR_DO(),
      Low Level     _MPBAR_HOME() and _MPBAR_END(), _MLBAR_HOME(), _MLBAR_END() 
      to turn off the highlighted menu bar in a pop or list menu.
      The menu ID must have been previously created by using
      NEW_MENU().                                               
      The menus record is not affected by this command.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MPLBAR_OFF(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().                                          
      If the ID does not exist or the ID belongs to a window or
      a menu type other than pop or list the results could be
      unpredictable.

      Example     : program MPLBAR_OFF;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                 {*DEF OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);               {*DYNAMICALY ALLOC   *}
         mt^[1] := 'Turn Bar Off';      {*MEM AND DEFINE MENU*}
         mt^[2] := 'Menu Option 2';     {*OPTION STRINGS     *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);              {*RECLAIM MEMORY AND  *}
         m1c := 10;                     {*DELETE POINTER TO IT*}
         while (m1c <> 0) do            {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,10,10);
     case m1c of
        1 : _MPLBAR_OFF(m1);       {*TURN OPTION BAR OFF*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure turns off a highlighted menu bar on a pop
      or list menu, by redrawing it in its normal attributes, 
      It does this by taking the character word, which is in
      screen ram format, directly from the mmenu word array, so 
      if any characters in the option have been changed using the
.PA 143
      MCHANGE_CHAR() procedure, they will still be displayed in
      there changed format, re:- the MCHANGE_CHAR() procedure.
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on pop
      and list menus. Using it on other menu types will trash 
      the screen and put incorrect values into that menus record 
      possibly rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open pop or list menu only, not any other menu 
      type or a window, the result of doing this will possibly 
      corrupt the record or be unpredictable.                   
      The example will turn off the highlighted menu option bar
      each time option 1 is chosen.                             
.PA 144
      ------------------------------------------------------------------------
      _MPLBAR_ON()                                                _MPLBAR_ON()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by _MPLBAR_UP(), _MPLBAR_DO(),
      Low Level     _MPBAR_HOME() and _MPBAR_END(), _MLMOV_UP(), _MLMOV_DO(),
      _MLBAR_TLST(), _MLBAR_BLST(), _MLBAR_HOME(), _MLBAR_END()  
      to turn on the highlighted menu bar in a pop or list menu.
      The menu ID must have been previously created by using
      NEW_MENU().                                               
      The menus record is not affected by this command.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MPLBAR_ON(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().                                          
      If the ID does not exist or the ID belongs to a window or
      a menu type other than pop or list the results could be
      unpredictable.

      Example     : program MBAR_ON;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                 {*DEF OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);               {*DYNAMICALY ALLOC   *}
         mt^[1] := 'Turn Bar On';       {*MEM AND DEFINE MENU*}
         mt^[2] := 'Bar Off';           {*OPTION STRINGS     *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);              {*RECLAIM MEMORY AND  *}
         m1c := 10;                     {*DELETE POINTER TO IT*}
         while (m1c <> 0) do            {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,10,10);
     case m1c of
        1 : _MPLBAR_ON(m1);      {*TURN OPTION BAR ON*}
        2 : _MPLBAR_OFF(m1);     {*TURN OPTION BAR OFF*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure turns on a highlighted menu bar on a pop
      or list menu, this is achieved by changing the screen ram
      character word's colour byte, using this ID's original 
.PA 145
      "tbc" and "tfc" complemented colours, which were calculated 
      from the specified "tb" and "tf" colours in the call to 
      NEW_MENU(), rather than using the colour values already in 
      the mmenu character word array and complementing them, as 
      they may be poor colours for complementing, provided the 
      original "tb" and "tf" colours were well chosen, this will 
      ensure that the highlighted option will always be visible, 
      regardles of how individual characters may have been 
      changed using the MCHANGE_CHAR() procedure.
      The pick (first) character will always appear in its
      originally defined colour "pc", even when highlighted,
      unless changed with MCHANGE_CHAR(). 
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on pop
      and list menus. Using it on other menu types will trash 
      the screen and put incorrect values into that menus record 
      possibly rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is
      for a valid open pop or list menu only, not any other menu 
      type or a window, the result of doing this will possibly 
      corrupt the record or be unpredictable.                           
      The example will turn on the highlighted menu option bar
      each time option 1 is chosen, and off when option 2 is                    
      chosen.                                                   
.PA 146
      ------------------------------------------------------------------------
      _MPLBAR_UP()                                                _MPLBAR_UP()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by POP_MENU() and LIST_MENU() to 
      Low Level     move the highlighted menu bar up by one option in a pop or
      list menu.
      If the highlighted menu bar is at the top of the menu the
      highlighted menu bar will be looped to the bottommost
      option.
      The menu ID must have been previously created by using
      NEW_MENU().
      The menus record is updated to reflect the changes.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _MPLBAR_UP(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not exist or the ID belongs to a window or
      a menu type other than pop or list the results could be
      unpredictable.

      Example     : program MPLBAR_UP;
      uses dos,crt,winmen;
      var
         mt : pt_mtext;                {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;
      begin
         getmem(mt,3*81);              {*DYNAMICALY ALLOCATE   *}
         mt^[1] := 'Go Up 1 Option';   {*MEMORY AND DEFINE MENU*}
         mt^[2] := 'Menu option 2';    {*OPTION STRINGS        *}
         mt^[3] := 'Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);             {*RECLAIM MEMORY AND  *}
         m1c := 10;                    {*DELETE POINTER TO IT*}
         while (m1c <> 0) do           {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,15,10);
     case m1c of
        1..3 : _MPLBAR_UP(m1);  {*MOVE OPTION BAR DOWN 1*}
     end;
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: This procedure will move the highlighted menu option bar
      on a pop or list menu only, up by one option. If the bar
      is found to be at the topmost option already, the bar will 
.PA 147
      be looped on down to the bottommost option.
      The procedure achieves its speed by using direct screen
      ram access techniques, to place characters directly into
      screen ram.
      The procedure will only work correctly when used on pop
      and list menus. Using it on other menu types will trash the 
      screen and put incorrect values into that menus record, 
      possibly rendering it useless.
      Being a low level procedure no checks are carried out on
      any data passed to it by the user, so make sure the ID is 
      for a valid open pop or list menu only, not any other menu 
      type or a window, the result of doing this will possibly
      corrupt the record or be unpredictable. 
      The example will move the highlighted menu option bar one
      option upwards each time options 1..3 are chosen.
.PA 148












        WINDOWS AND MENUS FOR TURBO PASCAL
        ----------------||----------------
        COMMON FUNCTIONS REFERENCE SECTION
.PA 149
      ------------------------------------------------------------------------
      _WMCHK_FOR_MREC()                                      _WMCHK_FOR_MREC()
      ------------------------------------------------------------------------ 

      Function    : Check to make sure that a maprec exists, for the object
      Low Level     specified by ID, by checking the 3D screen map at the
      map_inx location corresponding to the top left corner
      position of the object ID on the screen. 
         The function calculates map_inx its self, by using the 
      objects "l,t,r,b" values, from the objects wmrec.
         Only the linked list at location map_inx is checked, 
      this is sufficient, under normal circumstances, to prove 
      weather a complete maprec structure for the specified 
      object ID exists or not.
         The objects ID must have been previously created using
      NEW_WIN() or NEW_MENU().
         No checks are carried out on the validity of data 
      supplied to this low level function, so extra care is 
      needed.
         The objects wmrec is not affected by this function.  
         This function is called by:- WMADD_MREC_ALL(); 
      WMFLOAT(); WMDEL_MREC_ALL(); WMREMOVE(); WMSAVE_SCR();

      Syntax      : result := _WMCHK_FOR_MREC(ID)

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or 
      NEW_WIN() and as sent to WMSAVE_SCR(0-19), if it is a user 
      saved screen area.
         If the ID does not yet exist the results will be 
      unpredictable, as the ID is not checked.                   

      result : boolean;
      The function will return TRUE if a maprec does exist for
      the specified object,at the map_inx location corresponding 
      to the top left corner position of the the object ID on 
      the screen, and FALSE if not.

      Example     : procedure _DELRECS1(ID);                                    
        
      var                                                                       
         ncol,nrow : byte;                                             
         mapinx,map_inx,sc_inx : word;                                          
         IDWM : pt_wmrec;                                                       
        
      begin                                                                     
         if(_WMCHK_FOR_MREC(ID) = false) then
         begin                          {*CHK STRUCTURE EXISTS*}
     could print a message;
     exit or halt;
         end;
.PA 150
         IDWM := ID_WM[IDB];            {*GET POINTER TO WMREC*} 
         nrow := num rows in object     {*CALC ROWS & COLS*}     
         ncol := num cols in object      
         mapinx := top left char posn   {*CALC MAP ARRAY INDEX*} 
         sc_inx := 0;                   {*W/M SCREEN ARRAY INX*}

         for each row in object do      {*FOR EACH ROW IN OBJ*}
         begin                                                                  
     map_inx := mapinx;          {*UPDATE MAP COL INDEX*} 
     for each column in row do   {*FOR EACH COL IN ROW*}  
     begin                       {*ADD A RECORD TO MAP*}                        
        _WMDEL_MREC(ID,map_inx);  
        inc map_inx to next column{*INC MAP ROW INDEX*}    
        inc sc_inx to next column{*INC OBJ SCR ARY INX*}
     end;                        {*INC TO NEXT ROW*}                            
     inc mapinx to start of next row 
         end;                                                                   
      end {_DELRECS1};                                            

      Rules Notes
      Tips & Traps: The above example, in a mixture of Pascal and psuedo code,
      shows how to delete a complete maprec structure from the
      3D screen map, for any given object (window, menu or user 
      screen). 
         The example, in common with any of the Windows and 
      Menus low level routines, does not carry out any checking 
      on the parameters supplied to it by the user, so if, for 
      instance, an invalid object ID was supplied, there would 
      almost certainly be a fatal error during the 
      _WMCHK_FOR_MREC(); function, as pointer chasing is carried 
      out, this would result in the routine hanging the system.  
      This type of basic ID check, along with other checks, 
      would normaly be carried out in a high level type of 
      routine, prior to a low level call. This checking routine 
      does however provide the maprec creating and deleting 
      procedures with complete safety, provided it is passed a 
      valid ID. That is an ID for any OPEN menu, open or closed 
      window or user screen. As the wmrec "l,t,r,b" fields, 
      that it uses, become available at diferent times, 
      depending on the object type,(see section on wmrec types).
         As with any routine that uses pointers and allocates or 
      deallocates memory, as does _WMCHKL_FOR_MREC(); great care 
      needs to be taken and in particular checks needs to be 
      carried out prior to its use.
.PA 151
      ------------------------------------------------------------------------
      _WMSAVE_SCR()                                              _WMSAVE_SCR()
      ------------------------------------------------------------------------ 

      Function    : Save the area of screen bounded by the supplied l,t,r,b
      Low Level     values into an array of type "screen" and return a 
      pointer to it.  
         For details of the "screen" type, which is a Winmen 
      declared data type, see the section titled "Winmen 
      Declared Types", in chapter 2. 
         If there is insufficient contigious memory left to 
      allocate for the save, the programme will be aborted with 
      an error message, (see appendix for list).
         This procedure does not link the area of screen being
      saved into the 3D screen MAP[] system, if this is what you
      intened to do then please refer to the WMSAVE_SCR();
      procedure and the sub-section on "maprecs" in the "Winmen
      Declared Types" section.
         No other checks are carried out on the validity of data
      supplied to this low level function, so extra care is 
      needed.
         The objects wmrec is not affected by this function.  
         This function is called by:- WMMOVE(); and WMOPEN();

      Syntax      : my_area := _WMSAVE_SCR(l,r,t,b)

      Variables   : l : byte; (B1_80)
      Leftmost edge of the screen area to be saved, must be a 
      value between 1-80 or a range check error will occure.
 
                    t : byte; (B1_25)
      Topmost edge of the screen area to be saved, must be a 
      value between 1-25 or a range check error will occure.
 
                    r : byte; (B1_80)
      Rightmost edge of the screen area to be saved, must be a 
      value between 1-80 or a range check error will occure.

                    b : byte; (B1_25)
      Bottommost edge of the screen area to be saved, must be a 
      value between 1-25 or a range check error will occure.
        
      my_area : pt_scr;
                    This is the pointer value returned by the function and is
      the starting address of the block of memory allocated to
      hold the screen area bounded by the supplied l,t,r,b
      values. The area is stored in screen ram format.
         For a full explanation of the "pt_scr" type refer to
      section titled "Winmen Declared Types" in chapter one.
.PA 152
      Example     : program _WMSAVSC;                                           
        
      uses crt,dos,winmen;                                                      
        
      var                                                                       
         my_area : pt_scr;                                                      

      begin                             {*WRITE SOME TEXT*}                     
         clrscr;                                                                
         writeln('We will put a few lines of text');                            
         writeln('on the screen so we can copy the');                           
         writeln('screen to another position.');                                
         my_area := _WMSAVE_SCR(1,1,32,3);                                      
         gotoxy(1,9);                   {*SAVE SCREEN AREA*}                    
         write('<RET> key to copy area2 = area1, wid/hgt same....');            
         readln;                                                                
         _WMREST_SCR(my_area,35,19,66,21);                                      
         writeln;                       {*RESTOR SCREEN AREA*}                  
         write('<RET> key to copy area2 > area1....');                          
         readln;                                                                
         _WMREST_SCR(my_area,35,2,75,6);                                        
         writeln;                       {*RESTOR SCREEN AREA*}                  
         write('<RET> key to copy area2 = area1, wid/hgt not same....');        
         readln;                                                                
         _WMREST_SCR(my_area,4,18,15,25);                                       
         writeln;                       {*RESTOR SCREEN AREA*}                  
         write('<RET> key to continue....');                                    
         readln;                                                                
      end.                                                                      

      Rules Notes
      Tips & Traps: The example will effectively copy the area of screen
                    bounded by 1,1,32,3 to the new area 35,15,67,17. In this
      example the two areas have exactly the same width and
      height, it is important that the area you restore is the
      exact same size as the one you saved, although you could
      of course specify a diferent width and or height, as long
      as area1 = area2. If area2 is < area1 then no harm will
      come, you will just get less of the original text in the
      new position. As the "screen" array type that "pt_scr"
      points to is a linear array, it will be the later part of
      the text that will be missing. If area2 is > area1 the
      extra (area2-area1) that will be restored will just be
      whatever data exists in memory above the block reserved
      for the "my_area" screen array, probably rubbish.
.PA 153












       WINDOWS AND MENUS FOR TURBO PASCAL
       -----------------||----------------
       COMMON PROCEDURES REFERENCE SECTION
.PA 154
      ------------------------------------------------------------------------
      WMADD_MREC_ALL()                                        WMADD_MREC_ALL()
      ------------------------------------------------------------------------ 

      Procedure   : Add a complete maprec structure into the 3D screen map
      High Level    for the object specified by ID. This procedure adds a 
      new maprec, for each character position, in the object, 
      to the end of each appropriate linked list in the 3D
      screen map.
         If the object is closed, or a maprec already exists at
      the map_inx position corresponding to the top left hand
      corner of the open object on the screen, then no action is
      taken and the procedure exits, as closed objects are never
      linked into the Winmen 3D screen map system and if
      "maprecs" already exist for an object we do not want to
      double up, as this may prove disasterous.
         If there is insufficient contigious memory left to
      allocate for all the new maprec's required, for the object 
      specified by ID, then the programme will abort with an 
      error message (see appendix for list).
         The objects ID must have been previously created using 
      NEW_WIN() or NEW_MENU().
         The objects wmrec is not affected by this procedure
         This procedure is also called by:- WMMOVE(); 
      WMSAVE_SCR(); WOPEN(); 
         This procedure is NOT called by WMSLIDE(); refer to
      the WMSLIDE(); procedure for more information.

      Syntax      : WMADD_MREC_ALL(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or 
      NEW_WIN() and as sent to WMSAVE_SCR(0-19), if it is a user 
      saved screen area.
         If the ID does not yet exist the programme will abort 
      with an error message (see appendix for list).

      Example     : program ADD_MR_A;

      uses dos,crt,winmen;

      var
         w1,w2 : byte;
.PA 155
      begin                             {*DEFINE WINDOWS*}  
         w1 := NEW_WIN(1,8,25,14,1,7,0,0,3);   
         w2 := NEW_WIN(40,4,70,10,4,1,14,0,14);
         WOPEN(w1);                     {*OPEN WINDOWS*}
         WOPEN(w2);
         delay(750);
         WMDEL_MREC_ALL(w1);            {*DELETE ALL MRECS FOR*}
         WMSLIDE(w1,'r',30);            {*OBJECT BEFORE SLIDE*}
         WMADD_MREC_ALL(w1);            {*RE-CREATE AFTERWARDS*}
         delay(750);
         WMFLOAT(w2);                   {*FLOAT W2 TO PROVE   *}
         delay(750);                    {*THE MRECS WERE ADDED*}
         WMFLOAT(w1);                   {*PUT W1 BACK ON TOP*}
         delay(750);
         WMDELETE(w2);                  {*DELETE BOTH WINDOWS*}
         delay(750);
         WMDELETE(w1);
      end.

      Rules Notes
      Tips & Traps: The above example simply opens two windows then slides 
      the first one along to overlap the second one, the second 
      one is then floated to the top, then the first one is 
      floated to the top, the second one is then deleted, then 
      the first. 
         The purpose of all this is to demonstrate where the 
      WMADD_MREC_ALL(); and WMDEL_MREC_ALL(); procedures can or
      must be used.  Firstly, the WMSLIDE(); procedure does not 
      unlink, or relink, the object currently being slid, from 
      and to the 3D screen map, this is left to the user to do 
      manualy.
         The WMSLIDE(); procedure is the only routine that does
      not call either WMDEL_MREC_ALL(); or WMADD_MREC_ALL(); out 
      of all the routines that may need to do so.
         The WMSLIDE(); and WSELECT(); procedures are the only 
      routines that do not call WMFLOAT(); out of all the 
      routines that may need to do so.
         The reasons for this are as follows:-
         You may, for instance, want to slide an object in more 
      than one direction in two consecutive calls to WMSLIDE(); 
      if the WMSLIDE();procedure were to delete and add maprec's 
      and call WMFLOAT(); for you, each time it was called, we 
      would get a time lag at the start and end of each call, 
      while the object was floated and maprec's were deleted 
      from the objects old position and then added to the 
      objects new position in the 3D screen map, although this 
      lag is only very very small, it is enough to introduce a 
      jerk into what is otherwise a perfectly smooth sliding 
      action.  Dont forget we only want the object, currently 
      being slid, to be linked into the 3D screen map in its 
      final resting place.  All other maprec's belonging to that 
      object must be deleted, if they are not they will cause 
      unpredictable behavior, possibly resulting in a trashed 
      screen, or even a crash.
.PA 156
         On the other hand you may want to write to more than 
      one window at once, or at least, make it appear as if you 
      are. This can only be done by first selecting the window 
      to write to, using WSELECT(); then actualy writing to it. 
      If the WSELECT(); procedure had to call WMFLOAT() each 
      time it was called, this would slow things down 
      considerably and spoil our illusion, besides the windows 
      that you are writing to may all be fully visible, in 
      which case there is no need to float them to the surface 
      before writing to them. if they are obscured and you dont
      want to make use of the WWRITE(); or WWRITELN(); 
      procedures, to write text to a window in the background, 
      then you will have to call WMFLOAT(); either before or 
      after calling WSELECT(); and prior to writing to each 
      window. So as you can see the choice is best left up to 
      the user, for maximum flexibility.
.PA 157
      ------------------------------------------------------------------------
      WMATTRIBS()                                                  WMATTRIBS()
      ------------------------------------------------------------------------

      Procedure   : Change the attributes associated with a window, menu or a
      user saved screen area ID (saved using WMSAVE_SCR()), to 
      those in the supplied list. The change will be permenant or 
      untill changed again using this procedure or WCOLOUR() for 
      a window or user saved screen or _WMBOX_ATTRIBS(), for a 
      window or menu.  
      The window or menu ID must have been previously created by 
      using NEW_WINDOW() or NEW_MENU().
      The window or menus record is updated to reflect the 
      changes.        

      Syntax      : WMATTRIBS(ID,btype,bb,bf,tb,tf);

      Variables   : ID : byte;
      The unique identifier for the specified menu as returned
      from NEW_MENU().
      If the ID does not yet exist the programme will abort with 
      an error message (see appendix for list).

      btype : byte; (B1_4)
      Type of border required around the menu edge, must be a
      value between 1-4 or a range check error will occure.
      The possible border types are as follows:-
         1 = Double horizontal and double vertical bars.
         2 = Single horizontal and single vertical bars.
         3 = Double horizontal and single vertical bars.
         4 = Single horizontal and double vertical bars.

      bb : byte; (B0_15)
      Border background colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      bf : byte; (B0_15)
      Border foregroung colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      tb : byte; (B0_15)
      Text background colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      tf : byte; (B0_15)
      Text foreground colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).
.PA 158
       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink

      Example     : program WMATTRIB;

      uses dos,crt,winmen;

      var
         mt : pt_mtext;              {*DEFINE OPTION TEXT PTR*}
         m1 : byte;
         m1c : word;

      begin
         getmem(mt,3*81);                 {*DYNAMICALY ALLOC   *}
         mt^[1] := '1) Change Attribs';   {*MEMORY AND DEF MENU*}
         mt^[2] := '2) Change Pic Colour';{*OPTION STRINGS     *}
         mt^[3] := '3) Menu Option 3';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,3); {*CREATE MENU*}
         freemem(mt,3*81);           {*RECLAIM MEMORY AND  *}
         m1c := 10;                  {*DELETE POINTER TO IT*}
         while (m1c <> 0) do         {*USE THE MENU*}
         begin
     m1c := POP_MENU(m1,5,5);
     case m1c of              {*RANDOMLY CHANGE ATTRIBS*}
        1 : WMATTRIBS(m1,random(4),random(15),
           random(15)random(15),random(15));
        2 : WMPIC_COL(m1,random(15));
     end;                     {*RANDOMLY CHANGE PIC COL*}
     WMCLOSE(m1);
         end;
         normvideo;
      end.

      Rules Notes
      Tips & Traps: The attributes changeable by this procedure are the border
      type, border and text background and foreground colours.
      The menu option pic character (first character) can be
.PA 159
      changed using the MPIC_COL() procedure.
      Both the window or menus record variables are updated to
      reflect the changes, all the colour attributes are changed
      bb,bf,tb,tf, and the calculated values tbc and tfc.
      If the ID belongs to a menu then the menus mmenu screen ram
      format array is also updated with the changes.
      Any changes made will become effective:-
      a) The next time a window is written to,if it is the active
         one, for tb and tf.
      b) The next time a window is selected for use, and written
         to, if it is a non-active one, for tb and tf.
      c) The next time a window or menu is opened for use, for
         btype,bb,bf,tb,tf and the calculated values tbc and tfc.
      This procedure may also be used on user saved screen areas
      (saved using WMSAVE_SCR()), although only the tb and tf
      values will have any affect, as there is no border. Again
      the changes will become effective the next time that ID
      screen area is written to.
      Any window, menu or user screen area, open, closed, active 
      or not active can have its attributes changed ready for the
      next time it is used.
.PA 160
      ------------------------------------------------------------------------
      WMCLOSE()                                                      WMCLOSE()
      ------------------------------------------------------------------------

      Procedure   : Close the window, menu or user saved screen area,
      High Level    associated with ID, and restore the screen behind it, such
      that it is visually correct, regardless of weather the
      object in question is partially, or fully obscured, or
      not obscured at all, by other objects.
         This system switch gives the user two diferent methods
      of closing objects and removing them from the screen, they
      are:-

         CLOSE_WITH_REMOVE := false; All objects will be closed
         by firstly floating them to the surface with WMFLOAT()
         and then restoring the objects ID_SC[] background
         screen array using WMREST_SCR(); to effectively remove
         them.
         CLOSE_WITH_REMOVE := true; All objects will be closed
         by using the WMREMOVE(); procedure, this makes it
         appear as if the object is being removed from the back
         of the screen, instead of from the front, as the other
         method does.
         CLOSE_WITH_REMOVE := false; Default.

         Any object, that for some reason, does not have any
      "maprecs" linking it into the Winmen 3D screen map system,
      can not be WMFLOATed or WMREMOVEd, depending on the system
      switch above, as both these routines will exit, with no
      action, if no "maprecs" exist for the object in question.
      The object will however still be closed, by WMCLOSE(); but
      using just the WMREST_SCR(); procedure as its main effort.
         WARNING... If the WMCLOSE() procedure reveals that no
      "maprecs" exist, for the object, at the appropriate 3D
      screen map locations, then it is forced to make the
      assumption that the object is not obscured by any other
      object, as no other facts are available to it, so a close
      by using WMREST_SCR(); to restore the objects ID_SC[]
      background screen array should be sufficient. If the
      object is obscured by others, either partially or fully,
      then you can expect some display corruption at some stage,
      possibly sooner rather than later.
         After closing, the window or user screens text cursor,
      used by the WWRITE(); and WWRITELN(); procedures only, is
      positioned at x1,y1 which is the top left most corner of a
      user screen and excluding the border for a window.
         The Winmen window/user text cursor can be repositioned
      using the WGOTOXY(); procedure, it has no effect at all on
      the Turbo Pascal text cursor, which is used with the
      standard Turbo Pascal write(); and read(); procedures and
      positioned with gotoxy().
.PA 161
         The window, menu or user screen ID must have been
      previously created by using NEW_WINDOW(), NEW_MENU() or
      WMSAVE_SCR(); respectively and opened for use with one of
      the appropriate window/user or menu functions and/or
      procedures.
         This procedure is also called by WMDELETE().
         The window, menu or user screen record is updated to
      reflect the changes.

      Syntax      : WMCLOSE(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a 
      user saved screen area.
      If the ID does not yet exist the programme will abort with 
      an error message (see appendix for list).                 

      Example     : program WM_CLOSE;

      uses dos,crt,winmen;

      var
         ID,c,cpx,cpy : byte;     {*DEFINE VARIABLES*}
         W1 : pt_wmrec;

      begin                       {*CREATE WINDOW*}  
         ID := NEW_WIN(1,1,15,5,1,1,14,7,0); 
         W1 := ID_WM[ID];
         for c := 1 to 20 do      {*MOVE WINDOW DIAGONALLY*}
         begin
     WOPEN(ID);
     delay(250);           {*DELAY TO SMOOTH OUT*}
     WMCLOSE(ID);          {*INCREMENT WINDOW PARAMETERS*}
     inc(W1^.l,3);         {*IN WINDOW RECORD, ALL THESE*}           
     inc(W1^.r,3);         {*NEED TO BE MODIFIED TO MOVE*}           
     inc(W1^.t);           {*A WINDOWS POSITION AND THEN*}           
     inc(W1^.b);           {*OPEN IT IN ANOTHER POSITION*}           

     cpx := ((W1^.r-W1^.l) div 2)+W1^.l;   
     cpy := ((W1^.b-W1^.t) div 2)+W1^.t;
     W1^.cp := ((((cpy-1)*80)+cpx)*2)-2;
     W1^.lt := cpx-1;
     W1^.tt := cpy-1;
     W1^.rt := cpx+1;
     W1^.bt := cpy+1;
         end;
         WMDELETE(ID);            {*DELETE THE WINDOW REC ETC*}
         normvideo;
      end.                                                        
.PA 162
      Rules Notes
      Tips & Traps: Closing a non active window does not affect the currency
      of the active window, for example if the currently active
      window is closed then the new active window effectively
      becomes the full screen, so any write operations will now
      go to the normal full screen, if a non active window is
      closed the active window remains the one currently in use.
         Closing a window, menu or user saved screen area is not
      the same as deleting it, closing it does not release any
      of the memory being used by it, ie. The objects "wmrec"
      record, background "screen" array, "mmenu" array, or a bar
      menus ID_MC[] comment array, if one exists, everything is
      left intact, including the "ID", which is the only link
      the user has with that particular window, menu or user
      saved screen area, this is so that it may be used again.
         If you are finished with that window, menu or user
      saved screen area for good, then to reclaim the memory
      space occupied by it you will need to use the WMDELETE()
      procedure.
         However, closing an object will result in it being
      completely removed from the 3D screen map system, as the
      3D screen map is only interested in objects that are
      open on the screen. This is done by deleting all of the
      objects "maprec" records from the 3D screen map, if for
      some reason no "maprecs" exist for the object in
      question then no harm will come, as WMCLOSE(); checks
      this first and makes no attempt to delete "maprecs" that
      do not exist. For more detailed information on the 3D
      screen map system, refer to the "maprec" type in the
      section "Winmen Declared Types".
         The example will open, close and move the window, then
      repeat the cycle 20 times, moving the window diagonally,
      this shows how you could construct a MOVE type procedure.
         Any window, menu or USER saved screen area that has a
      title will loose the title when it is closed using the
      WMCLOSE() procedure, this is so that you can have a
      titleless window menu or USER saved screen, each time it
      is opened, of course there are thoes that will say the
      titles ought to be retained, for windows and USER saved
      screens this is not possible as they are generated each
      time they are opened, they do not have a permenant array
      like the mmenu array for menus, and to have one would take
      up much more valuable memory space, so there is nowhere to
      put the title, menus could conceivably retain there titles
      by removing the code which deletes them from the WMCLOSE()
      procedure, the only problem then would be deleting a title
      if one were not required, we would need to send a string
      of that menus border type chars each time we wanted to
      delete a title.
.PA 163
      ------------------------------------------------------------------------
      WMDELETE()                                                    WMDELETE()
      ------------------------------------------------------------------------

      Procedure   : Delete a window, menu or user saved screen area (saved
      High Level    using WMSAVE_SCR();), by returning all memory allocated
      to that particular ID to the heap and NILing all the 
      pointers associated with it. This allows that particular
      ID reference to be reused, in a subsequent call to either
      NEW_WIN(); NEW_MENU(); or WMSAVE_SCR(0-19);
         Deleting the window, menu or user saved screen area,
      associated with ID, will restore the screen behind it,
      such that it is visually correct, regardless of weather
      the object in question is partially, or fully obscured, or
      not obscured at all, by other objects.
         If the window, menu or user saved screen area is open
      it will first be closed with WMCLOSE();
         This system switch gives the user two diferent methods
      of deleting objects and removing them from the screen, they
      are:-

         CLOSE_WITH_REMOVE := false; All objects will be deleted
         by firstly floating them to the surface and then
         restoring the objects ID_SC[] background screen array,
         to effectively remove them.
         CLOSE_WITH_REMOVE := true; All objects will be deleted
         by firstly using the WMREMOVE(); procedure, this makes it
         appear as if the object is being removed from the back
         of the screen, instead of from the front, as the other
         method does.
         CLOSE_WITH_REMOVE := false; Default.

         Please refer to the WMCLOSE(); procedure and the
      section "Winmen Declared Types" for more information.
         The ID returned from NEW_WIN(); and NEW_MENU(); or the
      one used in the calls to WMSAVE_SCR(0-19); is unlinked
      from its memory area pointers, thus a structure such as
      this once deleted is not recoverable, so use with care.
         Deleting an object will result in it being completely
      removed from the 3D screen map system, as the 3D screen
      map is only interested in objects that are open on the
      screen.  This is done by deleting all of the objects
      "maprec" records from the 3D screen map, if for some
      reason no "maprecs" exist for the object in question then
      no harm will come, as WMDELETE(); checks this first and
      makes no attempt to delete "maprecs" that do not exist.
      For more detailed information on the 3D screen map system,
      refer to the "maprec" type in the section "Winmen Declared
      Types".
         If the ID does not yet exist the programme will abort
      with an error message (see appendix for list).
         This procedure is not called by any of the functions
      or procedures in the Winmen Unit.
.PA 164
      Syntax      : WMDELETE(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a 
      user saved screen area.
      If the ID does not yet exist the programme will abort with 
      an error message (see appendix for list).                 

      Example     : program WM_DELETE;
      uses dos,crt,winmen;
      var
         ID : byte;
      begin
         ID := NEW_WIN(5,5,50,20,1,1,14,7,0);
         WOPEN(ID);
         write(scr,'Press Return To DELETE The Window');
         readln;
         WMDELETE(ID);
         normvideo;
      end.   

      Rules Notes
      Tips & traps: If you find you have run out of available window/menu ID
      references, and have windows or menus that you no longer
      require, you should use WMDELETE() to reclaim all that
      valuable memory, this will also free up that ID, allowing
      you to once again create another window or menu, using
      NEW_WINDOW() or NEW_MENU(). Although this is a most
      unlikely situation, as you will almost certainly run out
      of memory before ID's, depending on the size of each
      window or menu.
         The example just creates a window then deletes it when
      the user presses the return key.
.PA 165
      ------------------------------------------------------------------------
      WMDEL_MREC_ALL()                                        WMDEL_MREC_ALL()
      ------------------------------------------------------------------------ 

      Procedure   : Delete a complete maprec structure from the 3D screen map
      High Level    for the object specified by ID. This procedure deletes a 
      maprec, for each character position, in the object, from 
      the end of each appropriate linked list in the 3D screen 
      map. 
         If the object is closed, or no maprec exists at the 
      map_inx position corresponding to the top left hand corner 
      of the object on the screen, then no action is taken and 
      the procedure exits.
         Once all the maprec's, for the specified object, have 
      been deleted, the memory occupied by them is returned to 
      the heap for reuse.  
         The objects ID must have been previously created using 
      NEW_WIN() or NEW_MENU().
         The objects wmrec is not affected by this procedure
         This procedure is also called by:- WMMOVE(); WMCLOSE(); 
      WMREMOVE();
         This procedure is NOT called by WMSLIDE();

      Syntax      : WMDEL_MREC_ALL(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or 
      NEW_WIN() and as sent to WMSAVE_SCR(0-19), if it is a user 
      saved screen area.
         If the ID does not yet exist the programme will abort 
      with an error message (see appendix for list).

      Example     : program DEL_MR_A;

      uses dos,crt,winmen;

      var
         w1,w2 : byte;
.PA 166
      begin                             {*DEFINE WINDOWS*}  
         w1 := NEW_WIN(1,8,25,14,1,7,0,0,3);   
         w2 := NEW_WIN(40,4,70,10,4,1,14,0,14);
         WOPEN(w1);                     {*OPEN WINDOWS*}
         WOPEN(w2);
         delay(750);
         WMDEL_MREC_ALL(w1);            {*DELETE ALL MRECS FOR*}
         WMSLIDE(w1,'r',30);            {*OBJECT BEFORE SLIDE*}
         WMADD_MREC_ALL(w1);            {*RE-CREATE AFTERWARDS*}
         delay(750);
         WMFLOAT(w2);                   {*FLOAT W2 TO PROVE   *}
         delay(750);                    {*THE MRECS WERE ADDED*}
         WMFLOAT(w1);                   {*PUT W1 BACK ON TOP*}
         delay(750);
         WMDELETE(w2);                  {*DELETE BOTH WINDOWS*}
         delay(750);
         WMDELETE(w1);
      end.

      Rules Notes
      Tips & Traps: The above example simply opens two windows then slides 
      the first one along to overlap the second one, the second 
      one is then floated to the top, then the first one is 
      floated to the top, the second one is then deleted, then 
      the first. 
         The purpose of all this is to demonstrate where the 
      WMADD_MREC_ALL(); and WMDEL_MREC_ALL(); procedures can or
      must be used.  Firstly, the WMSLIDE(); procedure does not 
      unlink, or relink, the object currently being slid, from 
      and to the 3D screen map, this is left to the user to do 
      manualy.
         The WMSLIDE(); procedure is the only routine that does 
      not call either WMDEL_MREC_ALL(); or WMADD_MREC_ALL(); out 
      of all the routines that may need to do so.
         The WMSLIDE(); and WSELECT(); procedures are the only 
      routines that do not call WMFLOAT(); out of all the 
      routines that may need to do so.
         The reasons for this are as follows:-
         You may, for instance, want to slide an object in more 
      than one direction in two consecutive calls to WMSLIDE(); 
      if the WMSLIDE();procedure were to delete and add maprec's 
      and call WMFLOAT(); for you, each time it was called, we 
      would get a time lag at the start and end of each call, 
      while the object was floated and maprec's were deleted 
      from the objects old position and then added to the 
      objects new position in the 3D screen map, although this 
      lag is only very very small, it is enough to introduce a 
      jerk into what is otherwise a perfectly smooth sliding 
      action.  Dont forget we only want the object, currently 
      being slid, to be linked into the 3D screen map in its 
      final resting place.  All other maprec's belonging to that 
      object must be deleted, if they are not they will cause 
      unpredictable behavior, possibly resulting in a trashed 
      screen, or even a crash.
.PA 167
         On the other hand you may want to write to more than 
      one window at once, or at least, make it appear as if you 
      are. This can only be done by first selecting the window 
      to write to, using WSELECT(); then actualy writing to it. 
      If the WSELECT(); procedure had to call WMFLOAT() each 
      time it was called, this would slow things down 
      considerably and spoil our illusion, besides the windows 
      that you are writing to may all be fully visible, in 
      which case there is no need to float them to the surface 
      before writing to them.  if they are obscured and you dont 
      want to make use of the WWRITE(); or WWRITELN(); 
      procedures, to write text to a window in the background, 
      then you will have to call WMFLOAT(); either before or 
      after calling WSELECT(); and prior to writing to each 
      window. So as you can see the choice is best left up to 
      the user, for maximum flexibility.    
.PA 168
      ------------------------------------------------------------------------
      WMFLOAT()                                                      WMFLOAT()
      ------------------------------------------------------------------------ 

      Procedure   : Bring to the surface, by floating, the object specified 
      High Level    by ID. 
         By bringing to the surface we mean, put that object on 
      top of the pile and make it wholy visible to the user, 
      this includes any contents that a window or user screen 
      might have had.
         If the object is closed and it is a window or user 
      screen (ID < 20) then it will be opened, there will be no 
      need to float it, the objects wmrec will be updated to 
      reflect the change.
         If the object is closed and it is a menu, the 
      programme will be halted with an error message, as menus 
      can not be opened from within the WMFLOAT(); procedure 
      (see appendix for list).  
         If no maprec exists for the object at the map_inx 
      position corresponding to the top left hand corner of the 
      object on the screen, then no action is taken and the 
      procedure exits, it will not be floated.
         The objects ID must have been previously created using 
      NEW_WIN() or NEW_MENU().
         The objects wmrec is not affected by this procedure
         This procedure is also called by:- WMMOVE(); WMCLOSE(); 
         This procedure is NOT called by WMSLIDE();              

      Syntax      : WMFLOAT(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or 
      NEW_WIN() and as sent to WMSAVE_SCR(0-19), if it is a user 
      saved screen area.
         If the ID does not yet exist the programme will abort 
      with an error message (see appendix for list).             

      Example     : program WM_FLOAT;

      uses dos,crt,winmen;

      var
         w1,w2,w3,w4,w5,w6,c,c1 : byte;

      begin                             {*DEFINE WINDOWS*}
         w1 := NEW_WIN(1,8,25,14,1,7,0,2,3);   
         w2 := NEW_WIN(40,4,70,10,4,1,14,3,14);                   
         w3 := NEW_WIN(10,6,48,17,2,2,3,1,9);                     
         w4 := NEW_WIN(28,14,65,23,3,4,6,2,11);                     
         w5 := NEW_WIN(1,16,40,25,1,0,15,0,13);                     
         w6 := NEW_WIN(15,15,68,18,2,4,1,5,1);                     
.PA 169
         for c := w1 to w6 do           {*OPEN WINDOWS*}
     WOPEN(c);

         for c := w1 to w6 do           {*FLOAT & WRITE*}
         begin
     WMFLOAT(c);
     WSELECT(c);
     write('This Window Is Well And Truely Open');
     delay(750);
         end;

         for c := 1 to 10 do            {*FLOAT WINDOWS*}
     for c1 := w1 to w6 do
     begin
        WMFLOAT(c1);
        delay(750);
     end;

         for c := w1 to w6 do           {*DELETE WINDOWS*} 
         begin                                             
     WMDELETE(c);                                   
     delay(750);                                    
         end;                                              
      end.                                                 

      Rules Notes
      Tips & Traps: The above example demonstrates the power of the 
      WMFLOAT(); procedure, by showing just how simple it is 
      to use it.      
         You should not experience any problems when using 
      this procedure, as it has been tried and tested over a 
      long period.  
         Being a high level routine numerous checks are carried 
      out on any parameters and data supplied to, or used by, 
      this routine.  The checking is carried out by the Winmen 
      Unit its self and include all the necassary checks on the 
      3D screen map, to ensure its integrity.  This procedure, 
      as with all other high level routines, in the Winmen Unit, 
      also benifits from the Winmen Units memory management 
      techniques, which are explained more fully in the 
      sections titled "Winmen Declared Types" and "Winmen 
      Declared Global Variables". 
         For the more technicaly minded, the paragraphs below 
      may be of interest. 
         Firstly, the WMSLIDE(); procedure does not unlink, or 
      relink, the object currently being slid, from and to the 
      3D screen map, this is left to the user to do manualy.
         The WMSLIDE(); procedure is the only routine that does 
      not call either WMDEL_MREC_ALL(); or WMADD_MREC_ALL(); out 
      of all the routines that may need to do so.
         The WMSLIDE(); and WSELECT(); procedures are the only 
      routines that do not call WMFLOAT(); out of all the 
      routines that may need to do so.
.PA 170
         The reasons for this are as follows:-
         You may, for instance, want to slide an object in more 
      than one direction in two consecutive calls to WMSLIDE(); 
      if the WMSLIDE();procedure were to delete and add maprec's 
      and call WMFLOAT(); for you, each time it was called, we 
      would get a time lag at the start and end of each call, 
      while the object was floated and maprec's were deleted 
      from the objects old position and then added to the 
      objects new position in the 3D screen map, although this 
      lag is only very very small, it is enough to introduce a 
      jerk into what is otherwise a perfectly smooth sliding 
      action.  Dont forget we only want the object, currently 
      being slid, to be linked into the 3D screen map in its 
      final resting place.  All other maprec's belonging to that 
      object must be deleted, if they are not they will cause 
      unpredictable behavior, possibly resulting in a trashed 
      screen, or even a crash.
         On the other hand you may want to write to more than 
      one window at once, or at least, make it appear as if you 
      are. This can only be done by first selecting the window 
      to write to, using WSELECT(); then actualy writing to it. 
      If the WSELECT(); procedure had to call WMFLOAT() each 
      time it was called, this would slow things down 
      considerably and spoil our illusion, besides the windows 
      that you are writing to may all be fully visible, in 
      which case there is no need to float them to the surface 
      before writing to them.  if they are obscured and you dont 
      want to make use of the WWRITE(); or WWRITELN(); 
      procedures, to write text to a window in the background, 
      then you will have to call WMFLOAT(); either before or 
      after calling WSELECT(); and prior to writing to each 
      window. So as you can see the choice is best left up to 
      the user, for maximum flexibility.                         
.PA 171
      ------------------------------------------------------------------------
      WMMEM_STAT()                                                WMMEM_STAT()
      ------------------------------------------------------------------------

      Procedure   : Write out the current status of the heap and free list
      High Level    areas of memory or heap and free block records (depending
      on the version of Turbo Pascal being used), which are
      being managed by the Turbo Pascal heap manager routines,
      to the file assigned to the file type variable "handle".
         See below for a sample and explanation of the output
      that is generated. (usefull for debugging purposes)
         This procedure does not affect any of the window or
      menu record variables.
         This procedure is not called by any of the functions
      or procedures in the Winmen Unit.

      Syntax      : WMMEM_STAT(header,handle,r_a);

      Variables   : header : string; 
      This is a string which will be the first thing to be writen
      to the file assigned to the file variable "handle" when
      the WMMEM_STAT() procedure is called.
      The maximum string length is 255 characters, if the string
      exceeds this length, a range check error will occure.
      For your convienience there is a Winmen predefined string
      variable for this purpose, called 'comment', this is the
      string that the Winmen unit uses each time it writes to
      the Memory.Log file using the predefined file variable 
      'log', see below.

      handle : file;
      This is a file type variable, which firstly needs to be
      assigned to a particular filename, using the assign()
      procedure, if "handle" is not first assigned or a dos 
      file error occures, the programme will abort with an error
      message (see appendix for list).
      The units predefined file variable "log" may also be used,
      there is no need to assign this variable as this is done
      automatically when the windows and menus unit is called up
      in a uses clause. The file that "log" is assigned to is
      called "Memory.Log".  If the predefined file variable "log"
      is used then output goes to the "Memory.Log" file, to which 
      all the units memory information goes each time the unit is
      called up from a uses clause and if any memory allocation
      errors occure during the running of the programme, if you 
      do it this way all the information is kept in one place,
      otherwise it is split between the "Memory.Log" file and
      your own file.
.PA 172
      r_a : char;
      This character defines weather the output file is to be
      created/truncated or appended to, it can only have one of
      four values:- 'R','r','A' or 'a', if any other value is
      sent the programme will abort with an error message (see
      apendix for list). If 'R' or 'r' is sent the filename 
      assigned to "handle" will be created, or if it already
      exists as a file it will be truncated. If 'A' or 'a' is 
      sent the file assigned to "handle" will be appended to,
      or created if it does not yet exist.
.PA 173
      Example     : program MEM_STAT;
  
      uses dos,crt,winmen;
  
      var
        ID,l,t,r,b : byte;             {*WINDOW PARAMETERS*}
        wind : array[1..50]of byte;    {*ARRAY TO HOLD IDs*}
        num : shortint;                {*ARRAY INDEX*}
      
      begin
         textbackground(0);
         clrscr;
         num := 0;
           {*WHILE A KEY NO PRESS*}
         while not(keypressed)and(num < 50)  do
         begin
     l := 90; t := 90; r := 90; b := 90;
     while(r-l < 3)or(l = 0) do {*MIN WINDOW WIDTH*}
     begin
        l := random(80);
        r := random(80);
     end;
     while(b-t < 3)or(t = 0) do {*MIN WINDOW HEIGHT*}
     begin
        t := random(25);
        b := random(25);
     end;
     ID := NEW_WIN(l,t,r,b,1,1,14,7,0);              
     inc(num);                  {*CREATE THE WINDOW*}                   
     wind[num] := ID;           {*INCREMENT INDEX*}                     
     gotoxy(1,1);               {*SAVE ID TO ARRAY*} 
     textbackground(7);
     clreol;
     write(scr,'Window ID=',wind[num],' l=',l,' t=',t,
        ' r=',r,' b=',b,' ',r-l,'x',b-t);
     WOPEN(ID);                 {*OPEN THE WINDOW*}
     WMCLOSE(ID);               {*CLOSE THE WINDOW*}
         end {while};
    
         while(num > 3) do             {*WHILE ARRAY INDEX IS*}
         begin                         {*GREATER THAN 1      *}
     dec(num,2);                {*DEC INX BY 2 TO MAKE*}
     WMDELETE(wind[num]);       {*HOLE IN HEAP AREA AND*}
         end;                          {*DELETE WINDOW        *}
  
         WMMEM_STAT('**Memory Condition At Abort**',log,'a');
         normvideo;
      end.                             {*WRITE MEMORY CONDITION*}
.PA 174
      Turbo Pascal
      Version Note: The freelist records for TP V5.X used to be grouped
      together in 8 byte records, starting at heapend and
      growing downwards into free memory, as each new record was
      added, the system variable Freeptr used to point to the
      end of the list, each record was made up of two fields,
      blk_org and blk_end, each one was a normalised pointer
      value, giving the starting and ending addresses of each
      free block in the programmes heap, a free block, or hole,
      is created in the heap each time a dynamic variable is
      disposed of using either freemem() or the dispose()
      procedures, the freelist records keep track of all these
      blocks.
         With the freelist records being in a block, on there
      own, at the top of ram, made them extremley dificult to
      overwrite.
         The freelist records for TP V6.0 are no longer grouped
      together, they are now each attached to there respective
      free blocks of ram in the programmes heap, they occupy the
      first 8 bytes of each free block. The two fields have
      also changed, they are now, next and size, both are still
      normalised pointers, the next field points to the next
      freeblock and record, and the size field gives the size of
      the free block.  The first freeblock is pointed to by the
      Freelist pointer variable, the Freeptr variable is no
      longer used.
         With this type of structure it becomes very easy in
      deed to overwrite the freelist record at the start of a
      particular block.  This could occure if, for instance,
      getmem() were called to allocate memory for part of a
      dynamic array.  If the array had 10 elements and we knew
      we were only ever going to use 5 of them, we could, to
      save space, allocate just enough memory for the 5, if we
      then, by mistake, use elements 6,7,8,9 and this would be
      quite legal, as we are still in range for the array index,
      we could in fact be overwriting the record belonging to
      another free block. This could occure if the free block
      allocated for the array was previously a hole in the
      programmes heap with a very small data area above it and
      then another hole in the heap above that, before the next
      chunk of data, the array elements 6,7,8, and 9 would
      overwrite both the small data area and the freelist record
      for that next free hole, the maxavail() function will fail
      when its search reaches the free hole that has just had
      its record overwritten, the value for the next field will
      be a load of old rubbish, sending maxavail() into outer
      space. This could also of occured by just allocating
      insufficient space for a structure in the call to
      getmem(), by mistake.
         Of course there is no excuse for poor or inaccurate
      programming, but at least the memory management system for
      TP V5.X did give us a small, but very greatfull margin to
      play with. From now on you had better make sure that
      every call to getmem() is bit perfect.
.PA 175
      For Owners
      Of Winmen
      Source Files: In case you are having trouble finding where your
      programme is stuck, due to the problem explained above, we
      have included another version of the _WMCHK_MEM();
      procedure in the WMCOMON2.PRS source file, to compile this
      version, instead of the standard one, all you need to do
      is delete the $ character from the {$DEFINE
      STANDARD_WMCHK_MEM} compiler statement and insert a $
      character in the {DEFINE SPECIAL_WMCHK_MEM} complier
      statement and recompile the Winmen Unit, due to some of
      the routines used in the above procedure, it will only
      compile with TP V6.X.
         It does its own free block checking and pointer
      chasing, in, I guess, much the same way as maxavail()
      does, only this one counts each pointer it has chased in
      the loop, if it reaches 10000, it will abort with an
      informative error message, as it's going to be very
      unlikely that any application will produce more than 10000
      holes in the heap, it's more likely that the above
      situation has occured. This gives us a gracefull exit,
      instead of having to ctrl, alt, del. Also refer to the
      description of the _WMCHK_MEM(); procedure for further
      information.
         The WMCOMON1.PFS file contains two versions of the
      WMMEM_STAT(); procedure one is for Turbo Pascal V5.X and
      the other is for Turbo Pascal V6.X, each one takes acount
      of the differences in the Turbo Pascal memory management
      system, each one will only compile succesfully with with
      its respective version of Turbo Pascal. The file is set
      so that the V6.X version compiles, to recompile with
      the V5.X version delete the $ character from the
      {$DEFINE VERSION_6} complier statement, insert a $
      character into the {DEFINE VERSION_4_5_55} compiler
      statement and recompile the Winmen Unit.

      General     : The two outputs below are from the above example
      programme after compliing with both the V5.0 and V6.0
      versions of the Turbo Pascal compiler. The differences,
      as far as the user is concerned, are minimal. Any
      differences that do exist between two common sections,
      ie. 1.4 in the V5.0 and 1.4 in the V6.0 are explained in
      the relevant sub-section in the explaination section
      below. If nothing is mentioned then there are no
      differences
.PA 176
      Sample Of Output From Above Programme Complled Using Turbo Pascal V5.0:-

 A)  ==========================================================================
 1.0 TURBO PASCAL WINDOWS & MENUS MEMORY LOG
     Memory Condition At Initialization:-
 1.1 There Are [0] Entries In The Freelist
     ****MEMORY STATUS TABLE FOLLOWS:-
 1.2 [FREE BLOCK NUMBER][ENDING ADDRESS][STARTING ADDRESS][BLOCK SIZE(BYTES)]

 1.3 There Are [0] Entries In The Freelist
     ****FREELIST RECORD BYTES TABLE FOLLOWS:-
 1.4 [LIST RECORD NUMBER] [BY1] [BY2] [BY3] [BY4] [BY5] [BY6] [BY7] [BY8]

 1.5 The Top Of The Freelist Is At Address [589824]
 1.6 The Top Of The Heap Is At Address [122592]
 1.7 The Heap Oragin Is At Address [122592]
 1.8 Maxavail=[532512] Memavail=[532512]
     ==========================================================================


 B)  ==========================================================================
 1.0 **Memory Condition At Abort**
 1.1 There Are [8] Entries In The Freelist
     ****MEMORY STATUS TABLE FOLLOWS:-
 1.2 [FREE BLOCK NUMBER][ENDING ADDRESS][STARTING ADDRESS][BLOCK SIZE(BYTES)]
        8             123996            123318              678
        7             124648            124146              502
        6             125826            125604              222
        5             126402            125940              462
        4             127374            126776              598
        3             127776            127512              264
        2             130764            130470              294
        1             131894            131312              582

 1.3 There Are [8] Entries In The Freelist
     ****FREELIST RECORD BYTES TABLE FOLLOWS:-
 1.4 [LIST RECORD NUMBER] [BY1] [BY2] [BY3] [BY4] [BY5] [BY6] [BY7] [BY8]
  8            6     0    27    30    12     0    69    30
  7            2     0    79    30     8     0   110    30
  6            4     0   170    30     2     0   184    30
  5            4     0   191    30     2     0   220    30
  4            8     0   243    30    14     0    24    31
  3            8     0    33    31     0     0    50    31
  2            6     0   218    31    12     0   236    31
  1            0     0    15    32     6     0    51    32

 1.5 The Top Of The Freelist Is At Address [655168]
 1.6 The Top Of The Heap Is At Address [161584]
 1.7 The Heap Oragin Is At Address [122592]
 1.8 Maxavail=[493328] Memavail=[509510]
     ==========================================================================
.PA 177
     Sample Of Output From Above Programme Complled Using Turbo Pascal V6.0:-

 A)  ==========================================================================
 1.0 TURBO PASCAL WINDOWS & MENUS MEMORY LOG
     Memory Condition At Initialization:-
 1.1 There Are [0] Free Blocks In The Heap.
     ****MEMORY STATUS TABLE FOLLOWS:-
 1.2 [FREE BLOCK NUMBER][ENDING ADDRESS][STARTING ADDRESS][BLOCK SIZE(BYTES)]

 1.3 There Are [0] Free Blocks In The Heap.
     ****FREELIST RECORD BYTES TABLE FOLLOWS:-
 1.4                        POINTER TO NEXT BLOCK   SIZE OF THIS BLOCK
     [BLOCK RECORD NUMBER] [BY1] [BY2] [BY3] [BY4] [BY5] [BY6] [BY7] [BY8]

 1.9 The First Free Block Record Is At Address [133136]
 2.0 The End Of The Heap Is At Address [655360]
 1.6 The Top Of The Heap Is At Address [133136]
 1.7 The Heap Oragin Is At Address [133136]
 1.8 Maxavail=[522224] Memavail=[522224]
     ==========================================================================


 B)  ==========================================================================
 1.0 **Memory Condition At Abort**
 1.1 There Are [8] Free Blocks In The Heap.
     ****MEMORY STATUS TABLE FOLLOWS:-
 1.2 [FREE BLOCK NUMBER][ENDING ADDRESS][STARTING ADDRESS][BLOCK SIZE(BYTES)]
        1             134552            133872              680
        2             135208            134704              504
        3             136392            136168              224
        4             136976            136512              464
        5             137952            137352              600
        6             138368            138096              272
        7             141360            141064              296
        8             142496            141912              584

 1.3 There Are [8] Free Blocks In The Heap.
     ****FREELIST RECORD BYTES TABLE FOLLOWS:-
 1.4                        POINTER TO NEXT BLOCK   SIZE OF THIS BLOCK
     [BLOCK RECORD NUMBER] [BY1] [BY2] [BY3] [BY4] [BY5] [BY6] [BY7] [BY8]
   1            8     0   226    32     8     0    42     0
   2            0     0    62    33     8     0    31     0
   3            8     0    83    33     0     0    14     0
   4            0     0   136    33     0     0    29     0
   5            8     0   182    33     8     0    37     0
   6            0     0   112    34     0     0    17     0
   7            0     0   165    34     8     0    18     0
   8            8     0    61    35     8     0    36     0

 1.9 The First Free Block Record Is At Address [133864]
 2.0 The End Of The Heap Is At Address [655360]
 1.6 The Top Of The Heap Is At Address [172328]
 1.7 The Heap Oragin Is At Address [133136]
 1.8 Maxavail=[483032] Memavail=[499320]
     ==========================================================================
.PA 178
      Explanation : A)  Section 'A' will always be present in the "Memory.Log"
   file and should always have zero entries in the free
   list, although the actual memory address values may
   differ from above. Of course in you chose to use your
   own output file, instead of the predefined handle log
   then this section along with any runtime memory alloc
   errors will not appear in your file, but will be in
   the "Memory.Log" file. If you happen to call your own
   file "Memory.Log" then the default file will be
   overwritten. See below for an explanation of each
   subsection, as they are the same for each output.

      B)  Section 'B' is written out each time the user calls the
   WMMEM_STAT() procedure and will appear after section A
   unless as mentioned in sect A) above you have defined
   your own output file, in which case the file will only
   contain a section 'B' for each call of WMMEM_STAT().
   Below is an explanation of each part of the output:-

      1.0 This is the "comment" string sent in the WMMEM_STAT()
   procedure call.

      1.1 For TP V5.X this tells you how many entries there are
   in the free list.
      For TP V6.X this tells you how many free blocks
   there are in the programmes heap.
      Effectively, for all TP versions this tells you how
   many vacant holes there are in memory between the heap
   oragin and the heap pointer, (The programmes heap goes
   from HeapOrg to HeapPtr) due to some dynamic variable
   or record structure having been deleted and returned
   to the programmes heap by a call to either dispose()
   or freemem(), or as is the case with this unit a call
   to WMDELETE(). The area of ram above the heap pointer
   and up to HeapEnd is called free memory, and may or
   may not be owned soley by the current programme, this
   is always a contigious block of ram.
      The heap is an area of ram above a programme which
   has already been allocated by that programme, for all
   of its dynamic variables, this includes variables
   created using new(), getmem() and any procedures or
   functions local variables, as and when they are
   called, or in the case of this unit any new windows or
   menus that have been created using NEW_WIN();
   NEW_MENU(); or WMSAVE_SCR(); The heap grows upwards as
   more dynamic variables are created, these holes
   appear, in the programmes heap, when non-contigous
   dynamic variables are deleted and returned to the
   programmes heap.
      The amount of heap memory available to a programme
   is controled by the {$M} compiler directive, or if
   none is specified then that programme owns all
.PA 179
   available ram above its code, right up to the top of
   dos memory, ie. From HeapOrg to HeapEnd. The
   programmes heap will grow up towards the top of its
   free memory (HeapEnd) as dynamic variables are
   created. The programmes stack size is also controled
   by the same directive, see the Turbo Pascal reference
   guide for more detailed information.
      For TP V5.X, in the area of memory above the
   programmes heap, which is called free memory, you will
   find the actual freelist, this contains the records
   which give the starting and ending addresses of each
   free block of memory (hole) in the programmes heap
   area. The freelist is set up like a stack, and grows
   downwards from HeapEnd, as a block of ram becomes
   available, as describe above, a record which describes
   its position and size is pushed onto the freelist.
      For TP V6.X there is no special area, in free
   memory set asside for a freelist, instead each free
   block in the programmes heap has its own record, this
   record takes up the first eight bytes of each free
   block, it contains two fields, the first is a pointer
   to the next free block in the programmes heap and the
   second gives the actual size of the current free
   block. The Freeptr variable, which, in TP V5.X used
   to point to the top of the freelist stack no longer
   exists, this is replaced by the Freelist variable in
   TP V6.X and points to the first free block in the
   programmes heap.
      See the Turbo Pascal V5.X Reference Guide, P197 or
   the Turbo Pascal V6.X Programmers Guide P215 for more
   detailed information.

      1.2 This is the actual table of available free blocks of
   ram in the programmes heap, taken from the TP V5.X
   freelist or the TP V6.X free block records and gives
   the block number, its ending address, starting address
   and the block size, all values and addresses are in
   decimal, rather than being in segment:offset format as
   this makes more sense to look at. There are thoes of
   course that will want segment:offset format, please
   refer to sub-sect 1.4.

      1.3 See sub-section 1.1.
.PA 180
      1.4 For TP V5.X this is the table of freelist records,
   each record is 8 bytes long and there is one record
   for each entry or each free block, the bytes are as
   follows:-

   Record
   BY1 : Low Byte  \
   BY2 : High Byte >ÄÄÄOffset \      Field 1
   BY3 : Low Byte  \           >ÄÄÄÄÄBlock Oragin Address.
   BY4 : High Byte >ÄÄÄSegment/
   BY5 : Low Byte  \
   BY6 : High Byte >ÄÄÄOffset \      Field 2
   BY7 : Low Byte  \           >ÄÄÄÄÄBlock End Address
   BY8 : High byte >ÄÄÄSegment/

      All the table values are in decimal, so if we take
   list record number 1 for example, to get the proper
   hex segment:offset address format, we do the following

         Hexidecimal      20 Bit    Block
         Dec  Hex       Seg : Ofs    =  Address   Size
   BY1 :   0    0 >ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
   BY2 :   0    0 >ÄÄÄÄÄÄÄÄÄÄÄ¿ ³
   BY3 :  15   0F >ÄÄÄÄÄÄ¿    ³ ³       (200F0H)
   BY4 :  32   20 >ÄÄÄÄ200F : 0000   =   131312Ä¿
   BY5 :   6    6 >ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               ³ (246H)
   BY6 :   0    0 >ÄÄÄÄÄÄÄÄÄÄÄ¿ ³               ÃÄÄ582
   BY7 :  51   33 >ÄÄÄÄÄÄ¿    ³ ³       (20336H)³
   BY8 :  32   20 >ÄÄÄÄ2033 : 0006   =   131894ÄÙ

      The actual 20 bit address is calculated by shifting
   the segment value left 4 bits (*16) and then adding
   the offset.

      For TP V6.X this is the table of free block
   records, each record is 8 bytes long and occupies the
   first 8 bytes of each free block in the programmes
   heap.
      For each free block, the bytes are as follows:-

   Record
   BY1 : Low Byte  \                 Field 1
   BY2 : High Byte >ÄÄÄOffset \      Address Of Next
   BY3 : Low Byte  \           >ÄÄÄÄÄFree Block In The
   BY4 : High Byte >ÄÄÄSegment/      Free Heap.
   BY5 : Low Byte  \
   BY6 : High Byte >ÄÄÄOffset \      Field 2
   BY7 : Low Byte  \           >ÄÄÄÄÄSize Of Current
   BY8 : High byte >ÄÄÄSegment/      Block.
.PA 181
   All the table values are in decimal, so if we take
   free block record number 8 for example, to get the
   proper hex segment:offset address format, we do the
   following

            20 Bit    Curr
         Hexidecimal      Next Blk  Block
         Dec  Hex       Seg : Ofs    =  Address   Size
   BY1 :   8    8 >ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿          ³       ³
   BY2 :   0    0 >ÄÄÄÄÄÄÄÄÄÄÄ¿ ³          ³       ³
   BY3 :  61   3D >ÄÄÄÄÄÄ¿    ³ ³       (233D8H)   ³
   BY4 :  35   23 >ÄÄÄÄ233D : 0008   =>ÄÄ144344    ³
   BY5 :   8    8 >ÄÄÄÄÄÄÄÄÄÄÄÄÄ¿                  ³
   BY6 :   0    0 >ÄÄÄÄÄÄÄÄÄÄÄ¿ ³                  ³
   BY7 :  36   24 >ÄÄÄÄÄÄ¿    ³ ³                 (248H)
   BY8 :   0    0 >ÄÄÄÄ0024 : 0008   =>ÄÄÄÄÄÄÄÄÄÄÄÄ584

      The actual 20 bit address or value is calculated by
   shifting the segment value left 4 bits (*16) and then
   adding the offset.
      NB. The TP V5.X freelist records are in reverse
   order, this is because its a stack type system, last
   on first off, thus record 1 from the TP V5.X printout
   is the same as record 8 from the TP V6.X printout.

      1.5 This sub-section only applies to TP V5.X
   This is the address of the top of the freelist stack
   it is important to make sure that the freelist records
   have room to grow downwards, towards the top of the
   heap which of course is growing upwards to meat it, so
   the setting of the {$M} complier directive will have
   an affect on this, for more information on how to
   achieve this see the Turbo Pascal V5.X reference
   manual.

      1.6 This is the address of the top of the heap, or the
   extent of memory that has already been allocated for a
   programmes dynamic variables, the area above this is
   the free memory, all or some of which, is available to
   that programme for further allocation, depending on
   the {$M} compiler directive settings, see the Turbo
   Pascal V5.X reference guide or the Turbo Pascal V6.X
   Programmers Guide for more information.

      1.7 This is the address of the heap oragin, the top of the
   heap minus the heap oragin gives you the total memory
   that has already been used by the current programme
   for its dynanmic variables.

      1.8 The maxavail value is the size of the largest block of
   contigous memory which is available to the current
   programme either in its heap area or from its free
   memory, which is above HeapPtr.
.PA 182
      The memavail value is the sum of all free blocks in
   that programmes heap plus any free memory that has
   been allocated to it above HeapPtr.
      Both maxavail and memavail will depend on how the
   {$M} complier directive has been set, see the Turbo
   Pascal V5.X reference guide or the Turbo Pascal V6.X
   Programmers Guide for more information.

      1.9 This sub-section applies only to TP V6.X
   This is the address of the first free block in the
   programmes heap area and is pointed to by the FreeList
   variable, if this value is the same as HeapPtr then
   there are no free blocks available in the programmes
   heap area, see the Turbo Pascal V6.X Programmers
   Guide for more information.

      2.0 This sub-section applies only to TP V6.X
   This is the address at the end or extent of the free
   memory that has been allocated to that programme.
   This is the memory area above the programmes heap,
   which is pointed to by HeapPtr. The amount of free
   memory allocated to a programme will depend on how
   the {$M} compiler directive has been set, see the
   Turbo Pascal V6.X Programmers Guide for more
   information.

      Rules Notes
      Tips & Traps: You will probably only find this procedure to be of any
      real value in a debugging situation.
         For more information on sub-sections 1.6, 1.7, 1.8, 1.9
      and 2.0 see the Turbo Pascal reference guide.
         The example creates up to 50 windows then deletes every
      third one, each deleted window becomes a vacant hole in
      the programmes heap area, its these holes that are listed
      in the freelist, for TP V5.X or at the start of each free
      block for TP V6.X, if two adjacent windows (adjacent in
      terms of memory) were to be deleted we would only create
      one hole, as the two memory areas become contigous, the
      heap manager takes care of this for us, if the lastmost
      window were to be deleted (the one occupying the memory
      area at the top of the heap) then an entry would not
      appear in the freelist for TP V5.X and there would be no
      free block record at the start of this memory area for TP
      V6.X, for this vacant memory area, as the heap manager
      will simply move the HeapPtr down, by the size of that
      block, effectively returning that area of memory to the
      free memory area above that programmes HeapPtr, for use by
      something else. The demo can be stoped before 50 windows
      is reached (69) by pressing the space bar.
.PA 183
      ------------------------------------------------------------------------
      WMMOVE()                                                        WMMOVE()
      ------------------------------------------------------------------------

      Procedure   : This procedure will move any window, menu or user screen
      (ID < 20), weather it is open or closed, from its current
      position to the new position specified in the supplied l,t
      values, if it was closed before the move it will be closed
      after the move and if open it will remain open.
         Any open object is firstly floated to the surface, so
      that it is wholy visible on the screen, the move is then
      carried out, this enables us to move objects that are
      partialy or even fully obscured by other objects, whilst
      leaving that area of screen visually correct after the
      move, this operation is so fast that the user may not
      even notice it being carried out.
         What you see on the screen is exactly what is moved,
      when the WMMOVE(); procedure is used, this includes the
      contents of windows and user screens and any titles that
      an object may have.
         After a move the objects ID_SC[] background screen
      array is always updated with the new background image from
      the area it is copied onto, this is true for windows,
      menus and user screens.
      The following conditions are errors:-
      If the IDB does not yet exist.
      If the new position will put any part of the window, menu
      or user screen off the screen.
         All the above conditions will result in the programme
      halting with an error message (see appendix for list).
         If the object moved is a window or user screen and it
      was open before the move, then it will be made the
      active one after the move, regardless of weather it was
      the active one before the move.
         The window or menus record is updated to reflect any
      changes if the operation was sucessful.
         This procedure is not called by any of the functions
      or procedures in the Winmen Unit.

      Syntax      : WMMOVE(ID,l,t);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      user screen (ID < 20), as returned from NEW_MENU(); or
      NEW_WIN(); or sent to WMSAVE_SCR() if its a user screen.
         If the ID does not yet exist the programme will abort
      with an error message (see appendix for list).

      l : byte (B1_80);
      The new leftmost edge of the object including border, must
      be a value between 1-80 or a range check error will
      occure.
.PA 184
      t : byte (B1_25);
      The new topmost edge of the window including border, must
      be a value between 1-25 or a range check error will
      occure.

      Example     : program WM_MOVE;

      var
         m1,m1c,l,t : byte;            {*DEFINE GLOBALS*}
         mt : pt_mtext;

      begin
         getmem(mt,4*81);              {*DEFINE MENU*}
         mt^[1] := '8) Move Up';
         mt^[2] := '4) Move Left';
         mt^[3] := '6) Move Right';
         mt^[4] := '2) Move Down';
         m1 := NEW_MENU(1,4,14,7,0,15,mt,4);
         freemem(mt,4*81);

         l := 32;                      {*INIT VARBS*}
         t := 9;
         m1c := 10;
         WMTITLE(m1,1,15,'MOVE ME');   {*DO TITLE STRING*}

         while not(m1c in[0]) do       {*WHILE NOT ESCAPE*}
         begin
     m1c := POP_MENU(m1,l,t);
     case m1c of
        1 : if(t > 2) then      {*MOVE MENU UP 1 ROW*}
     begin
        dec(t,2);
        WMMOVE(m1,l,t);
     end;
        2 : if(l > 4) then      {*MOVE MENU LEFT 1 COL*}
     begin
        dec(l,4);
        WMMOVE(m1,l,t);
     end;
        3 : if(ID_WM[m1]^.r+4 < 80) then
     begin               {*MOVE MENU RIGHT 1 COL*}
        inc(l,4);
        WMMOVE(m1,l,t);
     end;
        4 : if(ID_WM[m1]^.b+2 < 25) then
     begin               {*MOVE MENU DOWN 1 ROW*}
        inc(t,2);
        WMMOVE(m1,l,t);
     end;
     end {case};
         end {while};
         normvideo;
      end {WM_MOVE}.
.PA 185
      Rules Notes  
      Tips & Traps: The Winmen Units 3D screen map system is fundimental to
      the correct operation of this procedure, the object must
      be fully linked into the 3D screen map to enable this
      procedure to keep the screen display visually correct
      each time an object is moved, under normal circumstances
      the user need not be concerned with this, but if for
      some reason this particular object has had its "maprecs"
      deleted from the 3D screen map system, perhaps using the
      WMDEL_MREC_ALL(); procedure, then the user must make
      sure that the object is re-linked into the 3D screen map
      system by using the WMADD_MREC_ALL(); procedure prior to
      the WMMOVE(); procedure being called, if this is not
      done then the 3D screen map system will not be able to
      maintain the integrity of the screen and it will become
      visually corrupt.
         The 3D screen map system will automaticaly update the
      ID_SC[] background screen arrays of any objects that were
      partialy or fully obscuring the object being moved, so
      that if they are moved, slid, floated, removed or closed
      the screen display will always be visually correct. They
      are updated with the image data that was in the ID_SC[]
      background screen array of the object being moved.
         The 3D screen map also updates its self, when the
      WMMOVE(); procedure is called, by deleting all the moved
      objects "maprecs" from the 3D screen map at its old
      position and creating new ones in its new position.
         For more detailed information on the 3D screen map
      system and it associated "types" and "variables" refer
      to the sections "Winmen Declared Types" and "Winmen
      Declared Global Variables", in particular the "maprec"
      subsection.
.PA 186
      ------------------------------------------------------------------------
      WMREMOVE()                                                    WMREMOVE()
      ------------------------------------------------------------------------ 

      Procedure   : This procedure will remove the object specified by ID 
      High Level    from the display, regardless of what other objects there 
      may be, either partially or completely covering it.
         Unlike the WMCLOSE(); procedure, which firstly floats 
      the object specified by ID to the surface, prior to 
      closing it, WMREMOVE(); appears to remove only the parts 
      of the object that are visible to the user.
         Any part of the ID object, that is covered by any other 
      object, will have its image removed from the background 
      screen array of all objects that are immediatly above it.  
      The screen arrays, of the affected objects, will then be 
      updated with the appropriate data from the background 
      screen array of the ID object, so that if the affected 
      objects are moved, slid, closed, or themselves removed 
      from view, the display will always remain visualy correct.
         As you can see there is a lot of complex array juggling 
      to be done by this procedure, however users need not 
      concern themselves with any of this, if they do not want 
      to, the WMREMOVE(); procedure takes care of everything, 
      extremely quickly and efficiently.
         If no "maprec" exists for the object at the "map_inx" 
      position corresponding to the top left hand corner of the 
      object on the screen, then no action is taken and the 
      procedure exits, it will not be removed from the display.  
      For more information on the "maprec" type refer to the 
      section "Winmen Declared Types". For more information on 
      the "map_inx" 3D screen map array index, refer to the 
      _WMADD_MREC(); and _WMDEL_MREC(); procedure definitions 
      and the MAP[map_inx] global variable, in the section 
      "Winmen Declared Variables".
         If the object specified by ID is closed or has already 
      been removed from the display, no action is taken and the 
      procedure will exit and take no action.
         The objects ID must have been previously created using
      NEW_WIN() or NEW_MENU().
         The objects wmrec field ".op_cl" is updated by this 
      procedure, by setting it to "false" (closed). 
         This procedure is not called by any of the functions 
      or procedures in the Winmen Unit. 

      Syntax      : WMREMOVE(ID);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or 
      NEW_WIN() and as sent to WMSAVE_SCR(0-19), if it is a user 
      saved screen area.
         If the ID does not yet exist the programme will abort 
      with an error message (see appendix for list).             
.PA 187
      Example     : program REMOVE;
        
      uses dos,crt,winmen;                                                      
        
      var                                                                       
         w1,w2,w3,w4,w5,w6,win,n : byte;                                        
        
      begin                                                                     
         textbackground(0);                                                     
         clrscr;                        {*DEFINE WINDOWS*}                      
         w1 := NEW_WIN(1,8,25,14,1,7,0,2,3);                                    
         w2 := NEW_WIN(40,4,70,10,4,1,14,3,14);                                 
         w3 := NEW_WIN(10,6,48,17,2,2,3,1,9);                                   
         w4 := NEW_WIN(28,14,65,23,3,4,6,2,11);                                 
         w5 := NEW_WIN(1,16,40,25,1,0,15,0,13);                                 
         w6 := NEW_WIN(15,15,68,18,2,4,1,5,1);                                  
        
         for n := 1 to 3 do             {*OPEN AND REMOVE*}                     
         begin                                                                  
     for win := w1 to w6 do                                                   
        WOPEN(win);                                                           
     for win := w1 to w6 do                                                   
     begin                                                                    
        delay(1000);                                                          
        WMREMOVE(win);                                                        
     end;                                                                     
         end;                                                                   
        
         for win := w1 to w6 do         {*OPEN LAST TIME*}                      
     WOPEN(win);                                                              
        
         for win := w1 to w6 do         {*DELETE STRUCTURES*}                   
         begin                                                                  
     delay(1000);                                                             
     WMDELETE(win);                                                           
         end;                                                                   
      end.
.PA 188
      Rules Notes
      Tips & Traps: The above example has been slowed down, with delays, so 
      that you can see exactly what is happening. The windows 
      are opened and then removed, starting with the first one, 
      to finish the windows are closed, which calls the 
      WMFLOAT(); procedure, this is so that you can see the 
      difference between the WMREMOVE(); and WMCLOSE(); 
      procedures.     
         The method that the WMCLOSE(); procedure uses to take
      an object from the display, depends on the setting of the
      system variable "WMCLOSE_WITH_REMOVE". If it is set to
      "FALSE", the default, then WMCLOSE(); will continue to use
      the WMFLOAT(); and WMREST_SCR(); procedures to close
      objects. If it is set to "TRUE" then WMCLOSE(); will use
      WMREMOVE(); to close objects. Please refer to the
      WMCLOSE(); procedure and the section "Winmen Declared
      Types, for more information.
.PA 189
      ------------------------------------------------------------------------
      WMREST_SCR()                                                WMREST_SCR()
      ------------------------------------------------------------------------
    
      Procedure   : Restore the screen data, previously saved in calls to
      High Level/   WMSAVE_SCR(), to the screen at the position specified by
      Low Level     the parameters l,t,r,b.
         The calls to WMSAVE_SCR(); may have been made by any
      one of the following Winmen routines WOPEN(); BAR_MENU();
      LIST_MENU(); or POP_MENU(); or by a users call to
      WMSAVE_SCR(); if the ID was in the range 0-19 (User saved
      screen area).
         The object and ID must have previously been created
      using either NEW_WIN(); NEW_MENU(); or WMSAVE_SCR(), if it
      is a user screen (ID < 20).
         The screen data is restored from the objects ID_SC[]
      background "screen" array, so whatever is in it at the
      time WMREST_SCR(); is caled will be used to display on the
      screen at the specified l,t,r,b area.
         Areas of screen saved during calls to the three menu
      routines and the window open routine may also be restored
      at any desired position, but bear in mind the fact that
      the objects record variables l,t,r,b will not be altered,
      so this will not actualy move the object, it will mearly
      restore a copy of what is in that objects ID_SC[]
      background "screen" array, the image under the object,
      unless this has for some reason been changed or replaced,
      to the screen at the specified position, if you want to
      move an object please refer to the WMMOVE() procedure.
         This procedure may also be called by the WMCLOSE()
      procedure, depending on how the following system switch
      is set:-

         CLOSE_WITH_REMOVE := false; All objects will be closed
         by firstly floating them to the surface and then
         restoring the objects ID_SC[] background screen array,
         with WMREST_SCR(); to effectively remove them.
         CLOSE_WITH_REMOVE := true; All objects will be closed
         by using the WMREMOVE(); procedure, this makes it
         appear as if the object is being removed from the back
         of the screen, instead of from the front, as the other
         method does and does not call WMREST_SCR();
         CLOSE_WITH_REMOVE := false; Default.

         Please refer to the WMCLOSE(); procedure and the
      section "Winmen Declared Types" for more information.
         The objects "wmrec" is not affected in any way by this
      procedure.

      Syntax      : WMREST_SCR(ID,l,t,r,b);
.PA 190
      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a 
      user saved screen area.
         If the ID does not yet exist the programme will abort
      with an error message (see appendix for list).

      l : (byte) B1_80;
      Leftmost edge of the area to restore to, must be a
      value between 1-80 or a range check error will occure.

      t : byte; (B1_25)
      Topmost edge of the area to restore to, must be a
      value between 1-25 or a range check error will occure.   

      r : byte; (B1_80)
      Rightmost edge of the area to restore to, must be a
      value between 1-80 or a range check error will occure.

      b : byte; (B1_25)
      Bottommost edge of the area to restore to, must be a
      value between 1-25 or a range check error will occure.

      Example     : program REST_SCR;

      uses dos,crt,winmen;

      var
         m1,l,t : byte;                {*DEFINE VARIABLES*}
         choice : word;
         mt : pt_mtext;

      begin {main}
         textbackground(0);
         textcolor(7);
         clrscr;                       {*SET UP USER TEXT AREA*}

         gotoxy(1,12);                 {*SET UP TEXT UNDER MENU*}
         textcolor(14);
         textbackground(4);
         writeln(scr,'This area of text         ');
         writeln(scr,'was under the menu        ');
         writeln(scr,'and will be copied        ');
         writeln(scr,'using WMREST_SCR()        ');
         writeln(scr,'without affecting the     ');
         writeln(scr,'menu position             ');
         delay(1500);
.PA 191
         getmem(mt,4*81);              {*DEFINE MENU*}
         mt^[1] := 'Copy Area Under The Menu';
         mt^[2] := '';
         mt^[3] := '';
         mt^[4] := '';
         m1 := NEW_MENU(1,1,2,3,4,15,mt,4);
         freemem(mt,4*81);

         choice := 10;
         while not(choice in[0]) do    {*WHILE NOT ESCAPE*}
         begin                         {*DISPLAY MENU*}
     choice := POP_MENU(m1,1,12);
     case choice of
        1 : begin
        l := 80; t := 25;{*COPY AREA UNDER MENU*}
        while(l+25 > 80)or(l < 27) do
           l := random(80);
        while(t+5 > 25)or(t < 1) do
           t := random(25);
        WMREST_SCR(m1,l,t,l+25,t+5);
     end;
     end {case};
         end {while};
         normvideo;
         delay(1500);
      end {main}.

      Rules Notes
      Tips & Traps: The above example puts an area of text on the screen,
      which will just be copied around using the WMREST_SCR()
      procedure, the initial area of text will be covered up by
      a menu.
         Any saved screen area that is under (obscured) by any
      other object(s) will just be copied (restored) to the new
      specified position, having no effect at all on the
      obscuring object(s).
         The procedure also checks to see that the new area that
      is specified by the user is less than or equal to, but not
      greater than the existing area covered, although the hight
      and length may of course differ (area = h*l).  This is to
      ensure that only valid data is displayed and that the end
      of the screen array is not exceeded.  If this condition
      occures the programme will abort with an error message.
         This procedure is not influenced by, and has no affect
      at all on, the Winmen 3D screen map system.  For more
      information on the Winmen 3D screen map system and its
      associated type and variables refer to the sections
      "Winmen Declared Types" and "Winmen Declared Global
      Variables", in particular the sub-section on "maprecs".
.PA 192
      ------------------------------------------------------------------------
      WMSAVE_SCR()                                                WMSAVE_SCR()
      ------------------------------------------------------------------------

      Procedure   : Save the screen area bounded by the supplied values l,t,r
      Low Level/    ,b into an ID_SC[] background "screen" array, which was
      High Level    dynamically allocated to the window or menu ID in question
      when it was created using either NEW_WIN(); or NEW_MENU();
         If the ID is less than 20 its a USER saved screen area,
      in which case a new "wmrec" record is allocated along with
      an appropriatly sized ID_SC[] background "screen" array.
      If the USER saved screen is one that already exists
      through a call to WMSAVE_SCR(0-19) then no new "wmrec"
      record is allocated, but the ID_SC[] background "screen"
      array is deleted and re-allocated (see notes below for
      more detail).
         If the USER saved screen is one that exists and it is
      open it will firstly be closed with WMCLOSE(); How it is
      closed will depend on the setting of the following
      system switch:-

         CLOSE_WITH_REMOVE := false; All objects will be closed
         by firstly floating them to the surface and then
         restoring the objects ID_SC[] background screen array,
         to effectively remove them.
         CLOSE_WITH_REMOVE := true; All objects will be closed
         by using the WMREMOVE(); procedure, this makes it
         appear as if the object is being removed from the back
         of the screen, instead of from the front, as the other
         method does.
         CLOSE_WITH_REMOVE := false; Default.

         Please refer to the WMCLOSE(); procedure and the
      section "Winmen Declared Types" for more information.
         So unless you want to create a USER saved screen area
      (ID=0-19), you need not concern your self with this
      procedure. Unless for some reason you want to change the
      screen area data that is kept in an objects ID_SC[]
      background "screen" array, which is a copy of the screen
      image from under a window or menu before it was
      displayed. Dont forget the data in the objects ID_SC[]
      background "screen" array is the data that will be
      replaced on the screen at the l,t,r,b area when an object
      is closed or removed fron the display.
         Checking is done to see if the USER is trying to access
      a "wmrec" record structure ID above 19, if this occures
      then further checks are carried out to make sure that the
      new requested screen area to be saved, into the window or
      menus ID_SC[] background "screen" array, is less than or
      equal to the area that already exists for that window or
      menu, it is an error for the new area to be larger than
      the existing one (see below).
.PA 193
         This procedure is also called automatically by
      POP_MENU() LIST_MENU(), BAR_MENU() and the WOPEN()
      procedures, prior to the object being placed on the
      screen.
         The objects ID must have been previously created using
      NEW_WIN() or NEW_MENU().
         The objects "wmrec" is updated to reflect any changes.

      Winmen 3D   :    This procedure also links the object into the Winmen 3D
      Screen Map    screen map system, this is the units main routine for
      carrying out this function, it does so by making a call to
      WMADD_MREC_ALL(); so any other function or procedure that
      calls WMSAVE_SCR(); with an object ID will automatically
      have the object linked into the 3D screen map system ie.
      POP_MENU(); LIST_MENU(); BAR_MENU(): and WOPEN();
         The Winmen 3D screen map system allows objects, that
      are open on the screen (closed objects are not linked), to
      be treated is special ways, for example, objects that are
      obscured, either partially, or fully by other objects can
      be floated to the surface, removed from the back of the
      display, or closed, windows that are obscured may be
      written to, any obscured object can be moved or slid, all
      these operations can be carried out using the normal
      WMFLOAT(); WMCLOSE(); WMMOVE(); WMSLIDE(); WMREMOVE();
      WMDELETE(); WOPEN(); and WSELECT(); procedures without the
      user having to worry about the screen becoming corrupt,
      the Winmen 3D screen map system makes sure that the
      screen display is always visualy correct, no mater what
      you throw at it.
         Any obscuring object that is affected by the action of
      manipulating the obscured object, will automatically have
      its ID_SC[] background "screen" array updated with the
      correct new background image data from the object that was
      being manipulated, for example, if an obscured object is
      floated to the surface, the obscuring objects ID_SC[]
      background "screen" array will be updated to reflect the
      changing background underneath it, the object being
      manipulated will also have its ID_SC[] background "screen"
      array updated with the new screen image that it now
      covers.
         The Winmen 3D screen map system also updates its self
      automatically with the new positions of any open objects
      that have been manipulated, using any of the above
      routines.
         WMSAVE_SCR(); will only link objects into the Winmen 3D
      screen map system under the following conditions:-
         1) The ID specified is for a new user saved screen
     ID<20.
         2) The ID specified is for an existing user saved
     screen ID<20.
         3) The ID specified is for a new window, this must have
     been a call from WOPEN(); ID>19.
.PA 194
        WMSAVE_SCR(); knows if it is a call from WOPEN();
     because WOPEN(); sets the ID^.op_cl flag to "true"
     just before it calls WMSAVE_SCR(); ID^.typ is set
     to "W" and the call it makes to _WMCHK_FOR_MREC();
     will return "false" as no "maprecs" will exist for
     any object that has not yet been created.
        If the ID specified is for an existing window
     that is open then the call to _WMCHK_FOR_MREC();
     will return "true", as all open objects are linked
     into the 3D screen map, no new "maprecs" will be
     created for this ID as we do not want to double up.
        If the ID specified is for an existing window
     that is closed ID^.op_cl is set to "false" then the
     programme will abort with an error message, as this
     is an invalid operation, the ID_SC[] background
     "screen" array for that ID would be overwritten
     the next time WOPEN(); was called.

         4) The ID specified is for a new menu, this must have
     been a call from either POP_MENU(); LIST_MENU(); or
     BAR_MENU(); ID>19.
        WMSAVE_SCR(); knows if it is a call from any of
     the above three menu functions because WOPEN(); sets
     the ID^.op_cl flag to "true" just before it calls
     WMSAVE_SCR(); ID^.typ is set to "M" and the call it
     makes to _WMCHK_FOR_MREC(); will return "false" as
     no "maprecs" will exist for any object that has not
     yet been created.
        If the ID specified is for an existing menu that
     is open then the call to _WMCHK_FOR_MREC(); will
     return "true", as all open objects are linked into
     the 3D screen map, no new "maprecs" will be created
     for this ID as we do not want to double up.
        If the ID specified is for an existing menu that
     is closed ID^.op_cl is set to "false" then the
     programme will abort with an error message, as this
     is an invalid operation, the ID_SC[] background
     "screen" array for that ID would be overwritten the
     next time any of the three menu functions were
     called.
         The only other possible condition is if the ID value is
      greater than 19 (ID is for a window or a menu) and it does
      not yet exist the programme will abort with an error
      message (see appendix for list).
         For more detailed information on the Winmen 3D screen
      map system and its associated data types and variables
      plese refer to the "maprec" sub-section in the "Winmen
      Declared types" section.

      Syntax      : WMSAVE_SCR(ID,l,t,r,b);
.PA 195
      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      user saved screen area, as returned from the NEW_MENU() or
      NEW_WIN() functions, or if it is to be a USER saved
      screen area a value between 0-19, as stated above this
      procedure is called automatically from WOPEN(), POP_MENU(),
      LIST_MENU() and BAR_MENU() so the user need not concern
      him or her self with any actual values for the ID other
      than those for a USER saved screen area (0-19), as the ID
      for a window or menu will normally be contained in the
      byte variable returned from NEW_WIN() or NEW_MENU().
         If the ID value is greater than 19 (ID is for a window
      or a menu) and it does not yet exist the programme will
      abort with an error message (see appendix for list).

      l : byte; (B1_80)
      Leftmost edge of the area to save, must be a value between
      1-80 or a range check error will occure.

      t : byte; (B1_25)
      Topmost edge of the arae to save, must be a value between
      1-25 or a range check error will occure.

      r : byte; (B1_80)
      Rightmost edge of the area to save, must be a value
      between 1-80 or a range check error will occure.

      b : byte; (B1_25)
      Bottommost edge of the area to save, must be a value
      between 1-25 or a range check error will occure.

      Example     : Below is an example programme.
program SAVE_SCR;

{***********}
{*UNIT USED*}
{***********}
uses dos,crt,winmen;

{*************************}
{*DEFINE GLOBAL VARIABLES*}
{*************************}
var
   dist,               {*MOVE DISTANCE*}
   dirnum,             {*DIRECTION NUMBER*}
   sp,                 {*SPRITE NUMBER*}
   nsp : byte;         {*NUMBER OF SPRITES*}
   dir : char;         {*DIRECTION CHARACTER*}
.PA 196
{****************}
{*FUNCTION:HIT_L*}
{****************}
function HIT_L(sp,nsp : byte) : boolean;

var
   n,hit : byte;                           {*DEFINE LOCAL VARIABLES*}

begin
   hit := 0;                               {*COLISION FLAG*}
   for n := 1 to nsp do                    {*CHECK SP AGAINST ALL SPRITES*}
      if(n <> sp)and(hit = 0) then         {*EXCEPT ITS SELF*}
      begin                                {*CHECK TOP AND LEFT OF SP*}
 if(ID_WM[sp]^.t >= ID_WM[n]^.t) then
    if(ID_WM[sp]^.t <= ID_WM[n]^.b) then
       if(ID_WM[sp]^.l > ID_WM[n]^.r) then
   if(ID_WM[sp]^.l-dist <= ID_WM[n]^.r) then
      hit := 1;              {*COLLISION, SET HIT FLAG*}
 if(hit = 0) then                   {*CHECK BOTTOM AND LEFT OF SP*}
    if(ID_WM[sp]^.b >= ID_WM[n]^.t) then
       if(ID_WM[sp]^.b <= ID_WM[n]^.b) then
   if(ID_WM[sp]^.l > ID_WM[n]^.r) then
      if(ID_WM[sp]^.l-dist <= ID_WM[n]^.r) then
         hit := 1;           {*COLLISION, SET HIT FLAG*}
      end {if n};

   if(hit = 0) then
      HIT_L := false                       {*IF NO HITS RETURN FALSE*}
   else
      HIT_L := true;                       {*ELSE RETURN TRUE*}
end {*HIT_L*};
.PA 197
{****************}
{*FUNCTION:HIT_R*}
{****************}
function HIT_R(sp,nsp : byte) : boolean;

var
   n,hit : byte;                           {*DEFINE LOCAL VARIABLES*}

begin
   hit := 0;                               {*COLISION FLAG*}
   for n := 1 to nsp do                    {*CHECK SP AGAINST ALL SPRITES*}
      if(n <> sp)and(hit = 0) then         {*EXCEPT ITS SELF*}
      begin                                {*CHECK TOP AND RIGHT OF SP*}
 if(ID_WM[sp]^.t >= ID_WM[n]^.t) then
    if(ID_WM[sp]^.t <= ID_WM[n]^.b) then
       if(ID_WM[sp]^.r < ID_WM[n]^.l) then
   if(ID_WM[sp]^.r+dist >= ID_WM[n]^.l) then
      hit := 1;              {*COLLISION, SET HIT FLAG*}
 if(hit = 0) then                   {*CHECK BOTTOM AND RIGHT OF SP*}
    if(ID_WM[sp]^.b >= ID_WM[n]^.t) then
       if(ID_WM[sp]^.b <= ID_WM[n]^.b) then
   if(ID_WM[sp]^.r < ID_WM[n]^.l) then
      if(ID_WM[sp]^.r+dist >= ID_WM[n]^.l) then
         hit := 1;           {*COLLISION, SET HIT FLAG*}
      end {if n};

   if(hit = 0) then
      HIT_R := false                       {*IF NO HITS RETURN FALSE*}
   else
      HIT_R := true;                       {*ELSE RETURN TRUE*}
end {*HIT_R*};
.PA 198
{****************}
{*FUNCTION:HIT_U*}
{****************}
function HIT_U(sp,nsp : byte) : boolean;

var
   n,hit : byte;                           {*DEFINE LOCAL VARIABLES*}

begin
   hit := 0;                               {*COLISION FLAG*}
   for n := 1 to nsp do                    {*CHECK SP AGAINST ALL SPRITES*}
      if(n <> sp)and(hit = 0) then         {*EXCEPT ITS SELF*}
      begin                                {*CHECK LEFT AND TOP OF SP*}
 if(ID_WM[sp]^.l >= ID_WM[n]^.l) then
    if(ID_WM[sp]^.l <= ID_WM[n]^.r) then
       if(ID_WM[sp]^.t > ID_WM[n]^.b) then
   if(ID_WM[sp]^.t-dist <= ID_WM[n]^.b) then
      hit := 1;              {*COLLISION, SET HIT FLAG*}
 if(hit = 0) then                   {*CHECK RIGHT AND TOP OF SP*}
    if(ID_WM[sp]^.r >= ID_WM[n]^.l) then
       if(ID_WM[sp]^.r <= ID_WM[n]^.r) then
   if(ID_WM[sp]^.t > ID_WM[n]^.b) then
      if(ID_WM[sp]^.t-dist <= ID_WM[n]^.b) then
         hit := 1;           {*COLLISION, SET HIT FLAG*}
      end {if n};

   if(hit = 0) then
      HIT_U := false                       {*IF NO HITS RETURN FALSE*}
   else
      HIT_U := true;                       {*ELSE RETURN TRUE*}
end {*HIT_U*};
.PA 199
{****************}
{*FUNCTION:HIT_D*}
{****************}
function HIT_D(sp,nsp : byte) : boolean;

var
   n,hit : byte;                           {*DEFINE LOCAL VARIABLES*}

begin
   hit := 0;                               {*COLISION FLAG*}
   for n := 1 to nsp do                    {*CHECK SP AGAINST ALL SPRITES*}
      if(n <> sp)and(hit = 0) then         {*EXCEPT ITS SELF*}
      begin                                {*CHECK LEFT AND BOTTOM OF SP*}
 if(ID_WM[sp]^.l >= ID_WM[n]^.l) then
    if(ID_WM[sp]^.l <= ID_WM[n]^.r) then
       if(ID_WM[sp]^.b < ID_WM[n]^.t) then
   if(ID_WM[sp]^.b+dist >= ID_WM[n]^.t) then
      hit := 1;              {*COLLISION, SET HIT FLAG*}
 if(hit = 0) then                   {*CHECK RIGHT AND BOTTOM OF SP*}
    if(ID_WM[sp]^.r >= ID_WM[n]^.l) then
       if(ID_WM[sp]^.r <= ID_WM[n]^.r) then
   if(ID_WM[sp]^.b < ID_WM[n]^.t) then
      if(ID_WM[sp]^.b+dist >= ID_WM[n]^.t) then
         hit := 1;           {*COLLISION, SET HIT FLAG*}
      end {if n};

   if(hit = 0) then
      HIT_D := false                       {*IF NO HITS RETURN FALSE*}
   else
      HIT_D := true;                       {*ELSE RETURN TRUE*}
end {*HIT_D*};
.PA 200
{****************}
{*PROCEDURE:MAIN*}
{****************}
begin {*MAIN*}
   textbackground(0);
   textcolor(0);
   clrscr;
   WMSAVE_SCR(1,1,1,4,2);         {*SAVE THE SPRITE AREA*}
   WMSAVE_SCR(2,1,4,4,5);         {*SAVE THE SPRITE AREA*}
   WMSAVE_SCR(3,1,7,4,8);         {*SAVE THE SPRITE AREA*}
   WMSAVE_SCR(4,1,10,4,11);       {*SAVE THE SPRITE AREA*}
   WMSAVE_SCR(5,1,13,4,14);       {*SAVE THE SPRITE AREA*}
   WMSAVE_SCR(6,1,16,4,17);       {*SAVE THE SPRITE AREA*}
   WMSAVE_SCR(7,1,19,4,20);       {*SAVE THE SPRITE AREA*}
   WMSAVE_SCR(8,1,22,4,23);       {*SAVE THE SPRITE AREA*}

   textbackground(1);
   writeln(scr,' 1  ');           {*PUT SPRITE 1 ON SCREEN*}
   writeln(scr,'    ');

   gotoxy(1,4);
   textbackground(2);
   writeln(scr,' 2  ');           {*PUT SPRITE 2 ON SCREEN*}
   writeln(scr,'    ');

   gotoxy(1,7);
   textbackground(3);
   writeln(scr,' 3  ');           {*PUT SPRITE 3 ON SCREEN*}
   writeln(scr,'    ');

   gotoxy(1,10);
   textbackground(4);
   writeln(scr,' 4  ');           {*PUT SPRITE 4 ON SCREEN*}
   writeln(scr,'    ');

   gotoxy(1,13);
   textbackground(5);
   writeln(scr,' 5  ');           {*PUT SPRITE 5 ON SCREEN*}
   writeln(scr,'    ');

   gotoxy(1,16);
   textbackground(6);
   writeln(scr,' 6  ');           {*PUT SPRITE ON SCREEN*}
   writeln(scr,'    ');

   gotoxy(1,19);
   textbackground(7);
   writeln(scr,' 7  ');           {*PUT SPRITE ON SCREEN*}
   writeln(scr,'    ');

   gotoxy(1,22);
   textbackground(1);
.PA 201
   writeln(scr,' 8  ');           {*PUT SPRITE ON SCREEN*}
   writeln(scr,'    ');
   textbackground(0);

   nsp := 8;
   while not(keypressed) do       {*WHILE NO KEY PRESSED*}
   begin
      dist := 0;                  {*MOVE DISTANCE*}
      dirnum := 0;                {*DIRECTION*}
      sp := 0;                    {*SPRITE NUMBER*}

      while not(dirnum in[1,2,3,4]) do
  dirnum := random(5);     {*GET VALID DIRECTION*}

      while not(sp in[1,2,3,4,5,6,7,8]) do
  sp := random(9);         {*GET VALID SPRITE NUMBER*}

      case dirnum of
  1 : begin                                    {*DIRECTION LEFT*}
  dir := 'l';
  dist := random(81);
  if(dist > 0)and(ID_WM[sp]^.l-dist > 1) then
     if(HIT_L(sp,nsp) = false) then     {*IF NO HITS*}
        WMSLIDE(sp,dir,dist);           {*MOVE SPRITE*}
      end {case 1};
  2 : begin                                    {*DIRECTION RIGHT*}
  dir := 'r';
  dist := random(81);
  if(dist > 0)and(ID_WM[sp]^.r+dist < 80) then
     if(HIT_R(sp,nsp) = false) then     {*IF NO HITS*}
        WMSLIDE(sp,dir,dist);           {*MOVE SPRITE*}
      end {case 2};
  3 : begin                                    {*DIRECTION UP*}
  dir := 'u';
  dist := random(26);
  if(dist > 0)and(ID_WM[sp]^.t-dist > 1) then
     if(HIT_U(sp,nsp) = false) then     {*IF NO HIT*}
        WMSLIDE(sp,dir,dist);           {*MOVE SPRITE*}
      end {case 3};
  4 : begin                                    {*DIRECTION DOWN*}
  dir := 'd';
  dist := random(26);
  if(dist > 0)and(ID_WM[sp]^.b+dist < 25) then
     if(HIT_D(sp,nsp) = false) then     {*IF NO HITS*}
        WMSLIDE(sp,dir,dist);           {*MOVE SPRITE*}
      end {case 4};
      end {case dirnum};
   end {while};
   window(1,1,80,25);
   textcolor(7);
   clrscr;
end {*MAIN*}.
.PA 202
      Rules Notes
      Tips & Traps: The above "well over the top" example shows just what can
      be achieved using the WMSAVE_SCR() procedure, although
      obviously there are a lot more features than those
      demonstrated here, the WMSLIDE() procedure is also used.
         The programme consists of the main procedure, which
      sets up all the sprites or user saved screen areas and
      controls the flow of the programme through a while loop
      and case statement.
         The case statement decides which direction the sprite
      will move in and then calls one of the four separate
      procedures which are used to detect if the sprite will
      collide with any other sprite on the screen, if it would
      then it is not moved and the loop is executed again.
         The four separate HIT_?  procedures provide a basic
      means of collision detection, but it does assume that all
      the sprites or user saved screen areas are the same size,
      they can however be as small or large as you like, you can
      also have as many or as few as you like, but make sure
      that you adjust all the appropriate variables.  Its
      probably best if you study the example for a while.
         All the sprite movements and directions are completely
      random and they will never colide with each other.
         This procedure saves the screen area bounded by the
      supplied values l,t,r,b into an IS_SC[] background
      "screen" array, which is part of the win/men record
      structure created when NEW_WIN() or NEW_MENU() was called.
         If the supplied ID number is less than 20 then it is a
      USER WMSAVE_SCR() and checks are carried out to see if a
      "wmrec" record already exists for that ID, if one does
      then WMSAVE_SCR() has already been called at least once
      with that ID and so a new "wmrec" is not required, only
      the ID_SC[] background "screen" array is deleted and
      recreated for that ID. If a "wmrec" does not exist for
      that ID then one is created along with an IS_SC[]
      background "screen" array, the screen area is saved into
      it and the "wmrec" is filled in appropriatly.
         A user screen area when cleared using WCLRSCR() is
      cleared to the limits of l,t,r,b ie.  It is active right
      to its edges, unlike a window which is only active to
      l+1,t+1,r-1,b-1 ie.  1 char less all round, this is to
      allow for the window boarder.
         When the USER saves an area with WMSAVE_ SCR(0-19);
      information held in the "wmrec" record apart from its
      l,t,r,b bounds are the text background and foreground
      colours, tb and tf, which are taken from the TEXTATTR
      system variable at the time of saving, so whatever colours
      were active when WMSAVE_SCR(0-19); was called, will be
      used each time the USER saved screen is cleared, using
      WCLRSCR(); or written to, after selecting using WSELECT();
      The colours of a USER screen or indeed any window can be
.PA 203
      changed using WCOLOUR(); or WMATTRIBS(); the later will
      also change the colours of menus, these will all update
      the "wmrec" record, the only other USER saved screen
      "wmrec" record variable to be assigned is ID^.horc this is
      assigned thus:- ID^.horc := (TEXTATTR shl 8) + ' ';
      refer to the "wmrec" record type sub-section in the
      "Winmen Declared Types" section for information on all
      of the "wmrec" fields.
         Another subtle difference between USER saved screens
      and windows and menus, that is transparent to the user is
      as follows:-
         When a USER screen is saved using WMSAVE_SCR(); the
      procedure allocates the ID sent a record for holding the
      various parameters & attributes associated with the screen
      area, it also allocates just enough memory to hold the
      data for that area of screen and no more. The record is
      allocated just once to an ID, during that first call to
      WMSAVE_SCR(); and is used each time that ID calls
      WMSAVE_SCR(); The ID_SC[] background "screen" array, data
      area however is freed and re-allocated each time that ID
      calls WMSAVE_SCR(); this is to prevent data above that
      "screen" data area being overwriten should the user
      request a larger screen area to be saved into an existing
      IDs screen data area. This problem does not occure with
      windows & menus as they are always of a fixed size prior
      to displaying and are only changed through calls NEW_WIN()
      and NEW_MENU(), which handles the allocating of memory for
      both record & screen (see above).
.PA 204
      ------------------------------------------------------------------------
      WMSLIDE()                                                      WMSLIDE()
      ------------------------------------------------------------------------

      Procedure :   Slide the window, menu or user saved screen area (ID<20)
      High Level    whose reference is "ID", "nchars" in the direction of
      "dir".
         The window, menu or user saved screen area must have
      previously been created using NEW_WIN(); NEW_MENU(); or the
      WMSAVE_SCR(); procedure.
         The WMSLIDE(); procedure will slide any object in any
      direction required, left, right, up or down.
         All objects are slid exactly as you see them, so what
      is on the screen is exactly what is slid, this includes
      the contents of a window or user screen.
         The WMSLIDE(); procedure, for reasons of speed and
      efficiency, that will be explained later, does not have
      any dealings at all with the Winmen 3D screen map. What
      this means is that objects that are either partially or
      fully obscured, by other objects, are not automatically
      floated to the surface prior to the slide being carried
      out, neither are the objects "maprecs" deleted at its
      old position or created at the position it has been slid
      to, this is all left up to the user.
         The WMSLIDE(); procedure is the only routine that does
      not call either WMDEL_MREC_ALL(); WMADD_MREC_ALL(); or
      WMFLOAT(); out of all the routines that may need to do so.
         The reasons for this are as follows:-
         You may, for instance, want to slide an object in more
      than one direction in two consecutive calls to WMSLIDE();
      if the WMSLIDE(); procedure were to delete and add
      maprec's and call WMFLOAT(); for you, each time it was
      called, we would get a time lag at the start and end of
      each call, while the object was floated and maprec's were
      deleted from the objects old position and then added to
      the objects new position in the 3D screen map, although
      this lag is only very very small, it is enough to
      introduce a jerk into what is otherwise a perfectly smooth
      sliding action. Dont forget we only want the object,
      currently being slid, to be linked into the 3D screen map
      in its final resting place. All other maprec's belonging
      to that object must be deleted, if they are not they will
      cause unpredictable behavior, possibly resulting in a
      trashed screen, or even a crash.
         The above implication does of course mean that the user
      will have to carry out these operations for himself,
      making sure that the object to be slid is firstly brought
      to the surface using WMFLOAT(); unless it is already one
      of the topmost objects, in which case there is no need to
      call WMFLOAT(); next you will have to make a call to
.PA 205
      WMDEL_MREC_ALL(); to delete the objects "maprecs" from the
      current position, you are now free to slide the object as
      many times, and in as many directions as you wish, or just
      the once, without making further calls to either
      WMDEL_MREC_ALL(); or WMADD_MREC_ALL(); When you have
      completely finished sliding the object, for this
      particular operation, and it is in its final resting
      place, you must now make the call to WMADD_MREC_ALL(); to
      relink the object into the Winmen 3D screen map system.
         If the above procedure is not followed, make no
      mistake, you will finish up with a corrupted display,
      especially if obscured objects are slid before firstly
      floating them to the surface. If you have missed a call
      to either WMDEL_MREC_ALL(); or WMADD_MREC_ALL(); it may
      be a while before a problem manifests its self, but it
      will. However the WMADD_MREC_ALL(); procedure does make
      it impossible for the user to create more than one set
      of "maprecs" in the same screen position, as it will
      simply exit with no action if this is attempted.
         For more information on the Winmen 3D screen map system
      and its associated types and variables please refer to
      the "maprec" type sub-section in the "Winmen Declared
      Types" section.
         The screen background image you may expect to see
      revealed from under an object, during sliding, may be
      diferent to what you are expecting, if for some reason it
      has been changed by using the WMSAVE_SCR() procedure on
      that particular ID at some time.
         The window, menu or user saved screen "wmrec" record is
      updated to reflect any changes.
         This procedure is not called by any of the functions
      or procedures in the Winmen unit.

      Syntax      : WMSLIDE(ID,dir,nchar);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a 
      user saved screen area.
         If the ID does not yet exist the programme will abort
      with an error message (see appendix for list).

      dir : char;
      This is the direction in which you wish to move the window
      menu or USER saved screen area and can be any of the 
      following characters:- 'l','L','r','R','u','U','d','D'.
      Any other character is ignored and no slide will take 
      place. 
.PA 206
      nchar : byte;
      This is the distance or number of characters that you wish
      to slide the window, menu or USER saved screen area. If 
      this distance will put any part of the window, menu or USER
      saved screen area off the screen, then the programme will 
      be aborted with an error message to that effect (see 
      apendix for list).

      Example 1   : program WM_SLIDE1;

      uses dos,crt,winmen;

      var
         m1,m1c,l,t : byte;            {*DEFINE GLOBALS*}
         mt : pt_mtext;
         first : boolean;

      begin
         getmem(mt,4*81);              {*DEFINE MENU*}
         mt^[1] := '8) Slide Up';
         mt^[2] := '4) Slide Left';
         mt^[3] := '6) Slide Right';
         mt^[4] := '2) Slide Down';
         m1 := NEW_MENU(1,4,14,7,0,15,mt,4);
         freemem(mt,4*81);

         l := 32;                      {*INIT VARBS*}
         t := 9;
         m1c := 10;
         WMTITLE(m1,1,15,'SLIDE ME');  {*DO TITLE STRING*}

         first := true;                {*SET FIRST FLAG TRUE*}
         while not(m1c in[0]) do       {*WHILE NOT ESCAPE*}
         begin
     m1c := POP_MENU(m1,l,t);
     case m1c of                {*IF FIRST TIME OPENED*}
     1..4 : if(first = true) then
     begin
        WMFLOAT(m1);     {*FLOAT MENU TO SURFACE*}
        WMDEL_MREC_ALL(m1);{*DELETE ALL MRECS*}
        first := false;  {*SET FIRST FLAG FALSE*}
     end;
     end {case}
     case m1c of
        1 : if(t > 1) then      {*MOVE MENU UP 1 ROW*}
     begin
        dec(t);
        WMSLIDE(m1,'u',1);
     end;
.PA 207
        2 : if(l > 2) then      {*MOVE MENU LEFT 1 COL*}
     begin
        dec(l,2);
        WMSLIDE(m1,'l',2);
     end;
        3 : if(ID_WM[m1]^.r+2 < 80) then
     begin               {*MOVE MENU RIGHT 1 COL*}
        inc(l,2);
        WMSLIDE(m1,'r',2);
     end;
        4 : if(ID_WM[m1]^.b+1 < 25) then
     begin               {*MOVE MENU DOWN 1 ROW*}
        inc(t);
        WMSLIDE(m1,'d',1);
     end;
     end {case};
         end {while};
         normvideo;
      end {WM_SLIDE}.

      Example 2   : program WM_SLIDE2;

      uses dos,crt,winmen;

      var
         m1,m1c,l,t : byte;            {*DEFINE GLOBALS*}
         mt : pt_mtext;

      begin
         getmem(mt,4*81);              {*DEFINE MENU*}
         mt^[1] := '8) Slide Up';
         mt^[2] := '4) Slide Left';
         mt^[3] := '6) Slide Right';
         mt^[4] := '2) Slide Down';
         m1 := NEW_MENU(1,4,14,7,0,15,mt,4);
         freemem(mt,4*81);

         l := 32;                      {*INIT VARBS*}
         t := 9;
         m1c := 10;
         WMTITLE(m1,1,15,'SLIDE ME');  {*DO TITLE STRING*}

         while not(m1c in[0]) do       {*WHILE NOT ESCAPE*}
         begin
     m1c := POP_MENU(m1,l,t);
     case m1c of
        1 : if(t > 1) then      {*MOVE MENU UP 1 ROW*}
     begin
        dec(t);
        WMFLOAT(m1);
        WMDEL_MREC_ALL(m1);
        WMSLIDE(m1,'u',1);
        WMADD_MREC_ALL(m1);
     end;
.PA 208
        2 : if(l > 2) then      {*MOVE MENU LEFT 1 COL*}
     begin
        dec(l,2);
        WMFLOAT(m1);
        WMDEL_MREC_ALL(m1);
        WMSLIDE(m1,'l',2);
        WMADD_MREC_ALL(m1);
     end;
        3 : if(ID_WM[m1]^.r+2 < 80) then
     begin               {*MOVE MENU RIGHT 1 COL*}
        inc(l,2);
        WMFLOAT(m1);
        WMDEL_MREC_ALL(m1);
        WMSLIDE(m1,'r',2);
        WMADD_MREC_ALL(m1);
     end;
        4 : if(ID_WM[m1]^.b+1 < 25) then
     begin               {*MOVE MENU DOWN 1 ROW*}
        inc(t);
        WMFLOAT(m1);
        WMDEL_MREC_ALL(m1);
        WMSLIDE(m1,'d',1);
        WMADD_MREC_ALL(m1);
     end;
     end {case};
         end {while};
         normvideo;
      end {WM_SLIDE}.

      Rules Notes
      Tips & Traps: The two above examples show quite clearly when and where
      to use the WMFLOAT(); WMDEL_MREC_ALL(); and
      WMADD_MREC_ALL(); procedures to support the WMSLIDE();
      procedure.
         Before we start make sure you have the Num Lock, on
      the keyboard set to on before you start, then you can
      use the 8,6,2 and 4 direction keys on the keypad.
         The first example shows the correct way to tackle that
      particular situation. A flag is set prior to entering the
      while loop, which is the main control loop for the
      POP_MENU(); function, as soon as a valid menu option is
      returned, requiring the menu to be slid, the first case
      statement picks this up and if the "first" flag is set
      "true", indicating this is the first time the menu has
      been used in this while loop, and the chosen option
      was 1..4 then we float the menu to the surface, not realy
      necassary here but put in for the purposes of the
      demonstration, we then delete all the menus "maprecs",
      this is necassary as the menu is about to be slid. The
      second case statement then picks up and slides the menu in
      the correct direction, the desired number of characters.
      No mater how many times we now slide the menu from within
      that while loop no more calls will need to be made to
.PA 209
      WMFLOAT(); WMDEL_MREC_ALL(); or WMADD_MREC_ALL(); as the
      "first" flag remains "false", it is not necassary, as the
      menu is never obscured by other objects and there are no
      other operations to worry about in that while loop. The
      menu and while loop both exit when the escape key is
      pressed. The "first" flag would be set back to "true" if
      the programme flow ever returned to this while loop. We
      never need to make a call to WMADD_MREC_ALL(); because,
      just as we finish using the menu, it is automatically
      closed when we press the escape key and closed objects
      never need to be linked into the Winmen 3D screen map
      system, the fact that the POP_MENU(); function will be
      calling the WMCLOSE(); procedure, which in turn calls
      WMFLOAT(); or WMREMOVE(); without there being any
      "maprecs" present in the 3D screen map, for this menu,
      will not cause any harm, as both WMFLOAT(); and
      WMREMOVE(); will exit, without taking any action, if there
      checks reveal ther are no "maprecs" for this object, and
      as the menu is already on the surface, no float is
      required anyway and a close by WMREST_SCR(); will be
      visibly, exactly the same as a close by WMREMOVE(); if the
      object is on the surface (Please refer to the WMCLOSE();
      procedure for more detail). If for example we only exited
      the menu and did not automaticaly close it, by having an
      option on the menu to do this, then we would need to make
      a call to the WMADD_MREC_ALL(); procedure to ensure the
      menu was fully linked into the 3D screen map. Tackeling
      the above situation in this way makes the sliding much
      smoother, as I am sure you will aggree, when you compare
      it to the second example.
         The second example is still correct, but is a good
      example of overkill, a real belt and braces effort, this
      shows up in the speed of sliding, if you keep any one of
      the numeric keypad 8,6,2, or 4 keys pressed, with Num
      Lock on, you will see how jerkey it is when compared
      with the first example, this is because we are calling
      WMFLOAT(); and WMDEL_MREC_ALL(); before every single
      slide and WMADD_MREC_ALL(); after every slide, this is
      totaly unnecessary as we do not intend carrying out any
      other operations in between any of the slide operations,
      that will require the Winmen 3D scren map system to be
      updated with the menus new position, if we were then we
      would indeed have to use this method.
         If we just wanted to make one slide in one direction,
      as is most often the case, then we would carry out the
      following operations:-

         code.....
         WMFLOAT(ID);
         WMDEL_MREC_ALL(ID);
         WMSLIDE(ID,dir,nchar);
         WMADD_MREC_ALL();
         code.....
.PA 210
         Or if we wanted to make more than one slide at the
      same time:-

         code.....
         WMFLOAT(ID);
         WMDEL_MREC_ALL(ID);
         WMSLIDE(ID,dir,nchar);
         WMSLIDE(ID,dir,nchar);
         WMSLIDE(ID,dir,nchar);
         WMADD_MREC_ALL();
         code.....

         So it would pay you to study the examples to enable
      you to get the most from the WMSLIDE(); procedure.
         This procedure determines the direction of sliding and
      calls all the required routines to facilitate the slide,
      it also checks that the win/menu ID is a valid one and
      that sliding it nchars will not take it off the screen,
      both of these are error conditions and cause the currently
      executing programme to terminate with an error message.
         If the chosen window exists but is not open it is
      opened for use and will be the active one after sliding.
         If the window was open but was not the active one it
      will be slid and will be the active one after sliding.
         An attempt to slide a closed menu will result in a
      termination as it cannot be opened from the slide
      routine. Open active and non active menus may be slid,
      Including POP LIST and BAR Menus.
         A user saved screen area may also be slid.
.PA 211
      ------------------------------------------------------------------------
      WMTITLE()                                                      WMTITLE()
      ------------------------------------------------------------------------

      Procedure   : Put a title string "str" into the top edge of a window, 
      High Level    menu or USER saved screen area (ID < 20), whoose reference 
      is ID, the "str" must be <= (r-l-3), to enable it to fit.
         The window, menu or USER saved screen area must have
      previously been created using NEW_WIN(), NEW_MENU() or
      WMSAVE_SCR().
         The objects border background "bb" and foreground "bf"
      colours must also be specified.
         A window or USER saved screen area should be titled
      after it is opened, whereas a menu should be titled before
      it is opened (see below).
         A window or USER saved screen may be titled when it is
      open or closed, active or non active.
         A closed window or USER screen will be opened
      automaticaly when WMTITLE() is used, thus it will become
      the active one after titling, so if you do not want it to
      become the active one, you must title it after opening.
         A menu may be titled when it is open and non-active or
      when it is closed, it cannot be titled if it is open and
      active, as you are actually inside the menu procedure, it
      becomes non-active as soon as a selection is made and
      control is passed back to the main programme, so If you
      want a title to appear on a menu as soon as it is opened
      then you must title it before it is opened, thus:-
         WMTITLE(ID,bb,bf,str);
         POP_MENU(ID,t,l);
         The earliest a menu can be titled after opening is when
      it becomes non-active ie.  after a selection has been
      made:-
         POP_MENU(ID,t,l);
         WMTITLE(ID,bb,bf,str);
         So in the later case the title would not appear on the
      menu until a selection had been made and control passed
      back to the main programmes next instruction which is
      WMTITLE().
         The WMTITLE() procedure does not normaly affect window,
      menu or USER saved screen currency, only as above.
         The procedure will halt the programme with an error
      message if an attempt is made to title a window, menu or
      USER saved screen that does not yet exist.
         This procedure does not alter any of the IDs record
      varbs, except for the ID^.op_cl flag of a window or USER
      screen, if it was closed prior to using WMTITLE(), the
      flag will of course be set to true after use.
         This procedure is not called by any of the Winmen units
      functions or procedures.

      Syntax      : WMTITLE(ID,bb,bf,str);              
.PA 212
      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      USER saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a
      USER saved screen area.
         If the ID does not yet exist the programme will abort
      with an error message (see appendix for list).

      bb : byte; (B0_15)
      Border background colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).

      bf : byte; (B0_15)
      Border foregroung colour, must be a value in the range
      0-15 or a range check error will occure. (see colour
      table below).                                             

      str : string;
      This is the actual title string to be used, it will be
      placed, centred, in the top edge of the border, or the top
      row, if it is a USER saved screen area.
         The string must conform to the following formula:-
      "str" <= (r-l-3) where r and l are the right and lefthand
      edges of the window, menu or USER saved screen area
      respectivly, if "str" is too long the programme will abort
      with an error message (see apendix for list).
         The string will be displayed between two vertical twin
      bar characters thus:- ºTitle Stringº the string can of
      course be consatenated from any of the valid ascii type
      characters.

      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink                            
.PA 213
      Example     : program WM_TITLE;

      uses dos,crt,winmen;              {*DEFINE USED UNITS*}

      var                               {*DEFINE GLOBALS*}
         m1,m1c,w1 : byte;
         mt : pt_mtext;

      begin
         textbackground(0);
         textcolor(7);
         clrscr;
         getmem(mt,9*81);               {*DEFINE MENU & WINDOW*}
         mt^[1] := '1) Open Window Then Title';
         mt^[2] := '2) Title Window Then Open';
         mt^[3] := '3) Title Menu Then Open';
         mt^[4] := '4) Title An Open Menu';
         mt^[5] := '5) Clear Menu Title';
         mt^[6] := '6) Slide Window (No Effect)';
         mt^[7] := '7) Move Window (No Effect)';
         mt^[8] := '8) Slide Menu (No Effect)';
         mt^[9] := '9) Move Menu, (No Effect)';
         m1 := NEW_MENU(4,4,14,7,0,15,mt,9);
         freemem(mt,9*81);

         w1 := NEW_WIN(3,15,34,24,4,4,14,7,0);

         m1c := 10;
         while not(m1c in[0]) do        {*WHILE ESC NOT PRESSED*}
         begin
     m1c := POP_MENU(m1,3,3);
     case m1c of
        1 : begin                {*OPEN WIN THEN TITLE*}
        WOPEN(w1);
        write(scr,'Opened <RET> = Title');
        readln;
        WMTITLE(w1,1,15,'WINDOW TITLE');
        write(scr,'Titled <RET> = Re-Title');
        readln;
        WMTITLE(w1,1,15,'TITLE');
        write(scr,'Re-Titled <RET> = Menu');
        readln;
        WMCLOSE(w1);
     end;
.PA 214
        2 : begin                {*TITLE WIN AUTO OPEN*}
        WMTITLE(w1,1,15,'WINDOW TITLE');
        writeln(scr,'Opened = Automatic');
        write(scr,'Titled <RET> = Re-Title');
        readln;
        WMTITLE(w1,1,15,'TITLE');
        write(scr,'Re-Titled <RET> = Menu');
        readln;
        WMCLOSE(w1);
     end;
        3 : begin                {*TITLE MENU THEN OPEN*}
        WMCLOSE(m1);
        WMTITLE(m1,1,15,'MENU TITLE');
        gotoxy(3,3);
        normvideo;
        write(scr,'Titled <RET> = Menu');
        readln;
        clrscr;
     end;
        4 : begin                {*OPEN MENU THEN TITLE*}
        WMTITLE(m1,1,15,'ITS A MENU TITLE');
        gotoxy(3,2);
        normvideo;
        write(scr,'Titled <RET> = Re-Title');
        readln;
        WMTITLE(m1,1,15,'TITLE');
        gotoxy(3,2);
        clreol;
        write(scr,'Re-Titled <RET> = Menu');
        readln;
        gotoxy(3,2);
        clreol;
     end;
        5 : WMCLOSE(m1);         {*CLEAR MENU TITLE*}
        6 : begin
        WMTITLE(w1,1,15,'WINDOW TITLE');
        WMDEL_MREC_ALL(w1);
        WMSLIDE(w1,'r',46);
        WMSLIDE(w1,'l',46);
        WMADD_MREC_ALL(w1);
        delay(1000);      {*SLIDE WINDOW, TITLE*}
        WMCLOSE(w1);      {*WILL NOT BE LOST*}
     end;
        7 : begin                {*MOVE WINDOW, TITLE*}
        WOPEN(w1);        {*WILL NOT BE LOST*}
        WMTITLE(w1,1,15,'WINDOW TITLE');
        delay(1000);      {*AS WMMOVE() DOES'NT*}
        WMMOVE(w1,49,15); {*CALL WMCLOSE(), IT*}
        delay(1000);      {*DID IN V1.0*}
        WMMOVE(w1,3,15);
        delay(1000);
        WMCLOSE(w1);
     end;
.PA 215
        8 : begin                {*SLIDE MENU, TITLE*}
        WMFLOAT(m1);      {*WILL NOT BE LOST*}
        WMDEL_MREC_ALL(m1);
        WMSLIDE(m1,'r',46);
        WMSLIDE(m1,'l',46);
        WMADD_MREC_ALL(m1);
     end;
        9 : begin                {*MOVE MENU, TITLE*}
        WMMOVE(m1,49,3);  {*WILL NOT BE LOST*}
        delay(1000);
        WMMOVE(m1,3,3);
     end;
     end {case};
         end {while};
         normvideo;
      end {WM_TITLE}.

      Rules Notes
      Tips & Traps: If the WMTITLE() proc is used just prior to WMATTRIBS() the
      WMATTRIBS() procedure will effectively delete the title
      just issued, THIS WILL ONLY APPLY TO CLOSED MENUS, as the
      WMATTRIBS() procedure re-builds the mmenu array using the
      new supplied values, open menus will not be affected as the
      WMATTRIBS() procedure does not write to the screen, open or 
      closed windows and USER saved screens will not be affected 
      as they are generated and do not have a permenant array 
      like the mmenu array for menus. 
         Closed menus that are titled and intended for opening
      as bar menus will not display a title as the border part
      of the mmenu array is not used in the _MBDISPLAY()
      procedure which displays the bar menu, as it seems
      inapropriate to give a bar menu a titling facility.
         Open bar menus that are titled will not have there title
      displayed even after a call to WMMOVE(), for the same 
      reason as above. 
         All other open or closed windows, menus and USER saved
      screens may be titled, if they are open the title will be
      displayed immediatly, menus will also have the title put
      into there ID_MM[] "mmenu" array, so that if they are
      moved with the WMMOVE() procedure the title will still
      display, we need to do this as the WMMOVE() procedure
      calls the _MDISPLAY() procedure for list and pop menus,
      which does use all of the ID_MM[] "mmenu" array, including
      the border, for bar menus see above.
         If a title already exists and it is longer than the new
      one, it will still be fully overwritten by the WMTITLE()
      procedure, both on the screen, if open, and in a menus
      ID_MM[] "mmenu" array. If the new shorter title was put
      into the menus ID_MM[] "mmenu" array, without padding it
      out to fully overwrite the old longer one and an open menu
      was retitled, with a shorter title, the screen would be
      visually correct, because we overwrite the old title
      there, but if the menu was moved with the WMMOVE()
.PA 216
      procedure both the old and new underlapping title would
      appear after the move, this is because WMMOVE() calls the
      _MDISPLAY() procedure, which uses all of the menus
      ID_MM[] "mmenu" array to display the menu, which would
      still contain part of the old longer title.
         The character used to wipe out the old title when
      WMTITLE() or WMCLOSE() are called will be in the "wmrec"
      record variable "horc" which was set up in the call to
      NEW_WIN() or NEW_MENU(). USER saved screens also use this
      character word which in there case was set up in the call
      to WMSAVE_SCR()
      and is made up thus "horc" := (TEXTATTR shl 8) + ' '; so 
      any existing title will be wiped using space characters, 
      the colours will be as per the TEXTATTR byte at the time of 
      calling the WMSAVE_SCR() procedure.
         Any window, menu or USER saved screen area that has a
      title will loose the title when it is closed using the
      WMCLOSE() procedure, this is so that you can have a
      titleless window menu or USER saved screen, each time it
      is opened, of course there are thoes that will say the
      titles ought to be retained. For windows and USER saved
      screens this is not possible as they are generated each
      time they are opened, they do not have a permenant array
      like the ID_MM[] "mmenu" array for menus, and to have one
      would take up much more valuable memory space, so there is
      nowhere to put the title, menus could conceivably retain
      there titles by removing the code which deletes them from
      the WMCLOSE() procedure, the only problem then would be
      deleting a title if one were not required, we would need
      to send a string of that menus border type chars each time
      we wanted to delete a title.
         The example takes you through and demonstrates
      everything we have discussed here, it would pay you to
      study it.
.PA 217
      ------------------------------------------------------------------------
      _WMADD_MREC()                                              _WMADD_MREC()
      ------------------------------------------------------------------------
     
      Procedure   : Creat a new maprec and add it to the 3D screen map on the
      Low Level     END of the list at the specified position.
         The new record is created dynamically and the three 
      fields are filled in [IDB,sc_inx,next].
         Memory for the maprec is allocated from the heap, no 
      checks are carried out to see if there is sufficient 
      contigious memory available for the new record.  
         The "next" field of the maprec preceding the current 
      one, in the singley linked list, is filled in with the 
      pointer value (address) of the newley created record, thus 
      linking the new record to the end of the list at the 
      specified map index position (map_inx).
         If the newley created maprec is the first one in the 
      list then its pointer value (address) is put into the 3D 
      screen map array (MAP[map_inx]) at the specified index 
      (map_inx). The "next" field of the newley created record 
      is always set to nil, indicating the END of the list.
         This procedure assumes that no maprec exists for the 
      object, at the specified map_inx location.  If one does 
      then there will be two in the list at location map_inx 
      after this routine has been called, this may cause 
      unpredictable results, or even a fatal error when calling 
      any ather routine that uses the 3D screen map.  
         The objects ID must have been previously created using 
      NEW_WIN() or NEW_MENU().  
         No checks are carried out on the validity of data 
      supplied to this low level procedure, so extra care is 
      needed.
         The objects wmrec is not affected by this procedure.        
         This procedure is called by:- WMADD_MREC_ALL();

      Syntax      : _WMADD_MREC(ID,sc_inx,map_inx);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or 
      NEW_WIN() and as sent to WMSAVE_SCR(0-19), if it is a user 
      saved screen area.
         If the ID does not yet exist the results will be 
      unpredictable, as the ID is not checked.                   

      sc_inx : word;
      This is the index into the objects screen array,
      (ID_SC[ID]^[sc_inx]), that corresponds to the "map_inx"
      position in the 3D screen map array. This effectively
      crossreferences and locates that objects screen array
      within the 3D screen map.
.PA 218
      map_inx : word;
      This is the 3D screen map location index, (MAP[map_inx])
      at which the newley created maprec is to be linked, at the
      END of the list, or to MAP[map_rec] if it is the first
      record. 
         The address of the new record is put into the 
      MAP[map_rec] pointer variable if it is the first record in 
      the list, or into the "next" field pointer variable of the 
      maprec on the END of the list if some already exist.

      Example     : procedure _ADDRECS(ID);                                     
        
      var                                                                       
         ncol,nrow : byte;                                             
         mapinx,map_inx,sc_inx : word;                                          
         IDWM : pt_wmrec;                                                       
        
      begin                                                                     
         IDWM := ID_WM[IDB];            {*GET POINTER TO WMREC*} 
         nrow := num rows in object     {*CALC ROWS & COLS*}     
         ncol := num cols in object      
         mapinx := top left char posn   {*CALC MAP ARRAY INDEX*} 
         sc_inx := 0;                   {*W/M SCREEN ARRAY INX*} 
         for each row in object do      {*FOR EACH ROW IN OBJ*}  
         begin                                                                  
     map_inx := mapinx;          {*UPDATE MAP COL INDEX*} 
     for each column in row do   {*FOR EACH COL IN ROW*}  
     begin                       {*ADD A RECORD TO MAP*}                        
        _WMADD_MREC(ID,sc_inx,map_inx);  
        inc map_inx to next column{*INC MAP ROW INDEX*}    
        inc sc_inx to next column{*INC OBJ SCR ARY INX*}
     end;                        {*INC TO NEXT ROW*}                            
     inc mapinx to start of next row 
         end;                                                                   
      end {_ADDRECS};                                                          

      Rules Notes
      Tips & Traps: The above example, in a mixture of Pascal and psuedo code,
      shows how to add, or link in, a complete maprec structure 
      to the 3D screen map, for any given object (window, menu 
      or user screen). 
         The example, in common with any of the Windows and 
      Menus low level routines, does not carry out any checking 
      on the parameters supplied to it by the user.  There would 
      also normaly be quite a few internal checks caried out, 
      for instance, on memory, to ensure there is going to be 
      sufficient to allocate for the new records, this would 
      usualy be done by a preceding high level type routine.
         As with any routine that uses pointers and allocates or 
      deallocates memory, as does _WMADD_MREC(); great care 
      needs to be taken and in particular checks needs to be 
      carried out prior to its use.
.PA 219
      ------------------------------------------------------------------------
      _WMBOX_ATTRIBS()                                        _WMBOX_ATTRIBS()
      ------------------------------------------------------------------------
      Procedure   : Internal procedure called by the following functions an
      Low Level     procedures:- NEW_WIN(), NEW_MENU(), EXPLODE(), WMATTRIBS()  
      to set the six record variables "tlc", "trc", "blc", "brc
      "horc" and "verc", to there correct screen ram format wor
      values, for the supplied "btype", "bb" and "bf" values
      for that particular window or menu
      These six values will be a combination of character valu
      and the textbackground and foreground colours, see belo
      for a fuller explanation
      The window or menus record is updated to reflect th
      changes
      No checks are carried out on the validity of data supplie
      to this low level procedure, so extra care is needed.

      Syntax      : _WMBOX_ATTRIBS(btype,bb,bf,tlc,trc,blc,brc,horc,verc)

      Variables   : btype : byte; (B1_4)
      Type of border required around the menu edge, must be 
      value between 1-4 or a range check error will occure
      The possible border types are as follows:
         1 = Double horizontal and double vertical bars
         2 = Single horizontal and single vertical bars
         3 = Double horizontal and single vertical bars
         4 = Single horizontal and double vertical bars

      bb : byte; (B0_15)
      Border background colour, must be a value in the rang
      0-15 or a range check error will occure. (see colou
      table below)
 
      bf : byte; (B0_15)
      Border foregroung colour, must be a value in the rang
      0-15 or a range check error will occure. (see colou
      table below).                                            
 
      var tlc : word
      Address of the word attribute variable for the top left
      corner character of the box
 
      var trc : word
      Address of the word attribute variable for the top right
      corner character of the box
 
      var blc : word
      Address of the word attribute variable for the bottom left
      corner character of the box

      var brc : word
      Address of the word attribute variable for the bottom right
      corner character of the box
.PA 220
      var horc : word
      Address of the word attribute variable for the horizontal
      characters in the box
 
      var verc: word
      Address of the word attribute variable for the vertical
      characters in the box.                                                 

      All the values used for the character/colour attribute word are made up
      in the following way:
      CHARACTER,COLOUR (but DOS requires the low byte first!)
      Low byte first : Colour value. High byte last : ASCII character value

      The colour byte is made up in the following way:
      Bit 7 : blink flag 0=off 1=on --------------[0 100 1000]
      Bit 6 : -|                                      |    |
      Bit 5 :  -> Character background colour 0-7 ----|    |
      Bit 4 : -|                                           |
      Bit 3 : -|                                           |
      Bit 2 :  -> Character foreground colour 0-15 --------|
      Bit 1 : -|
      Bit 0 : -|

      A xref of useful box type ASCII char values and a colour value chart
      follows:
       CGA                    MDA
      Colour Table: Value      Foreground     Background
      0          Black          Black          Normal Background
      1          Blue           Blue           Underline
      2          Green          Green
      3          Cyan           Cyan
      4          Red            Red
      5          Magenta        Magenta
      6          Brown          Brown
      7          White          White          Normal Foreground
      8          Grey           Black+Blink
      9          Light Blue     Blue+Blink
      10         Light Green    Green+Blink
      11         Light Cyan     Cyan+Blink
      12         Light Red      Red+Blink
      13         Light Magenta  Magenta+Blink
      14         Yellow         Brown+Blink
      15         Bright Wight   White+Blink
.PA 221
      Dec   | 169 170 179 180 181 182 183 184 185 186 187 188 189 190 191
      Hex   |  A9  AA  B3  B4  B5  B6  B7  B8  B9  BA  BB  BC  BD  BE  BF
      ASCII |  ©   ª   ³   ´   µ   ¶   ·   ¸   ¹   º   »   ¼   ½   ¾   ¿

      Dec   | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
      Hex   |  C0  C1  C2  C3  C4  C5  C6  C7  C8  C9  CA  CB  CC  CD  CE
      ASCII |  À   Á   Â   Ã   Ä   Å   Æ   Ç   È   É   Ê   Ë   Ì   Í   Î

      Dec   | 207 208 209 210 211 212 213 214 215 216 217 218
      Hex   |  CF  D0  D1  D2  D3  D4  D5  D6  D7  D8  D9  DA
      ASCII |  Ï   Ð   Ñ   Ò   Ó   Ô   Õ   Ö   ×   Ø   Ù   Ú                  

      Example     : program BOX_ATTR
     
      uses dos,crt,winmen;             {*UNITS USED*}
     
      var                              {*DEFINE GLOBALS*}
         IDB : byte
         ID : pt_wmrec
         key : char
         btype,bb,bf : byte
     
      begin                            {*CREATE WINDOW*}
         IDB := NEW_WIN(5,5,50,20,1,1,14,7,0)
         ID := ID_WM[IDB]
         key := '1';                   {*INITIALISE KEY*}
     
         while(key <> ' ') do          {*WHILE SPACE BAR NOT*}
         begin                         {*PRESSED            *}
     WOPEN(IDB)
     clrscr
     writeln(scr,'Press A Key For A New Window')
     write(scr,'Or Space To Quit:')
     while not(keypressed) do;  {*WAIT FOR KEYPRESS*}
     key := readkey;            {*ASSIGN KEYPRESS TO KEY*}
     WMCLOSE(IDB)
     btype := 20; bb := 20; bf := 20
     while(btype > 4)or(btype = 0) d
        btype := random(4);     {*ASSIGN NEW RANDOM VALS*}
     while(bb > 15) d
        bb := random(15)
     while(bf > 15) d
        bf := random(15)
     _WMBOX_ATTRIBS(btype,      {*CALL THE ROUTINE*}
      bb,bf
      ID^.tlc,ID^.trc,ID^.blc,ID^.brc
      ID^.horc,ID^.verc)
         end
     
         normvideo;                    {*RESET TEXT ATTRIBS*}
      end
.PA 222
      Rules Notes
      Tips & Traps: The above example closes and opens a window, with a new
      box type "btype", border background "bb" and foreground
      "bf" colour each time a key is pressed, the values are
      all randomly chosen, the programme is exited by pressing
      the space bar
      This procedure will only have full effect on windows a
      they are generated using the above record variables each
      time they are opened. It will have no effect on USER save
      screens as they cannot be opened and do not have a border
      It will also have no effect on menus as there border is
      not generated each time they are opened, they are displayed
      from there mmenu array and that is only built in the call
      NEW_MENU(), although if _WMBOX_ATTRIBS() is called and
      the menu is then closed using WMCLOSE(), the next time it
      is opened the top row of the border will display in the
      new "btype", "bb" and "bf" values, this is because the
      WMCLOSE() procedure wipes the top row of the border to
      erase any title there may be, it uses the record variable
      tlc,trc,blc,brc,horc and verc to do so, which are of course
      changed in the call to _WMBOX_ATTRIBS()
.PA 223
      ------------------------------------------------------------------------
      _WMCHK_EXIST()                                            _WMCHK_EXIST()
      ------------------------------------------------------------------------
      Procedure   : Internal procedure called by the following functions an
      Low Level     procedures:- WOPEN(), WSELECT(), WCLRSCR(), WCOLOUR()
      POP_MENU(), LIST_MENU(), BAR_MENU(), MPIC_COL()
      MBAR_COMMENT(), MCHANGE_CHAR(), WMSAVE_SCR(), WMREST_SCR(
      WMCLOSE(), WMSLIDE(), WMDELETE(), WMTITLE(), WMATTRIBS()
      WMMOVE()
      The procedure is called by all the functions and procedure
      that take a wmrec record structure IDB byte identifier a
      one of the parameters
      A check is made to see if the requested record structur
      exists, the complete record structure comprises of the     
      wmrec structure and the screen array, for both windows and
      menus and additionally the mmenu array and comment array
      for menus, the check is done by seeing if the pointer for
      that IDs wmrec is nil or not thus:-
      if(ID_WM[ID] = nil) then; As you can see the pointer is in
      the appropriate pointer array.
      If the pointer is nil then the record structure does not
      yet exist, has been deleted with WMDELETE(), or it has
      somehow become unlinked, effectively that window, menu or
      USER saved screen does not exist and the current programme
      will abort with an error message (see appendix for list).
      If the pointer has a proper value then no action is taken
      and execution continues at the next instruction.
      This procedure does not alter any of the window, menu or
      USER saved screen areas wmrec variables or associated
      structure.

      Syntax      : _WMCHK_EXIST(ID,proc);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a
      user saved screen area.
      If the ID does not yet exist the programme will abort with
      an error message (see appendix for list).

      proc : string;
      This string should be the name of the function or procedure
      that is calling the _WMCHK_EXIST() procedure, i.e. if say
      a procedure called MYPROC() were to call _WMCHK_EXIST()
      the format should be as follows _WMCHK_EXIST(IDB,'MYPROC');
      Although of course in reality you could use any string you
      want up to a maximum of 255 characters.
      If the string is longer than 255 characters a range check
      error will occure.
.PA 224
      Example     : procedure MYPROC(ID : byte; a,b,c : sometype);
      var
         variables.....
      begin
         _WMCHK_EXIST(ID,'MYPROC');
         remainder
     of
        procedure
    statements
      end;

      Rules Notes
      Tips & Traps: No special instructions to be observed.
.PA 225
      ------------------------------------------------------------------------  
      _WMCHK_MEM()                                                _WMCHK_MEM()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by the following functions and
      Low Level     procedures:- NEW_WIN(), NEW_MENU(), MBAR_COMMENT(),
      WMSAVE_SCR().
         This procedure checks to see if there is enough
      contigous free memory space available for the calling
      procedure to allocate a new dynamic variable of size
      "blk_reqd".
         If there is then no action is taken and control is
      returned to the calling routine.
         If there are no memory blocks large enough then the
      current programme will be aborted with an error message to
      that effect (see appendix for list).
         The procedure actualy checks to see if blk_reqd >
      maxavail (maxavail is the Turbo function which returns the
      size of the largest contigous block of memory, as opposed
      to the Turbo function memavail which returns the total
      amount of free ram available in all free blocks).
         Please refer also to the Turbo Pascal version note
      section and the section for owners of the Winmen source
      files below.

      Syntax      : _WMCHK_MEM(proc,blk_reqd);

      Variables   : proc : string;
      This string should be the name of the function or procedure
      that is calling the _WMCHK_MEM() procedure, i.e. if say
      a procedure called MYPROC() were to call _WMCHK_MEM()
      the format should be as follows _WMCHK_MEM(2048,'MYPROC');
      Although of course in reality you could use any string you
       want up to a maximum of 255 characters.
      If the string is longer than 255 characters a range check
      error will occure.                                        

      blk_reqd : word;
      This word value is the size of block of contigous memory 
      that the calling routine requires.
      This must be a value in the range 0-65535 or a range check
      error will occure.                                        

      Example     : procedure MYPROC(ID : byte; a,b,c : sometype);
      var
         variables.....
      begin
         _WMCHK_MEM('MYPROC',57632);
         remainder
     of
       procedure
    statements
      end;                                                      
.PA 226
      Turbo Pascal
      Version Note: The freelist records for TP V5.X used to be grouped
      together in 8 byte records, starting at heapend and
      growing downwards into free memory, as each new record was
      added, the system variable Freeptr used to point to the
      end of the list, each record was made up of two fields,
      blk_org and blk_end, each one was a normalised pointer
      value, giving the starting and ending addresses of each
      free block in the programmes heap, a free block, or hole,
      is created in the heap each time a dynamic variable is
      disposed of using either freemem() or the dispose()
      procedures, the freelist records keep track of all these
      blocks.
         With the freelist records being in a block, on there
      own, at the top of ram, made them extremley dificult to
      overwrite.
         The freelist records for TP V6.0 are no longer grouped
      together, they are now each attached to there respective
      free blocks of ram in the programmes heap, they occupy the
      first 8 bytes of each free block. The two fields have
      also changed, they are now, next and size, both are still
      normalised pointers, the next field points to the next
      freeblock and record, and the size field gives the size of
      the free block.  The first freeblock is pointed to by the
      Freelist pointer variable, the Freeptr variable is no
      longer used.
         With this type of structure it becomes very easy in
      deed to overwrite the freelist record at the start of a
      particular block.  This could occure if, for instance,
      getmem() were called to allocate memory for part of a
      dynamic array.  If the array had 10 elements and we knew
      we were only ever going to use 5 of them, we could, to
      save space, allocate just enough memory for the 5, if we
      then, by mistake, use elements 6,7,8,9 and this would be
      quite legal, as we are still in range for the array index,
      we could in fact be overwriting the record belonging to
      another free block. This could occure if the free block
      allocated for the array was previously a hole in the
      programmes heap with a very small data area above it and
      then another hole in the heap above that, before the next
      chunk of data, the array elements 6,7,8, and 9 would
      overwrite both the small data area and the freelist record
      for that next free hole, the maxavail() function will fail
      when its search reaches the free hole that has just had
      its record overwritten, the value for the next field will
      be a load of old rubbish, sending maxavail() into outer
      space. This could also of occured by just allocating
      insufficient space for a structure in the call to
      getmem(), by mistake.
         Of course there is no excuse for poor or inaccurate
      programming, but at least the memory management system for
      TP V5.X did give us a small, but very greatfull margin to
      play with. From now on you had better make sure that
      every call to getmem() is bit perfect.
.PA 227
      For Owners
      Of Winmen
      Source Files: In case you are having trouble finding where your
      programme is stuck, due to the problem explained above, we
      have included another version of the _WMCHK_MEM();
      procedure in the WMCOMON2.PRS source file, to compile this
      version, instead of the standard one, all you need to do
      is delete the $ character from the {$DEFINE
      STANDARD_WMCHK_MEM} compiler statement and insert a $
      character in the {DEFINE SPECIAL_WMCHK_MEM} complier
      statement and recompile the Winmen Unit, due to some of
      the routines used in the above procedure, it will only
      compile with TP V6.X.
         It does its own free block checking and pointer
      chasing, in, I guess, much the same way as maxavail()
      does, only this one counts each pointer it has chased in
      the loop, if it reaches 10000, it will abort with an
      informative error message, as it's going to be very
      unlikely that any application will produce more than 10000
      holes in the heap, it's more likely that the above
      situation has occured. This gives us a gracefull exit,
      instead of having to ctrl, alt, del.
         Also refer to the description of the WMMEM_STAT();
      procedure for further information.

      Rules Notes
      Tips & Traps: There are no other special instruction to be observed
      for this procedure other than thoes above.
.PA 228
      ------------------------------------------------------------------------
      _WMCHK_TYPE()                                              _WMCHK_TYPE()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by the following functions and
      Low Level     procedures:- LIST_MENU(), POP_MENU(), BAR_MENU(),           
      MPIC_COL(), MBAR_COMMENT(), MCHANGE_CHAR(), WOPEN(), 
      WSELECT(), WCLRSCR(), WCOLOUR(), infact you will notice
      that it is called by all routines that take an ID as the
      first argument and which are not common to both windows and
      menus and are also high level routines ie. routines that 
      are specific to windows or menus and are most likely to be  
      called by the end user.
      All the above routines call this procedure to make sure 
      that the window/menu ID, sent by the user, is compatible
      with that routine and what it is going to be doing with
      that IDBs record and associated data, ie. is the user
      trying to POP or LIST a window or maybe WOPEN a menu ?
      This is possible because windows and menus use the same
      array of pointers (ID_WM[]) to address there records, as
      there record type is exactly the same. What the procedure
      actualy does is to compare the "typ" parameter char with 
      the "ID^.typ" char in that IDBs record, if they match then 
      the user is deemed to have sent a compatible ID to that
      particular procedure, if they do not match then they are
      incompatible and the programme will be aborted with an
      error message (see appendix for list). This obviously
      makes sense in the context in which I have used it, but
      it will probably be of very limited use to an end user
      unless he/she has a specific use for it, similar to this.
      No window, menu or user saved screens records or associated
      date are affected by this procedure.
      No checks are carried out on the validity of data supplied
      to this low level procedure, so extra care is needed.               

      Syntax      : _WMCHK_TYPE(ID,proc,typ);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a
      user saved screen area.
      If the ID does not yet exist then the results could be 
      unpredictable.                                             
.PA 229
      proc : string;
      This string should be the name of the function or procedure
      that is calling the _WMCHK_TYPE() procedure, i.e. if say
      a procedure called MYPROC() were to call _WMCHK_TYPE() the
      format should be as follows _WMCHK_TYPE(ID,'MYPROC','M');
      Although of course in reality you could use any string you
      want up to a maximum of 255 characters.
      If the string is longer than 255 characters a range check
      error will occure.                                        
    
      typ : char;
      This character should normaly be either a 'M' or 'W'
      for the application in which I have used the procedure
      and denotes weather the routine, in which the _WMCHK_TYPE()
      procedure has been put, is specific to just windows or just
      menus. It will be used to check against the supplied IDs
      record variable "ID^.typ", as explained above.

      Example     : procedure MENU_PROC(ID : byte; a,b,c : sometype);
      var
         variables.....
      begin
         _WMCHK_TYPE(ID,'MENU_PROC','M');
         remainder
     of
       procedure
    statements
      end;                                                      

      Rules Notes
      Tips & Traps: No special instructions to be observed.                   
.PA 230
      ------------------------------------------------------------------------
      _WMCOPY_SCR()                                              _WMCOPY_SCR()
      ------------------------------------------------------------------------ 

      Procedure   : Copy the area of screen bounded by the supplied l1,t1,r,b
      Low Level     values, to the position specified by the supplied l2,t2
                    values, the width (r-l1) and height (b-t1) will be the
      same as the area being copied.
         No checks are carried out on the validity of data 
      supplied to this low level procedure, so extra care is 
      needed.
         The objects wmrec is not affected by this procedure.        
         This procedure is not called by any of the routines 
      in the Winmen Unit. 

     Syntax       : _WMCOPY_SCR(l1,t1,r,b,l2,t2);

     Variables    : l1 : byte; (B1_80)
      Leftmost edge of the screen area to be saved, must be a 
      value between 1-80 or a range check error will occure.
 
      t1 : byte; (B1_25)
      Topmost edge of the screen area to be saved, must be a 
      value between 1-25 or a range check error will occure.
 
      r : byte; (B1_80)
      Rightmost edge of the screen area to be saved, must be a 
      value between 1-80 or a range check error will occure.

      b : byte; (B1_25)
      Bottommost edge of the screen area to be saved, must be a 
      value between 1-25 or a range check error will occure.     

      l2 : byte; (B1_80)
      New column at which to position the leftmost edge of the 
      screen area to be copied, must be a value between 1-80 or 
      a range check error will occure.
 
      t2 : byte; (B1_80)
      New row at which to position the topmost edge of the 
      screen area to be copied, must be a value between 1-25 or 
      a range check error will occure.

      Example     : program _WMCOPSC;                                           
        
      uses crt,dos,winmen;                                                      
.PA 231
      begin                                                                     
         clrscr;                                                                
         writeln('Some text to copy to another');                               
         writeln('part of the screen.');                                        
         writeln('As you can see, it can be wrapped');                          
         writeln('around the screen as well');                                  
         writeln;                                                               
         write('<RET> to copy text....');                                       
         readln;                                                                
         _WMCOPY_SCR(1,1,33,4,40,1);    {*COPY AREA OF SCREEN*}  
         write('<RET> to copy text....');                                       
         readln;                                                                
         _WMCOPY_SCR(1,1,33,4,40,8);    {*COPY AGAIN*}           
         write('<RET> to wrap text around screen....');                         
         readln;                                                                
         _WMCOPY_SCR(1,1,33,4,65,15);   {*COPY IT PART OFF SCR*} 
         write('<RET> to continue....');                                        
         readln;                                                                
      end.                                                                      

      Rules Notes
      Tips & Traps: Just one interesting point here, as you will notice from 
      the example, if you specify a new top left position, for 
      the screen area to be copied to, which effectively then 
      puts some of the text off the right hand edge of the 
      screen, the remainder of each line of text will appear 
      on the left hand side of the screen and on the next 
      line. This is because screen ram is a linear block of 
      memory, with character position 1,1 being at offset zero 
      [VIDEO:0000/0001], a character position of 80,1 would be 
      at offset [VIDEO:0159/0160], and character position 1,2, 
      which is the first character on the next line, is at 
      offset [VIDEO:0161/0162], which is the next pair of linear 
      addresses in screen ram. As you have probably already 
      guessed the _WMCOPY_SCR() procedure writes directly to 
      screen ram in a linear fashion, producing the wrapping 
      effect in the example.   
         For more information on the VIDEO segment addresses 
      refer to the scctions on "Winmen Declared Global 
      Variables" and "Winmen Initialisation". 
         Each screen character cell occupies one word (2 Bytes)
      of screen ram, for more information see the sub-section on 
      the mmenu type in the "Winmen Declared Types" section.
.PA 232
      ------------------------------------------------------------------------
      _WMDEL_MREC()                                              _WMDEL_MREC()
      ------------------------------------------------------------------------ 
      
      Procedure   : Delete the maprec, for the object specified by ID,(window,
      Low Level     menu or user screen), at the 3D screen map location 
      indicated by map_inx. 
         Once the maprec has been sucessfuly deleted, the 
      memory occupied by it is returned to the heap for reuse. 
         If the maprec deleted was in the middle of a list, the 
      maprec above is automaticaly linked to the maprec below, 
      i.e the "next" pointer field of the maprec below is given 
      the address of the record above.
         If the maprec deleted was on the end of a list then the 
      "next" pointer field of the maprec below is given nil, to 
      indicate that this record is now the last one in the list.  
         If the maprec deleted was the first in the list then 
      the 3D screen map array pointer at location MAP[map_inx] 
      is given either nil, if there are no other records in the 
      list, or the address of the maprec above.
         This routine assumes that a maprec will be present in 
      the list, for the given object, at location map_inx, if 
      one does not the routine will hang the system.  
         The objects ID must have been previously created using 
      NEW_WIN() or NEW_MENU().
         No checks are carried out on the validity of data 
      supplied to this low level procedure, so extra care is 
      needed.
         The objects wmrec is not affected by this procedure.        
         This procedure is called by:- WMDEL_MREC_ALL();
                    
      Syntax      : _WMDEL_MREC(ID,map_inx);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or 
      user saved screen area, as returned from NEW_MENU() or 
      NEW_WIN() and as sent to WMSAVE_SCR(0-19), if it is a user 
      saved screen area.
         If the ID does not yet exist the results will be 
      unpredictable, as the ID is not checked.                   

      map_inx : word;
      This is the 3D screen map location index, (MAP[map_inx])
      at which the maprec, for the specified object ID, is to be 
      deleted.  

      Example     : procedure _DELRECS(ID);                                     
        
      var                                                                       
         ncol,nrow : byte;                                             
         mapinx,map_inx,sc_inx : word;                                          
         IDWM : pt_wmrec;                                                       
.PA 233
      begin                                                                     
         IDWM := ID_WM[IDB];            {*GET POINTER TO WMREC*} 
         nrow := num rows in object     {*CALC ROWS & COLS*}     
         ncol := num cols in object      
         mapinx := top left char posn   {*CALC MAP ARRAY INDEX*} 
         sc_inx := 0;                   {*W/M SCREEN ARRAY INX*} 
         for each row in object do      {*FOR EACH ROW IN OBJ*}  
         begin                                                                  
     map_inx := mapinx;          {*UPDATE MAP COL INDEX*} 
     for each column in row do   {*FOR EACH COL IN ROW*}  
     begin                       {*ADD A RECORD TO MAP*}                        
        _WMDEL_MREC(ID,map_inx);  
        inc map_inx to next column{*INC MAP ROW INDEX*}    
        inc sc_inx to next column{*INC OBJ SCR ARY INX*}
     end;                        {*INC TO NEXT ROW*}                            
     inc mapinx to start of next row 
         end;                                                                   
      end {_DELRECS};                                              

      Rules Notes
      Tips & Traps: The above example, in a mixture of Pascal and psuedo code,
      shows how to delete a complete maprec structure from the
      3D screen map, for any given object (window, menu or user 
      screen). 
         The example, in common with any of the Windows and Menus 
      low level routines, does not carry out any checking on the 
      parameters supplied to it by the user.  There would also 
      normaly be quite a few internal checks caried out, for 
      instance, to make sure that maprecs do exist at the 
      location indicated by map_inx, prior to the _WMDEL_MREC(); 
      procedure trying to do a delete, this will prevent the 
      scenario outlined in the procedure description from 
      happening, this would usualy be done by a preceding high 
      level type routine, along with other checks.  
         As with any routine that uses pointers and allocates or 
      deallocates memory, as does _WMDEL_MREC(); great care needs 
      to be taken and in particular checks needs to be carried 
      out prior to its use.
.PA 234
      ------------------------------------------------------------------------
      _WMREST_SCR()                                               WMREST_SCR()
      ------------------------------------------------------------------------

      Procedure   : Restore, to the area of screen bounded by the supplied 
      Low Level     l,t,r,b values, the data in an array of type "screen" 
      which is pointed to by a pointer of type "pt_scr". 
         For details of the "screen" and "pt_scr" types, which 
      are Winmen declared data types, see the section titled 
      "Winmen Declared Types", in chapter 2.
         No checks are carried out on the validity of data 
      supplied to this low level procedure, so extra care is 
      needed.
         The objects wmrec is not affected by this function.  
         This function is called by:- WMMOVE(); and WMOPEN();    

      Syntax      : _WMREST_SCR(my_area,l,r,t,b);

      Variables   : l : byte; (B1_80)
      Leftmost edge of the window including border, must be a
      value between 1-80 or a range check error will occure.
 
      t : byte; (B1_25)
      Topmost edge of the window including border, must be a
      value between 1-25 or a range check error will occure.
 
      r : byte; (B1_80)
      Rightmost edge of the window including border, must be a
      value between 1-80 or a range check error will occure.

      b : byte; (B1_25)
      Bottommost edge of the window including border, must be a
      value between 1-25 or a range check error will occure.
        
      my_area : pt_scr;
      This is the pointer value that was returned when the 
      _WMSAVE_SCR(); function was called and is the starting 
      address of the block of memory allocated to hold the 
      screen area bounded by the supplied l,t,r,b values. The 
      area is stored in screen ram format.
         For a full explanation of the "pt_scr" type refer to
      section titled "Winmen Declared Types" in chapter one.     

      Example     : program _WMRESSC;                                           
        
      uses crt,dos,winmen;                                                      
        
      var                                                                       
         my_area : pt_scr;                                                      
.PA 235
      begin                             {*WRITE SOME TEXT*}                     
         clrscr;                                                                
         writeln('We will put a few lines of text');                            
         writeln('on the screen so we can copy the');                           
         writeln('screen to another position.');                                
         my_area := _WMSAVE_SCR(1,1,32,3);                                      
         gotoxy(1,9);                   {*SAVE SCREEN AREA*}                    
         write('<RET> key to copy area2 = area1, wid/hgt same....');            
         readln;                                                                
         _WMREST_SCR(my_area,35,19,66,21);                                      
         writeln;                       {*RESTOR SCREEN AREA*}                  
         write('<RET> key to copy area2 > area1....');                          
         readln;                                                                
         _WMREST_SCR(my_area,35,2,75,6);                                        
         writeln;                       {*RESTOR SCREEN AREA*}                  
         write('<RET> key to copy area2 = area1, wid/hgt not same....');        
         readln;                                                                
         _WMREST_SCR(my_area,4,18,15,25);                                       
         writeln;                       {*RESTOR SCREEN AREA*}                  
         write('<RET> key to continue....');                                    
         readln;                                                                
      end.                                                                      

      Rules Notes
      Tips & Traps: The example will effectively copy the area of screen
      bounded by 1,1,32,3 to the new area 35,15,67,17. In this
      example the two areas have exactly the same width and
      height, it is important that the area you restore is the
      exact same size as the one you saved, although you could
      of course specify a diferent width and or height, as long
      as area1 = area2. If area2 is < area1 then no harm will
      come, you will just get less of the original text in the
      new position. As the "screen" array type that "pt_scr"
      points to is a linear array, it will be the later part of
      the text that will be missing. If area2 is > area1 the
      extra (area2-area1) that will be restored will just be
      whatever data exists in memory above the block reserved
      for the "my_area" screen array, probably rubbish.          
.PA 236
      ------------------------------------------------------------------------
      _WMSLIDE_D()                                                _WMSLIDE_D()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by the folowing functions and
      Low Level     and procedures:- WMSLIDE().
         This procedure facilitates the sliding of an existing
      text window, menu or user saved screen area (ID<20) plus
      its contents nchar(s) downwards.
         The objects record is then updated with its new
      position.
         The new area underneath the object is then saved away
      into the objects ID_SC[] background "screen" array,
      replacing the previous image data.
         No checks are carried out on the validity of data
      supplied to this low level procedure, so extra care is
      needed.
         For more information on sliding please refer to the
      section covering the WMSLIDE(); procedure.

      Syntax      : _WMSLIDE_D(ID,nchar,l,t,r,b,ncol,nrow);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a
      user saved screen area.
         If the ID does not yet exist then the results could be
      unpredictable, as no checks are carried out.

      nchar : byte; (B1_25)
      This is the number of characters or lines that the menu,
      window or USER saved screen area will be moved in a
      downwards direction. Must be a value in the range:-
      1-(number of lines left between the botom of window/
      menu/USER area and the bottom of the screen). If this
      rule is not adhered to the results could and probably
      will be unpredictable, resulting possibly in a crash of
      your programme. If the value supplied is outside the
      range 1-25 then the programme will abort with a range
      check error
         Normaly of course these low level type routines are
      called from a high level routine, in this case WMSLIDE(),
      which takes care of all the checks reqd on the data
      suplied to the low level routine, so as mentioned above a
      great deal more care is required.
.PA 237
      l : byte; (B1_80)
      Leftmost edge of the win/menu/user including border, must
      be a value in the range 1-80 or the programme will abort
      with a range check error.
         This would normaly be the '.l' field of the objects
      wmrec.

      t : byte; (B1_25)
      Topmost edge of the win/menu/user including border, must
      be a value in the range 1-25, or the programme will abort
      with a range check error.
         This would normaly be the '.t' field of the objects
      wmrec.

      r : byte; (B1_80)
      Rightmost edge of the win/menu/user including border, must
      be a value in the range 1-80, or the programme will abort
      with a range check error.
         This would normaly be the '.r' field of the objects
      wmrec.

      b : byte; (B1_25)
      Bottommost edge of the win/menu/user including border,
      must be a value in the range 1-25, or the programme will
      abort with a range check error.
         This would normaly be the '.b' field of the objects
      wmrec.

      ncol : byte; (B1_80)
      This value is the absolute number of columns that the 
      window, menu or USER saved screen area covers or its
      width (r-l+1). Must be a value in the range 1-80, or the
      programme will abort with a range check error.
      
      nrow : byte; (B1_25)
      This value is the absolute number of rows that the
      window, menu or USER saved screen area covers or its
      depth (b-t+1). Must be a value in the range 3-25, or the
      programme will abort with a range check error.

         The example below covers all of the low level slide
      procedures:- _WMSLIDE_U(), _WMSLIDE_D(), _WMSLIDE_L() and
      _WMSLIDE_R().

      Example     : program SLI_UDLR;

      var
         m1,m1c,l,t : byte;            {*DEFINE GLOBALS*}
         mt : pt_mtext;
         ID : pt_wmrec;
.PA 238
      begin
         getmem(mt,4*81);              {*DEFINE MENU*}
         mt^[1] := '8) Slide Up';
         mt^[2] := '4) Slide Left';
         mt^[3] := '6) Slide Right';
         mt^[4] := '2) Slide Down';
         m1 := NEW_MENU(1,4,14,7,0,,1,mt,4);
         freemem(mt,4*81);

         l := 32;                      {*INIT VARBS*}
         t := 9;
         m1c := 10;
         ID := ID_WM[m1];
         WMTITLE(m1,1,15,'SLIDE ME');  {*DO TITLE STRING*}

         while not(m1c in[0]) do       {*WHILE NOT ESCAPE*}
         begin
     m1c := POP_MENU(m1,l,t);
     case m1c of
        1 : if(t > 1) then      {*MOVE MENU UP 1 ROW*}
     begin
        dec(t);
        _WMSLIDE_U(m1,1,ID^.l,ID^.t,ID^.r,ID^.b,
           (ID^.r-ID^.l+1),(ID^.b-ID^.l+t)); 
     end;
        2 : if(l > 2) then      {*MOVE MENU LEFT 1 COL*}
     begin
        dec(l,2);
        _WMSLIDE_L(m1,2,ID^.l,ID^.t,ID^.r,ID^.b,
           (ID^.r-ID^.l+1),(ID^.b-ID^.l+t));
     end;
        3 : if(ID_WM[m1]^.r+2 < 80) then
     begin               {*MOVE MENU RIGHT 1 COL*}
        inc(l,2);
        _WMSLIDE_R(m1,2,ID^.l,ID^.t,ID^.r,ID^.b,
           (ID^.r-ID^.l+1),(ID^.b-ID^.l+t));
     end;
        4 : if(ID_WM[m1]^.b+1 < 25) then
     begin               {*MOVE MENU DOWN 1 ROW*}
        inc(t);
        _WMSLIDE_D(m1,1,ID^.l,ID^.t,ID^.r,ID^.b,
           (ID^.r-ID^.l+1),(ID^.b-ID^.l+t));
     end;
     end {case};
         end {while};
         normvideo;
      end {WM_SLIDE}.                                           
.PA 239
      Rules Notes
      Tips & Traps: The above example shows quite clearly how to use each of
      the low level slide procedures, of course extra care must
      be taken to ensure that only valid data is fed to the
      slide procedures,or in fact any of the low level procedures
      as no checking whatsoever is carried out on perameter data
      passed to the procedure, by the user, this is clearly the
      users responsability, as normaly all perameter data 
      checking is done by the high level procedures.
         The screen area which comprises the window, menu or
      USER saved screen area, is actualy slid in the following
      sequence of events:-

      a) The screen character data immediatly in front of the
         window/menu to be moved, is saved away in a temporary
         storage array to protect it from the leading edge of
         the window/menu.

      b) The screen block which is the window/menu is then moved
         or reshuffled the specified number of characters in the
         specified direction, this is simply a move within the
         screen ram area.

      c) The screen character data that was under the trailing
         edge of the window/menu, before it was moved in (b), is
         now put back into screen ram from the window/menu
         screen array, this completes the slide and restores the
         screen under the window/menu to the condition it was in
         prior to the window/menu covering it up.

      d) Finaly but most importantly, the cleaver bit.  The
         menu/ window screen array (char data from under that
         area) is now restacked or reshuffled so that the screen
         character data from (a), which is in its temporary
         storage array, is placed in the correct position,
         column or row, depending on the direction of the slide,
         in that menu or windows screen array, as you can
         imagine this involves guite a bit of juggling to
         achieve.

      Well that at least should give you some idea of how things
      are done without giving too many secrets away.
.PA 240
      ------------------------------------------------------------------------
      _WMSLIDE_L()                                                _WMSLIDE_L()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by the folowing functions and
      Low Level     and procedures:- WMSLIDE().
         This procedure facilitates the sliding of an existing
      text window, menu or user saved screen area (ID<20) plus
      its contents nchar(s) leftwards.
         The objects record is then updated with its new
      position.
         The new area underneath the object is then saved away
      into the objects ID_SC[] background "screen" array,
      replacing the previous image data.
         No checks are carried out on the validity of data
      supplied to this low level procedure, so extra care is
      needed.
         For more information on sliding please refer to the
      section covering the WMSLIDE(); procedure.

      Syntax      : _WMSLIDE_L(ID,nchar,l,t,r,b,ncol,nrow);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a
      user saved screen area.
         If the ID does not yet exist then the results could be
      unpredictable, as no checks are carried out.

      nchar : byte; (B1_80)
      This is the number of characters or lines that the menu,
      window or USER saved screen area will be moved in a
      downwards direction. Must be a value in the range:-
      1-(number of lines left between the left of the window/
      menu/USER area and the left edge of the screen). If this
      rule is not adhered to the results could and probably
      will be unpredictable, resulting possibly in a crash of
      your programme. If the value supplied is outside the
      range 1-80 then the programme will abort with a range
      check error
         Normaly of course these low level type routines are
      called from a high level routine, in this case WMSLIDE(),
      which takes care of all the checks reqd on the data
      suplied to the low level routine, so as mentioned above a
      great deal more care is required.
.PA 241
      l : byte; (B1_78)
      Leftmost edge of the win/menu/user including border, must
      be a value in the range 1-80, or the programme will abort
      with a range check error.
         This would normaly be the '.l' field of the objects
      wmrec.

      t : byte; (B1_25)
      Topmost edge of the win/menu/user including border, must
      be a value in the range 1-25, or the programme will abort
      with a range check error.
         This would normaly be the '.t' field of the objects
      wmrec.

      r : byte; (B1_80)
      Rightmost edge of the win/menu/user including border, must
      be a value in the range 1-80, or the programme will abort
      with a range check error.
         This would normaly be the '.r' field of the objects
      wmrec.

      b : byte; (B1_25)
      Bottommost edge of the win/menu/user including border,
      must be a value in the range 1-25, or the programme will
      abort with a range check error.
         This would normaly be the '.b' field of the objects
      wmrec.

      ncol : byte; (B1_80)
      This value is the absolute number of columns that the
      window, menu or USER saved screen area covers or its
      width (r-l+1). Must be a value in the range 1-80, or the
      programme will abort with a range check error.
  
      nrow : byte; (B1_25)
      This value is the absolute number of rows that the
      window, menu or USER saved screen area covers or its
      depth (b-t+1). Must be a value in the range 1-25, or the
      programme will abort with a range check error.

      Example     : The example for _WMSLIDE_D() covers all of the low level 
      slide procedures:- _WMSLIDE_U(), _WMSLIDE_D(), _WMSLIDE_L()
      and _WMSLIDE_R().                                         

      Rules Notes
      Tips & Traps: The above example shows quite clearly how to use each of
      the low level slide procedures, of course extra care must
      be taken to ensure that only valid data is fed to the
      slide procedures,or in fact any of the low level
      procedures as no checking whatsoever is carried out on
.PA 242
      perameter data passed to the procedure, by the user, this
      is clearly the users responsability, as normaly all
      perameter data checking is done by the high level
      procedures. The screen area which comprises the window,
      menu or USER saved screen area, is actualy slid in the
      following sequence of events:-

      a) The screen character data immediatly in front of the
         window/menu to be moved, is saved away in a temporary
         storage array to protect it from the leading edge of
         the window/menu.

      b) The screen block which is the window/menu is then moved
         or reshuffled the specified number of characters in the
         specified direction, this is simply a move within the
         screen ram area.

      c) The screen character data that was under the trailing
         edge of the window/menu, before it was moved in (b), is
         now put back into screen ram from the window/menu
         screen array, this completes the slide and restores the
         screen under the window/menu to the condition it was in
         prior to the window/menu covering it up.

      d) Finaly but most importantly, the cleaver bit. The
         menu/window screen array (char data from under that
         area) is now restacked or reshuffled so that the screen
         character data from (a), which is in its temporary
         storage array, is placed in the correct position,
         column or row, depending on the direction of the slide,
         in that menu or windows screen array, as you can
         imagine this involves guite a bit of juggling to
         achieve.

      Well that at least should give you some idea of how things
      are done without giving too many secrets away.
.PA 243
      ------------------------------------------------------------------------
      _WMSLIDE_R()                                                _WMSLIDE_R()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by the folowing functions and
      Low Level     and procedures:- WMSLIDE().
         This procedure facilitates the sliding of an existing
      text window, menu or user saved screen area (ID<20) plus
      its contents nchar(s) rightwards.
         The objects record is then updated with its new
      position.
         The new area underneath the object is then saved away
      into the objects ID_SC[] background "screen" array,
      replacing the previous image data.
         No checks are carried out on the validity of data
      supplied to this low level procedure, so extra care is
      needed.
         For more information on sliding please refer to the
      section covering the WMSLIDE(); procedure.

      Syntax      : _WMSLIDE_R(ID,nchar,l,t,r,b,ncol,nrow);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a
      user saved screen area.
         If the ID does not yet exist then the results could be
      unpredictable, as no checks are carried out.

      nchar : byte; (B1_80)
      This is the number of characters or lines that the menu,
      window or USER saved screen area will be moved in a
      downwards direction.  Must be a value in the range:-
      1-(number of lines left between the right edge of window/
      menu/USER area and the right edge of the screen). If this
      rule is not adhered to the results could and probably will
      be unpredictable, resulting possibly in a crash of your
      programme.  If the value supplied is outside the range
      1-80 then the programme will abort with a range check
      error
         Normaly of course these low level type routines are
      called from a high level routine, in this case WMSLIDE(),
      which takes care of all the checks reqd on the data
      suplied to the low level routine, so as mentioned above a
      great deal more care is required.
.PA 244
      l : byte; (B1_80)
      Leftmost edge of the win/menu/user including border, must
      be a value in the range 1-80, or the programme will abort
      with a range check error.
         This would normaly be the '.l' field of the objects
      wmrec.

      t : byte; (B1_25)
      Topmost edge of the win/menu/user including border, must
      be a value in the range 1-25, or the programme will abort
      with a range check error.
         This would normaly be the '.t' field of the objects
      wmrec.

      r : byte; (B1_80)
      Rightmost edge of the win/menu/user including border, must
      be a value in the range 1-80, or the programme will abort
      with a range check error.
         This would normaly be the '.r' field of the objects
      wmrec.

      b : byte; (B1_25)
      Bottommost edge of the win/menu/user including border,
      must be a value in the range 1-25, or the programme will
      abort with a range check error.
         This would normaly be the '.b' field of the objects
      wmrec.

      ncol : byte; (B1_80)
      This value is the absolute number of columns that the
      window, menu or USER saved screen area covers or its
      width (r-l+1). Must be a value in the range 1-80, or the
      programme will abort with a range check error.
  
      nrow : byte; (B1_25)
      This value is the absolute number of rows that the
      window, menu or USER saved screen area covers or its
      depth (b-t+1). Must be a value in the range 1-25, or the
      programme will abort with a range check error.

      Example     : The example for _WMSLIDE_D() covers all of the low level 
      slide procedures:- _WMSLIDE_U(), _WMSLIDE_D(), _WMSLIDE_L()
      and _WMSLIDE_R().                                         

      Rules Notes
      Tips & Traps: The above example shows quite clearly how to use each of
      the low level slide procedures, of course extra care must
      be taken to ensure that only valid data is fed to the
      slide procedures,or in fact any of the low level procedures
      as no checking whatsoever is carried out on perameter data
      passed to the procedure, by the user, this is clearly the
.PA 245
      users responsability, as normaly all perameter data 
      checking is done by the high level procedures.
      The screen area which comprises the window, menu or USER
      saved screen area, is actualy slid in the following
      sequence of events:-

      a) The screen character data immediatly in front of the
         window/menu to be moved, is saved away in a temporary
         storage array to protect it from the leading edge of
         the window/menu.

      b) The screen block which is the window/menu is then moved
         or reshuffled the specified number of characters in the
         specified direction, this is simply a move within the
         screen ram area.

      c) The screen character data that was under the trailing
         edge of the window/menu, before it was moved in (b), is
         now put back into screen ram from the window/menu
         screen array, this completes the slide and restores the
         screen under the window/menu to the condition it was in
         prior to the window/menu covering it up.

      d) Finaly but most importantly, the cleaver bit.  The
         menu/ window screen array (char data from under that
         area) is now restacked or reshuffled so that the screen
         character data from (a), which is in its temporary
         storage array, is placed in the correct position,
         column or row, depending on the direction of the slide,
         in that menu or windows screen array, as you can
         imagine this involves guite a bit of juggling to
         achieve.

      Well that at least should give you some idea of how things
      are done without giving too many secrets away.
.PA 246
      ------------------------------------------------------------------------
      _WMSLIDE_U()                                                _WMSLIDE_U()
      ------------------------------------------------------------------------

      Procedure   : Internal procedure called by the folowing functions and
      Low Level     and procedures:- WMSLIDE().
         This procedure facilitates the sliding of an existing
      text window, menu or user saved screen area (ID<20) plus
      its contents nchar(s) upwards.
         The objects record is then updated with its new
      position.
         The new area underneath the object is then saved away
      into the objects ID_SC[] background "screen" array,
      replacing the previous image data.
         No checks are carried out on the validity of data
      supplied to this low level procedure, so extra care is
      needed.
         For more information on sliding please refer to the
      section covering the WMSLIDE(); procedure.

      Syntax      : _WMSLIDE_U(ID,nchar,l,t,r,b,ncol,nrow);

      Variables   : ID : byte;
      The unique identifier for the specified menu, window or
      user saved screen area, as returned from NEW_MENU() or
      NEW_WINDOW() and as sent to WMSAVE_SCR(0-19), if it is a
      user saved screen area.
         If the ID does not yet exist then the results could be
      unpredictable, as no checks are carried out.

      nchar : byte; (B1_25)
      This is the number of characters or lines that the menu,
      window or USER saved screen area will be moved in a
      downwards direction.  Must be a value in the range:-
      1-(number of lines left between the top edge of window/
      menu/USER area and the top edge of the screen). If this
      rule is not adhered to the results could and probably will
      be unpredictable, resulting possibly in a crash of your
      programme.  If the value supplied is outside the range
      1-25 then the programme will abort with a range check
      error
         Normaly of course these low level type routines are
      called from a high level routine, in this case WMSLIDE(),
      which takes care of all the checks reqd on the data
      suplied to the low level routine, so as mentioned above a
      great deal more care is required.
.PA 247
      l : byte; (B1_80)
      Leftmost edge of the win/menu/user including border, must
      be a value in the range 1-80, or the programme will abort
      with a range check error.
         This would normaly be the '.l' field of the objects
      wmrec.

      t : byte; (B1_25)
      Topmost edge of the win/menu/user including border, must
      be a value in the range 1-25, or the programme will abort
      with a range check error.
         This would normaly be the '.t' field of the objects
      wmrec.

      r : byte; (B1_80)
      Rightmost edge of the win/menu/user including border, must
      be a value in the range 1-80, or the programme will abort
      with a range check error.
         This would normaly be the '.r' field of the objects
      wmrec.

      b : byte; (B1_25)
      Bottommost edge of the win/menu/user including border,
      must be a value in the range 1-25, or the programme will
      abort with a range check error.
         This would normaly be the '.b' field of the objects
      wmrec.

      ncol : byte; (B1_80)
      This value is the absolute number of columns that the
      window, menu or USER saved screen area covers or its
      width (r-l+1). Must be a value in the range 1-80, or the
      programme will abort with a range check error.
  
      nrow : byte; (B1_25)
      This value is the absolute number of rows that the
      window, menu or USER saved screen area covers or its
      depth (b-t+1). Must be a value in the range 1-25, or the
      programme will abort with a range check error.

      Example     : The example for _WMSLIDE_D() covers all of the low level 
      slide procedures:- _WMSLIDE_U(), _WMSLIDE_D(), _WMSLIDE_L()
      and _WMSLIDE_R().                                         

      Rules Notes
      Tips & Traps: The above example shows quite clearly how to use each of
      the low level slide procedures, of course extra care must
      be taken to ensure that only valid data is fed to the
      slide procedures,or in fact any of the low level procedures
      as no checking whatsoever is carried out on perameter data
      passed to the procedure, by the user, this is clearly the
.PA 248
      users responsability, as normaly all perameter data 
      checking is done by the high level procedures.
      The screen area which comprises the window, menu or USER
      saved screen area, is actualy slid in the following
      sequence of events:-

      a) The screen character data immediatly in front of the
         window/menu to be moved, is saved away in a temporary
         storage array to protect it from the leading edge of
         the window/menu.

      b) The screen block which is the window/menu is then moved
         or reshuffled the specified number of characters in the
         specified direction, this is simply a move within the
         screen ram area.

      c) The screen character data that was under the trailing
         edge of the window/menu, before it was moved in (b), is
         now put back into screen ram from the window/menu
         screen array, this completes the slide and restores the
         screen under the window/menu to the condition it was in
         prior to the window/menu covering it up.

      d) Finaly but most importantly, the cleaver bit.  The
         menu/ window screen array (char data from under that
         area) is now restacked or reshuffled so that the screen
         character data from (a), which is in its temporary
         storage array, is placed in the correct position,
         column or row, depending on the direction of the slide,
         in that menu or windows screen array, as you can
         imagine this involves guite a bit of juggling to
         achieve.

      Well that at least should give you some idea of how things
      are done without giving too many secrets away.
.PA 249
       P                      A                       R                      T 
      -----------------------------------------------------------------------
              2





      APPENDICES
.PA 250
      A         P         P         E         N          D         I         X
      ------------------------------------------------------------------------
              A




      ERROR MESSAGES


      The following pages list all the possible error messages that may be
      generated during the run time of a programme that uses the Winmen unit
      and which are, of course, connected only to the Winmen Unit. A brief
      description of the error along with suggested remedial action is also
      included, although the error message its is normaly self explanitary.
      In all the error messages below, where 'NAME' appears, this will be
      replaced by the actual name of the Winmen function or procedure,in which
      the error occured, during the runtime of the programme.
      In all the error messages below, where 'value' appears, this will be
      replaced by an actual integer value, when the message appears during
      the runtime of a programme.
      In all the error messages below, where 'char' appears, this will be
      replaced by an actual character, when the message appears during
      the runtime of a programme.


      Windows Error Messages

      WMMOVE() ERROR:Window Now Goes Off The Screen, Please Check New
      l & t values.                                                           
  The coordinates supplied to the WMMOVE() procedure, for the new top
  lefthand corner of the window, will place the righthand side or the
  bottom edge of the window off the screen, this is not allowed.


      NAME() ERROR:Window/Box To Small To Display.                            

  The minimum size for any window or box which is put on the screen is
  3x3 screen character cells, any thing less than this will be useless
  as there would be no display area.

.PA 251
      Menus Error Messages

      BAR_MENU() ERROR:Option Text+Min Gap Of 1 Between Options Is > blen
      Unable To Display.
      Minimum blen Value For A Min Gap Of 1 Would Be:['value'], For This Menu.

  The value you have supplied to the BAR_MENU() function, for the bar 
  manu length, is too small to enable all the menu options to be   
  displayed, along with the minimum gap of 1 between each option and
  before and after the first and last options, you will need to 
  increase the bar menu length parameter. The following formula can 
  be used to determine the minimum bar menu length :-
  sum of all option texts + number options + 1;  


      WMFLOAT() ERROR:Can"t Open Menus From Within This Procedure, Only
      Windows.

  A window can be opened from within the WMFLOAT() procedure but a menu
  can not. Once a menu is activated we are stuck inside that menu
  function until an option is chosen, so we would not return to the
  WMFLOAT() procedure, to actualy float the menu to the surface, until
  a choice was made, which does seem a bit pointless.


      WMSLIDE() ERROR:Menu not yet open use POP_MENU() Or LIST_MENU().
      Can,t Open a Menu From WMSLIDE().                                       

  You have called the WMSLIDE() procedure, for a menu that does exist
  but is not yet open. A window can be opened from within the
  WMSLIDE() procedure but a menu can not, because once a menu is
  activated we are stuck inside that menu function until an option is
  chosen, so we would not return to the WMSLIDE() procedure, to actualy
  carry out the slide, until a choice was made, which does seem a bit
  pointless.


      NAME() ERROR:Menu Goes Off The Screen, Unable To Display.               

  The coordinates supplied to the NAME() function or procedure, for the 
  top lefthand corner of the menu, will place the righthand side or the
  bottom edge of the menu off the screen, this is not allowed.                  


      Common Error Messages

      NAME() ERROR:No More Windows+Menus Left, Max is 254.                 

  You are trying to exceed the maximum number of combined windows and
  menus available, before you can define any more, you will have to
  delete some of the existing ones, that you no langer require, using
  WMDELETE().                                                          
.PA 252

      NAME() ERROR:Record ID Does Not Yet Exist, Or It Has Been DELETED
      Use NEW_WIN(), NEW_MENU() Or WMSAVE_SCR().

  The window, menu or user saved screen area identifier 'ID' that you
  are using for NAME() function or procedure, has not yet been  
  allocated, ie. that object does not exist. Check that you have the
  correct one, or if you have, then that object has somehow become
  unlinked from its identifier 'ID', most probably you have inadvert-
  antly deleted it with WMDELETE(), or one of your own routines that
  uses direct memory access techniques, has overwritten some Winmen
  record data!


      NAME() ERROR:Not Enough Contigous Memory Left To Create Dynamic
      Variable.

  The NAME() function or procedure is unable to allocate enough memory
  to carry out an essential operation, so execution is terminated. 
  In other words you have run out of ram memory, you may have to get
  rid of some windows or menus which are not required.


      NAME() ERROR:ID Not Compatible With Procedure Type.

  The window, menu or user saved screen identifier, which has been
  passed to the NAME() function or procedure, was the 'ID' for a menu
  when it should have been for a window or vice-versa. Some functions
  and procedures are exclusive to either menus or windows, check the
  'ID' that you are using.


      _WMCHK_MEM() ERROR:Stuck In Pointer Chasing Loop.
      A Freelist Record, Which Is At The Start Of A Free Block, Has Possibly
      Been Overwritten.
      Tip:- Check all Getmem() Sizes In Your Source Code.

  This display of this error message depends on which version of the
  _WMCHK_MEM(); procedure you have compiled, for full details please
  refer to the section on the _WMCHK_MEM(); procedure.

      WMMEM_STAT() ERROR:'errstr'
  possibles for 'errstr':-
     Path Not Found.
     Too Many Open Files.
     File Access Denied.
     File Not Assigned.
     Invalid Parameter r_a='char', Must Be:- A,a,R or r.

  Path Not Found. The name assigned to a file variable is invalid or
  specifies an unexisting subdirectory.
.PA 253
  Too Many Open Fles. The programme has too many open files, check that
  the config.sys file in the root directory has enough FILES=nnn speci-
  fied for this particular application.

  File Access Denied. The file assigned to a file variable does not
  have write access, This will only occure if the file assigned to
  the file variable, was not created by the WMMEM_STAT() procedure,
  but by another routine, and only posseses read accesss rights.

  File Not Assigned. The file variable has not been assigned a name
  through a call to Assign().

  Invalid Parameter. The r_a parameter in the call to WMMEM_STAT() is
  incorrect, it can only be one of the following:-
  'R', 'r', 'A' or 'a'.


      WMREST_SCR() ERROR:The New Requested Screen Area For This Window,
      Menu Or User Screen Is Larger Than The Existing One, It Must Be The
      Same Size Or Less.

  It is not possible to redisplay an area of screen, from an objects
  ID_SC[] background "screen" array, that is larger than the one
  originaly saved away, doing so would cause corruption of part or all
  of the screen, by data belonging to somthing else.

      WMSAVE_SCR() ERROR:The new Requested Screen Area For This Window Or
      Menu Is Larger Than The Existing One, It must be The Same Size Or Less.

  You can't put a quart into a pint pot, if the new requested area of
  screen to be saved away, into an objects ID_SC[] background "screen"
  array, was larger that the original, it would over-write data
  belonging to somthing else, with disasterous consequences.


      WMSAVE_SCR() ERROR:The Window Or Menu, For Which You Are Trying To
      Replace The Background Screen Area Is Closed. This Is An Invalid
      Operation.

  This is an invalid operation because the ID_SC[] background "screen"
  array t

Comments

Popular posts from this blog

BOTTOM LIVE script

Fawlty Towers script for "A Touch of Class"