home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 15 / CDACTUAL15.iso / cdactual / program / basic / QBSCR20.ZIP / QBSCR.BAS < prev    next >
Encoding:
BASIC Source File  |  1992-07-08  |  211.7 KB  |  5,399 lines

  1. '┌────────────────────────────────────────────────────────────────────────┐
  2. '│                                                                        │
  3. '│                           Q B S C R . B A S                            │
  4. '│                                                                        │
  5. '│       The QBSCR Screen Routines for QuickBASIC 4.0+ Programmers        │
  6. '│                              Version 2.0                               │
  7. '│                                                                        │
  8. '│                   (C) Copyright 1992 by Tony Martin                    │
  9. '│                                                                        │
  10. '├────────────────────────────────────────────────────────────────────────┤
  11. '│                                                                        │
  12. '│  This source code is copyright 1992 by Tony Martin.  You may change    │
  13. '│  it to suit your programming needs, but you may not distribute any     │
  14. '│  modified copies of the library itself.  I retain all rights to the    │
  15. '│  source code and all library modules included with the QBSCR package,  │
  16. '│  as well as to the example programs.  You may not remove this notice   │
  17. '│  from any copies of the library itself you distribute.                 │
  18. '│                                                                        │
  19. '│  You are granted the right to use this source code for your own pro-   │
  20. '│  grams, without royalty payments or credits to me (though, if you      │
  21. '│  feel so inclined to give me credit, feel free to do so).  You MUST    │
  22. '│  register this software if you release a shareware or commercial       │
  23. '│  program that uses it.  You may use these routines in any type of      │
  24. '│  software you create, as long as it is not a programming toolbox or    │
  25. '│  package of routines OF ANY KIND.                                      │
  26. '│                                                                        │
  27. '│  This package is shareware.  If you find it useful or use it in any    │
  28. '│  software you release, you are requested to send a registration fee of │
  29. '│  $25.00 (U.S. funds only) to:                                          │
  30. '│                                                                        │
  31. '│                            Tony Martin                                 │
  32. '│                       1611 Harvest Green Ct.                           │
  33. '│                          Reston, VA 22094                              │
  34. '│                                                                        │
  35. '│  All registered users receive an 'official' disk set containing the    │
  36. '│  latest verison of the QBSCR routines.  For more information, see      │
  37. '│  the QBSCR documentation.                                              │
  38. '│                                                                        │
  39. '├────────────────────────────────────────────────────────────────────────┤
  40. '│                                                                        │
  41. '│  For information on using these routines and incorporating them into   │
  42. '│  your own programs, see the accompanying documentation.                │
  43. '│                                                                        │
  44. '└────────────────────────────────────────────────────────────────────────┘
  45.  
  46. '──────────────────────────────────────────────────────────────────────────
  47. ' Include the mouse support routines.
  48. '──────────────────────────────────────────────────────────────────────────
  49. REM $INCLUDE: 'MOUSE.BI'
  50.  
  51. '──────────────────────────────────────────────────────────────────────────
  52. ' Use the QBSCR include file to get our function declare statements.
  53. '──────────────────────────────────────────────────────────────────────────
  54. REM $INCLUDE: 'QBSCR.INC'
  55.  
  56. '──────────────────────────────────────────────────────────────────────────
  57. ' CONSTants required by the Screen Routines
  58. '──────────────────────────────────────────────────────────────────────────
  59. CONST LEFTARROWCODE = -99
  60. CONST RIGHTARROWCODE = -98
  61. CONST LEFTMOUSEEXIT = -97
  62. CONST RIGHTMOUSEEXIT = -96
  63.  
  64. COMMON SHARED mouseExists%, mouseState%
  65.  
  66. SUB Banner (st$, row%) STATIC
  67.     
  68.     '┌────────────────────────────────────────────────────────────────────────┐
  69.     '│  This subroutine displays a scrolling banner on any line of the        │
  70.     '│  display screen.  The scrolling effect is achieved through successive  │
  71.     '│  calls to this subfunction.  Each call shifts the string by 1 char-    │
  72.     '│  acter and redisplays it.                                              │
  73.     '│                                                                        │
  74.     '│  Parameters are as follows:                                            │
  75.     '│                                                                        │
  76.     '│      st$ - The string containing the text to be scrolled.  Must be     │
  77.     '│            80 characters or less.                                      │
  78.     '│      row% - The row of the screen on which to scroll the text.  Valid  │
  79.     '│             range is 1 through 23.                                     │
  80.     '└────────────────────────────────────────────────────────────────────────┘
  81.     
  82.     '──────────────────────────────────────────────────────────────────────────
  83.     ' Check to see if this is the first time Banner has been called
  84.     '──────────────────────────────────────────────────────────────────────────
  85.         temp$ = ""
  86.         IF NOT (bannerFlag) THEN
  87.             bannerFlag = -1
  88.             text$ = st$
  89.         END IF
  90.     
  91.     '──────────────────────────────────────────────────────────────────────────
  92.     ' Move each character in the banner string one space to the left
  93.     '──────────────────────────────────────────────────────────────────────────
  94.         FOR n = 1 TO LEN(text$) - 1
  95.             temp$ = temp$ + MID$(text$, n + 1, 1)
  96.         NEXT n
  97.     
  98.     '──────────────────────────────────────────────────────────────────────────
  99.     ' Set the last character in Temp$ to the first character of the string
  100.     '──────────────────────────────────────────────────────────────────────────
  101.         temp$ = temp$ + LEFT$(text$, 1)
  102.     
  103.     '──────────────────────────────────────────────────────────────────────────
  104.     ' Determine the column to display the new string on, centered
  105.     '──────────────────────────────────────────────────────────────────────────
  106.         text$ = temp$
  107.         x% = INT((80 - (LEN(text$))) / 2) + 1
  108.     
  109.     '──────────────────────────────────────────────────────────────────────────
  110.     ' Print the newly adjusted string
  111.     '──────────────────────────────────────────────────────────────────────────
  112.         LOCATE row%, x%, 0
  113.         PRINT text$;
  114.     
  115. END SUB
  116.  
  117. SUB BlockRestore (l%, r%, t%, b%, scrArray%(), segment!)
  118.     
  119.     '┌──────────────────────────────────────────────────────────────────┐
  120.     '│  This subprogram will restore a rectanglar portion of the screen │
  121.     '│  that was saved using the QBSCR routine 'BlockSave.'  The first  │
  122.     '│  four parameters are the left, right, top, and bottom sides of   │
  123.     '│  the rectangular area to restore.  They should be the same as    │
  124.     '│  the ones used when the area was saved.  The scrArray% is an     │
  125.     '│  integer array passed to this routine, that was originally used  │
  126.     '│  to save the screen area.  The segment parameter is the segment  │
  127.     '│  of the screen memory to restore the saved info to.  For this    │
  128.     '│  parameter, simply use the QBSCR GetVideoSegment function.       │
  129.     '└──────────────────────────────────────────────────────────────────┘
  130.     
  131.     '────────────────────────────────────────────────────────────────────
  132.     ' Determine where to start restoring in screen memory
  133.     '────────────────────────────────────────────────────────────────────
  134.         wdth% = 2 * (r% - l%) + 1
  135.         offset% = 160 * (t% - 1) + 2 * (l% - 1)
  136.         z% = 0
  137.     
  138.     '────────────────────────────────────────────────────────────────────
  139.     ' Set the memory segment to the screen memory address
  140.     '────────────────────────────────────────────────────────────────────
  141.         DEF SEG = segment
  142.     
  143.     '────────────────────────────────────────────────────────────────────
  144.     ' Restore the rectangular area of the screen by POKEing the stored
  145.     ' screen display info into the display memory
  146.     '────────────────────────────────────────────────────────────────────
  147.         FOR x% = t% TO b%
  148.             FOR y% = 0 TO wdth%
  149.                 POKE offset% + y%, scrArray%(z%)
  150.                 z% = z% + 1
  151.             NEXT y%
  152.             offset% = offset% + 160
  153.         NEXT x%
  154.     
  155.     '────────────────────────────────────────────────────────────────────
  156.     ' Restore BASIC's default data segment
  157.     '────────────────────────────────────────────────────────────────────
  158.         DEF SEG
  159.     
  160. END SUB
  161.  
  162. SUB BlockSave (l%, r%, t%, b%, scrArray%(), segment!)
  163.     
  164.     '┌──────────────────────────────────────────────────────────────────┐
  165.     '│  This subprogram will save a rectanglar portion of the screen    │
  166.     '│  in an integer array.  The first four parameters are the left,   │
  167.     '│  right, top, and bottom sides of the rectangular area to         │
  168.     '│  restore.  The scrArray% is an integer array passed to this      │
  169.     '│  routine in which to save the screen area. The segment parameter │
  170.     '│  is the segment of the screen memory to save from.  For this     │
  171.     '│  parameter, simply use the QBSCR GetVideoSegment function.       │
  172.     '└──────────────────────────────────────────────────────────────────┘
  173.     
  174.     '────────────────────────────────────────────────────────────────────
  175.     ' Determine where to start saving in screen memory
  176.     '────────────────────────────────────────────────────────────────────
  177.         wdth% = 2 * (r% - l%) + 1
  178.         offset% = 160 * (t% - 1) + 2 * (l% - 1)
  179.         z% = 0
  180.     
  181.     '────────────────────────────────────────────────────────────────────
  182.     ' Set the memory segment to the screen memory address
  183.     '────────────────────────────────────────────────────────────────────
  184.         DEF SEG = segment
  185.     
  186.     '────────────────────────────────────────────────────────────────────
  187.     ' Save the rectangular area of the screen by PEEKing into the
  188.     ' screen display memory at the right place
  189.     '────────────────────────────────────────────────────────────────────
  190.         FOR x% = t% TO b%
  191.             FOR y% = 0 TO wdth%
  192.                 scrArray%(z%) = PEEK(offset% + y%)
  193.                 z% = z% + 1
  194.             NEXT y%
  195.             offset% = offset% + 160
  196.         NEXT x%
  197.     
  198.     '────────────────────────────────────────────────────────────────────
  199.     ' Restore BASIC's default data segment
  200.     '────────────────────────────────────────────────────────────────────
  201.         DEF SEG
  202.     
  203. END SUB
  204.  
  205. FUNCTION BlockSize% (l%, r%, t%, b%)
  206.     
  207.     '┌──────────────────────────────────────────────────────────────────┐
  208.     '│  This function will calculate the number of elements required    │
  209.     '│  for an array used to save a rectangular area of the screen.     │
  210.     '│  The four parameters are the left, right, top, and bottom values │
  211.     '│  of the rectangular area of the screen.  Use the function right  │
  212.     '│  inside the DIM statement, like this:                            │
  213.     '│              DIM scrArray%(BlockSize%(1, 1, 10, 20))             │
  214.     '└──────────────────────────────────────────────────────────────────┘
  215.     
  216.         BlockSize% = ((r% - l% + 1) * (b% - t% + 1)) * 2
  217.     
  218. END FUNCTION
  219.  
  220. SUB BuildScreen (file$, mode%)
  221.     
  222.     '┌────────────────────────────────────────────────────────────────────────┐
  223.     '│  This routine allows you to place on the screen a predefined display   │
  224.     '│  that was created with Screen Builder.  It will place the display on   │
  225.     '│  the screen in any of sixteen different ways.  Note that the methods   │
  226.     '│  of displaying the screen are identical to the methods used in the     │
  227.     '│  ClrScr routine.  Some code differences will be apparent for obvious   │
  228.     '│  reasons.                                                              │
  229.     '│                                                                        │
  230.     '│  Parameters are as follows:                                            │
  231.     '│                                                                        │
  232.     '│      file$ - The name of the screen file that was saved using the      │
  233.     '│              Screen Builder program.                                   │
  234.     '│      mode% - The method to use when placing the screen on the display. │
  235.     '└────────────────────────────────────────────────────────────────────────┘
  236.     
  237.     '──────────────────────────────────────────────────────────────────────────
  238.     ' The delay local variable is used here for dummy loops that create a
  239.     ' very brief pauses of execution at points in the routine that need it,
  240.     ' particularly in the vertical motion.  Change this value to suit the
  241.     ' speed of your machine, or make it 0 to get rid of it.
  242.     '──────────────────────────────────────────────────────────────────────────
  243.         delay = 10
  244.         COLOR f%, b%
  245.     
  246.     '──────────────────────────────────────────────────────────────────────────
  247.     ' Load the screen file into an array for later access
  248.     '──────────────────────────────────────────────────────────────────────────
  249.         DIM scrArray(4000) AS STRING * 1
  250.         DIM sArray%(4000)
  251.         DEF SEG = VARSEG(scrArray(0))
  252.         BLOAD file$, VARPTR(scrArray(0))
  253.         DEF SEG
  254.     
  255.     '──────────────────────────────────────────────────────────────────────────
  256.     ' Convert the array to one that runs much faster
  257.     '──────────────────────────────────────────────────────────────────────────
  258.         FOR x% = 0 TO 3999
  259.             sArray%(x%) = ASC(scrArray(x%))
  260.         NEXT x%
  261.     
  262.     '──────────────────────────────────────────────────────────────────────────
  263.     ' Determine the memory segment of the video display for all direct screen
  264.     ' writes and save it in vidSeg
  265.     '──────────────────────────────────────────────────────────────────────────
  266.         vidseg = GetVideoSegment
  267.     
  268.         SELECT CASE mode%
  269.         
  270.         CASE 0      ' ─ Horizontal build, middle out ────────────────────────────────
  271.             y% = 12
  272.             FOR x% = 13 TO 1 STEP -1
  273.                 FOR d = 1 TO delay
  274.                 NEXT d
  275.                 y% = y% + 1
  276.                 xOffSet% = (x% - 1) * 160
  277.                 yOffSet% = (y% - 1) * 160
  278.                 DEF SEG = vidseg
  279.                 FOR a% = 0 TO 159
  280.                     POKE xOffSet% + a%, sArray%(xOffSet% + a%)
  281.                     POKE yOffSet% + a%, sArray%(yOffSet% + a%)
  282.                 NEXT a%
  283.                 DEF SEG
  284.             NEXT x%
  285.         
  286.         CASE 1      ' ─ Horizontal build, ends in ───────────────────────────────────
  287.             y% = 26
  288.             FOR x% = 1 TO 13
  289.                 FOR d = 1 TO delay      ' Delay loop - change delay above to
  290.                 NEXT d  '              regulate speed
  291.                 y% = y% - 1
  292.                 xOffSet% = (x% - 1) * 160
  293.                 yOffSet% = (y% - 1) * 160
  294.                 DEF SEG = vidseg
  295.                 FOR a% = 0 TO 159
  296.                     POKE xOffSet% + a%, sArray%(xOffSet% + a%)
  297.                     POKE yOffSet% + a%, sArray%(yOffSet% + a%)
  298.                 NEXT a%
  299.                 DEF SEG
  300.             NEXT x%
  301.         
  302.         CASE 2      ' ─ Vertical build, middle out ───────────────────────────────────
  303.             y% = 39
  304.             FOR x% = 39 TO 0 STEP -1
  305.                 y% = y% + 1
  306.                 DEF SEG = vidseg
  307.                 FOR i% = 1 TO 25
  308.                     xOffSet% = ((i% - 1) * 160) + (x% * 2)
  309.                     yOffSet% = ((i% - 1) * 160) + (y% * 2)
  310.                     POKE xOffSet%, sArray%(xOffSet%): POKE xOffSet% + 1, sArray%(xOffSet% + 1)
  311.                     POKE yOffSet%, sArray%(yOffSet%): POKE yOffSet% + 1, sArray%(yOffSet% + 1)
  312.                 NEXT i%
  313.                 DEF SEG
  314.                 FOR d = 1 TO delay
  315.                 NEXT d
  316.             NEXT x%
  317.         
  318.         CASE 3      ' ─ Vertical build, ends in ──────────────────────────────────────
  319.             y% = 80
  320.             FOR x% = 0 TO 40
  321.                 y% = y% - 1
  322.                 DEF SEG = vidseg
  323.                 FOR i% = 1 TO 25
  324.                     xOffSet% = ((i% - 1) * 160) + (x% * 2)
  325.                     yOffSet% = ((i% - 1) * 160) + (y% * 2)
  326.                     POKE xOffSet%, sArray%(xOffSet%): POKE xOffSet% + 1, sArray%(xOffSet% + 1)
  327.                     POKE yOffSet%, sArray%(yOffSet%): POKE yOffSet% + 1, sArray%(yOffSet% + 1)
  328.                 NEXT i%
  329.                 DEF SEG
  330.                 FOR d = 1 TO delay
  331.                 NEXT d
  332.             NEXT x%
  333.         
  334.         CASE 4      ' ─ Left to right screen build ───────────────────────────────────
  335.             FOR x% = 0 TO 79
  336.                 DEF SEG = vidseg
  337.                 FOR i% = 1 TO 25
  338.                     xOffSet% = ((i% - 1) * 160) + (x% * 2)
  339.                     POKE xOffSet%, sArray%(xOffSet%): POKE xOffSet% + 1, sArray%(xOffSet% + 1)
  340.                 NEXT i%
  341.                 DEF SEG
  342.                 FOR d = 1 TO delay
  343.                 NEXT d
  344.             NEXT x%
  345.         
  346.         CASE 5      ' ─ Right to left screen build ───────────────────────────────────
  347.             FOR x% = 79 TO 0 STEP -1
  348.                 DEF SEG = vidseg
  349.                 FOR i% = 1 TO 25
  350.                     xOffSet% = ((i% - 1) * 160) + (x% * 2)
  351.                     POKE xOffSet%, sArray%(xOffSet%): POKE xOffSet% + 1, sArray%(xOffSet% + 1)
  352.                 NEXT i%
  353.                 DEF SEG
  354.                 FOR d = 1 TO delay
  355.                 NEXT d
  356.             NEXT x%
  357.         
  358.         CASE 6      ' ─ All sides in to center ───────────────────────────────────────
  359.             y% = 25
  360.             FOR x% = 0 TO 13
  361.                 y% = y% - 1
  362.                 topOffSet% = x% * 160
  363.                 botOffSet% = y% * 160
  364.                 DEF SEG = vidseg
  365.             ' Top-most row
  366.                 FOR j% = (x% * 3) TO (y% * 3) + 7
  367.                     POKE topOffSet% + (j% * 2), sArray%(topOffSet% + (j% * 2))
  368.                     POKE topOffSet% + (j% * 2) + 1, sArray%(topOffSet% + (j% * 2) + 1)
  369.                 NEXT j%
  370.             ' Left and right sides
  371.                 FOR j% = x% TO y%
  372.                     FOR i% = 0 TO 5
  373.                         POKE (j% * 160) + (x% * 6) + i%, sArray%((j% * 160) + (x% * 6) + i%)
  374.                         POKE (j% * 160) + (y% * 6) + 10 + i%, sArray%((j% * 160) + (y% * 6) + 10 + i%)
  375.                     NEXT i%
  376.                 NEXT j%
  377.             
  378.             ' Bottom-most row
  379.                 FOR j% = (x% * 3) TO (y% * 3) + 7
  380.                     POKE botOffSet% + (j% * 2), sArray%(botOffSet% + (j% * 2))
  381.                     POKE botOffSet% + (j% * 2) + 1, sArray%(botOffSet% + (j% * 2) + 1)
  382.                 NEXT j%
  383.                 DEF SEG
  384.             NEXT x%
  385.         
  386.         CASE 7      ' ─ All sides out from center ────────────────────────────────────
  387.             y% = 11
  388.             FOR x% = 12 TO 0 STEP -1
  389.                 y% = y% + 1
  390.                 topOffSet% = x% * 160
  391.                 botOffSet% = y% * 160
  392.                 DEF SEG = vidseg
  393.             ' Top-most row
  394.                 FOR j% = (x% * 3) TO (y% * 3) + 7
  395.                     POKE topOffSet% + (j% * 2), sArray%(topOffSet% + (j% * 2))
  396.                     POKE topOffSet% + (j% * 2) + 1, sArray%(topOffSet% + (j% * 2) + 1)
  397.                 NEXT j%
  398.             ' Left and right sides
  399.                 FOR j% = x% TO y%
  400.                     FOR i% = 0 TO 5
  401.                         POKE (j% * 160) + (x% * 6) + i%, sArray%((j% * 160) + (x% * 6) + i%)
  402.                         POKE (j% * 160) + (y% * 6) + 10 + i%, sArray%((j% * 160) + (y% * 6) + 10 + i%)
  403.                     NEXT i%
  404.                 NEXT j%
  405.             ' Bottom-most row
  406.                 FOR j% = (x% * 3) TO (y% * 3) + 7
  407.                     POKE botOffSet% + (j% * 2), sArray%(botOffSet% + (j% * 2))
  408.                     POKE botOffSet% + (j% * 2) + 1, sArray%(botOffSet% + (j% * 2) + 1)
  409.                 NEXT j%
  410.                 DEF SEG
  411.             NEXT x%
  412.         
  413.         CASE 8      ' ─ Vertical split - left down, right up ─────────────────────────
  414.             y% = 26
  415.             FOR x% = 1 TO 25
  416.                 FOR d = 1 TO delay
  417.                 NEXT d
  418.                 y% = y% - 1
  419.                 DEF SEG = vidseg
  420.                 offset% = (x% - 1) * 160
  421.                 FOR i% = 0 TO 79
  422.                     POKE offset% + i%, sArray%(offset% + i%)
  423.                 NEXT i%
  424.                 offset% = (y% - 1) * 160
  425.                 FOR i% = 80 TO 159
  426.                     POKE offset% + i%, sArray%(offset% + i%)
  427.                 NEXT i%
  428.                 DEF SEG
  429.             NEXT x%
  430.         
  431.         CASE 9      ' ─ Horizontal split - top right to left, bottom left to right ───
  432.             y% = 80
  433.             FOR x% = 0 TO 79
  434.                 y% = y% - 1
  435.                 DEF SEG = vidseg
  436.                 FOR i% = 1 TO 12
  437.                     offset% = ((i% - 1) * 160) + (x% * 2)
  438.                     POKE offset%, sArray%(offset%): POKE offset% + 1, sArray%(offset% + 1)
  439.                 NEXT i%
  440.                 FOR i% = 13 TO 25
  441.                     offset% = ((i% - 1) * 160) + (y% * 2)
  442.                     POKE offset%, sArray%(offset%): POKE offset% + 1, sArray%(offset% + 1)
  443.                 NEXT i%
  444.                 DEF SEG
  445.             NEXT x%
  446.         
  447.         CASE 10     ' ─ Spiral inward ────────────────────────────────────────────────
  448.         
  449.             FOR x% = 1 TO 25
  450.                 offset% = (x% - 1) * 160
  451.                 DEF SEG = vidseg
  452.                 FOR y% = 0 TO 31
  453.                     POKE offset% + y%, sArray%(offset% + y%)
  454.                 NEXT y%
  455.                 DEF SEG
  456.             NEXT x%
  457.             offset% = 19 * 160
  458.             FOR x% = 16 TO 79
  459.                 DEF SEG = vidseg
  460.                 FOR y% = 0 TO 5
  461.                     POKE 3040 + (x% * 2) + (y% * 160), sArray%(3040 + (x% * 2) + (y% * 160))
  462.                     POKE 3041 + (x% * 2) + (y% * 160), sArray%(3041 + (x% * 2) + (y% * 160))
  463.                 NEXT y%
  464.                 DEF SEG
  465.             NEXT x%
  466.             FOR x% = 19 TO 1 STEP -1
  467.                 offset% = (x% - 1) * 160 + 127
  468.                 DEF SEG = vidseg
  469.                 FOR y% = 0 TO 32
  470.                     POKE offset% + y%, sArray%(offset% + y%)
  471.                 NEXT y%
  472.                 DEF SEG
  473.             NEXT x%
  474.         
  475.             FOR x% = 63 TO 16 STEP -1
  476.                 DEF SEG = vidseg
  477.                 FOR y% = 0 TO 5
  478.                     POKE 1 + (x% * 2) + (y% * 160), sArray%(1 + (x% * 2) + (y% * 160))
  479.                     POKE (x% * 2) + (y% * 160), sArray%((x% * 2) + (y% * 160))
  480.                 NEXT y%
  481.                 DEF SEG
  482.             NEXT x%
  483.             FOR x% = 7 TO 19
  484.                 offset% = (x% - 1) * 160 + 32
  485.                 DEF SEG = vidseg
  486.                 FOR y% = 0 TO 31
  487.                     POKE offset% + y%, sArray%(offset% + y%)
  488.                 NEXT y%
  489.                 DEF SEG
  490.             NEXT x%
  491.             offset% = 19 * 160
  492.             FOR x% = 32 TO 63
  493.                 DEF SEG = vidseg
  494.                 FOR y% = 0 TO 5
  495.                     POKE 2240 + (x% * 2) + (y% * 160), sArray%(2240 + (x% * 2) + (y% * 160))
  496.                     POKE 2241 + (x% * 2) + (y% * 160), sArray%(2241 + (x% * 2) + (y% * 160))
  497.                 NEXT y%
  498.                 DEF SEG
  499.             NEXT x%
  500.             FOR x% = 14 TO 6 STEP -1
  501.                 offset% = (x% - 1) * 160 + 95
  502.                 DEF SEG = vidseg
  503.                 FOR y% = 1 TO 31
  504.                     POKE offset% + y%, sArray%(offset% + y%)
  505.                 NEXT y%
  506.                 DEF SEG
  507.             NEXT x%
  508.             offset% = 6 * 160
  509.             FOR x% = 47 TO 32 STEP -1
  510.                 DEF SEG = vidseg
  511.                 FOR y% = 0 TO 5
  512.                     POKE offset% + 1 + (x% * 2) + (y% * 160), sArray%(offset% + 1 + (x% * 2) + (y% * 160))
  513.                     POKE offset% + (x% * 2) + (y% * 160), sArray%(offset% + (x% * 2) + (y% * 160))
  514.                 NEXT y%
  515.                 DEF SEG
  516.             NEXT x%
  517.             FOR x% = 13 TO 14
  518.                 offset% = (x% - 1) * 160 + 64
  519.                 DEF SEG = vidseg
  520.                 FOR y% = 0 TO 31
  521.                     POKE offset% + y%, sArray%(offset% + y%)
  522.                 NEXT y%
  523.                 DEF SEG
  524.             NEXT x%
  525.         
  526.         CASE 11     ' ─ Top to bottom ────────────────────────────────────────────────
  527.         
  528.             FOR x% = 1 TO 25
  529.                 FOR d = 1 TO delay
  530.                 NEXT d
  531.                 DEF SEG = vidseg
  532.                 offset% = (x% - 1) * 160
  533.                 FOR i% = 0 TO 159
  534.                     POKE offset% + i%, sArray%(offset% + i%)
  535.                 NEXT i%
  536.                 DEF SEG
  537.             NEXT x%
  538.         
  539.         CASE 12     ' ─ Bottom to top ────────────────────────────────────────────────
  540.         
  541.             FOR x% = 25 TO 1 STEP -1
  542.                 FOR d = 1 TO delay
  543.                 NEXT d
  544.                 DEF SEG = vidseg
  545.                 offset% = (x% - 1) * 160
  546.                 FOR i% = 0 TO 159
  547.                     POKE offset% + i%, sArray%(offset% + i%)
  548.                 NEXT i%
  549.                 DEF SEG
  550.             NEXT x%
  551.         
  552.         CASE 13     ' ─ Upper-left corner to lower-right ────────────────────────────
  553.         
  554.             FOR x% = 1 TO 25
  555.             
  556.             ' The horizontal portion...
  557.                 offset% = (x% - 1) * 160
  558.                 DEF SEG = vidseg
  559.                 FOR i% = offset% TO offset% + (x% * 6)
  560.                     POKE i%, sArray%(i%)
  561.                 NEXT i%
  562.             
  563.             ' ...and the vertical portion.
  564.                 FOR y% = 1 TO x%
  565.                     offset% = ((y% - 1) * 160) + (x% * 6)
  566.                     DEF SEG = vidseg
  567.                     FOR j% = 0 TO 5
  568.                         POKE offset% + j%, sArray%(offset% + j%)
  569.                     NEXT j%
  570.                     DEF SEG
  571.                 NEXT y%
  572.             NEXT x%
  573.         
  574.         ' Take care of the remaining two columns
  575.             FOR y% = 1 TO 25
  576.                 offset% = ((y% - 1) * 160) + 155
  577.                 DEF SEG = vidseg
  578.                 FOR j% = 0 TO 4
  579.                     POKE offset% + j%, sArray%(offset% + j%)
  580.                 NEXT j%
  581.                 DEF SEG
  582.             NEXT y%
  583.         
  584.         CASE 14     ' ─ Lower-right corner to upper-left ────────────────────────────
  585.         
  586.         ' Take care of the last two columns
  587.             FOR y% = 1 TO 25
  588.                 offset% = ((y% - 1) * 160) + 155
  589.                 DEF SEG = vidseg
  590.                 FOR j% = 0 TO 4
  591.                     POKE offset% + j%, sArray%(offset% + j%)
  592.                 NEXT j%
  593.                 DEF SEG
  594.             NEXT y%
  595.         
  596.             FOR x% = 25 TO 1 STEP -1
  597.             
  598.             ' The hori(zontal portion...
  599.                 offset% = (x% - 1) * 160
  600.                 DEF SEG = vidseg
  601.                 FOR i% = offset% TO offset% + (x% * 6)
  602.                     POKE i%, sArray%(i%)
  603.                 NEXT i%
  604.             
  605.             ' ...and the vertical portion.
  606.                 FOR y% = 1 TO x%
  607.                     offset% = ((y% - 1) * 160) + (x% * 6)
  608.                     DEF SEG = vidseg
  609.                     FOR j% = 0 TO 5
  610.                         POKE offset% + j%, sArray%(offset% + j%)
  611.                     NEXT j%
  612.                     DEF SEG
  613.                 NEXT y%
  614.             NEXT x%
  615.         
  616.         CASE 15     ' ─ Random blocks ───────────────────────────────────────────────
  617.         
  618.             RANDOMIZE TIMER
  619.             DIM screenGrid%(1 TO 5, 1 TO 10)
  620.         
  621.             FOR x% = 1 TO 50
  622.             
  623.             ' Find a block of the screen that hasn't been displayed yet
  624.                 validBlock% = FALSE
  625.                 DO
  626.                     row% = INT(RND(1) * 5) + 1
  627.                     col% = INT(RND(1) * 10) + 1
  628.                     IF screenGrid%(row%, col%) = FALSE THEN
  629.                         validBlock% = TRUE
  630.                         screenGrid%(row%, col%) = TRUE
  631.                     END IF
  632.                 LOOP UNTIL validBlock%
  633.             
  634.             ' Display the block
  635.                 FOR i% = ((row% - 1) * 5) TO ((row% - 1) * 5) + 4
  636.                     offset% = (i% * 160) + ((col% - 1) * 16)
  637.                     DEF SEG = vidseg
  638.                     FOR j% = offset% TO offset% + 15
  639.                         POKE j%, sArray%(j%)
  640.                     NEXT j%
  641.                     DEF SEG
  642.                 NEXT i%
  643.             NEXT x%
  644.         
  645.         CASE 16     ' ─ Interlacing ─────────────────────────────────────────────────
  646.             DEF SEG = vidseg
  647.             FOR x% = 0 TO 219
  648.                 FOR y% = 0 TO 3959 STEP 180
  649.                     POKE x% + y%, sArray%(x% + y%)
  650.                     POKE x% + y% + 1, sArray%(x% + y% + 1)
  651.                 NEXT y%
  652.             NEXT x%
  653.             DEF SEG
  654.         
  655.         CASE 17     ' ─ Vertical Blinds - Left to Right ─────────────────────────────
  656.             DEF SEG = vidseg
  657.             FOR j% = 0 TO 9
  658.                 FOR x% = 0 TO 79 STEP 10
  659.                     FOR i% = 1 TO 25
  660.                         xOffSet% = ((i% - 1) * 160) + ((x% + j%) * 2)
  661.                         POKE xOffSet%, sArray%(xOffSet%): POKE xOffSet% + 1, sArray%(xOffSet% + 1)
  662.                     NEXT i%
  663.                 NEXT x%
  664.             NEXT j%
  665.             DEF SEG
  666.         
  667.         CASE 18     ' ─ Vertical Blinds - Right to Left ─────────────────────────────
  668.             DEF SEG = vidseg
  669.             FOR j% = 9 TO 0 STEP -1
  670.                 FOR x% = 0 TO 79 STEP 10
  671.                     FOR i% = 1 TO 25
  672.                         xOffSet% = ((i% - 1) * 160) + ((x% + j%) * 2)
  673.                         POKE xOffSet%, sArray%(xOffSet%): POKE xOffSet% + 1, sArray%(xOffSet% + 1)
  674.                     NEXT i%
  675.                 NEXT x%
  676.             NEXT j%
  677.             DEF SEG
  678.         
  679.         CASE ELSE   ' Programmer passed an invalid Mode% - do nothing
  680.         
  681.         END SELECT
  682.     
  683. END SUB
  684.  
  685. FUNCTION CalcScrollPos% (listSize%, numDivisions%, currentPos%)
  686.  
  687.     '┌────────────────────────────────────────────────────────────────────────┐
  688.     '│  This function is used internally by the routines that utilize a       │
  689.     '│  scroll bar, such as SelectList and ViewList.  It calculates the next  │
  690.     '│  position of a scroll bar elevator.                                    │
  691.     '│                                                                        │
  692.     '│  Parameters are as follows:                                            │
  693.     '│                                                                        │
  694.     '│      listSize% - The number of items being scrolled through, total.    │
  695.     '│      numDivisions% - The number of elevator floors in the scroll bar   │
  696.     '│                  (i.e., number of possible scroll bar positions).      │
  697.     '│                  Numbered starting with 1.                             │
  698.     '│      currentPos% - The current position through the list (not the      │
  699.     '│                  position of the scroll bar elevator).                 │
  700.     '└────────────────────────────────────────────────────────────────────────┘
  701.  
  702.     '──────────────────────────────────────────────────────────────────────────
  703.     ' Find the percentage through the list.
  704.     '──────────────────────────────────────────────────────────────────────────
  705.         percent! = currentPos% / listSize%
  706.  
  707.     '──────────────────────────────────────────────────────────────────────────
  708.     ' Determine the final answer.  If division results in less that 1, return
  709.     ' 1 anyway.  If more than numDivisions%, return numDivisions%.
  710.     '──────────────────────────────────────────────────────────────────────────
  711.         answer% = percent! * numDivisions%
  712.         IF answer% < 1 THEN
  713.             answer% = 1
  714.         END IF
  715.         IF answer% > numDivisions% THEN
  716.             answer% = numDivisions%
  717.         END IF
  718.  
  719.     '──────────────────────────────────────────────────────────────────────────
  720.     ' Return the final answer.
  721.     '──────────────────────────────────────────────────────────────────────────
  722.         CalcScrollPos% = answer%
  723.  
  724. END FUNCTION
  725.  
  726. SUB Center (st$, row%)
  727.     
  728.     '┌────────────────────────────────────────────────────────────────────────┐
  729.     '│  This subroutine will display a string passed to it centered on the    │
  730.     '│  row passed to it.  Parameters are as follows:                         │
  731.     '│                                                                        │
  732.     '│      st$ - The string to center on the screen.  String must be 80      │
  733.     '│            characters or less.                                         │
  734.     '│      row% - The row of the screen on which to center the string.       │
  735.     '│             Must be in the range 1 through 25.                         │
  736.     '└────────────────────────────────────────────────────────────────────────┘
  737.     
  738.     '──────────────────────────────────────────────────────────────────────────
  739.     ' Calculate X-Coordinate (column) on which to locate the string
  740.     '──────────────────────────────────────────────────────────────────────────
  741.         x% = INT((80 - (LEN(st$))) / 2) + 1
  742.     
  743.     '──────────────────────────────────────────────────────────────────────────
  744.     ' Display the text string
  745.     '──────────────────────────────────────────────────────────────────────────
  746.         LOCATE row%, x%, 0: PRINT st$;
  747.     
  748. END SUB
  749.  
  750. SUB ClrScr (mode%, fillChar$)
  751.     
  752.     '┌────────────────────────────────────────────────────────────────────────┐
  753.     '│  This routine clears the screen in any of 10 different ways.  The      │
  754.     '│  parameters are as follows:                                            │
  755.     '│                                                                        │
  756.     '│    mode% - A number indicating which way you want the screen cleared.  │
  757.     '│            The number must be in the range of 0 through 14.  See the   │
  758.     '│            QBSCR documentation or the REF program for more info.       │
  759.     '│    fillChar$ - This is a single character string containing the        │
  760.     '│                character you want to clear the screen with.  Under     │
  761.     '│                most circumstances, this will simply be a space.        │
  762.     '└────────────────────────────────────────────────────────────────────────┘
  763.     
  764.     '──────────────────────────────────────────────────────────────────────────
  765.     ' The Delay local variable is used here for dummy loops that create a
  766.     ' very brief pauses of execution at points in the routine that need it,
  767.     ' particularly in the vertical motion.  Change this value to suit the
  768.     ' speed of your machine.
  769.     '──────────────────────────────────────────────────────────────────────────
  770.         delay = 5
  771.     
  772.     '──────────────────────────────────────────────────────────────────────────
  773.     ' Clear the screen.  Method used is based on the passed Mode parameter
  774.     '──────────────────────────────────────────────────────────────────────────
  775.         SELECT CASE mode%
  776.         
  777.         CASE 0      ' ─ Horizontal clear, middle out ────────────────────────────
  778.             y = 12
  779.             FOR x = 13 TO 1 STEP -1
  780.                 FOR a = 1 TO delay
  781.                 NEXT a
  782.                 y = y + 1
  783.                 LOCATE x, 1, 0: PRINT STRING$(80, fillChar$);
  784.                 LOCATE y, 1, 0: PRINT STRING$(80, fillChar$);
  785.             NEXT x
  786.         
  787.         CASE 1      ' ─ Horizontal clear, ends in ───────────────────────────────
  788.             y = 26
  789.             FOR x = 1 TO 13
  790.                 FOR a = 1 TO delay
  791.                 NEXT a
  792.                 y = y - 1
  793.                 LOCATE x, 1, 0: PRINT STRING$(80, fillChar$);
  794.                 LOCATE y, 1, 0: PRINT STRING$(80, fillChar$);
  795.             NEXT x
  796.         
  797.         CASE 2      ' ─ Vertical clear, middle out ───────────────────────────────
  798.             y% = 39
  799.             FOR x% = 39 TO 1 STEP -2
  800.                 y% = y% + 2
  801.                 FOR a% = 1 TO 25
  802.                     LOCATE a%, x%, 0: PRINT fillChar$ + fillChar$;
  803.                     LOCATE a%, y%, 0: PRINT fillChar$ + fillChar$;
  804.                 NEXT a%
  805.             NEXT x%
  806.         
  807.         CASE 3      ' ─ Vertical clear, ends in ──────────────────────────────────
  808.             y% = 81
  809.             FOR x% = 1 TO 40 STEP 2
  810.                 y% = y% - 2
  811.                 FOR a% = 1 TO 25
  812.                     LOCATE a%, x%, 0: PRINT fillChar$ + fillChar$;
  813.                     LOCATE a%, y%, 0: PRINT fillChar$ + fillChar$;
  814.                 NEXT a%
  815.             NEXT x%
  816.         
  817.         CASE 4      ' ─ Left to right screen wipe ────────────────────────────────
  818.             FOR x% = 1 TO 79 STEP 2
  819.                 FOR a% = 1 TO 25
  820.                     LOCATE a%, x%, 0: PRINT fillChar$ + fillChar$;
  821.                 NEXT a%
  822.             NEXT x%
  823.         
  824.         CASE 5      ' ─ Right to left screen wipe ────────────────────────────────
  825.             FOR x% = 79 TO 1 STEP -2
  826.                 FOR a% = 1 TO 25
  827.                     LOCATE a%, x%, 0: PRINT fillChar$ + fillChar$;
  828.                 NEXT a%
  829.             NEXT x%
  830.         
  831.         CASE 6      ' ─ All sides in to center ───────────────────────────────────
  832.             y% = 26
  833.             FOR x% = 1 TO 13
  834.                 y% = y% - 1
  835.                 LOCATE x%, 1, 0: PRINT STRING$(80, fillChar$);
  836.                 LOCATE y%, 1, 0: PRINT STRING$(80, fillChar$);
  837.                 FOR a1% = 1 TO 25
  838.                     LOCATE a1%, x% * 3 - 2, 0: PRINT fillChar$ + fillChar$ + fillChar$;
  839.                     LOCATE a1%, y% * 3 + 3, 0: PRINT fillChar$ + fillChar$ + fillChar$;
  840.                 NEXT a1%
  841.             NEXT x%
  842.         
  843.         CASE 7      ' ─ All sides out from center ────────────────────────────────
  844.             y% = 12
  845.             FOR x% = 13 TO 1 STEP -1
  846.                 y% = y% + 1
  847.                 LOCATE x%, x% * 3 + 1, 0: PRINT STRING$((y% * 3 - x% * 3) + 2, fillChar$);
  848.                 LOCATE y%, x% * 3 + 1, 0: PRINT STRING$((y% * 3 - x% * 3) + 2, fillChar$);
  849.                 FOR a1% = x% TO y%
  850.                     LOCATE a1%, x% * 3 - 2, 0: PRINT fillChar$ + fillChar$ + fillChar$;
  851.                     LOCATE a1%, y% * 3 + 3, 0: PRINT fillChar$ + fillChar$ + fillChar$;
  852.                 NEXT a1%
  853.             NEXT x%
  854.         
  855.         CASE 8      ' ─ Vertical split - left down, right up ─────────────────────
  856.             y = 26
  857.             FOR x = 1 TO 25
  858.                 FOR a = 1 TO delay
  859.                 NEXT a
  860.                 y = y - 1
  861.                 LOCATE x, 1, 0: PRINT STRING$(40, fillChar$);
  862.                 LOCATE y, 41, 0: PRINT STRING$(40, fillChar$);
  863.             NEXT x
  864.         
  865.         CASE 9      ' ─ Horizontal split - top right to left, bottom left to right
  866.             y% = 81
  867.             FOR x% = 1 TO 80 STEP 2
  868.                 y% = y% - 2
  869.                 FOR a% = 1 TO 12
  870.                     LOCATE a%, x%, 0: PRINT fillChar$ + fillChar$;
  871.                 NEXT a%
  872.                 FOR a% = 13 TO 25
  873.                     LOCATE a%, y%, 0: PRINT fillChar$ + fillChar$;
  874.                 NEXT a%
  875.             NEXT x%
  876.         
  877.         CASE 10     ' ─ Spiral inward ────────────────────────────────────────────
  878.             FOR x = 1 TO 25
  879.                 FOR y = 1 TO delay
  880.                 NEXT y
  881.                 LOCATE x, 1, 0: PRINT STRING$(16, fillChar$);
  882.             NEXT x
  883.             FOR x% = 16 TO 78 STEP 3
  884.                 FOR y% = 20 TO 25
  885.                     LOCATE y%, x%, 0: PRINT STRING$(5, fillChar$);
  886.                 NEXT y%
  887.             NEXT x%
  888.             FOR x = 19 TO 1 STEP -1
  889.                 FOR y = 1 TO delay
  890.                 NEXT y
  891.                 LOCATE x, 65, 0: PRINT STRING$(16, fillChar$);
  892.             NEXT x
  893.             FOR x% = 65 TO 16 STEP -3
  894.                 FOR y% = 1 TO 6
  895.                     LOCATE y%, x%, 0: PRINT STRING$(5, fillChar$);
  896.                 NEXT y%
  897.             NEXT x%
  898.             FOR x = 7 TO 19
  899.                 FOR y = 1 TO delay
  900.                 NEXT y
  901.                 LOCATE x, 17, 0: PRINT STRING$(16, fillChar$);
  902.             NEXT x
  903.             FOR x% = 32 TO 64 STEP 3
  904.                 FOR y% = 15 TO 19
  905.                     LOCATE y%, x%, 0: PRINT STRING$(5, fillChar$);
  906.                 NEXT y%
  907.             NEXT x%
  908.             FOR x = 14 TO 6 STEP -1
  909.                 FOR y = 1 TO delay
  910.                 NEXT y
  911.                 LOCATE x, 49, 0: PRINT STRING$(16, fillChar$);
  912.             NEXT x
  913.             FOR x% = 48 TO 33 STEP -3
  914.                 FOR y% = 7 TO 10
  915.                     LOCATE y%, x%, 0: PRINT STRING$(5, fillChar$);
  916.                 NEXT y%
  917.             NEXT x%
  918.             FOR x = 11 TO 14
  919.                 FOR y = 1 TO delay
  920.                 NEXT y
  921.                 LOCATE x, 33, 0: PRINT STRING$(16, fillChar$);
  922.             NEXT x
  923.         
  924.         CASE 11     ' ─ Top to bottom ────────────────────────────────────────────
  925.         
  926.             FOR x = 1 TO 25
  927.                 FOR a = 1 TO delay
  928.                 NEXT a
  929.                 LOCATE x, 1, 0: PRINT STRING$(80, fillChar$);
  930.             NEXT x
  931.         
  932.         CASE 12     ' ─ Bottom to top ────────────────────────────────────────────
  933.         
  934.             FOR x = 25 TO 1 STEP -1
  935.                 FOR a = 1 TO delay
  936.                 NEXT a
  937.                 LOCATE x, 1, 0: PRINT STRING$(80, fillChar$);
  938.             NEXT x
  939.         
  940.         CASE 13     ' ─ Upper-left corner to lower-right ─────────────────────────
  941.         
  942.             fill$ = ""
  943.             FOR x% = 1 TO 25
  944.                 fill$ = fill$ + STRING$(3, fillChar$)
  945.                 LOCATE x%, 1, 0
  946.                 PRINT fill$;
  947.                 FOR y% = 1 TO x%
  948.                     LOCATE y%, x% * 3, 0
  949.                     PRINT STRING$(3, fillChar$);
  950.                 NEXT y%
  951.             NEXT x%
  952.             FOR y% = 1 TO 25
  953.                 LOCATE y%, 78, 0
  954.                 PRINT STRING$(3, fillChar$);
  955.             NEXT y%
  956.         
  957.         CASE 14     ' ─ Lower-right corner to upper-left ─────────────────────────
  958.         
  959.             FOR y% = 1 TO 25
  960.                 LOCATE y%, 78, 0
  961.                 PRINT STRING$(3, fillChar$);
  962.             NEXT y%
  963.             fill$ = STRING$(80, fillChar$)
  964.             FOR x% = 25 TO 1 STEP -1
  965.                 fill$ = LEFT$(fill$, LEN(fill$) - 3)
  966.                 LOCATE x%, 1, 0
  967.                 PRINT fill$;
  968.                 FOR y% = 1 TO x%
  969.                     LOCATE y%, x% * 3, 0
  970.                     PRINT STRING$(3, fillChar$);
  971.                 NEXT y%
  972.             NEXT x%
  973.         
  974.         CASE 15     ' ─ Random blocks ────────────────────────────────────────────
  975.         
  976.             RANDOMIZE TIMER
  977.             DIM screenGrid%(1 TO 5, 1 TO 10)
  978.         
  979.         ' Initialize grid tracking array to all false
  980.             FOR row% = 1 TO 5
  981.                 FOR col% = 1 TO 10
  982.                     screenGrid%(row%, col%) = FALSE
  983.                 NEXT col%
  984.             NEXT row%
  985.         
  986.             FOR x% = 1 TO 50
  987.             
  988.             ' Find a block of the scren that hasn't been blanked yet
  989.                 validBlock% = FALSE
  990.                 DO
  991.                     row% = INT(RND(1) * 5) + 1
  992.                     col% = INT(RND(1) * 10) + 1
  993.                     IF screenGrid%(row%, col%) = FALSE THEN
  994.                         validBlock% = TRUE
  995.                         screenGrid%(row%, col%) = TRUE
  996.                     END IF
  997.                 LOOP UNTIL validBlock%
  998.             
  999.             ' Blank out the block
  1000.                 FOR i% = ((row% * 5 + 1) - 5) TO ((row% * 5 + 1) - 5) + 4
  1001.                     LOCATE i%, (col% * 8 + 1) - 8, 0
  1002.                     PRINT STRING$(8, fillChar$);
  1003.                 NEXT i%
  1004.             
  1005.             NEXT x%
  1006.         
  1007.         CASE 16     ' ─ Interlacing ─────────────────────────────────────────────────
  1008.             FOR y% = 0 TO 79
  1009.                 FOR x% = 1 TO 25
  1010.                     LOCATE x%, ((x% - 1) * 10 + y%) MOD 80 + 1, 0
  1011.                     PRINT fillChar$;
  1012.                 NEXT x%
  1013.             NEXT y%
  1014.         
  1015.         CASE 17     ' ─ Vertical Blinds - Left to Right ─────────────────────────────
  1016.             FOR x% = 0 TO 9
  1017.                 FOR y% = 1 TO 80 STEP 10
  1018.                     offset% = x% + y%
  1019.                     FOR z% = 1 TO 25
  1020.                         LOCATE z%, offset%, 0
  1021.                         PRINT fillChar$;
  1022.                     NEXT z%
  1023.                 NEXT y%
  1024.             NEXT x%
  1025.         
  1026.         CASE 18     ' ─ Vertical Blinds - Right to Left ─────────────────────────────
  1027.             FOR x% = 9 TO 0 STEP -1
  1028.                 FOR y% = 1 TO 80 STEP 10
  1029.                     offset% = x% + y%
  1030.                     FOR z% = 1 TO 25
  1031.                         LOCATE z%, offset%, 0
  1032.                         PRINT fillChar$;
  1033.                     NEXT z%
  1034.                 NEXT y%
  1035.             NEXT x%
  1036.         
  1037.         CASE ELSE   ' Programmer passed an invalid Mode% - do nothing
  1038.         
  1039.         END SELECT
  1040.     
  1041.         LOCATE 1, 1, 0
  1042.     
  1043. END SUB
  1044.  
  1045. FUNCTION ColorChk
  1046.     
  1047.     '┌────────────────────────────────────────────────────────────────────────┐
  1048.     '│  This function when called checks the value stored at the machine      │
  1049.     '│  memory location that contains the video display type.  If the value   │
  1050.     '│  is hex B4 then the display is mono.  Otherwise, it is color.  The     │
  1051.     '│  function returns a value of False (Zero) if mono, True (Non-Zero) if  │
  1052.     '│  color.                                                                │
  1053.     '└────────────────────────────────────────────────────────────────────────┘
  1054.     
  1055.     '──────────────────────────────────────────────────────────────────────────
  1056.     ' Set default segment to 0
  1057.     '──────────────────────────────────────────────────────────────────────────
  1058.         DEF SEG = 0
  1059.     
  1060.     '──────────────────────────────────────────────────────────────────────────
  1061.     ' PEEK at value stored at video adapter address
  1062.     '──────────────────────────────────────────────────────────────────────────
  1063.         adapter = PEEK(&H463)
  1064.     
  1065.     '──────────────────────────────────────────────────────────────────────────
  1066.     ' Set ColorChk to True or False based on value at hex &H463
  1067.     '──────────────────────────────────────────────────────────────────────────
  1068.         IF adapter = &HB4 THEN
  1069.             ColorChk = 0              ' Mono (False/Zero)
  1070.         ELSE
  1071.             ColorChk = 1              ' Color (True/Non-Zero)
  1072.         END IF
  1073.     
  1074. END FUNCTION
  1075.  
  1076. SUB DisplayEntry (entry$, qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wid%, actionCode%)
  1077.     
  1078.     '┌─────────────────────────────────────────────────────────────────────────┐
  1079.     '│  This routine is used only by the MakeMenu% Function.  It is not meant  │
  1080.     '│  for use on its own.  The routine displays the passed menu entry on the │
  1081.     '│  screen, and highlights the character that proceeds the marker          │
  1082.     '│  character.  Also interprets and displays menu dividers.                │
  1083.     '│                                                                         │
  1084.     '│  Parameters are as follows:                                             │
  1085.     '│                                                                         │
  1086.     '│      entry$ - the actual text entry to display on the screen            │
  1087.     '│      qfg% - Foreground color for 'Quick Access' key character           │
  1088.     '│      qbg% - Background color for 'Quick Access' key character           │
  1089.     '│      hfg% - Foreground color for entry at highlight bar                 │
  1090.     '│      hbg% - Background color for entry at highlight bar                 │
  1091.     '│      fg%  - Foreground color for normal entry                           │
  1092.     '│      bg%  - Background color for normal entry                           │
  1093.     '│      marker$ - the character used in menu entry strings that indicates  │
  1094.     '│                the next character is a 'Quick Access' key.              │
  1095.     '│      divider$ - The string or character that denotes a menu divider.    │
  1096.     '│      wid% - The full width of the menu window.                          │
  1097.     '│      actionCode% - Has value of 1 or 2.  1 indicates that the entry     │
  1098.     '│                    being displayed is a normal, unhighlighted entry,    │
  1099.     '│                    thus the 'Quick Access' character in the entry will  │
  1100.     '│                    be highlighted.  If 2, 'Quick Access' key is not     │
  1101.     '│                    highlighted, since entry is in highlight bar.        │
  1102.     '└─────────────────────────────────────────────────────────────────────────┘
  1103.     
  1104.     '──────────────────────────────────────────────────────────────────────────
  1105.     ' Assumes cursor is already at the right spot to display entry on.
  1106.     ' Display each character until the marker char is found.  Print highlighted
  1107.     ' 'Quick Access' char if ActionCode% is 1, otherwise print normal 'Quick
  1108.     ' Access' char.  Then print rest of entry and return to MakeMenu%.
  1109.     '──────────────────────────────────────────────────────────────────────────
  1110.  
  1111.     '──────────────────────────────────────────────────────────────────────────
  1112.     ' Set colors.
  1113.     '──────────────────────────────────────────────────────────────────────────
  1114.         SELECT CASE actionCode%
  1115.         CASE 1
  1116.             COLOR fg%, bg%
  1117.         CASE 2
  1118.             COLOR hfg%, hBG%
  1119.         CASE ELSE
  1120.         END SELECT
  1121.     
  1122.     '──────────────────────────────────────────────────────────────────────────
  1123.     ' If the entry is a menu divider, draw it.  Otherwise, display text.
  1124.     '──────────────────────────────────────────────────────────────────────────
  1125.         IF entry$ = divider$ THEN
  1126.  
  1127.             LOCATE CSRLIN, POS(0) - 1, 0
  1128.             PRINT STRING$(wid% + 2, 196);
  1129.  
  1130.         ELSE
  1131.  
  1132.             FOR x% = 1 TO LEN(entry$)
  1133.                 IF MID$(entry$, x%, 1) = marker$ THEN
  1134.                     x% = x% + 1
  1135.                     SELECT CASE actionCode%
  1136.                     CASE 1
  1137.                         COLOR qfg%, qbg%
  1138.                     CASE 2
  1139.                         COLOR hfg%, hBG%
  1140.                     CASE ELSE
  1141.                     END SELECT
  1142.                 END IF
  1143.                 PRINT MID$(entry$, x%, 1);
  1144.                 IF actionCode% = 2 THEN
  1145.                     COLOR hfg%, hBG%
  1146.                 ELSE
  1147.                     COLOR fg%, bg%
  1148.                 END IF
  1149.             NEXT x%
  1150.  
  1151.         END IF
  1152.     
  1153. END SUB
  1154.  
  1155. SUB EditString (st$, leftCol%, row%, foreColor%, backColor%)
  1156.     
  1157.     '┌────────────────────────────────────────────────────────────────────────┐
  1158.     '│  This function returns a user-entered string.  You can limit the       │
  1159.     '│  length of the string they enter as they type, a capability not        │
  1160.     '│  possible with the INPUT statement.  With minor modification of the    │
  1161.     '│  SELECT CASE statements, you can also allow only certain characters    │
  1162.     '│  to be entered.  Parameters are as follows:                            │
  1163.     '│                                                                        │
  1164.     '│      st$ -  This is the string to edit.  If there is no starting       │
  1165.     '│             value, then it should be all spaces.  Make sure the        │
  1166.     '│             string is as lon as its maximum length.                    │
  1167.     '│      leftCol% - This is the column of the screen to allow the user to  │
  1168.     '│                start typing on.  Valid range is 1 through 79.          │
  1169.     '│      row% - This is the row of the screen on which the user will type  │
  1170.     '│             Allowable range is 1 through 25.                           │
  1171.     '│      foreColor% - The foreground color to display the user's entry     │
  1172.     '│                   in.  Alowable range is 0 through 15.                 │
  1173.     '│      backColor% - The background color to display the user's entry     │
  1174.     '│                   in.  Allowable range is 0 through 7.                 │
  1175.     '└────────────────────────────────────────────────────────────────────────┘
  1176.     
  1177.     '─────────────────────────────────────────────────────────────────────────
  1178.     ' Save the string passed in just in case ESC is hit
  1179.     '─────────────────────────────────────────────────────────────────────────
  1180.         oldSt$ = st$
  1181.     
  1182.     '─────────────────────────────────────────────────────────────────────────
  1183.     ' Define variables to contain keycodes
  1184.     '─────────────────────────────────────────────────────────────────────────
  1185.         enter$ = CHR$(13)
  1186.         esc$ = CHR$(27)
  1187.         backspace$ = CHR$(8)
  1188.         ins$ = CHR$(0) + CHR$(82)
  1189.         LeftArrowKey$ = CHR$(0) + CHR$(75)
  1190.         RightArrowKey$ = CHR$(0) + CHR$(77)
  1191.         HomeKee$ = CHR$(0) + CHR$(71)
  1192.         EndKee$ = CHR$(0) + CHR$(79)
  1193.         del$ = CHR$(0) + CHR$(83)
  1194.         ctrlLeftArrow$ = CHR$(0) + CHR$(115)
  1195.         ctrlRightArrow$ = CHR$(0) + CHR$(116)
  1196.         ctrlY$ = CHR$(25)
  1197.         ctrlT$ = CHR$(20)
  1198.     
  1199.     '─────────────────────────────────────────────────────────────────────────
  1200.     ' Define initial values for insert mode and cursor size
  1201.     '─────────────────────────────────────────────────────────────────────────
  1202.         IF ColorChk THEN
  1203.             topScan% = 1
  1204.             botScan% = 7
  1205.         ELSE
  1206.             topScan% = 1
  1207.             botScan% = 12
  1208.         END IF
  1209.         insON% = FALSE
  1210.     
  1211.     '─────────────────────────────────────────────────────────────────────────
  1212.     ' Define errortone string to use with PLAY
  1213.     '─────────────────────────────────────────────────────────────────────────
  1214.         errorTone$ = "L60 N1 N0 N1"
  1215.     
  1216.     '─────────────────────────────────────────────────────────────────────────
  1217.     ' Clear variable that holds keystroke
  1218.     '─────────────────────────────────────────────────────────────────────────
  1219.         key$ = ""
  1220.     
  1221.     '─────────────────────────────────────────────────────────────────────────
  1222.     ' Set cursor position to first char in string
  1223.     '─────────────────────────────────────────────────────────────────────────
  1224.         charPos% = 1
  1225.     
  1226.     '─────────────────────────────────────────────────────────────────────────
  1227.     ' Set colors and locate the cursor
  1228.     '─────────────────────────────────────────────────────────────────────────
  1229.         COLOR foreColor%, backColor%
  1230.         LOCATE row%, leftCol%, 1
  1231.     
  1232.     '─────────────────────────────────────────────────────────────────────────
  1233.     ' Display the passed in string and relocate the cursor to beginning
  1234.     '─────────────────────────────────────────────────────────────────────────
  1235.         PRINT st$;
  1236.         LOCATE row%, leftCol%, 1, topScan%, botScan%
  1237.     
  1238.     '─────────────────────────────────────────────────────────────────────────
  1239.     ' Read keystrokes until ENTER or ESC is pressed
  1240.     '─────────────────────────────────────────────────────────────────────────
  1241.         done% = FALSE
  1242.         DO
  1243.         
  1244.             key$ = ""
  1245.             WHILE key$ = ""
  1246.                 key$ = INKEY$
  1247.             WEND
  1248.         
  1249.         '─────────────────────────────────────────────────────────────────────
  1250.         '== Decide what to do with the returned key
  1251.         '─────────────────────────────────────────────────────────────────────
  1252.             SELECT CASE key$
  1253.             
  1254.             '─────────────────────────────────────────────────────────────────
  1255.             ' The CASE statement below is what checks for allowable characters.
  1256.             ' If you wish to change the set of allowable characters, change the
  1257.             ' conditions of the CASE statement.
  1258.             '─────────────────────────────────────────────────────────────────
  1259.             
  1260.             CASE " " TO "■"           ' ASCII 32 to 254 - allowable characters
  1261.             
  1262.             '─────────────────────────────────────────────────────────────
  1263.             ' Place new character in the string.  If in INS mode, then
  1264.             ' move all chars to right 1 first.
  1265.             '─────────────────────────────────────────────────────────────
  1266.                 IF charPos% <= LEN(st$) THEN
  1267.                     IF insON% THEN
  1268.                         FOR x% = LEN(st$) - 1 TO charPos% STEP -1
  1269.                             MID$(st$, x% + 1, 1) = MID$(st$, x%, 1)
  1270.                         NEXT x%
  1271.                     END IF
  1272.                     MID$(st$, charPos%, 1) = key$
  1273.                 END IF
  1274.             
  1275.             '─────────────────────────────────────────────────────────────
  1276.             ' Move character position right 1 if not at max pos already
  1277.             '─────────────────────────────────────────────────────────────
  1278.                 charPos% = charPos% + 1
  1279.                 IF charPos% > LEN(st$) + 1 THEN
  1280.                     charPos% = charPos% - 1
  1281.                 END IF
  1282.             
  1283.             
  1284.             CASE EndKee$              ' Move to last non-space char, plus one
  1285.             
  1286.             '─────────────────────────────────────────────────────────────
  1287.             ' Move cursor to last NON-SPACE character of string
  1288.             '─────────────────────────────────────────────────────────────
  1289.                 charPos% = LEN(st$)
  1290.                 WHILE MID$(st$, charPos%, 1) = " " AND charPos% > 1
  1291.                     charPos% = charPos% - 1
  1292.                 WEND
  1293.                 IF charPos% > 1 THEN
  1294.                     charPos% = charPos% + 1
  1295.                 END IF
  1296.             
  1297.             CASE HomeKee$             ' Move to first char position
  1298.             
  1299.             '─────────────────────────────────────────────────────────────
  1300.             ' Move cursor to first char position
  1301.             '─────────────────────────────────────────────────────────────
  1302.                 charPos% = 1
  1303.             
  1304.             CASE LeftArrowKey$           ' Cursor left one position
  1305.             
  1306.             '─────────────────────────────────────────────────────────────
  1307.             ' If charPos not already at first pos, move it left 1
  1308.             '─────────────────────────────────────────────────────────────
  1309.                 IF charPos% > 1 THEN
  1310.                     charPos% = charPos% - 1
  1311.                 END IF
  1312.             
  1313.             CASE RightArrowKey$          ' Cursor right one position
  1314.             
  1315.             '─────────────────────────────────────────────────────────────
  1316.             ' If not already at end of string, move charPos right 1
  1317.             '─────────────────────────────────────────────────────────────
  1318.                 IF charPos% < LEN(st$) THEN
  1319.                     charPos% = charPos% + 1
  1320.                 END IF
  1321.             
  1322.             CASE del$ ' Delete char at cursor
  1323.             
  1324.             '─────────────────────────────────────────────────────────────
  1325.             ' Move all characters to left of cursor left 1
  1326.             '─────────────────────────────────────────────────────────────
  1327.                 FOR i% = charPos% TO LEN(st$) - 1
  1328.                     MID$(st$, i%, 1) = MID$(st$, i% + 1, 1)
  1329.                 NEXT i%
  1330.                 MID$(st$, LEN(st$), 1) = " "
  1331.             
  1332.             CASE ins$ ' Change from insert mode to overtype and back
  1333.             
  1334.             '─────────────────────────────────────────────────────────────
  1335.             ' Toggle ins mode
  1336.             '─────────────────────────────────────────────────────────────
  1337.                 IF insON% THEN
  1338.                     insON% = FALSE
  1339.                 ELSE
  1340.                     insON% = TRUE
  1341.                 END IF
  1342.             
  1343.             '─────────────────────────────────────────────────────────────
  1344.             ' Change cursor scan lines so it's BIG for overtype mode,
  1345.             ' small for insert mode
  1346.             '─────────────────────────────────────────────────────────────
  1347.                 IF insON% THEN          ' Make cursor small - insert mode
  1348.                     IF ColorChk THEN
  1349.                         topScan% = 6
  1350.                         botScan% = 7
  1351.                     ELSE
  1352.                         topScan% = 11
  1353.                         botScan% = 12
  1354.                     END IF
  1355.                 ELSE    ' Make cursor BIG - overtype mode
  1356.                     IF ColorChk THEN
  1357.                         topScan% = 1
  1358.                         botScan% = 7
  1359.                     ELSE
  1360.                         topScan% = 1
  1361.                         botScan% = 12
  1362.                     END IF
  1363.                 END IF
  1364.             
  1365.             CASE backspace$           ' Delete char left of cursor and move left one
  1366.             
  1367.             '─────────────────────────────────────────────────────────────
  1368.             ' Move cursor left 1 if not already at beginning of string,
  1369.             ' and then shift all chars right of cursor left 1.
  1370.             '─────────────────────────────────────────────────────────────
  1371.                 IF charPos% > 1 THEN
  1372.                     charPos% = charPos% - 1
  1373.                     FOR i% = charPos% TO LEN(st$) - 1
  1374.                         MID$(st$, i%, 1) = MID$(st$, i% + 1, 1)
  1375.                     NEXT i%
  1376.                     MID$(st$, LEN(st$), 1) = " "
  1377.                 END IF
  1378.             
  1379.             CASE ctrlY$               ' Erase entire entry field
  1380.             
  1381.             '─────────────────────────────────────────────────────────────
  1382.             ' Delete the entire line - reset string to spaces and move cursor
  1383.             ' to beginning of field
  1384.             '─────────────────────────────────────────────────────────────
  1385.                 st$ = SPACE$(LEN(st$))
  1386.                 charPos% = 1
  1387.             
  1388.             CASE ctrlT$               ' Erase the word to the right of the cursor
  1389.             
  1390.             '─────────────────────────────────────────────────────────────
  1391.             ' Remove characters from right of string until a space is
  1392.             ' found, or we have removed the whole line from the cursor.
  1393.             '─────────────────────────────────────────────────────────────
  1394.                 charsLeft% = LEN(st$) - charPos%
  1395.                 count% = charPos%
  1396.                 WHILE MID$(st$, charPos%, 1) <> " " AND count% < charsLeft%
  1397.                     FOR x% = charPos% TO LEN(st$) - 1
  1398.                         MID$(st$, x%, 1) = MID$(st$, x% + 1)
  1399.                     NEXT x%
  1400.                     MID$(st$, LEN(st$), 1) = " "
  1401.                     count% = count% + 1
  1402.                 WEND
  1403.             
  1404.             '─────────────────────────────────────────────────────────────
  1405.             ' Remove any spaces until a char is found.  DO NOT delete the
  1406.             ' char!
  1407.             '─────────────────────────────────────────────────────────────
  1408.                 WHILE MID$(st$, charPos%, 1) = " " AND count% < charsLeft%
  1409.                     FOR x% = charPos% TO LEN(st$) - 1
  1410.                         MID$(st$, x%, 1) = MID$(st$, x% + 1)
  1411.                     NEXT x%
  1412.                     MID$(st$, LEN(st$), 1) = " "
  1413.                     count% = count% + 1
  1414.                 WEND
  1415.             
  1416.             CASE ctrlLeftArrow$       ' Find next word left
  1417.             
  1418.             '─────────────────────────────────────────────────────────────
  1419.             ' Move to either the next non-space or position 1, whichever
  1420.             ' is first
  1421.             '─────────────────────────────────────────────────────────────
  1422.                 IF charPos% > 1 THEN
  1423.                     DO
  1424.                         charPos% = charPos% - 1
  1425.                     LOOP UNTIL (MID$(st$, charPos%, 1) <> " ") OR charPos% = 1
  1426.                 END IF
  1427.             
  1428.             '─────────────────────────────────────────────────────────────
  1429.             ' Move left until space or pos 1 is found, whichever is first
  1430.             '─────────────────────────────────────────────────────────────
  1431.                 IF charPos% > 1 THEN
  1432.                     DO
  1433.                         charPos% = charPos% - 1
  1434.                     LOOP UNTIL (MID$(st$, charPos%, 1) = " ") OR charPos% = 1
  1435.                 END IF
  1436.             
  1437.             '─────────────────────────────────────────────────────────────
  1438.             ' Move forward one if at a space
  1439.             '─────────────────────────────────────────────────────────────
  1440.                 IF MID$(st$, charPos%, 1) = " " THEN
  1441.                     charPos% = charPos% + 1
  1442.                 END IF
  1443.             
  1444.             CASE ctrlRightArrow$      ' Find next word right
  1445.             
  1446.             '─────────────────────────────────────────────────────────────
  1447.             ' Move right until space or last pos is found, whichever is
  1448.             ' first
  1449.             '─────────────────────────────────────────────────────────────
  1450.                 IF charPos% <= LEN(st$) THEN
  1451.                     DO
  1452.                         charPos% = charPos% + 1
  1453.                     LOOP UNTIL (MID$(st$, charPos%, 1) = " ") OR charPos% = LEN(st$) + 1
  1454.                 END IF
  1455.             
  1456.             '─────────────────────────────────────────────────────────────
  1457.             ' Move to either the next non-space or last position, which-
  1458.             ' ever is first
  1459.             '─────────────────────────────────────────────────────────────
  1460.                 IF charPos% <= LEN(st$) THEN
  1461.                     DO
  1462.                         charPos% = charPos% + 1
  1463.                     LOOP UNTIL (MID$(st$, charPos%, 1) <> " ") OR charPos% = LEN(st$) + 1
  1464.                 END IF
  1465.             
  1466.             CASE esc$ ' Exit the operation
  1467.             
  1468.             '─────────────────────────────────────────────────────────────
  1469.             ' Restore original value of string and exit
  1470.             '─────────────────────────────────────────────────────────────
  1471.                 st$ = oldSt$
  1472.                 done% = TRUE
  1473.             
  1474.             CASE enter$               ' Accept entry and exit operation
  1475.             
  1476.             '─────────────────────────────────────────────────────────────
  1477.             ' Edit finished - exit subroutine
  1478.             '─────────────────────────────────────────────────────────────
  1479.                 done% = TRUE
  1480.             
  1481.             CASE ELSE ' Invalid keypresses fall here
  1482.             
  1483.             '─────────────────────────────────────────────────────────────
  1484.             ' Unacceptable key was hit
  1485.             '─────────────────────────────────────────────────────────────
  1486.                 PLAY errorTone$
  1487.             
  1488.             END SELECT                ' CASE Key$
  1489.         
  1490.         '─────────────────────────────────────────────────────────────────────
  1491.         ' Redisplay string after edits
  1492.         '─────────────────────────────────────────────────────────────────────
  1493.             LOCATE row%, leftCol%, 0
  1494.             PRINT st$;
  1495.         
  1496.         '─────────────────────────────────────────────────────────────────────
  1497.         ' Make sure cursor is at the right spot
  1498.         '─────────────────────────────────────────────────────────────────────
  1499.             LOCATE row%, leftCol% + charPos% - 1, 1, topScan%, botScan%
  1500.         
  1501.         LOOP UNTIL done%
  1502.     
  1503. END SUB
  1504.  
  1505. FUNCTION GetBackground% (row%, col%)
  1506.     
  1507.     '┌──────────────────────────────────────────────────────────────────┐
  1508.     '│  This function will return the background color of the character │
  1509.     '│  cell at the specified row and column of the screen.             │
  1510.     '└──────────────────────────────────────────────────────────────────┘
  1511.     
  1512.     '────────────────────────────────────────────────────────────────────
  1513.     ' Set the memory segment to the address of screen memory
  1514.     '────────────────────────────────────────────────────────────────────
  1515.         DEF SEG = GetVideoSegment
  1516.     
  1517.     '────────────────────────────────────────────────────────────────────
  1518.     ' Determine the background color of the cell at row%, col%
  1519.     '────────────────────────────────────────────────────────────────────
  1520.     ' Get color attribute byte
  1521.         attr% = PEEK(((row% - 1) * 160) + ((col% - 1) * 2) + 1)
  1522.     ' Calculate background
  1523.         step1% = (attr% AND &HFF) \ 16
  1524.         IF step1% > 7 THEN          ' Foreground is blinking
  1525.             GetBackground% = step1% - 8
  1526.         ELSE        ' Foreground is NOT blinking
  1527.             GetBackground% = step1%
  1528.         END IF
  1529.     
  1530.     '────────────────────────────────────────────────────────────────────
  1531.     ' Restore BASIC's default data segment
  1532.     '────────────────────────────────────────────────────────────────────
  1533.         DEF SEG
  1534.     
  1535. END FUNCTION
  1536.  
  1537. FUNCTION GetForeground% (row%, col%)
  1538.     
  1539.     '┌──────────────────────────────────────────────────────────────────┐
  1540.     '│  This function will return the foreground color of the character │
  1541.     '│  cell at the specified row and column of the screen.             │
  1542.     '└──────────────────────────────────────────────────────────────────┘
  1543.     
  1544.     '────────────────────────────────────────────────────────────────────
  1545.     ' Set the memory segment to the address of screen memory
  1546.     '────────────────────────────────────────────────────────────────────
  1547.         DEF SEG = GetVideoSegment
  1548.     
  1549.     '────────────────────────────────────────────────────────────────────
  1550.     ' Determine the foreground color of the cell at row%, col%
  1551.     '────────────────────────────────────────────────────────────────────
  1552.     ' Calculate color attribute byte
  1553.         attr% = PEEK(((row% - 1) * 160) + ((col% - 1) * 2) + 1)
  1554.     ' Calculate foreground color
  1555.         step1% = attr% AND &HFF
  1556.         IF step1% > 127 THEN        ' Color is blinking
  1557.             GetForeground% = ((step1% - 128) MOD 16) + 16
  1558.         ELSE        ' Color is NOT blinking
  1559.             GetForeground% = step1% MOD 16
  1560.         END IF
  1561.     
  1562.     '────────────────────────────────────────────────────────────────────
  1563.     ' Restore BASIC's default data segment
  1564.     '────────────────────────────────────────────────────────────────────
  1565.         DEF SEG
  1566.     
  1567. END FUNCTION
  1568.  
  1569. SUB GetScreen (file$)
  1570.     
  1571.     '┌──────────────────────────────────────────────────────────────────┐
  1572.     '│  This subprogram will copy the contents of the display to a disk │
  1573.     '│  file specified by the file$ parameter.  The save is very fast.  │
  1574.     '└──────────────────────────────────────────────────────────────────┘
  1575.     
  1576.     '────────────────────────────────────────────────────────────────────
  1577.     ' Set the memory segment to the address of screen memory
  1578.     '────────────────────────────────────────────────────────────────────
  1579.         DEF SEG = GetVideoSegment
  1580.     
  1581.     '────────────────────────────────────────────────────────────────────
  1582.     ' Use the BASIC BSAVE statement to save the 4000 bytes of video RAM
  1583.     '────────────────────────────────────────────────────────────────────
  1584.         BSAVE file$, 0, 4000
  1585.     
  1586.     '────────────────────────────────────────────────────────────────────
  1587.     ' Restore BASIC's default data segment
  1588.     '────────────────────────────────────────────────────────────────────
  1589.         DEF SEG
  1590.     
  1591. END SUB
  1592.  
  1593. FUNCTION GetVideoSegment
  1594.     
  1595.     '┌──────────────────────────────────────────────────────────────────────────┐
  1596.     '│  This function returns as a value the memory address where the video     │
  1597.     '│  display memory begins.  There are only two possible return values, one  │
  1598.     '│  for monochrome and one for color.  This routine is used to obtain the   │
  1599.     '│  video segment for use with the QBSCR routines ScrnSave and ScrnRestore. │
  1600.     '│  Call this routine, obtain the segment, and then pass it to the two      │
  1601.     '│  above listed routines.                                                  │
  1602.     '└──────────────────────────────────────────────────────────────────────────┘
  1603.     
  1604.     '──────────────────────────────────────────────────────────────────────────
  1605.     ' Set default segment to 0.
  1606.     '──────────────────────────────────────────────────────────────────────────
  1607.         DEF SEG = 0
  1608.     
  1609.     '──────────────────────────────────────────────────────────────────────────
  1610.     ' PEEK at value stored at video adapter address.
  1611.     '──────────────────────────────────────────────────────────────────────────
  1612.         adapter = PEEK(&H463)
  1613.     
  1614.     '──────────────────────────────────────────────────────────────────────────
  1615.     ' Set function equal to proper segment value.
  1616.     '──────────────────────────────────────────────────────────────────────────
  1617.         IF adapter = &HB4 THEN
  1618.             GetVideoSegment = &HB000  ' Mono
  1619.         ELSE
  1620.             GetVideoSegment = &HB800  ' Color
  1621.         END IF
  1622.     
  1623. END FUNCTION
  1624.  
  1625. FUNCTION MakeMenu% (choice$(), numOfChoices%, justify$, leftColumn, rightColumn, row%, marker$, divider$, fg%, bg%, hfg%, hBG%, qfg%, qbg%, useMouse%)
  1626.     
  1627.     '┌────────────────────────────────────────────────────────────────────────┐
  1628.     '│  The MakeMenu function displays a menu list on the screen and allows   │
  1629.     '│  the user to move a scrolling selection bar to highlight the entry of  │
  1630.     '│  their choice.  Selection is made by hitting the ENTER key.  Other     │
  1631.     '│  allowable keys include Home or PgUp to move to the first menu entry,  │
  1632.     '│  and End or PgDn to move to the last entry.  Scroll bar wraps from top │
  1633.     '│  to bottom and bottom to top.  The function returns as a value the     │
  1634.     '│  position of the entry in the list of the user's selection.  For ex-   │
  1635.     '│  ample, if the user selected the third item in a list of eight, the    │
  1636.     '│  function would return a value of three.  Parameters for this function │
  1637.     '│  are:                                                                  │
  1638.     '│                                                                        │
  1639.     '│  choice$() - An array of strings that contains the actual menu         │
  1640.     '│              entries.  Example: Choice$(1) = 'Menu selcection 1'.      │
  1641.     '│              Strings must be 78 characters or less in length.          │
  1642.     '│  numOfChoices% - The number of menu choices available.  The same as    │
  1643.     '│                  the number of elements in Choices$().  Allowable      │
  1644.     '│                  range is 1 through 25.                                │
  1645.     '│  justify$ - This string will contain a single letter, either an L, C,  │
  1646.     '│             or a R.  L means left-justify the menu entries.  C means   │
  1647.     '│             center them with respect to the left and right sides of    │
  1648.     '│             the menu (see LeftColumn and RightColumn parameters below) │
  1649.     '│             and an R means right-justify the menu entries.             │
  1650.     '│  leftColumn - A numerical value containing the left-most column on     │
  1651.     '│               which menu entries will be displayed.  Allowable range   │
  1652.     '│               is 1 though 76.                                          │
  1653.     '│  rightColumn - A numerical value containing the right-most column on   │
  1654.     '│                which menu entries will be displayed.  Allowable range  │
  1655.     '│                is 5 through 80.                                        │
  1656.     '│  row% - A numerical value containing the first row on which to display │
  1657.     '│         menu entries.  Allowable range is 1 through 24.                │
  1658.     '│  marker$ - The character used in the menu entry strings that indicates │
  1659.     '│            the next character is a 'Quick Access' key.                 │
  1660.     '│  divider$ - The character used as a menu entry if a dividing line is   │
  1661.     '│             desired.
  1662.     '│  fg% - The foreground color of normal menu entries.  Allowable range   │
  1663.     '│        is 0 to 15.                                                     │
  1664.     '│  bg% - The background color of normal menu entries.  Allowable range   │
  1665.     '│        is 0 to 7.                                                      │
  1666.     '│  hfg% - The foreground color of the highlighted menu entry.  Allowable │
  1667.     '│         range is 0 to 15.                                              │
  1668.     '│  hbg% - The background color of the highlighted menu entry.  Allowable │
  1669.     '│         range is 0 to 7.                                               │
  1670.     '│  qfg% - The foreground color of the Quick Access keys.  Allowable      │
  1671.     '│         range is 0 to 15.                                              │
  1672.     '│  qbg% - The background color of the Quick Access keys.  Allowable      │
  1673.     '│         range is 0 to 7.                                               │
  1674.     '│  useMouse% - 1 = use mouse support, 0 = don't.
  1675.     '└────────────────────────────────────────────────────────────────────────┘
  1676.     
  1677.     '─────────────────────────────────────────────────────────────────────────
  1678.     ' Set local variables - extended scan codes for keypad keys
  1679.     '─────────────────────────────────────────────────────────────────────────
  1680.         up$ = CHR$(0) + CHR$(72)
  1681.         down$ = CHR$(0) + CHR$(80)
  1682.         enter$ = CHR$(13)
  1683.         home$ = CHR$(0) + CHR$(71)
  1684.         EndKee$ = CHR$(0) + CHR$(79)
  1685.         PgUpKey$ = CHR$(0) + CHR$(73)
  1686.         PgDnKey$ = CHR$(0) + CHR$(81)
  1687.         esc$ = CHR$(27)
  1688.  
  1689.     '─────────────────────────────────────────────────────────────────────────
  1690.     ' Define other local variables.
  1691.     '─────────────────────────────────────────────────────────────────────────
  1692.         mx% = 0
  1693.         my% = 0
  1694.         lmCnt% = 0
  1695.         rmCnt% = 0
  1696.         returnIt% = FALSE
  1697.         updateMenu% = FALSE
  1698.     
  1699.     '─────────────────────────────────────────────────────────────────────────
  1700.     ' Define the error tone string to use with PLAY
  1701.     '─────────────────────────────────────────────────────────────────────────
  1702.         errorTone$ = "MB T120 L50 O3 AF"
  1703.     
  1704.     '─────────────────────────────────────────────────────────────────────────
  1705.     ' Set type of justification to uppercase
  1706.     '─────────────────────────────────────────────────────────────────────────
  1707.         justify$ = UCASE$(justify$)
  1708.         wdth% = (rightColumn - leftColumn - 1)
  1709.     
  1710.     '─────────────────────────────────────────────────────────────────────────
  1711.     ' Check for out-of-bounds parameters.  If any are out of range,
  1712.     ' quit the function
  1713.     '─────────────────────────────────────────────────────────────────────────
  1714.         IF numOfChoices% < 1 OR numOfChoices% > 25 THEN EXIT FUNCTION
  1715.         IF justify$ <> "C" AND justify$ <> "L" AND justify$ <> "R" THEN EXIT FUNCTION
  1716.         IF leftColumn < 1 OR rightColumn > 80 THEN EXIT FUNCTION
  1717.         IF row% < 1 OR row% > 24 THEN EXIT FUNCTION
  1718.     
  1719.     '─────────────────────────────────────────────────────────────────────────
  1720.     ' Calculate the array of character identifiers
  1721.     '─────────────────────────────────────────────────────────────────────────
  1722.         REDIM charID(numOfChoices%) AS STRING * 1
  1723.         FOR x% = 1 TO numOfChoices%
  1724.             FOR y% = 1 TO LEN(choice$(x%))
  1725.                 IF MID$(choice$(x%), y%, 1) = marker$ THEN
  1726.                     charID(x%) = UCASE$(MID$(choice$(x%), y% + 1, 1))
  1727.                     EXIT FOR
  1728.                 END IF
  1729.             NEXT y%
  1730.         NEXT x%
  1731.     
  1732.     '─────────────────────────────────────────────────────────────────────────
  1733.     ' Calculate length of longest menu choice and store value in ChoiceLen%
  1734.     '─────────────────────────────────────────────────────────────────────────
  1735.         choiceLen% = 0
  1736.         FOR x% = 1 TO numOfChoices%
  1737.             IF LEN(choice$(x%)) > choiceLen% THEN
  1738.                 IF INSTR(choice$(x%), marker$) THEN
  1739.                     choiceLen% = LEN(choice$(x%))
  1740.                 ELSE
  1741.                     choiceLen% = LEN(choice$(x%)) + 1
  1742.                 END IF
  1743.             END IF
  1744.         NEXT x%
  1745.         choiceLen% = choiceLen% - 1
  1746.     
  1747.     '─────────────────────────────────────────────────────────────────────────
  1748.     ' Determine left-most column to display highlight bar on
  1749.     '─────────────────────────────────────────────────────────────────────────
  1750.         col = (((rightColumn - leftColumn - 1) - choiceLen%) / 2) + leftColumn
  1751.  
  1752.     '─────────────────────────────────────────────────────────────────────────
  1753.     ' At this point, we must turn off the mouse cursor if it's available.  We
  1754.     ' don't want to write overtop of it, leaving a hole when it's moved later.
  1755.     '─────────────────────────────────────────────────────────────────────────
  1756.         IF useMouse% THEN
  1757.             MouseHide
  1758.         END IF
  1759.     
  1760.     '─────────────────────────────────────────────────────────────────────────
  1761.     ' Print menu choices to screen based on the type of Justification
  1762.     ' selected (Center, Left, Right).
  1763.     '─────────────────────────────────────────────────────────────────────────
  1764.         COLOR fg%, bg%
  1765.         SELECT CASE justify$
  1766.         CASE "C"
  1767.             FOR x% = 1 TO numOfChoices%
  1768.                 xCol% = ((wdth% - (LEN(choice$(x%))) - 1) \ 2 + leftColumn) + 1
  1769.                 LOCATE (row% - 1) + x%, leftColumn - 1, 0
  1770.                 PRINT SPACE$(choiceLen% + 2);
  1771.                 LOCATE (row% - 1) + x%, xCol%, 0
  1772.                 DisplayEntry choice$(x%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  1773.             NEXT x%
  1774.         CASE "R"
  1775.             FOR x% = 1 TO numOfChoices%
  1776.                 LOCATE (row% - 1) + x%, leftColumn - 1, 0
  1777.                 PRINT SPACE$(choiceLen% + 2);
  1778.                 LOCATE (row% - 1) + x%, (rightColumn - LEN(choice$(x%)))
  1779.                 DisplayEntry choice$(x%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  1780.             NEXT x%
  1781.         CASE "L"
  1782.             FOR x% = 1 TO numOfChoices%
  1783.                 LOCATE (row% - 1) + x%, leftColumn - 1, 0
  1784.                 PRINT SPACE$(choiceLen% + 2);
  1785.                 LOCATE (row% - 1) + x%, leftColumn, 0
  1786.                 DisplayEntry choice$(x%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  1787.             NEXT x%
  1788.         END SELECT
  1789.     
  1790.     '─────────────────────────────────────────────────────────────────────────
  1791.     ' Highlight the first entry in the list.  Must take into account the
  1792.     ' justification type.
  1793.     '─────────────────────────────────────────────────────────────────────────
  1794.         currentLocation% = 1
  1795.         oldLocation% = 1
  1796.         COLOR hfg%, hBG%
  1797.         LOCATE row%, leftColumn - 1: PRINT SPACE$(choiceLen% + 2);
  1798.         SELECT CASE justify$
  1799.         CASE "C"
  1800.             xCol% = ((wdth% - (LEN(choice$(currentLocation%))) - 1) \ 2 + leftColumn) + 1
  1801.             LOCATE (row% - 1 + currentLocation%), xCol%, 0
  1802.             DisplayEntry choice$(currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  1803.         CASE "R"
  1804.             LOCATE (row% - 1) + currentLocation%, (rightColumn - LEN(choice$(currentLocation%)))
  1805.             DisplayEntry choice$(currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  1806.         CASE "L"
  1807.             LOCATE (row% - 1) + currentLocation%, leftColumn
  1808.             DisplayEntry choice$(currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  1809.         END SELECT
  1810.     
  1811.     '─────────────────────────────────────────────────────────────────────────
  1812.     ' Read keystrokes and change the highlighted entry appropriately.  Also
  1813.     ' drain out any pending mouse button presses if the mouse is available.
  1814.     '─────────────────────────────────────────────────────────────────────────
  1815.         exitCode% = FALSE
  1816.         IF useMouse% THEN
  1817.             MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  1818.             MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  1819.             lmCnt% = 0
  1820.             rmCnt% = 0
  1821.         END IF
  1822.         WHILE exitCode% = FALSE
  1823.  
  1824.         '─────────────────────────────────────────────────────────────────────
  1825.         ' If we're using the mouse, turn it on.
  1826.         '─────────────────────────────────────────────────────────────────────
  1827.             IF useMouse% THEN
  1828.                 MouseShow
  1829.             END IF
  1830.         
  1831.         '─────────────────────────────────────────────────────────────────────
  1832.         ' Read keystrokes and/or mouse presses.
  1833.         '─────────────────────────────────────────────────────────────────────
  1834.             key$ = ""
  1835.             lmCnt% = 0
  1836.             rmCnt% = 0
  1837.             IF useMouse% THEN
  1838.                 MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  1839.                 MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  1840.  
  1841.             '───────────────────────────────────────────────────────────────────
  1842.             ' Did we have any left mouse button presses?  If not, check the
  1843.             ' keyboard for input.
  1844.             '───────────────────────────────────────────────────────────────────
  1845.                 IF lmCnt% = 0 THEN
  1846.                     key$ = UCASE$(INKEY$)
  1847.                 END IF
  1848.             ELSE
  1849.  
  1850.             '───────────────────────────────────────────────────────────────────
  1851.             ' No mouse available, so wait for keyboard input.
  1852.             '───────────────────────────────────────────────────────────────────
  1853.                 WHILE key$ = ""
  1854.                     key$ = UCASE$(INKEY$)
  1855.                 WEND
  1856.             END IF
  1857.  
  1858.         '─────────────────────────────────────────────────────────────────────
  1859.         ' If the left mouse button was pressed, check to see if a menu item
  1860.         ' was selected by it.
  1861.         '─────────────────────────────────────────────────────────────────────
  1862.             IF (useMouse%) AND (lmCnt% > 0) THEN
  1863.  
  1864.             '───────────────────────────────────────────────────────────────────
  1865.             ' Convert virtual screen mouse coordinates to real 80x25 coords.
  1866.             '───────────────────────────────────────────────────────────────────
  1867.                 mx% = (mx% \ 8) + 1
  1868.                 my% = (my% \ 8) + 1
  1869.  
  1870.             '───────────────────────────────────────────────────────────────────
  1871.             ' If mouse was inside menu window then return the item pointed to.
  1872.             '───────────────────────────────────────────────────────────────────
  1873.                 IF (mx% >= leftColumn) AND (mx% <= rightColumn) AND (my% >= row%) AND (my% <= row% + numOfChoices% - 1) THEN
  1874.                     IF (choice$(my% - row% + 1) <> divider$) THEN
  1875.                         exitCode% = TRUE
  1876.                         updateMenu% = TRUE
  1877.                         currentLocation% = my% - row% + 1
  1878.                         key$ = charID(currentLocation%)
  1879.                         returnIt% = TRUE
  1880.                     END IF
  1881.                 END IF
  1882.             END IF
  1883.  
  1884.         '─────────────────────────────────────────────────────────────────────
  1885.         ' If right mouse button was pressed, then exit as if ESC were pressed.
  1886.         '─────────────────────────────────────────────────────────────────────
  1887.         IF (useMouse%) AND (rmCnt% > 0) THEN
  1888.             MakeMenu% = 0
  1889.             EXIT FUNCTION
  1890.         END IF
  1891.  
  1892.         '───────────────────────────────────────────────────────────────────
  1893.         ' Update currentLocation based on what user did, key-wise.
  1894.         '───────────────────────────────────────────────────────────────────
  1895.             SELECT CASE key$
  1896.  
  1897.             CASE up$
  1898.                 IF currentLocation% > 1 THEN
  1899.                     currentLocation% = currentLocation% - 1
  1900.                     IF (choice$(currentLocation%) = divider$) AND (currentLocation% > 0) THEN
  1901.                         currentLocation% = currentLocation% - 1
  1902.                     END IF
  1903.                 ELSE
  1904.                     currentLocation% = numOfChoices%
  1905.                 END IF
  1906.                 updateMenu% = TRUE
  1907.  
  1908.             CASE down$
  1909.                 IF currentLocation% < numOfChoices% THEN
  1910.                     currentLocation% = currentLocation% + 1
  1911.                     IF (choice$(currentLocation%) = divider$) AND (currentLocation% < numOfChoices%) THEN
  1912.                         currentLocation% = currentLocation% + 1
  1913.                     END IF
  1914.                 ELSE
  1915.                     currentLocation% = 1
  1916.                 END IF
  1917.                 updateMenu% = TRUE
  1918.  
  1919.             CASE home$, PgUpKey$
  1920.                 IF currentLocation% <> 1 THEN
  1921.                     currentLocation% = 1
  1922.                     updateMenu% = TRUE
  1923.                 END IF
  1924.  
  1925.             CASE EndKee$, PgDnKey$
  1926.                 IF currentLocation% <> numOfChoices% THEN
  1927.                     currentLocation% = numOfChoices%
  1928.                     updateMenu% = TRUE
  1929.                 END IF
  1930.  
  1931.             CASE enter$
  1932.                 MakeMenu% = currentLocation%
  1933.                 exitCode% = TRUE
  1934.  
  1935.             CASE esc$
  1936.                 MakeMenu% = 0
  1937.                 exitCode% = TRUE
  1938.  
  1939.             CASE ELSE
  1940.             '───────────────────────────────────────────────────────────────────
  1941.             ' Check quick access keys.
  1942.             '───────────────────────────────────────────────────────────────────
  1943.                 FOR i% = 1 TO numOfChoices%
  1944.                     IF charID(i%) = key$ THEN
  1945.                         currentLocation% = i%
  1946.                         updateMenu% = TRUE
  1947.                         MakeMenu% = i%
  1948.                         exitCode% = TRUE
  1949.                     END IF
  1950.                 NEXT i%
  1951.  
  1952.             END SELECT
  1953.  
  1954.         '───────────────────────────────────────────────────────────────────
  1955.         ' If required, update the display.
  1956.         '───────────────────────────────────────────────────────────────────
  1957.             IF updateMenu% THEN
  1958.  
  1959.             '───────────────────────────────────────────────────────────────────
  1960.             ' If mouse is around, turn it off, since we'll be displaying.
  1961.             '───────────────────────────────────────────────────────────────────
  1962.                 IF useMouse% THEN
  1963.                     MouseHide
  1964.                 END IF
  1965.  
  1966.             '─────────────────────────────────────────────────────────────────
  1967.             ' Restore the old highlighted item to normal colors.
  1968.             '─────────────────────────────────────────────────────────────────
  1969.                 COLOR fg%, bg%
  1970.                 LOCATE row% + oldLocation% - 1, leftColumn - 1: PRINT SPACE$(choiceLen% + 2);
  1971.                 SELECT CASE justify$
  1972.                 CASE "C"
  1973.                     xCol% = ((wdth% - (LEN(choice$(oldLocation%))) - 1) \ 2 + leftColumn) + 1
  1974.                     LOCATE (row% - 1 + oldLocation%), xCol%, 0
  1975.                     DisplayEntry choice$(oldLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  1976.                 CASE "R"
  1977.                     LOCATE (row% - 1) + oldLocation%, (rightColumn - LEN(choice$(oldLocation%)))
  1978.                     DisplayEntry choice$(oldLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  1979.                 CASE "L"
  1980.                     LOCATE (row% - 1) + oldLocation%, leftColumn
  1981.                     DisplayEntry choice$(oldLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  1982.                 END SELECT
  1983.  
  1984.             '─────────────────────────────────────────────────────────────────
  1985.             ' Display newly highlighted item in highlight colors.
  1986.             '─────────────────────────────────────────────────────────────────
  1987.                 COLOR hfg%, hBG%
  1988.                 LOCATE row% + currentLocation% - 1, leftColumn - 1: PRINT SPACE$(choiceLen% + 2);
  1989.                 SELECT CASE justify$
  1990.                 CASE "C"
  1991.                     xCol% = ((wdth% - (LEN(choice$(currentLocation%))) - 1) \ 2 + leftColumn) + 1
  1992.                     LOCATE (row% - 1 + currentLocation%), xCol%, 0
  1993.                     DisplayEntry choice$(currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  1994.                 CASE "R"
  1995.                     LOCATE (row% - 1) + currentLocation%, (rightColumn - LEN(choice$(currentLocation%)))
  1996.                     DisplayEntry choice$(currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  1997.                 CASE "L"
  1998.                     LOCATE (row% - 1) + currentLocation%, leftColumn
  1999.                     DisplayEntry choice$(currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  2000.                 END SELECT
  2001.  
  2002.             '─────────────────────────────────────────────────────────────────
  2003.             ' Reset old location to current.
  2004.             '─────────────────────────────────────────────────────────────────
  2005.                 oldLocation% = currentLocation%
  2006.                 updateMenu% = FALSE
  2007.  
  2008.             END IF
  2009.             
  2010.         '───────────────────────────────────────────────────────────────────
  2011.         ' If the mouse was used to click on a menu choice, then return it
  2012.         ' and exit now.
  2013.         '───────────────────────────────────────────────────────────────────
  2014.             IF returnIt% THEN
  2015.                 MakeMenu% = currentLocation%
  2016.                 EXIT FUNCTION
  2017.             END IF
  2018.         
  2019.         WEND
  2020.     
  2021. END FUNCTION
  2022.  
  2023. SUB MakeWindow (topRow!, leftCol!, botRow!, rightCol!, foreColor%, backColor%, windowType%, frameType%, shadowColor%, explodeType%, label$)
  2024.  
  2025.     '┌────────────────────────────────────────────────────────────────────────┐
  2026.     '│  The MakeWindow subroutine draws windows on the screen for you.  The   │
  2027.     '│  kinds of windows you can make is quite varied.  There are 10          │
  2028.     '│  window types, six different frame types, windows can have shadows     │
  2029.     '│  or not, you can 'explode' them onto the screen, and even place labels │
  2030.     '│  on them.  The parameters for MakeWindow are as follows:               │
  2031.     '│                                                                        │
  2032.     '│  topRow! - This is a numerical value containing the top-most row of    │
  2033.     '│            the window.  Allowable range is 1 through 22.               │
  2034.     '│  leftCol! - This is a numerical value containing the left-most side    │
  2035.     '│             of the window.  Allowable range is 1 to 79.                │
  2036.     '│  botRow! - This is a numerical value containing the bottom-most row    │
  2037.     '│            of the window.  Allowable range is 2 through 23.            │
  2038.     '│  rightCol! - This is a numerical value containing the right-most row   │
  2039.     '│              of the window.  Allowable range is 2 through 80.          │
  2040.     '│  foreColor% - This is the foreground color of the window.  Allowable   │
  2041.     '│               range is 0 through 15.                                   │
  2042.     '│  backColor% - This is the background color of the window.  Allowable   │
  2043.     '│               range is 0 through 7.                                    │
  2044.     '│  windowType% - This is a numerical value containing the type of window │
  2045.     '│                desired.  Allowable range is 0 through 9.  See the      │
  2046.     '│                QBSCR documentation for more info.                      │
  2047.     '│  frameType% - This is a numerical value containing the type of frame   │
  2048.     '│               you want your window to have.  Allowable range is 0      │
  2049.     '│               through 5.  See the QBSCR documentation for more info.   │
  2050.     '│  shadowColor% - This is a numerical value containing the color of the  │
  2051.     '│                 shadow for your window.  If you desire no shadow at    │
  2052.     '│                 all, use a value of -1.  Allowable range is -1 through │
  2053.     '│                 15.  See the QBSCR documentation for more detail.      │
  2054.     '│  explodeType% - This is a numerical value that indicates how you want  │
  2055.     '│                 your window to be placed on the screen.  A value of 0  │
  2056.     '│                 display it normally, top to bottom.  A value of 1      │
  2057.     '│                 means explode it onto the screen using auto mode.  A   │
  2058.     '│                 value of 2 means explode it onto the screen using the  │
  2059.     '│                 horizontal bias mode, and a value of 3 means explode   │
  2060.     '│                 it onto the screen using the vertical bias mode.  See  │
  2061.     '│                 the QBSCR documentation for more details.              │
  2062.     '│  label$ - This is a string used to label your window.  It is placed    │
  2063.     '│           along the top line of your window, framed by brackets.       │
  2064.     '│           A string of zero length ("") means don't display any label.  │
  2065.     '│           Allowable string length is equal to (RightCol - LeftCol) - 4 │
  2066.     '└────────────────────────────────────────────────────────────────────────┘
  2067.  
  2068.     '─────────────────────────────────────────────────────────────────────────
  2069.     ' Setup line$ as a dynamic array that can REDimensioned.  Line$()
  2070.     ' will contain the actual character strings that make up our window.
  2071.     '─────────────────────────────────────────────────────────────────────────
  2072.     '$DYNAMIC
  2073.         DIM line$(24)
  2074.  
  2075.     '─────────────────────────────────────────────────────────────────────────
  2076.     ' Initialize local variables
  2077.     '─────────────────────────────────────────────────────────────────────────
  2078.         part1 = 0: part2 = 0: numLines = 0
  2079.  
  2080.     '─────────────────────────────────────────────────────────────────────────
  2081.     ' Check all passed values for validity and set defaults
  2082.     '─────────────────────────────────────────────────────────────────────────
  2083.         numLines = 0
  2084.  
  2085.         IF topRow < 1 THEN topRow = 1: IF topRow > 24 THEN topRow = 24
  2086.         IF botRow < 2 THEN botRow = 2: IF botRow > 25 THEN botRow = 25
  2087.         IF rightCol < 2 THEN rightCol = 2: IF rightCol > 80 THEN rightCol = 80
  2088.         IF leftCol < 1 THEN leftCol = 1: IF leftCol > 80 THEN leftCol = 80
  2089.  
  2090.         IF foreColor% < 0 OR foreColor% > 31 THEN foreColor% = 7
  2091.         IF backColor% < 0 OR backColor% > 7 THEN backColor% = 0
  2092.  
  2093.         IF windowType% < 0 OR windowType% > 9 THEN windowType% = 0
  2094.         IF frameType% < 0 OR frameType% > 9 THEN frameType% = 0
  2095.         IF shadowColor% > 17 THEN shadowColor% = -1
  2096.         IF explodeType% < 0 OR explodeType% > 3 THEN explodeType% = 0
  2097.  
  2098.         IF LEN(label$) > ((rightCol - leftCol) - 4) THEN label$ = ""
  2099.  
  2100.     '─────────────────────────────────────────────────────────────────────────
  2101.     ' Setup graphics characters to use based on FrameType%
  2102.     '─────────────────────────────────────────────────────────────────────────
  2103.         SELECT CASE frameType%
  2104.  
  2105.         CASE 0, 6, 7   ' All lines SINGLE
  2106.  
  2107.             urc$ = CHR$(191): ulc$ = CHR$(218): llc$ = CHR$(192): lrc$ = CHR$(217)
  2108.             ver$ = CHR$(179): hor$ = CHR$(196)
  2109.             vtl$ = CHR$(195): vtr$ = CHR$(180)
  2110.             htt$ = CHR$(194): htb$ = CHR$(193)
  2111.             crs$ = CHR$(197): blk$ = CHR$(219)
  2112.             lbl$ = CHR$(180): lbr$ = CHR$(195)
  2113.  
  2114.         CASE 1, 8, 9   ' All lines DOUBLE
  2115.  
  2116.             urc$ = CHR$(187): ulc$ = CHR$(201): llc$ = CHR$(200): lrc$ = CHR$(188)
  2117.             ver$ = CHR$(186): hor$ = CHR$(205)
  2118.             vtl$ = CHR$(204): vtr$ = CHR$(185)
  2119.             htt$ = CHR$(203): htb$ = CHR$(202)
  2120.             crs$ = CHR$(206): blk$ = CHR$(219)
  2121.             lbl$ = CHR$(181): lbr$ = CHR$(198)
  2122.  
  2123.         CASE 2      ' Horizontals SINGLE / Verticals DOUBLE
  2124.  
  2125.             urc$ = CHR$(183): ulc$ = CHR$(214): llc$ = CHR$(211): lrc$ = CHR$(189)
  2126.             ver$ = CHR$(186): hor$ = CHR$(196)
  2127.             vtl$ = CHR$(199): vtr$ = CHR$(182)
  2128.             htt$ = CHR$(210): htb$ = CHR$(208)
  2129.             crs$ = CHR$(215): blk$ = CHR$(219)
  2130.             lbl$ = CHR$(180): lbr$ = CHR$(195)
  2131.  
  2132.         CASE 3      ' Horizontals DOUBLE / Verticals SINGLE
  2133.  
  2134.             urc$ = CHR$(184): ulc$ = CHR$(213): llc$ = CHR$(212): lrc$ = CHR$(190)
  2135.             ver$ = CHR$(179): hor$ = CHR$(205)
  2136.             vtl$ = CHR$(198): vtr$ = CHR$(181)
  2137.             htt$ = CHR$(209): htb$ = CHR$(207)
  2138.             crs$ = CHR$(216): blk$ = CHR$(219)
  2139.             lbl$ = CHR$(181): lbr$ = CHR$(198)
  2140.  
  2141.         CASE 4      ' Outside lines DOUBLE / Inside lines SINGLE
  2142.  
  2143.             urc$ = CHR$(187): ulc$ = CHR$(201): llc$ = CHR$(200): lrc$ = CHR$(188)
  2144.             ver$ = CHR$(186): ver1$ = CHR$(179): hor$ = CHR$(205): hor1$ = CHR$(196)
  2145.             vtl$ = CHR$(199): vtr$ = CHR$(182)
  2146.             htt$ = CHR$(209): htt1$ = CHR$(194): htb$ = CHR$(207): htb1$ = CHR$(193)
  2147.             crs$ = CHR$(197): blk$ = CHR$(219)
  2148.             lbl$ = CHR$(181): lbr$ = CHR$(198)
  2149.  
  2150.         CASE 5      ' Outside lines SINGLE / Inside Lines DOUBLE
  2151.  
  2152.             urc$ = CHR$(191): ulc$ = CHR$(218): llc$ = CHR$(192): lrc$ = CHR$(217)
  2153.             ver$ = CHR$(179): ver1$ = CHR$(186): hor$ = CHR$(196): hor1$ = CHR$(205)
  2154.             vtl$ = CHR$(198): vtr$ = CHR$(181)
  2155.             htt$ = CHR$(210): htt1$ = CHR$(203): htb$ = CHR$(208): htb1$ = CHR$(202)
  2156.             crs$ = CHR$(206): blk$ = CHR$(219)
  2157.             lbl$ = CHR$(180): lbr$ = CHR$(195)
  2158.  
  2159.         CASE ELSE
  2160.  
  2161.         ' Shouldn't be an 'else' !
  2162.  
  2163.         END SELECT
  2164.  
  2165.     '─────────────────────────────────────────────────────────────────────────
  2166.     ' Calculate the number of lines to be printed and redimension Lines$()
  2167.     '─────────────────────────────────────────────────────────────────────────
  2168.         numLines = (botRow - topRow) + 1
  2169.         REDIM line$(numLines)
  2170.  
  2171.     '─────────────────────────────────────────────────────────────────────────
  2172.     ' Determine ExplodeStep% for explode loop based on ExplodeType%
  2173.     '─────────────────────────────────────────────────────────────────────────
  2174.         SELECT CASE explodeType%
  2175.  
  2176.         CASE 0      ' Exploding Windows OFF
  2177.             explodeStep% = 0
  2178.  
  2179.         CASE 1      ' Explode automatic - determine explode ratio
  2180.             explodeStep% = INT((rightCol - leftCol) / (botRow - topRow))
  2181.  
  2182.         CASE 2      ' Explode ratio biased toward HORIZONTAL
  2183.             explodeStep% = 3
  2184.  
  2185.         CASE 3      ' Explode ratio biased toward VERTICAL
  2186.             explodeStep% = 1
  2187.  
  2188.         END SELECT
  2189.  
  2190.     '─────────────────────────────────────────────────────────────────────────
  2191.     ' Construct the window strings based on WindowType%
  2192.     '─────────────────────────────────────────────────────────────────────────
  2193.         SELECT CASE windowType%
  2194.  
  2195.         CASE 0      ' Regular box, no extra lines
  2196.  
  2197.             line$(1) = ulc$ + STRING$((rightCol - leftCol) - 1, hor$) + urc$
  2198.             FOR x% = 2 TO numLines - 1
  2199.                 line$(x%) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2200.             NEXT x%
  2201.             line$(numLines) = llc$ + STRING$((rightCol - leftCol) - 1, hor$) + lrc$
  2202.  
  2203.         CASE 1      ' Box with extra internal line at top and bottom
  2204.  
  2205.             line$(1) = ulc$ + STRING$((rightCol - leftCol) - 1, hor$) + urc$
  2206.             line$(2) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2207.             IF frameType% = 4 OR frameType% = 5 THEN
  2208.                 tempHOR$ = hor$
  2209.                 hor$ = hor1$
  2210.             END IF
  2211.             line$(3) = vtl$ + STRING$((rightCol - leftCol) - 1, hor$) + vtr$
  2212.             FOR x% = 4 TO numLines - 3
  2213.                 line$(x%) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2214.             NEXT x%
  2215.             line$(numLines - 2) = vtl$ + STRING$((rightCol - leftCol) - 1, hor$) + vtr$
  2216.             line$(numLines - 1) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2217.             IF frameType% = 4 OR frameType% = 5 THEN
  2218.                 hor$ = tempHOR$
  2219.             END IF
  2220.             line$(numLines) = llc$ + STRING$((rightCol - leftCol) - 1, hor$) + lrc$
  2221.  
  2222.         CASE 2      ' Box with extra internal line at top
  2223.  
  2224.             line$(1) = ulc$ + STRING$((rightCol - leftCol) - 1, hor$) + urc$
  2225.             line$(2) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2226.             IF frameType% = 4 OR frameType% = 5 THEN
  2227.                 tempHOR$ = hor$
  2228.                 hor$ = hor1$
  2229.             END IF
  2230.             line$(3) = vtl$ + STRING$((rightCol - leftCol) - 1, hor$) + vtr$
  2231.             FOR x% = 4 TO numLines - 1
  2232.                 line$(x%) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2233.             NEXT x%
  2234.             IF frameType% = 4 OR frameType% = 5 THEN
  2235.                 hor$ = tempHOR$
  2236.             END IF
  2237.             line$(numLines) = llc$ + STRING$((rightCol - leftCol) - 1, hor$) + lrc$
  2238.  
  2239.         CASE 3      ' Box with extra internal line at bottom
  2240.  
  2241.             line$(1) = ulc$ + STRING$((rightCol - leftCol) - 1, hor$) + urc$
  2242.             FOR x% = 2 TO numLines - 3
  2243.                 line$(x%) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2244.             NEXT x%
  2245.             IF frameType% = 4 OR frameType% = 5 THEN
  2246.                 tempHOR$ = hor$
  2247.                 hor$ = hor1$
  2248.             END IF
  2249.             line$(numLines - 2) = vtl$ + STRING$((rightCol - leftCol) - 1, hor$) + vtr$
  2250.             line$(numLines - 1) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2251.             IF frameType% = 4 OR frameType% = 5 THEN
  2252.                 hor$ = tempHOR$
  2253.             END IF
  2254.             line$(numLines) = llc$ + STRING$((rightCol - leftCol) - 1, hor$) + lrc$
  2255.  
  2256.         CASE 4      ' Box with vertical line down the center
  2257.  
  2258.             part1 = ((rightCol - leftCol) - 1) / 2
  2259.             IF INT(part1) = part1 THEN
  2260.                 part2 = part1 - 1
  2261.             ELSE
  2262.                 part1 = INT(part1)
  2263.                 part2 = part1
  2264.             END IF
  2265.             line$(1) = ulc$ + STRING$(part1, hor$) + htt$ + STRING$(part2, hor$) + urc$
  2266.             IF frameType% <> 4 AND frameType% <> 5 THEN
  2267.                 ver1$ = ver$
  2268.             END IF
  2269.             FOR x% = 2 TO numLines - 1
  2270.                 line$(x%) = ver$ + SPACE$(part1) + ver1$ + SPACE$(part2) + ver$
  2271.             NEXT x%
  2272.             line$(numLines) = llc$ + STRING$(part1, hor$) + htb$ + STRING$(part2, hor$) + lrc$
  2273.  
  2274.         CASE 5      ' Box with horizontal line down the center
  2275.  
  2276.             TopHalf = INT(numLines / 2)
  2277.             line$(1) = ulc$ + STRING$((rightCol - leftCol) - 1, hor$) + urc$
  2278.             FOR x% = 2 TO TopHalf
  2279.                 line$(x%) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2280.             NEXT x%
  2281.             IF frameType% = 4 OR frameType% = 5 THEN
  2282.                 tempHOR$ = hor$
  2283.                 hor$ = hor1$
  2284.             END IF
  2285.             line$(TopHalf + 1) = vtl$ + STRING$((rightCol - leftCol) - 1, hor$) + vtr$
  2286.             IF frameType% = 4 OR frameType% = 5 THEN
  2287.                 hor$ = tempHOR$
  2288.             END IF
  2289.             FOR x% = TopHalf + 2 TO numLines - 1
  2290.                 line$(x%) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2291.             NEXT x%
  2292.             line$(numLines) = llc$ + STRING$((rightCol - leftCol) - 1, hor$) + lrc$
  2293.  
  2294.         CASE 6      ' Box cross-divided into four sections
  2295.  
  2296.             TopHalf = INT(numLines / 2): part1 = ((rightCol - leftCol) - 1) / 2
  2297.             IF INT(part1) = part1 THEN
  2298.                 part2 = part1 - 1
  2299.             ELSE
  2300.                 part1 = INT(part1): part2 = part1
  2301.             END IF
  2302.             line$(1) = ulc$ + STRING$(part1, hor$) + htt$ + STRING$(part2, hor$) + urc$
  2303.             IF frameType% <> 4 AND frameType% <> 5 THEN ver1$ = ver$
  2304.             FOR x% = 2 TO TopHalf
  2305.                 line$(x%) = ver$ + SPACE$(part1) + ver1$ + SPACE$(part2) + ver$
  2306.             NEXT x%
  2307.             IF frameType% = 4 OR frameType% = 5 THEN
  2308.                 tempHOR$ = hor$: hor$ = hor1$
  2309.             END IF
  2310.             line$(TopHalf + 1) = vtl$ + STRING$(part1, hor$) + crs$ + STRING$(part2, hor$) + vtr$
  2311.             IF frameType% = 4 OR frameType% = 5 THEN hor$ = tempHOR$
  2312.             FOR x% = TopHalf + 2 TO numLines - 1
  2313.                 line$(x%) = ver$ + SPACE$(part1) + ver1$ + SPACE$(part2) + ver$
  2314.             NEXT x%
  2315.             line$(numLines) = llc$ + STRING$(part1, hor$) + htb$ + STRING$(part2, hor$) + lrc$
  2316.  
  2317.         CASE 7      ' Box with extra internal line at top and vertical
  2318.         ' dividing line for rest of window
  2319.  
  2320.             part1 = ((rightCol - leftCol) - 1) / 2
  2321.             IF INT(part1) = part1 THEN
  2322.                 part2 = part1 - 1
  2323.             ELSE
  2324.                 part1 = INT(part1)
  2325.                 part2 = part1
  2326.             END IF
  2327.             line$(1) = ulc$ + STRING$((rightCol - leftCol) - 1, hor$) + urc$
  2328.             line$(2) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2329.             IF frameType% <> 4 AND frameType% <> 5 THEN
  2330.                 htt1$ = htt$
  2331.                 ver1$ = ver$
  2332.                 hor1$ = hor$
  2333.             END IF
  2334.             line$(3) = vtl$ + STRING$(part1, hor1$) + htt1$ + STRING$(part2, hor1$) + vtr$
  2335.             FOR x% = 4 TO numLines - 1
  2336.                 line$(x%) = ver$ + SPACE$(part1) + ver1$ + SPACE$(part2) + ver$
  2337.             NEXT x%
  2338.             line$(numLines) = llc$ + STRING$(part1, hor$) + htb$ + STRING$(part2, hor$) + lrc$
  2339.  
  2340.         CASE 8      ' Box with extra internalline at bottom and vertical
  2341.         ' dividing line for rest of window
  2342.  
  2343.             part1 = ((rightCol - leftCol) - 1) / 2
  2344.             IF INT(part1) = part1 THEN
  2345.                 part2 = part1 - 1
  2346.             ELSE
  2347.                 part1 = INT(part1)
  2348.                 part2 = part1
  2349.             END IF
  2350.             line$(1) = ulc$ + STRING$(part1, hor$) + htt$ + STRING$(part2, hor$) + urc$
  2351.             IF frameType% <> 4 AND frameType% <> 5 THEN
  2352.                 htb1$ = htb$
  2353.                 ver1$ = ver$
  2354.                 hor1$ = hor$
  2355.             END IF
  2356.             FOR x% = 2 TO numLines - 3
  2357.                 line$(x%) = ver$ + SPACE$(part1) + ver1$ + SPACE$(part2) + ver$
  2358.             NEXT x%
  2359.             line$(numLines - 2) = vtl$ + STRING$(part1, hor1$) + htb1$ + STRING$(part2, hor1$) + vtr$
  2360.             line$(numLines - 1) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2361.             line$(numLines) = llc$ + STRING$((rightCol - leftCol) - 1, hor$) + lrc$
  2362.  
  2363.         CASE 9      ' Box with extra internal lines at top and bottom,
  2364.         ' with dividing line for rest of window
  2365.  
  2366.             part1 = ((rightCol - leftCol) - 1) / 2
  2367.             IF INT(part1) = part1 THEN
  2368.                 part2 = part1 - 1
  2369.             ELSE
  2370.                 part1 = INT(part1)
  2371.                 part2 = part1
  2372.             END IF
  2373.             line$(1) = ulc$ + STRING$((rightCol - leftCol) - 1, hor$) + urc$
  2374.             line$(2) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2375.             IF frameType% <> 4 AND frameType% <> 5 THEN
  2376.                 htt1$ = htt$
  2377.                 htb1$ = htb$
  2378.                 ver1$ = ver$
  2379.                 hor1$ = hor$
  2380.             END IF
  2381.             line$(3) = vtl$ + STRING$(part1, hor1$) + htt1$ + STRING$(part2, hor1$) + vtr$
  2382.             FOR x% = 4 TO numLines - 3
  2383.                 line$(x%) = ver$ + SPACE$(part1) + ver1$ + SPACE$(part2) + ver$
  2384.             NEXT x%
  2385.             line$(numLines - 2) = vtl$ + STRING$(part1, hor1$) + htb1$ + STRING$(part2, hor1$) + vtr$
  2386.             line$(numLines - 1) = ver$ + SPACE$((rightCol - leftCol) - 1) + ver$
  2387.             line$(numLines) = llc$ + STRING$((rightCol - leftCol) - 1, hor$) + lrc$
  2388.  
  2389.         CASE ELSE
  2390.  
  2391.         '─────────────────────────────────────────────────────────────────────
  2392.         ' Shouldn't be an 'else' !
  2393.         '─────────────────────────────────────────────────────────────────────
  2394.  
  2395.         END SELECT
  2396.  
  2397.     '─────────────────────────────────────────────────────────────────────────
  2398.     ' Print the Window, Please!  Set colors to those passed to MakeWindow
  2399.     '─────────────────────────────────────────────────────────────────────────
  2400.         COLOR foreColor%, backColor%
  2401.  
  2402.     '─────────────────────────────────────────────────────────────────────────
  2403.     ' Print the window on the screen, using method based on ExplodeType%
  2404.     '─────────────────────────────────────────────────────────────────────────
  2405.         SELECT CASE explodeType%
  2406.  
  2407.         CASE 0      ' No explosion - just a straight print.  See how easy?
  2408.  
  2409.             IF (frameType% < 6) THEN
  2410.                 FOR x% = 1 TO numLines
  2411.                     LOCATE (x% + (topRow - 1)), leftCol: PRINT line$(x%);
  2412.                 NEXT x%
  2413.             ELSE
  2414.                 IF (frameType% = 6 OR frameType% = 8) THEN    ' *** RAISED ***
  2415.                     tempFG1% = 15
  2416.                     tempFG2% = 0
  2417.                 ELSE                          ' *** DEPRESSED ***
  2418.                     tempFG1% = 0
  2419.                     tempFG2% = 15
  2420.                 END IF
  2421.                 LOCATE topRow, leftCol, 0
  2422.                 COLOR tempFG1%, backColor%
  2423.                 PRINT LEFT$(line$(1), LEN(line$(1)) - 1);
  2424.                 COLOR tempFG2%, backColor%
  2425.                 PRINT RIGHT$(line$(1), 1);
  2426.  
  2427.                 FOR x% = 2 TO numLines - 1
  2428.                     COLOR tempFG1%, backColor%
  2429.                     LOCATE (x% + (topRow - 1)), leftCol: PRINT LEFT$(line$(x%), LEN(line$(x%)) - 1);
  2430.                     COLOR tempFG2%, backColor%
  2431.                     PRINT RIGHT$(line$(x%), 1);
  2432.                 NEXT x%
  2433.  
  2434.                 LOCATE botRow, leftCol, 0
  2435.                 COLOR tempFG1%, backColor%
  2436.                 PRINT LEFT$(line$(numLines), 1);
  2437.                 COLOR tempFG2%, backColor%
  2438.                 PRINT RIGHT$(line$(numLines), LEN(line$(numLines)) - 1);
  2439.  
  2440.             END IF
  2441.  
  2442.         CASE 1, 2, 3                ' Explode that window!
  2443.  
  2444.             expX1% = INT(((rightCol - leftCol) / 2) + leftCol): expX2% = expX1%
  2445.             expY1% = INT(((botRow - topRow) / 2) + topRow): expY2% = expY1%
  2446.             WHILE (expX1% > leftCol + 1) OR (expY1% > topRow + 1)
  2447.                 IF expX1% > leftCol THEN expX1% = expX1% - explodeStep%
  2448.                 IF expX2% < rightCol THEN expX2% = expX2% + explodeStep%
  2449.                 IF expY1% > topRow THEN expY1% = expY1% - 1
  2450.                 IF expY2% < botRow THEN expY2% = expY2% + 1
  2451.                 IF expX1% < leftCol THEN expX1% = leftCol: expX2% = rightCol
  2452.                 IF expY1% < topRow THEN expY1% = topRow: expY2% = botRow
  2453.                 LOCATE expY1%, expX1%: PRINT ulc$ + STRING$((expX2% - expX1%) - 1, hor$) + urc$;
  2454.                 FOR x% = expY1% + 1 TO expY2% - 1
  2455.                     LOCATE x%, expX1%: PRINT ver$ + SPACE$((expX2% - expX1%) - 1) + ver$;
  2456.                 NEXT x%
  2457.                 LOCATE expY2%, expX1%: PRINT llc$ + STRING$((expX2% - expX1%) - 1, hor$) + lrc$;
  2458.             WEND
  2459.  
  2460.         '─────────────────────────────────────────────────────────────────
  2461.         ' Print a straight window now, after the explosion effect
  2462.         '─────────────────────────────────────────────────────────────────
  2463.             IF (frameType% < 6) THEN
  2464.                 FOR x% = 1 TO numLines
  2465.                     LOCATE (x% + (topRow - 1)), leftCol: PRINT line$(x%);
  2466.                 NEXT x%
  2467.             ELSE
  2468.                 IF (frameType% = 6) THEN      ' *** RAISED ***
  2469.                     tempFG1% = 15
  2470.                     tempFG2% = 0
  2471.                 ELSE                          ' *** DEPRESSED ***
  2472.                     tempFG1% = 0
  2473.                     tempFG2% = 15
  2474.                 END IF
  2475.                 LOCATE topRow, leftCol, 0
  2476.                 COLOR tempFG1%, backColor%
  2477.                 PRINT LEFT$(line$(1), LEN(line$(1)) - 1);
  2478.                 COLOR tempFG2%, backColor%
  2479.                 PRINT RIGHT$(line$(1), 1);
  2480.  
  2481.                 FOR x% = 2 TO numLines - 1
  2482.                     COLOR 15, backColor%
  2483.                     LOCATE (x% + (topRow - 1)), leftCol: PRINT LEFT$(line$(x%), LEN(line$(x%)) - 1);
  2484.                     COLOR 0, backColor%
  2485.                     PRINT RIGHT$(line$(x%), 1);
  2486.                 NEXT x%
  2487.  
  2488.                 LOCATE botRow, leftCol, 0
  2489.                 COLOR tempFG1%, backColor%
  2490.                 PRINT LEFT$(line$(1), 1);
  2491.                 COLOR tempFG2%, backColor%
  2492.                 PRINT RIGHT$(line$(1), LEN(line$(1)) - 1);
  2493.  
  2494.             END IF
  2495.  
  2496.         CASE ELSE
  2497.  
  2498.         '─────────────────────────────────────────────────────────────────────
  2499.         ' Shouldn't be an 'else' !
  2500.         '─────────────────────────────────────────────────────────────────────
  2501.  
  2502.         END SELECT
  2503.  
  2504.     '─────────────────────────────────────────────────────────────────────────
  2505.     ' Add a shadow if required
  2506.     '─────────────────────────────────────────────────────────────────────────
  2507.         SELECT CASE shadowColor%
  2508.         CASE 0 TO 15
  2509.  
  2510.         '─────────────────────────────────────────────────────────────────────
  2511.         ' Change colors to ShadowColor%
  2512.         '─────────────────────────────────────────────────────────────────────
  2513.             COLOR shadowColor%, 0
  2514.  
  2515.         '─────────────────────────────────────────────────────────────────────
  2516.         ' Define the characters to display for the side/bottom shadow
  2517.         '─────────────────────────────────────────────────────────────────────
  2518.             sideShadow$ = STRING$(2, 219)
  2519.             botShadow$ = STRING$((rightCol - leftCol), 219)
  2520.  
  2521.         '─────────────────────────────────────────────────────────────────────
  2522.         ' Print the side shadow
  2523.         '─────────────────────────────────────────────────────────────────────
  2524.             FOR x% = topRow + 1 TO botRow + 1
  2525.                 LOCATE x%, rightCol + 1: PRINT sideShadow$;
  2526.             NEXT x%
  2527.  
  2528.         '─────────────────────────────────────────────────────────────────────
  2529.         ' Print the bottom shadow
  2530.         '─────────────────────────────────────────────────────────────────────
  2531.             LOCATE botRow + 1, leftCol + 2: PRINT botShadow$;
  2532.  
  2533.         CASE 16
  2534.  
  2535.         '─────────────────────────────────────────────────────────────────────
  2536.         ' If shadow color is 16 use monochrome see-thru shadow
  2537.         '─────────────────────────────────────────────────────────────────────
  2538.  
  2539.         'Side shadow
  2540.             segment = GetVideoSegment!
  2541.             FOR x% = topRow TO botRow
  2542.                 offset% = (160 * x%) + (rightCol * 2) + 1
  2543.                 DEF SEG = segment
  2544.                 POKE offset%, 7
  2545.                 POKE offset% + 2, 7
  2546.                 DEF SEG
  2547.             NEXT x%
  2548.         'Bottom shadow
  2549.             offset% = (botRow * 160)
  2550.             FOR x% = ((leftCol + 1) * 2) TO ((rightCol + 1) * 2) STEP 2
  2551.                 DEF SEG = segment
  2552.                 POKE offset% + x% + 1, 7
  2553.                 DEF SEG
  2554.             NEXT x%
  2555.  
  2556.         CASE 17
  2557.  
  2558.         '─────────────────────────────────────────────────────────────────────
  2559.         ' Shadow type 17 - color see-thru shadow
  2560.         '─────────────────────────────────────────────────────────────────────
  2561.  
  2562.         'Side shadow
  2563.             segment = GetVideoSegment
  2564.             FOR x% = topRow TO botRow - 1
  2565.                 offset% = (160 * x%) + (rightCol * 2) + 1
  2566.                 sf% = GetForeground%(x% + 1, INT(rightCol + 1))
  2567.                 IF sf% > 15 THEN
  2568.                     blink% = 128
  2569.                 ELSE
  2570.                     blink% = 0
  2571.                 END IF
  2572.                 IF sf% > 7 THEN
  2573.                     sf% = (sf% MOD 8) + blink%
  2574.                 ELSE
  2575.                 END IF
  2576.                 DEF SEG = segment
  2577.                 POKE offset%, sf%
  2578.                 DEF SEG
  2579.                 sf% = GetForeground%(x% + 1, INT(rightCol + 2))
  2580.                 IF sf% > 15 THEN
  2581.                     blink% = 128
  2582.                 ELSE
  2583.                     blink% = 0
  2584.                 END IF
  2585.                 IF sf% > 7 THEN
  2586.                     sf% = (sf% MOD 8) + blink%
  2587.                 END IF
  2588.                 DEF SEG = segment
  2589.                 POKE offset% + 2, sf%
  2590.                 DEF SEG
  2591.             NEXT x%
  2592.         'Bottom shadow
  2593.             offset% = (botRow * 160)
  2594.             col% = INT(leftCol + 2)
  2595.             FOR x% = ((leftCol + 1) * 2) TO ((rightCol + 1) * 2) STEP 2
  2596.                 sf% = GetForeground%(INT(botRow) + 1, col%)
  2597.                 col% = col% + 1
  2598.                 IF sf% > 15 THEN
  2599.                     blink% = 128
  2600.                 ELSE
  2601.                     blink% = 0
  2602.                 END IF
  2603.                 IF sf% > 7 THEN
  2604.                     sf% = (sf% MOD 8) + blink%
  2605.                 END IF
  2606.                 DEF SEG = segment
  2607.                 POKE offset% + x% + 1, sf%
  2608.                 DEF SEG
  2609.             NEXT x%
  2610.  
  2611.         CASE ELSE
  2612.         END SELECT  ' shadowColor%
  2613.  
  2614.     '─────────────────────────────────────────────────────────────────────────
  2615.     ' Add the Window Label, if possible.  Set the colors to those passed
  2616.     ' to MakeWindow routine.
  2617.     '─────────────────────────────────────────────────────────────────────────
  2618.         IF (frameType% = 6) OR (frameType% = 8) THEN
  2619.             COLOR 15, backColor%
  2620.         ELSEIF (frameType% = 7) OR (frameType% = 9) THEN
  2621.             COLOR 0, backColor%
  2622.         ELSE
  2623.             COLOR foreColor%, backColor%
  2624.         END IF
  2625.  
  2626.     '─────────────────────────────────────────────────────────────────────────
  2627.     ' Add label to window if one was specified
  2628.     '─────────────────────────────────────────────────────────────────────────
  2629.         IF label$ <> "" THEN
  2630.             label$ = lbl$ + label$ + lbr$
  2631.             LOCATE topRow, leftCol + 1
  2632.             PRINT label$;
  2633.         END IF
  2634.  
  2635. END SUB
  2636.  
  2637. REM $STATIC
  2638. SUB MouseAdjustBox (minSens%, x%, y%, fg%, bg%, bpfg%, bpbg%, frType%, shadow%, explode%)
  2639.  
  2640.     '┌────────────────────────────────────────────────────────────────────────┐
  2641.     '│   This routine displays a window that contains some info about the     │
  2642.     '│   mouse and allows the user to adjust the sensitivity of the mouse.    │
  2643.     '│   OK and Cancel buttons are provided so that changes may be saved      │
  2644.     '│   or aborted.  This routine saves and restores the screen automatic-   │
  2645.     '│   ally for you, so don't bother with that.                             │
  2646.     '│                                                                        │
  2647.     '│   minSens% - The lowest value that the user may set the sensitivity    │
  2648.     '│              of the mouse to.  Suggested value is 10.  This prevents   │
  2649.     '│              the user from accidentally creating an unmoveable mouse.  │
  2650.     '│   x% - Column of upper left corner of window.                          │
  2651.     '│   y% - Row of upper-left corner of window.                             │
  2652.     '│   fg% - Foreground color of the window.                                │
  2653.     '│   bg% = Background color of the window.                                │
  2654.     '│   bpfg% - Foreground color of the pressed button.                      │
  2655.     '│   bfbg% - Background color of the pressed button.                      │
  2656.     '│   frType% - The frame type of the surrounding window.                  │
  2657.     '│   shadow% - The shadow type to use for the surrounding window.         │
  2658.     '│   explode% - The explode type to use for the window.                   │
  2659.     '│                                                                        │
  2660.     '│   *NOTE: This routine will RESET the mouse in accordance with the      │
  2661.     '│          rules in the MouseInit% routine (called by this routine).     │
  2662.     '│          The following settings will result:                           │
  2663.     '│                                                                        │
  2664.     '│                    Cursor Position: Center of screen                   │
  2665.     '│                       Cursor State: OFF                                │
  2666.     '│              Graphics Cursor Shape: Arrow                              │
  2667.     '│                        Text Cursor: Reverse Video                      │
  2668.     '│             Double-speed threshold: 64                                 │
  2669.     '└────────────────────────────────────────────────────────────────────────┘
  2670.  
  2671.     '──────────────────────────────────────────────────────────────────────────
  2672.     ' Define the special keys used in the routine.
  2673.     '──────────────────────────────────────────────────────────────────────────
  2674.         bothUp$ = CHR$(0) + CHR$(77)           ' Right arrow
  2675.         bothDown$ = CHR$(0) + CHR$(75)         ' Left arrow
  2676.         vertUp$ = CHR$(0) + CHR$(116)          ' Ctrl+Right arrow
  2677.         vertDown$ = CHR$(0) + CHR$(115)        ' Ctrl+Left arrow
  2678.         horzUp$ = CHR$(54)                     ' Shift+Right arrow
  2679.         horzDown$ = CHR$(52)                   ' Shift+Left arrow
  2680.         fastBothUp$ = CHR$(0) + CHR$(73)       ' PgUp
  2681.         fastBothDown$ = CHR$(0) + CHR$(81)     ' PgDn
  2682.         fastHorzUp$ = CHR$(57)                 ' Shift+PgUp
  2683.         fastHorzDown$ = CHR$(51)               ' Shift+Down arrow
  2684.         fastVertUp$ = CHR$(0) + CHR$(132)      ' Ctrl+PgUp
  2685.         fastVertDown$ = CHR$(0) + CHR$(118)    ' Ctrl+PgDn
  2686.         esc$ = CHR$(27)
  2687.         enter$ = CHR$(13)
  2688.  
  2689.     '──────────────────────────────────────────────────────────────────────────
  2690.     ' First step is to make sure the mouse exists.  This will reset the mouse
  2691.     ' as a side effect.  We can't set the mouse sensitivity if it's not there.
  2692.     '──────────────────────────────────────────────────────────────────────────
  2693.         numButtons% = MouseInit%
  2694.         IF numButtons% = 0 THEN
  2695.             EXIT SUB
  2696.         END IF
  2697.  
  2698.     '──────────────────────────────────────────────────────────────────────────
  2699.     ' Get info about the mouse now that we know it's there.
  2700.     '──────────────────────────────────────────────────────────────────────────
  2701.         MouseInfo ver$, mType%, IRQ%
  2702.  
  2703.     '──────────────────────────────────────────────────────────────────────────
  2704.     ' Now get the current mouse sensitivity for x and y directions.
  2705.     '──────────────────────────────────────────────────────────────────────────
  2706.         MouseGetSensitivity currentXsens%, currentYsens%, dst%
  2707.         oldXsens% = currentXsens%
  2708.         oldYsens% = currentYsens%
  2709.  
  2710.     '──────────────────────────────────────────────────────────────────────────
  2711.     ' Now that we've collected all our information, its time to move on with
  2712.     ' the real functionality of the routine.  First, turn the mouse off whilst
  2713.     ' we display the dialog box.
  2714.     '──────────────────────────────────────────────────────────────────────────
  2715.         MouseHide
  2716.  
  2717.     '──────────────────────────────────────────────────────────────────────────
  2718.     ' Now save what's on the screen before we overwrite it.
  2719.     '──────────────────────────────────────────────────────────────────────────
  2720.         DIM scr%(BlockSize%(x%, x% + 42, y%, y% + 20))
  2721.         BlockSave x%, x% + 42, y%, y% + 20, scr%(), GetVideoSegment!
  2722.  
  2723.     '──────────────────────────────────────────────────────────────────────────
  2724.     ' Display a window in which all elements of the dialog box will reside.
  2725.     '──────────────────────────────────────────────────────────────────────────
  2726.         MakeWindow CSNG(y%), CSNG(x%), CSNG(y%) + 20, CSNG(x%) + 42, fg%, bg%, 0, frType%, shadow%, explode%, " Mouse Adjustments "
  2727.  
  2728.     '──────────────────────────────────────────────────────────────────────────
  2729.     ' Now display each part of the dialog box.
  2730.     '──────────────────────────────────────────────────────────────────────────
  2731.     ' Mouse information.
  2732.         COLOR fg%, bg%
  2733.         LOCATE y% + 2, x% + 3, 0
  2734.         PRINT "Mouse Version: "; ver$
  2735.         LOCATE y% + 3, x% + 3, 0
  2736.         PRINT "Mouse Type   : ";
  2737.         SELECT CASE mType%
  2738.         CASE BUSMOUSE
  2739.             PRINT "BUS";
  2740.         CASE SERIALMOUSE
  2741.             PRINT "SERIAL";
  2742.         CASE INPORTMOUSE
  2743.             PRINT "IN-PORT";
  2744.         CASE PS2MOUSE
  2745.             PRINT "PS/2";
  2746.         CASE HEWLETTPACKARDMOUSE
  2747.             PRINT "HP";
  2748.         CASE ELSE
  2749.         END SELECT
  2750.         LOCATE y% + 4, x% + 3, 0
  2751.         PRINT "IRQ Number   :"; IRQ%;
  2752.         LOCATE y% + 5, x% + 3, 0
  2753.         PRINT "Num buttons  :"; numButtons%;
  2754.  
  2755.     ' Buttons
  2756.         LOCATE y% + 1, x% + 30, 0: PRINT "┌────────╖";
  2757.         LOCATE y% + 2, x% + 30, 0: PRINT "│   OK   ║";
  2758.         LOCATE y% + 3, x% + 30, 0: PRINT "╘════════╝";
  2759.         LOCATE y% + 4, x% + 30, 0: PRINT "┌────────╖";
  2760.         LOCATE y% + 5, x% + 30, 0: PRINT "│ Cancel ║";
  2761.         LOCATE y% + 6, x% + 30, 0: PRINT "╘════════╝";
  2762.  
  2763.     ' Horizontal sensitivity gadgets.
  2764.         LOCATE y% + 8, x% + 3, 0
  2765.         PRINT "Horizontal Sensitivity";
  2766.         MakeWindow y% + 9, x% + 3, y% + 11, x% + 7, fg%, bg%, 0, 0, -1, 0, ""
  2767.         LOCATE y% + 10, x% + 4, 0: PRINT "";
  2768.         MakeWindow y% + 9, x% + 8, y% + 11, x% + 12, fg%, bg%, 0, 0, -1, 0, ""
  2769.         LOCATE y% + 10, x% + 10, 0: PRINT "";
  2770.         MakeWindow y% + 9, x% + 13, y% + 11, x% + 24, fg%, bg%, 0, 0, -1, 0, ""
  2771.         MakeWindow y% + 9, x% + 25, y% + 11, x% + 29, fg%, bg%, 0, 0, -1, 0, ""
  2772.         LOCATE y% + 10, x% + 27, 0: PRINT "";
  2773.         MakeWindow y% + 9, x% + 30, y% + 11, x% + 34, fg%, bg%, 0, 0, -1, 0, ""
  2774.         LOCATE y% + 10, x% + 31, 0: PRINT "";
  2775.         MakeWindow y% + 9, x% + 35, y% + 11, x% + 39, fg%, bg%, 0, 0, -1, 0, ""
  2776.         LOCATE y% + 10, x% + 36, 0: PRINT LTRIM$(RTRIM$(STR$(currentXsens%)));
  2777.  
  2778.     ' Vertical sensitivity gadgets.
  2779.         LOCATE y% + 18, x% + 3, 0
  2780.         PRINT "Vertical Sensitivity";
  2781.         MakeWindow y% + 15, x% + 3, y% + 17, x% + 7, fg%, bg%, 0, 0, -1, 0, ""
  2782.         LOCATE y% + 16, x% + 4, 0: PRINT "";
  2783.         MakeWindow y% + 15, x% + 8, y% + 17, x% + 12, fg%, bg%, 0, 0, -1, 0, ""
  2784.         LOCATE y% + 16, x% + 10, 0: PRINT "";
  2785.         MakeWindow y% + 15, x% + 13, y% + 17, x% + 24, fg%, bg%, 0, 0, -1, 0, ""
  2786.         MakeWindow y% + 15, x% + 25, y% + 17, x% + 29, fg%, bg%, 0, 0, -1, 0, ""
  2787.         LOCATE y% + 16, x% + 27, 0: PRINT "";
  2788.         MakeWindow y% + 15, x% + 30, y% + 17, x% + 34, fg%, bg%, 0, 0, -1, 0, ""
  2789.         LOCATE y% + 16, x% + 31, 0: PRINT "";
  2790.         MakeWindow y% + 15, x% + 35, y% + 17, x% + 39, fg%, bg%, 0, 0, -1, 0, ""
  2791.         LOCATE y% + 16, x% + 36, 0: PRINT LTRIM$(RTRIM$(STR$(currentYsens%)));
  2792.  
  2793.     ' Gadgets for both.
  2794.         MakeWindow y% + 12, x% + 3, y% + 14, x% + 7, fg%, bg%, 0, 0, -1, 0, ""
  2795.         LOCATE y% + 13, x% + 4, 0: PRINT "";
  2796.         MakeWindow y% + 12, x% + 8, y% + 14, x% + 12, fg%, bg%, 0, 0, -1, 0, ""
  2797.         LOCATE y% + 13, x% + 10, 0: PRINT "";
  2798.         MakeWindow y% + 12, x% + 25, y% + 14, x% + 29, fg%, bg%, 0, 0, -1, 0, ""
  2799.         LOCATE y% + 13, x% + 27, 0: PRINT "";
  2800.         MakeWindow y% + 12, x% + 30, y% + 14, x% + 34, fg%, bg%, 0, 0, -1, 0, ""
  2801.         LOCATE y% + 13, x% + 31, 0: PRINT "";
  2802.         LOCATE y% + 13, x% + 17, 0: PRINT "BOTH";
  2803.  
  2804.     '──────────────────────────────────────────────────────────────────────────
  2805.     ' Set the initial sensitivity bar indicators.
  2806.     '──────────────────────────────────────────────────────────────────────────
  2807.         XsiBar$ = STRING$(currentXsens% \ 10, 219)
  2808.         IF ((currentXsens% MOD 10) >= 5) THEN
  2809.             XsiBar$ = XsiBar$ + CHR$(221)
  2810.         END IF
  2811.         XsiBar$ = LEFT$(XsiBar$ + SPACE$(10), 10)
  2812.         LOCATE y% + 10, x% + 14, 0
  2813.         PRINT XsiBar$;
  2814.         YsiBar$ = STRING$(currentYsens% \ 10, 219)
  2815.         IF ((currentYsens% MOD 10) >= 5) THEN
  2816.             YsiBar$ = YsiBar$ + CHR$(221)
  2817.         END IF
  2818.         YsiBar$ = LEFT$(YsiBar$ + SPACE$(10), 10)
  2819.         LOCATE y% + 16, x% + 14, 0
  2820.         PRINT YsiBar$;
  2821.  
  2822.     '──────────────────────────────────────────────────────────────────────────
  2823.     ' Now that the whole thing is displayed, we're ready for our event loop.
  2824.     ' We'll be capturing mouse and keyboard events and then acting on them.
  2825.     ' First, though, we'll drain out the mouse and keyboard buffers.
  2826.     '──────────────────────────────────────────────────────────────────────────
  2827.         MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  2828.         MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  2829.         done% = FALSE
  2830.         DO
  2831.  
  2832.         '────────────────────────────────────────────────────────────────────────
  2833.         ' Get mouse press information.
  2834.         '────────────────────────────────────────────────────────────────────────
  2835.             MouseShow     ' Don't know why, but had to call MouseShow twice.
  2836.             MouseShow
  2837.             k$ = ""
  2838.             lmCnt% = 0
  2839.             rmCnt% = 0
  2840.             MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  2841.             MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  2842.  
  2843.         '────────────────────────────────────────────────────────────────────────
  2844.         ' Did we have any left mouse button presses?  If not, check the
  2845.         ' keyboard for input.
  2846.         '────────────────────────────────────────────────────────────────────────
  2847.             IF lmCnt% = 0 THEN
  2848.                 k$ = UCASE$(INKEY$)
  2849.             END IF
  2850.  
  2851.         '────────────────────────────────────────────────────────────────────────
  2852.         ' Check for left mouse button presses on the many hot spots in our
  2853.         ' dialog box.  For operations that have keyboard equivalents, simply
  2854.         ' force the setting of k$, our keypress holder, and let the select
  2855.         ' statement handle it later.
  2856.         '────────────────────────────────────────────────────────────────────────
  2857.             IF (lmCnt%) THEN
  2858.  
  2859.             ' Convert mouse virtual screen coordinates to real 80x25 coordinates.
  2860.                 mx% = (mx% / 8) + 1
  2861.                 my% = (my% / 8) + 1
  2862.          
  2863.             ' OK Button
  2864.                 IF (mx% >= x% + 30) AND (mx% <= x% + 39) AND (my% >= y% + 1) AND (my% <= y% + 3) THEN
  2865.                     done% = TRUE
  2866.                     MouseHide
  2867.                     COLOR bpfg%, bpbg%
  2868.                     LOCATE y% + 1, x% + 30, 0: PRINT "┌────────╖";
  2869.                     LOCATE y% + 2, x% + 30, 0: PRINT "│   OK   ║";
  2870.                     LOCATE y% + 3, x% + 30, 0: PRINT "╘════════╝";
  2871.                 END IF
  2872.  
  2873.             ' Cancel Button
  2874.                 IF (mx% >= x% + 30) AND (mx% <= x% + 39) AND (my% >= y% + 4) AND (my% <= y% + 6) THEN
  2875.                     done% = TRUE
  2876.                     MouseHide
  2877.                     COLOR bpfg%, bpbg%
  2878.                     LOCATE y% + 4, x% + 30, 0: PRINT "┌────────╖";
  2879.                     LOCATE y% + 5, x% + 30, 0: PRINT "│ Cancel ║";
  2880.                     LOCATE y% + 6, x% + 30, 0: PRINT "╘════════╝";
  2881.                     MouseSetSensitivity oldXsens%, oldYsens%, dst%
  2882.                 END IF
  2883.  
  2884.             ' Fast Horizontal down
  2885.                 IF (mx% >= x% + 3) AND (mx% <= x% + 7) AND (my% >= y% + 9) AND (my% <= y% + 11) THEN
  2886.                     k$ = fastHorzDown$
  2887.                 END IF
  2888.  
  2889.             ' Horizontal down
  2890.                 IF (mx% >= x% + 8) AND (mx% <= x% + 12) AND (my% >= y% + 9) AND (my% <= y% + 11) THEN
  2891.                     k$ = horzDown$
  2892.                 END IF
  2893.  
  2894.             ' Horizontal Up
  2895.                 IF (mx% >= x% + 25) AND (mx% <= x% + 29) AND (my% >= y% + 9) AND (my% <= y% + 11) THEN
  2896.                     k$ = horzUp$
  2897.                 END IF
  2898.  
  2899.             ' Fast Horizontal Up
  2900.                 IF (mx% >= x% + 30) AND (mx% <= x% + 34) AND (my% >= y% + 9) AND (my% <= y% + 11) THEN
  2901.                     k$ = fastHorzUp$
  2902.                 END IF
  2903.              
  2904.             ' Fast Vertical down
  2905.                 IF (mx% >= x% + 3) AND (mx% <= x% + 7) AND (my% >= y% + 15) AND (my% <= y% + 17) THEN
  2906.                     k$ = fastVertDown$
  2907.                 END IF
  2908.  
  2909.             ' Vertical down
  2910.                 IF (mx% >= x% + 8) AND (mx% <= x% + 12) AND (my% >= y% + 15) AND (my% <= y% + 17) THEN
  2911.                     k$ = vertDown$
  2912.                 END IF
  2913.  
  2914.             ' Vertical Up
  2915.                 IF (mx% >= x% + 25) AND (mx% <= x% + 29) AND (my% >= y% + 15) AND (my% <= y% + 17) THEN
  2916.                     k$ = vertUp$
  2917.                 END IF
  2918.  
  2919.             ' Fast Vertical Up
  2920.                 IF (mx% >= x% + 30) AND (mx% <= x% + 34) AND (my% >= y% + 15) AND (my% <= y% + 17) THEN
  2921.                     k$ = fastVertUp$
  2922.                 END IF
  2923.  
  2924.             ' Fast Both down
  2925.                 IF (mx% >= x% + 3) AND (mx% <= x% + 7) AND (my% >= y% + 12) AND (my% <= y% + 14) THEN
  2926.                     k$ = fastBothDown$
  2927.                 END IF
  2928.  
  2929.             ' Both down
  2930.                 IF (mx% >= x% + 8) AND (mx% <= x% + 12) AND (my% >= y% + 12) AND (my% <= y% + 14) THEN
  2931.                     k$ = bothDown$
  2932.                 END IF
  2933.  
  2934.             ' Both Up
  2935.                 IF (mx% >= x% + 25) AND (mx% <= x% + 29) AND (my% >= y% + 12) AND (my% <= y% + 14) THEN
  2936.                     k$ = bothUp$
  2937.                 END IF
  2938.  
  2939.             ' Fast Both Up
  2940.                 IF (mx% >= x% + 30) AND (mx% <= x% + 34) AND (my% >= y% + 12) AND (my% <= y% + 14) THEN
  2941.                     k$ = fastBothUp$
  2942.                 END IF
  2943.          
  2944.             END IF    ' lmCnt%
  2945.  
  2946.         '────────────────────────────────────────────────────────────────────────
  2947.         ' Check for right mouse button presses.  Any press means abort this
  2948.         ' operation.
  2949.         '────────────────────────────────────────────────────────────────────────
  2950.             IF (rmCnt%) THEN
  2951.                 done% = TRUE
  2952.                 MouseHide
  2953.                 COLOR bpfg%, bpbg%
  2954.                 LOCATE y% + 4, x% + 30, 0: PRINT "┌────────╖";
  2955.                 LOCATE y% + 5, x% + 30, 0: PRINT "│ Cancel ║";
  2956.                 LOCATE y% + 6, x% + 30, 0: PRINT "╘════════╝";
  2957.                 MouseSetSensitivity oldXsens%, oldYsens%, dst%
  2958.             END IF    ' rmCnt%
  2959.  
  2960.         '────────────────────────────────────────────────────────────────────────
  2961.         ' Now act based on any keys that were pressed.  This includes all mouse
  2962.         ' operations that have a keyboard equivalent.
  2963.         '────────────────────────────────────────────────────────────────────────
  2964.             SELECT CASE k$
  2965.  
  2966.             CASE esc$
  2967.                 MouseSetSensitivity oldXsens%, oldYsens%, dst%
  2968.                 done% = TRUE
  2969.  
  2970.             CASE enter$
  2971.                 done% = TRUE
  2972.  
  2973.             CASE fastHorzDown$
  2974.                 IF (currentXsens% > minSens% + 10) THEN
  2975.                     currentXsens% = currentXsens% - 10
  2976.                 ELSE
  2977.                     currentXsens% = minSens%
  2978.                 END IF
  2979.                 updateBox% = TRUE
  2980.  
  2981.             CASE horzDown$
  2982.                 IF (currentXsens% > minSens%) THEN
  2983.                     currentXsens% = currentXsens% - 1
  2984.                     updateBox% = TRUE
  2985.                 END IF
  2986.  
  2987.             CASE horzUp$
  2988.                 IF (currentXsens% < 100) THEN
  2989.                     currentXsens% = currentXsens% + 1
  2990.                     updateBox% = TRUE
  2991.                 END IF
  2992.  
  2993.             CASE fastHorzUp$
  2994.                 IF (currentXsens% < 90) THEN
  2995.                     currentXsens% = currentXsens% + 10
  2996.                 ELSE
  2997.                     currentXsens% = 100
  2998.                 END IF
  2999.                 updateBox% = TRUE
  3000.  
  3001.             CASE fastVertDown$
  3002.                 IF (currentYsens% > minSens% + 10) THEN
  3003.                     currentYsens% = currentYsens% - 10
  3004.                 ELSE
  3005.                     currentYsens% = minSens%
  3006.                 END IF
  3007.                 updateBox% = TRUE
  3008.  
  3009.             CASE vertDown$
  3010.                 IF (currentYsens% > minSens%) THEN
  3011.                     currentYsens% = currentYsens% - 1
  3012.                     updateBox% = TRUE
  3013.                 END IF
  3014.  
  3015.             CASE vertUp$
  3016.                 IF (currentYsens% < 100) THEN
  3017.                     currentYsens% = currentYsens% + 1
  3018.                     updateBox% = TRUE
  3019.                 END IF
  3020.  
  3021.             CASE fastVertUp$
  3022.                 IF (currentYsens% < 90) THEN
  3023.                     currentYsens% = currentYsens% + 10
  3024.                 ELSE
  3025.                     currentYsens% = 100
  3026.                 END IF
  3027.                 updateBox% = TRUE
  3028.  
  3029.             CASE fastBothDown$
  3030.                 IF (currentYsens% > minSens% + 10) THEN
  3031.                     currentYsens% = currentYsens% - 10
  3032.                 ELSE
  3033.                     currentYsens% = minSens%
  3034.                 END IF
  3035.                 IF (currentXsens% > minSens% + 10) THEN
  3036.                     currentXsens% = currentXsens% - 10
  3037.                 ELSE
  3038.                     currentXsens% = minSens%
  3039.                 END IF
  3040.                 updateBox% = TRUE
  3041.                 
  3042.             CASE bothDown$
  3043.                 IF (currentYsens% > minSens%) THEN
  3044.                     currentYsens% = currentYsens% - 1
  3045.                     updateBox% = TRUE
  3046.                 END IF
  3047.                 IF (currentXsens% > minSens%) THEN
  3048.                     currentXsens% = currentXsens% - 1
  3049.                     updateBox% = TRUE
  3050.                 END IF
  3051.  
  3052.             CASE bothUp$
  3053.                 IF (currentYsens% < 100) THEN
  3054.                     currentYsens% = currentYsens% + 1
  3055.                     updateBox% = TRUE
  3056.                 END IF
  3057.                 IF (currentXsens% < 100) THEN
  3058.                     currentXsens% = currentXsens% + 1
  3059.                     updateBox% = TRUE
  3060.                 END IF
  3061.  
  3062.             CASE fastBothUp$
  3063.                 IF (currentXsens% < 90) THEN
  3064.                     currentXsens% = currentXsens% + 10
  3065.                 ELSE
  3066.                     currentXsens% = 100
  3067.                 END IF
  3068.                 IF (currentYsens% < 90) THEN
  3069.                     currentYsens% = currentYsens% + 10
  3070.                 ELSE
  3071.                     currentYsens% = 100
  3072.                 END IF
  3073.                 updateBox% = TRUE
  3074.  
  3075.             CASE ELSE
  3076.             END SELECT
  3077.      
  3078.         '────────────────────────────────────────────────────────────────────────
  3079.         ' If something was changed, update everything.  This includes:
  3080.         '
  3081.         '   1) Sensitivity indicator bars
  3082.         '   2) Sensitivity indicator values
  3083.         '   3) Reset mouse sensitivity
  3084.         '
  3085.         '────────────────────────────────────────────────────────────────────────
  3086.             IF (updateBox%) THEN
  3087.  
  3088.             '──────────────────────────────────────────────────────────────────────
  3089.             ' Hide mouse whilst we display.
  3090.             '──────────────────────────────────────────────────────────────────────
  3091.                 MouseHide
  3092.  
  3093.             '──────────────────────────────────────────────────────────────────────
  3094.             ' Reset update flag.
  3095.             '──────────────────────────────────────────────────────────────────────
  3096.                 updateBox% = FALSE
  3097.  
  3098.             '──────────────────────────────────────────────────────────────────────
  3099.             ' Set mouse sensitivity to current levels.
  3100.             '──────────────────────────────────────────────────────────────────────
  3101.                 MouseSetSensitivity currentXsens%, currentYsens%, dst%    ' (3)
  3102.                      
  3103.             '──────────────────────────────────────────────────────────────────────
  3104.             ' Calculate and display new sensitivity indicator bars.
  3105.             '──────────────────────────────────────────────────────────────────────
  3106.                 XsiBar$ = STRING$(currentXsens% \ 10, 219)                ' (1)
  3107.                 IF ((currentXsens% MOD 10) >= 5) THEN
  3108.                     XsiBar$ = XsiBar$ + CHR$(221)
  3109.                 END IF
  3110.                 XsiBar$ = LEFT$(XsiBar$ + SPACE$(10), 10)
  3111.                 LOCATE y% + 10, x% + 14, 0
  3112.                 PRINT XsiBar$;
  3113.                 YsiBar$ = STRING$(currentYsens% \ 10, 219)
  3114.                 IF ((currentYsens% MOD 10) >= 5) THEN
  3115.                     YsiBar$ = YsiBar$ + CHR$(221)
  3116.                 END IF
  3117.                 YsiBar$ = LEFT$(YsiBar$ + SPACE$(10), 10)
  3118.                 LOCATE y% + 16, x% + 14, 0
  3119.                 PRINT YsiBar$;
  3120.          
  3121.             '──────────────────────────────────────────────────────────────────────
  3122.             ' Display new values for sensitivities.
  3123.             '──────────────────────────────────────────────────────────────────────
  3124.                 LOCATE y% + 10, x% + 36, 0: PRINT LEFT$(LTRIM$(RTRIM$(STR$(currentXsens%))) + SPACE$(3), 3);
  3125.                 LOCATE y% + 16, x% + 36, 0: PRINT LEFT$(LTRIM$(RTRIM$(STR$(currentYsens%))) + SPACE$(3), 3);
  3126.  
  3127.             END IF    ' updateBox%
  3128.  
  3129.         LOOP UNTIL done%
  3130.  
  3131.     '──────────────────────────────────────────────────────────────────────────
  3132.     ' Now restore what was on the screen before we got here.
  3133.     '──────────────────────────────────────────────────────────────────────────
  3134.         BlockRestore x%, x% + 42, y%, y% + 20, scr%(), GetVideoSegment!
  3135.  
  3136. END SUB
  3137.  
  3138. SUB MultiMenu (menusArray$(), numEntries%(), menuTitles$(), x1%, y1%, x2%, justify$, marker$, divider$, frameType%, shadowCode%, fg%, bg%, hfg%, hBG%, qfg%, qbg%, menuSelected%, menuEntrySelected%, useMouse%)
  3139.     
  3140.     '┌──────────────────────────────────────────────────────────────────┐
  3141.     '│  This routine allows you to create a pull down menu system for   │
  3142.     '│  any program.  The parameters are as follows:                    │
  3143.     '│                                                                  │
  3144.     '│      menusArray$() - A 2-dimensional array that stores all the   │
  3145.     '│                      entries for each menu.  The FIRST index     │
  3146.     '│                      indicates the particular MENU, while the    │
  3147.     '│                      SECOND index indicates the particular entry │
  3148.     '│                      for the menu indicated by the FIRST index.  │
  3149.     '│      numEntries%() - A 1-dimensional array that contains the     │
  3150.     '│                      number of actual entries for each menu.     │
  3151.     '│                      The index for this array indicates which    │
  3152.     '│                      menu you're talking about.                  │
  3153.     '│      menuTitles$() - A 1-dimensional array that stores the       │
  3154.     '│                      title of each menu.                         │
  3155.     '│      x1%           - Starting column of menu bar.                │
  3156.     '│      y1%           - Starting row of menu bar.                   │
  3157.     '│      x2%           - Ending column of menu bar.                  │
  3158.     '│      justify$      - A single text character indicating the type │
  3159.     '│                      of justification to use when displaying the │
  3160.     '│                      menu will use when displaying the entries   │
  3161.     '│                      of each sub-menu.  The valid values are:    │
  3162.     '│                                  "C" - Centered                  │
  3163.     '│                                  "L" - Left justified            │
  3164.     '│                                  "R" - Right justified           │
  3165.     '│      marker$       - A single character used to identify the     │
  3166.     '│                      'Quick Access' key for each menu entry.     │
  3167.     '│      divider$      - Character or string used as a menu divider. │
  3168.     '│      shadowCode%   - A value indicating the type of shadowing    │
  3169.     '│                      to use for the menu windows.  Valid values: │
  3170.     '│                            -1   - No shadow at all               │
  3171.     '│                            0-15 - Shadow of this color           │
  3172.     '│                            16   - Special character shadow       │
  3173.     '│      fg%, bg%      - The foreground and background colors of the │
  3174.     '│                      normal, unhighlighted menu entries          │
  3175.     '│      hfg%, hbg%    - The foreground and background colors of the │
  3176.     '│                      highlighted menu entries                    │
  3177.     '│      qfg%, qbg%    - The foreground and background colors of the │
  3178.     '│                      'Quick Access' letters                      │
  3179.     '│      menuSelected% - This variable is an 'out' parameter.  It    │
  3180.     '│                      has no value when you call the routine.     │
  3181.     '│                      When the MultiMenu returns to the calling   │
  3182.     '│                      routine, this variable will contain the     │
  3183.     '│                      number of the menu the user made his/her    │
  3184.     '│                      selection from.                             │
  3185.     '│      menuEntrySelected% - This variable is an 'out' parameter.   │
  3186.     '│                      It has no value when you call the routine.  │
  3187.     '│                      When the MultiMenu returns to the calling   │
  3188.     '│                      routine, this variable will contain the     │
  3189.     '│                      number of the entry the user selected on    │
  3190.     '│                      the menu indicated by menuSelected%.        │
  3191.     '│      useMouse%     - 1 = use mouse support, 0 = don't.           │
  3192.     '│                                                                  │
  3193.     '│  See the QBSCR Screen Routines documentation for more details.   │
  3194.     '└──────────────────────────────────────────────────────────────────┘
  3195.     
  3196.     '────────────────────────────────────────────────────────────────────
  3197.     ' Define special keys
  3198.     '────────────────────────────────────────────────────────────────────
  3199.         LeftArrowKey$ = CHR$(0) + CHR$(75)
  3200.         RightArrowKey$ = CHR$(0) + CHR$(77)
  3201.         DownArrowKey$ = CHR$(0) + CHR$(80)
  3202.         HomeKee$ = CHR$(0) + CHR$(71)
  3203.         EndKee$ = CHR$(0) + CHR$(79)
  3204.         enter$ = CHR$(13)
  3205.         esc$ = CHR$(27)
  3206.     
  3207.     '────────────────────────────────────────────────────────────────────
  3208.     ' Determine number of menus
  3209.     '────────────────────────────────────────────────────────────────────
  3210.         numMenus% = UBOUND(menusArray$, 1)
  3211.  
  3212.     '────────────────────────────────────────────────────────────────────
  3213.     ' Define an array that will store the column locations or each menu
  3214.     ' title string.
  3215.     '────────────────────────────────────────────────────────────────────
  3216.         DIM menuXs%(numMenus%)
  3217.     
  3218.     '────────────────────────────────────────────────────────────────────
  3219.     ' Determine all QuickAccess keys for the menu titles, as well as the
  3220.     ' starting screen column that each menu item will be displayed on.
  3221.     ' Also, as long as we're here, determine the x-coordinate for each
  3222.     ' menu title.
  3223.     '────────────────────────────────────────────────────────────────────
  3224.         DIM charID(1 TO numMenus%) AS STRING * 1
  3225.         FOR x% = 1 TO numMenus%
  3226.  
  3227.         '──────────────────────────────────────────────────────────────────
  3228.         ' Get starting columns for each menu title.
  3229.         '──────────────────────────────────────────────────────────────────
  3230.             IF (x% > 1) THEN
  3231.                 menuXs%(x%) = menuXs%(x% - 1) + LEN(menuTitles$(x% - 1)) + 1
  3232.             ELSE
  3233.                 menuXs%(x%) = x1% + 1
  3234.             END IF
  3235.  
  3236.         '──────────────────────────────────────────────────────────────────
  3237.         ' Determine quick access key for menu item.
  3238.         '──────────────────────────────────────────────────────────────────
  3239.             FOR y% = 1 TO LEN(menuTitles$(x%))
  3240.                 IF MID$(menuTitles$(x%), y%, 1) = marker$ THEN
  3241.                     charID(x%) = UCASE$(MID$(menuTitles$(x%), y% + 1, 1))
  3242.                     EXIT FOR
  3243.                 END IF
  3244.             NEXT y%
  3245.  
  3246.         NEXT x%
  3247.     
  3248.     '─────────────────────────────────────────────────────────────────────
  3249.     ' At this point, we must turn off the mouse cursor if it's available.
  3250.     ' We don't want to write overtop of it, leaving a hole when it's moved
  3251.     ' later.
  3252.     '─────────────────────────────────────────────────────────────────────
  3253.         IF useMouse% THEN
  3254.             MouseHide
  3255.         END IF
  3256.  
  3257.     '────────────────────────────────────────────────────────────────────
  3258.     ' Display pull-down menus line
  3259.     '────────────────────────────────────────────────────────────────────
  3260.         COLOR fg%, bg%
  3261.         LOCATE y1%, x1%, 0: PRINT SPACE$(x2% - x1% + 1);
  3262.         colCount% = 0
  3263.         FOR x% = 1 TO numMenus%
  3264.             LOCATE y1%, x1% + colCount% + 1, 0: DisplayEntry menuTitles$(x%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wid%, 1
  3265.             colCount% = colCount% + LEN(menuTitles$(x%)) + 1
  3266.         NEXT x%
  3267.     
  3268.     '────────────────────────────────────────────────────────────────────
  3269.     ' Display highlight for first entry
  3270.     '────────────────────────────────────────────────────────────────────
  3271.         COLOR hfg%, hBG%
  3272.         LOCATE y1%, x1% + 1, 0: DisplayEntry menuTitles$(1), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wid%, 2
  3273.     
  3274.     '────────────────────────────────────────────────────────────────────
  3275.     ' Wait for keystrokes
  3276.     '────────────────────────────────────────────────────────────────────
  3277.         currentMenu% = 1
  3278.         oldMenu% = 1
  3279.         done% = FALSE
  3280.         updateMenu% = FALSE
  3281.         DO
  3282.  
  3283.         '──────────────────────────────────────────────────────────────────
  3284.         ' If we're using the mouse, turn it on.
  3285.         '──────────────────────────────────────────────────────────────────
  3286.             IF useMouse% THEN
  3287.                 MouseShow
  3288.             END IF
  3289.     
  3290.         '──────────────────────────────────────────────────────────────────
  3291.         ' Read keystrokes and/or mouse strokes.
  3292.         '──────────────────────────────────────────────────────────────────
  3293.             k$ = ""
  3294.             lmCnt% = 0
  3295.             rmCnt% = 0
  3296.             IF useMouse% THEN
  3297.                 MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  3298.                 MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  3299.  
  3300.             '────────────────────────────────────────────────────────────────
  3301.             ' Did we have any left mouse button presses?  If not, check the
  3302.             ' keyboard for input.
  3303.             '────────────────────────────────────────────────────────────────
  3304.                 IF lmCnt% = 0 THEN
  3305.                     k$ = UCASE$(INKEY$)
  3306.                 END IF
  3307.             ELSE
  3308.  
  3309.             '────────────────────────────────────────────────────────────────
  3310.             ' No mouse available, so wait for keyboard input.
  3311.             '────────────────────────────────────────────────────────────────
  3312.                 WHILE k$ = ""
  3313.                     k$ = UCASE$(INKEY$)
  3314.                 WEND
  3315.             END IF
  3316.  
  3317.         '─────────────────────────────────────────────────────────────────────
  3318.         ' If the left mouse button was pressed, check to see if a menu item
  3319.         ' was selected by it.
  3320.         '─────────────────────────────────────────────────────────────────────
  3321.             IF (useMouse%) AND (lmCnt% > 0) THEN
  3322.  
  3323.             '───────────────────────────────────────────────────────────────────
  3324.             ' Convert virtual screen mouse coordinates to real 80x25 coords.
  3325.             '───────────────────────────────────────────────────────────────────
  3326.                 mx% = (mx% \ 8) + 1
  3327.                 my% = (my% \ 8) + 1
  3328.  
  3329.             '───────────────────────────────────────────────────────────────────
  3330.             ' If mouse was inside menu bar then update currentMenu%
  3331.             '───────────────────────────────────────────────────────────────────
  3332.                 IF (my% = y1%) THEN
  3333.                     FOR i% = 1 TO numMenus%
  3334.                         IF (mx% >= menuXs%(i%)) AND (mx% <= menuXs%(i%) + LEN(menuTitles$(i%))) THEN
  3335.                             currentMenu% = i%
  3336.                             updateMenu% = TRUE
  3337.                             done% = TRUE
  3338.                             EXIT FOR
  3339.                         END IF
  3340.                     NEXT i%
  3341.                 END IF
  3342.             END IF
  3343.  
  3344.         '─────────────────────────────────────────────────────────────────────
  3345.         ' If right mouse button was pressed, then exit as if ESC were pressed.
  3346.         '─────────────────────────────────────────────────────────────────────
  3347.             IF (useMouse%) AND (rmCnt% > 0) THEN
  3348.                 menuSelected% = 0
  3349.                 menuEntrySelected% = 0
  3350.                 EXIT SUB
  3351.             END IF
  3352.  
  3353.         '─────────────────────────────────────────────────────────────────────
  3354.         ' If keys were pressed, act on them.
  3355.         '─────────────────────────────────────────────────────────────────────
  3356.             SELECT CASE k$
  3357.  
  3358.             CASE LeftArrowKey$           ' Move highlight to the left
  3359.                 IF currentMenu% > 1 THEN
  3360.                     currentMenu% = currentMenu% - 1
  3361.                 ELSE
  3362.                     currentMenu% = numMenus%
  3363.                 END IF
  3364.                 updateMenu% = TRUE
  3365.  
  3366.             CASE RightArrowKey$          ' Move highlight to the right
  3367.                 IF currentMenu% < numMenus% THEN
  3368.                     currentMenu% = currentMenu% + 1
  3369.                 ELSE
  3370.                     currentMenu% = 1
  3371.                 END IF
  3372.                 updateMenu% = TRUE
  3373.          
  3374.             CASE HomeKee$
  3375.                 currentMenu% = 1
  3376.                 updateMenu% = TRUE
  3377.          
  3378.             CASE EndKee$
  3379.                 currentMenu% = numMenus%
  3380.                 updateMenu% = TRUE
  3381.          
  3382.             CASE enter$, DownArrowKey$   ' Use the current menu and exit DO
  3383.                 done% = TRUE
  3384.          
  3385.             CASE esc$ ' Abort MultiMenu call
  3386.                 menuSelected% = 0
  3387.                 menuEntrySelected% = 0
  3388.                 EXIT SUB
  3389.          
  3390.             CASE ELSE
  3391.             '────────────────────────────────────────────────────────────
  3392.             ' Check for special quick access keys
  3393.             '────────────────────────────────────────────────────────────
  3394.                 FOR x% = 1 TO numMenus%
  3395.                     IF k$ = charID(x%) THEN
  3396.                         currentMenu% = x%
  3397.                         done% = TRUE
  3398.                         updateMenu% = TRUE
  3399.                         EXIT FOR
  3400.                     END IF
  3401.                 NEXT x%
  3402.             END SELECT
  3403.         
  3404.         '────────────────────────────────────────────────────────────────
  3405.         ' Update highlight, if required.
  3406.         '────────────────────────────────────────────────────────────────
  3407.             IF updateMenu% THEN
  3408.                 IF useMouse% THEN
  3409.                     MouseHide
  3410.                 END IF
  3411.                 LOCATE y1%, menuXs%(oldMenu%), 0: DisplayEntry menuTitles$(oldMenu%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wid%, 1
  3412.                 oldMenu% = currentMenu%
  3413.                 LOCATE y1%, menuXs%(currentMenu%), 0: DisplayEntry menuTitles$(currentMenu%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wid%, 2
  3414.                 updateMenu% = FALSE
  3415.             END IF
  3416.             colCount% = 0
  3417.         
  3418.         LOOP UNTIL done%
  3419.     
  3420.     '─────────────────────────────────────────────────────────────────────
  3421.     ' At this point, we must turn off the mouse cursor if it's available.
  3422.     ' We don't want to write overtop of it, leaving a hole when it's moved
  3423.     ' later.
  3424.     '─────────────────────────────────────────────────────────────────────
  3425.         IF useMouse% THEN
  3426.             MouseHide
  3427.         END IF
  3428.  
  3429.     '────────────────────────────────────────────────────────────────────
  3430.     ' Now we know the first menu to display.  Loop while the user hits
  3431.     ' the left or right arrow keys
  3432.     '────────────────────────────────────────────────────────────────────
  3433.         done% = FALSE
  3434.         DO
  3435.  
  3436.         '────────────────────────────────────────────────────────────────
  3437.         ' Calculate the longest menu entry in the list
  3438.         '────────────────────────────────────────────────────────────────
  3439.             longestEntry% = 0
  3440.             FOR x% = 1 TO numEntries%(currentMenu%)
  3441.                 IF longestEntry% < LEN(menusArray$(currentMenu%, x%)) THEN
  3442.                     longestEntry% = LEN(menusArray$(currentMenu%, x%))
  3443.                 END IF
  3444.             NEXT x%
  3445.         
  3446.         '────────────────────────────────────────────────────────────────
  3447.         ' Calculate box dimensions
  3448.         '────────────────────────────────────────────────────────────────
  3449.             lft% = menuXs%(currentMenu%) - 1
  3450.             IF lft% < x1% THEN
  3451.                 lft% = x1%
  3452.             END IF
  3453.             rght% = lft% + longestEntry% + 2
  3454.             IF rght% > x2% THEN
  3455.                 lft% = lft% - (rght% - x2%)
  3456.                 rght% = x2%
  3457.             END IF
  3458.             top% = y1% + 1
  3459.             bot% = top% + numEntries%(currentMenu%) + 1
  3460.  
  3461.         '────────────────────────────────────────────────────────────────
  3462.         ' Ony draw a box if we have menu entries to put in it.
  3463.         '────────────────────────────────────────────────────────────────
  3464.             IF numEntries%(currentMenu%) > 0 THEN
  3465.  
  3466.             '────────────────────────────────────────────────────────────────
  3467.             ' Save area of the screen that the window overwrites.
  3468.             '────────────────────────────────────────────────────────────────
  3469.                 REDIM blockArray%(BlockSize%(lft%, rght% + 2, top%, bot% + 1))
  3470.                 BlockSave lft%, rght% + 2, top%, bot% + 1, blockArray%(), GetVideoSegment
  3471.                         
  3472.             '────────────────────────────────────────────────────────────────
  3473.             ' Make the window to hold the entries.
  3474.             '────────────────────────────────────────────────────────────────
  3475.                 MakeWindow CSNG(top%), CSNG(lft%), CSNG(bot%), CSNG(rght%), fg%, bg%, 0, frameType%, shadowCode%, 0, ""
  3476.          
  3477.             '────────────────────────────────────────────────────────────────
  3478.             ' Make the menu for the current menu
  3479.             '────────────────────────────────────────────────────────────────
  3480.                 choice% = SubMenu%(menusArray$(), currentMenu%, numEntries%(currentMenu%), justify$, lft% + 2, CSNG(rght%), top% + 1, marker$, divider$, fg%, bg%, hfg%, hBG%, qfg%, qbg%, useMouse%, mx%, my%)
  3481.  
  3482.             ELSE
  3483.          
  3484.             '────────────────────────────────────────────────────────────────
  3485.             ' This section of code handles the case where there are no menu
  3486.             ' entries in the submenu, or in other words, no submenu at all.
  3487.             '────────────────────────────────────────────────────────────────
  3488.                 menuSelected% = currentMenu%
  3489.                 menuEntrySelected% = 0
  3490.                 IF (k$ = LeftArrowKey$) OR (k$ = RightArrowKey$) OR (choice% = LEFTARROWCODE) OR (choice% = RIGHTARROWCODE) THEN
  3491.                     choice% = 0
  3492.                     WHILE choice% = 0
  3493.                         kee$ = ""
  3494.                         WHILE kee$ = ""
  3495.                             kee$ = UCASE$(INKEY$)
  3496.                         WEND
  3497.                         SELECT CASE kee$
  3498.                         CASE LeftArrowKey$: choice% = LEFTARROWCODE
  3499.                         CASE RightArrowKey$: choice% = RIGHTARROWCODE
  3500.                         CASE enter$: EXIT SUB
  3501.                         CASE ELSE
  3502.                         END SELECT
  3503.                     WEND
  3504.                 ELSE
  3505.                     EXIT SUB
  3506.                 END IF
  3507.  
  3508.             END IF
  3509.  
  3510.         '────────────────────────────────────────────────────────────────
  3511.         ' Decide what to do based on the returned value of the call to
  3512.         ' the SubMenu function, which handles the individual menus
  3513.         '────────────────────────────────────────────────────────────────
  3514.             SELECT CASE choice%
  3515.  
  3516.             CASE LEFTARROWCODE        ' Move to the next menu to the left
  3517.                 IF currentMenu% > 1 THEN
  3518.                     currentMenu% = currentMenu% - 1
  3519.                 ELSE
  3520.                     currentMenu% = numMenus%
  3521.                 END IF
  3522.          
  3523.             CASE RIGHTARROWCODE       ' Move to the next menu to the right
  3524.                 IF currentMenu% < numMenus% THEN
  3525.                     currentMenu% = currentMenu% + 1
  3526.                 ELSE
  3527.                     currentMenu% = 1
  3528.                 END IF
  3529.          
  3530.             CASE LEFTMOUSEEXIT
  3531.             ' Find out if mouse was on a menu title.
  3532.                 FOR i% = 1 TO numMenus%
  3533.                     IF (mx% >= menuXs%(i%)) AND (mx% <= menuXs%(i%) + LEN(menuTitles$(i%))) THEN
  3534.                         currentMenu% = i%
  3535.                         EXIT FOR
  3536.                     END IF
  3537.                 NEXT i%
  3538.          
  3539.             CASE RIGHTMOUSEEXIT
  3540.                 menuSelected% = 0
  3541.                 menuEntrySelected% = 0
  3542.                 EXIT SUB
  3543.          
  3544.             CASE 1 TO numEntries%(currentMenu%)       ' See if an entry from the menu
  3545.                 menuEntrySelected% = choice%            ' was selected
  3546.                 menuSelected% = currentMenu%
  3547.                 EXIT SUB
  3548.          
  3549.             CASE 27   ' Escape ∙ Abort the menu
  3550.                 menuEntrySelected% = 0
  3551.                 menuSelected% = 0
  3552.                 done% = TRUE
  3553.          
  3554.             CASE ELSE
  3555.             END SELECT
  3556.         
  3557.         '────────────────────────────────────────────────────────────────
  3558.         ' Update highlight
  3559.         '────────────────────────────────────────────────────────────────
  3560.             IF useMouse% THEN
  3561.                 MouseHide
  3562.             END IF
  3563.             LOCATE y1%, menuXs%(oldMenu%), 0: DisplayEntry menuTitles$(oldMenu%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wid%, 1
  3564.             oldMenu% = currentMenu%
  3565.             LOCATE y1%, menuXs%(currentMenu%), 0: DisplayEntry menuTitles$(currentMenu%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wid%, 2
  3566.         
  3567.         '────────────────────────────────────────────────────────────────
  3568.         ' Restore screen block
  3569.         '────────────────────────────────────────────────────────────────
  3570.             BlockRestore lft%, rght% + 2, top%, bot% + 1, blockArray%(), GetVideoSegment
  3571.  
  3572.         LOOP UNTIL done%
  3573.     
  3574. END SUB
  3575.  
  3576. SUB OffCenter (st$, row%, leftCol%, rightCol%)
  3577.     
  3578.     '┌────────────────────────────────────────────────────────────────────────┐
  3579.     '│  This routine will center the text passed to it on the screen between  │
  3580.     '│  two specified columns.  Excellent for centering text in a window      │
  3581.     '│  that itself is not centered in the screen.  Parameters are:           │
  3582.     '│                                                                        │
  3583.     '│      st$ - the string to center.  Maximum length of string is 80       │
  3584.     '│            characters.                                                 │
  3585.     '│      row% - The row on which the string will be centered.  Allowable   │
  3586.     '│             range is 1 through 25.                                     │
  3587.     '│      leftCol! - The left-most column to center the text between.       │
  3588.     '│                 Allowable range is 1 through 79.                       │
  3589.     '│      rightCol! - The right-most column to center the text between.     │
  3590.     '│                  Allowable range is 2 through 80.                      │
  3591.     '└────────────────────────────────────────────────────────────────────────┘
  3592.     
  3593.     '─────────────────────────────────────────────────────────────────────────
  3594.     ' Calculate width available for string
  3595.     '─────────────────────────────────────────────────────────────────────────
  3596.         wdth% = (rightCol% - leftCol%)
  3597.     
  3598.     '─────────────────────────────────────────────────────────────────────────
  3599.     ' If ST$ fits in available width, determine X% for Locate.  Otherwise,
  3600.     ' quit the routine.
  3601.     '─────────────────────────────────────────────────────────────────────────
  3602.         IF LEN(st$) > wdth% THEN
  3603.             EXIT SUB
  3604.         ELSE
  3605.             x% = INT(((wdth% - (LEN(st$))) \ 2) + leftCol%) + 1
  3606.         END IF
  3607.     
  3608.     '─────────────────────────────────────────────────────────────────────────
  3609.     ' Print the string
  3610.     '─────────────────────────────────────────────────────────────────────────
  3611.         LOCATE row%, x%: PRINT st$;
  3612.     
  3613. END SUB
  3614.  
  3615. FUNCTION OkCancelMessageBox% (x1%, y1%, x2%, y2%, st$(), numLines%, justify%, fg%, bg%, frType%, shadow%, explode%, label$, useMouse%, buttonBorder%, buttonStyle%)
  3616.  
  3617.     '┌────────────────────────────────────────────────────────────────────────┐
  3618.     '│  This routine will display a window that contains a message that is    │
  3619.     '│  passed in by the caller.  If a mouse is available (indicated by the   │
  3620.     '│  useMouse% parameter), an OK button will be available to click on to   │
  3621.     '│  indicate user acceptance, and a Cancel button to indicate user        │
  3622.     '│  rejection.  A return value of TRUE is OK, FALSE is Cancel.            │
  3623.     '│                                                                        │
  3624.     '│    x1% - Upper-left corner column of box                               │
  3625.     '│    y1% - Upper-left corner row of box                                  │
  3626.     '│    x2% - Lower-right corner column of box                              │
  3627.     '│    y2% - Lower-right corner column of box                              │
  3628.     '│    st$() - An array of strings to display as the message               │
  3629.     '│    numLines% - The number of lines in the array st$()                  │
  3630.     '│    justify% - 0 = Flush Left, 1 = Flush Right, 2 = Centered.  Also     │
  3631.     '│          there are constants defined in QBSCR.INC you may use in       │
  3632.     '│          place of these numbers: FLUSHLEFT, FLUSHRIGHT, and CENTERED.  │
  3633.     '│    fg% - The foreground color to use for display of the window and     │
  3634.     '│          text contained in it.                                         │
  3635.     '│    bg% - The background color to use for display of the window and     │
  3636.     '│          text contained in it.                                         │
  3637.     '│    bpfg% - The foreground color for a pressed button.                  │
  3638.     '│    bpbg% - The background color for a pressed button.                  │
  3639.     '│    frType% - The type of frame to use for the window.                  │
  3640.     '│    shadow% - Shadow type to use for window.                            │
  3641.     '│    explode% - Explode mode to use for window.                          │
  3642.     '│    label$ - The window label string.                                   │
  3643.     '│    useMouse% - 1 = use mouse support, 0 = don't.                       │
  3644.     '│                                                                        │
  3645.     '└────────────────────────────────────────────────────────────────────────┘
  3646.  
  3647.     '─────────────────────────────────────────────────────────────────────────
  3648.     ' Define a couple of special keys.
  3649.     '─────────────────────────────────────────────────────────────────────────
  3650.         enter$ = CHR$(13)
  3651.         esc$ = CHR$(27)
  3652.  
  3653.     '─────────────────────────────────────────────────────────────────────────
  3654.     ' First step is to turn off the mouse if we plan to use it.  This is to
  3655.     ' prevent us from screwing up the display by drawing over top of it.
  3656.     '─────────────────────────────────────────────────────────────────────────
  3657.         IF useMouse% THEN
  3658.             MouseHide
  3659.         END IF
  3660.  
  3661.     '─────────────────────────────────────────────────────────────────────────
  3662.     ' Now display the window that will contain the message.
  3663.     '─────────────────────────────────────────────────────────────────────────
  3664.         MakeWindow CSNG(y1%), CSNG(x1%), CSNG(y2%), CSNG(x2%), fg%, bg%, 0, frType%, shadow%, explode%, label$
  3665.  
  3666.     '─────────────────────────────────────────────────────────────────────────
  3667.     ' Now display the text passed in as a message.  The constants used here,
  3668.     ' FLUSHLEFT, FLUSHRIGHT, and CENTERED, are found in the QBSCR.INC file.
  3669.     '─────────────────────────────────────────────────────────────────────────
  3670.         COLOR fg%, bg%
  3671.         SELECT CASE justify%
  3672.     
  3673.         CASE FLUSHLEFT
  3674.             FOR i% = 1 TO numLines%
  3675.                 LOCATE y1% + 1 + i%, x1% + 3, 0
  3676.                 PRINT st$(i%);
  3677.             NEXT i%
  3678.  
  3679.         CASE FLUSHRIGHT
  3680.             FOR i% = 1 TO numLines%
  3681.                 st$(i%) = RIGHT$(SPACE$(LEN(st$(i%))) + st$(i%), x2% - x1% - 5)
  3682.                 LOCATE y1% + 1 + i%, x1% + 3, 0
  3683.                 PRINT st$(i%);
  3684.             NEXT i%
  3685.  
  3686.         CASE CENTERED
  3687.             FOR i% = 1 TO numLines%
  3688.                 OffCenter st$(i%), y1% + i% + 1, x1%, x2%
  3689.             NEXT i%
  3690.  
  3691.         CASE ELSE
  3692.         END SELECT
  3693.  
  3694.     '─────────────────────────────────────────────────────────────────────────
  3695.     ' If we are to use the mouse, display a bitton to click on.  If not, then
  3696.     ' Display a text message on the bottom line of the window.
  3697.     '─────────────────────────────────────────────────────────────────────────
  3698.         IF useMouse% THEN
  3699.             DrawButton buttonBorder%, x1% + 2, y2% - 3, x1% + 11, y2% - 1, fg%, bg%, "OK", buttonStyle%
  3700.             DrawButton buttonBorder%, x1% + 13, y2% - 3, x1% + 22, y2% - 1, fg%, bg%, "Cancel", buttonStyle%
  3701.         ELSE
  3702.             IF (x2% - x1% >= 26) THEN
  3703.                 OffCenter " Enter = OK, Esc = Cancel ", y2% - 1, x1%, x2%
  3704.             ELSEIF (x2% - x1% >= 14) THEN
  3705.                 OffCenter " Enter = OK ", y2% - 2, x1%, x2%
  3706.                 OffCenter " Esc = CANCEL ", y2% - 1, x1%, x2%
  3707.             END IF
  3708.         END IF
  3709.  
  3710.     '─────────────────────────────────────────────────────────────────────────
  3711.     ' Now we wait.  If the mouse was clicked inside the button, then we're
  3712.     ' done.  If ESC or ENTER was hit, we're done.  If O (for OK) was hit, then
  3713.     ' we're done.  If the right mouse button is clicked, we're done.  If C
  3714.     ' (for Cancel) was hit, we're done.  If the left button was clicked in the
  3715.     ' Cancel button, we're done.
  3716.     '─────────────────────────────────────────────────────────────────────────
  3717.         MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  3718.         MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  3719.         lmcCnt% = 0
  3720.         rmCnt% = 0
  3721.         done% = FALSE
  3722.         DO
  3723.  
  3724.         '───────────────────────────────────────────────────────────────────────
  3725.         ' Get some input, mouse or keyboard.
  3726.         '───────────────────────────────────────────────────────────────────────
  3727.             k$ = ""
  3728.             lmCnt% = 0
  3729.             rmCnt% = 0
  3730.             IF useMouse% THEN
  3731.          
  3732.             '─────────────────────────────────────────────────────────────────────
  3733.             ' Turn the mouse cursor on.
  3734.             '─────────────────────────────────────────────────────────────────────
  3735.                 MouseShow
  3736.  
  3737.             '─────────────────────────────────────────────────────────────────────
  3738.             ' Did we have any left mouse button presses?  If not, check the
  3739.             ' keyboard for input.
  3740.             '─────────────────────────────────────────────────────────────────────
  3741.                 MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  3742.                 MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  3743.                 mx% = (mx% / 8) + 1
  3744.                 my% = (my% / 8) + 1
  3745.                 IF (lmCnt% = 0) AND (rmCnt% = 0) THEN
  3746.                     k$ = UCASE$(INKEY$)
  3747.                 END IF
  3748.             ELSE
  3749.  
  3750.             '─────────────────────────────────────────────────────────────────────
  3751.             ' No mouse available, so wait for keyboard input.
  3752.             '─────────────────────────────────────────────────────────────────────
  3753.                 WHILE k$ = ""
  3754.                     k$ = UCASE$(INKEY$)
  3755.                 WEND
  3756.             END IF
  3757.  
  3758.         '───────────────────────────────────────────────────────────────────────
  3759.         ' Act based on user's input, if there was any.  First check the left
  3760.         ' mouse button for activity.
  3761.         '───────────────────────────────────────────────────────────────────────
  3762.             IF (lmCnt%) THEN
  3763.             ' OK Button.
  3764.             DrawButton buttonBorder%, x1% + 2, y2% - 3, x1% + 11, y2% - 1, fg%, bg%, "OK", buttonStyle%
  3765.             
  3766.                 IF (mx% >= x1% + 2) AND (mx% <= x1% + 11) AND (my% >= y2% - 3) AND (my% <= y2% - 1) THEN
  3767.                     done% = TRUE
  3768.                     result% = TRUE
  3769.                     IF mouseExists% THEN
  3770.                         MouseHide
  3771.                     END IF
  3772.                     PressButton buttonBorder%, x1% + 2, y2% - 3, x1% + 11, y2% - 1, fg%, bg%, "OK", buttonStyle%
  3773.                 END IF
  3774.             ' Cancel Button.
  3775.                 IF (mx% >= x1% + 13) AND (mx% <= x1% + 22) AND (my% >= y2% - 3) AND (my% <= y2% - 1) THEN
  3776.                     done% = TRUE
  3777.                     result% = FALSE
  3778.                     IF mouseExists% THEN
  3779.                         MouseHide
  3780.                     END IF
  3781.                     PressButton buttonBorder%, x1% + 13, y2% - 3, x1% + 22, y2% - 1, fg%, bg%, "Cancel", buttonStyle%
  3782.                     'COLOR bpfg%, bpbg%
  3783.                     'LOCATE y2% - 3, x1% + 13, 0: PRINT "┌────────╖";
  3784.                     'LOCATE y2% - 2, x1% + 13, 0: PRINT "│ Cancel ║";
  3785.                     'LOCATE y2% - 1, x1% + 13, 0: PRINT "╘════════╝";
  3786.                 END IF
  3787.          
  3788.             END IF
  3789.  
  3790.         '───────────────────────────────────────────────────────────────────────
  3791.         ' Now see if the right mouse button was pressed.
  3792.         '───────────────────────────────────────────────────────────────────────
  3793.             IF (rmCnt%) THEN
  3794.                 done% = TRUE
  3795.                 result% = FALSE
  3796.             END IF
  3797.  
  3798.         '───────────────────────────────────────────────────────────────────────
  3799.         ' Were any of the exit keys pressed on the keyboard?
  3800.         '───────────────────────────────────────────────────────────────────────
  3801.             SELECT CASE k$
  3802.  
  3803.             CASE enter$, "O"
  3804.                 done% = TRUE
  3805.                 result% = TRUE
  3806.          
  3807.             CASE esc$, "C"
  3808.                 done% = TRUE
  3809.                 result% = FALSE
  3810.          
  3811.             CASE ELSE
  3812.             END SELECT
  3813.  
  3814.         LOOP UNTIL done%
  3815.  
  3816.     '─────────────────────────────────────────────────────────────────────────
  3817.     ' Return the result of the user's action.
  3818.     '─────────────────────────────────────────────────────────────────────────
  3819.         OkCancelMessageBox% = result%
  3820.  
  3821. END FUNCTION
  3822.  
  3823. SUB OkMessageBox (x1%, y1%, x2%, y2%, st$(), numLines%, justify%, fg%, bg%, frType%, shadow%, explode%, label$, useMouse%, buttonBorder%, buttonStyle%)
  3824.  
  3825.     '┌────────────────────────────────────────────────────────────────────────┐
  3826.     '│  This routine will display a window tha contains a message that is     │
  3827.     '│  passed in by the caller.  If a mouse is available (indicated by the   │
  3828.     '│  useMouse% parameter), an OK button will be available to click on to   │
  3829.     '│  indicate user completion.  There is no return value.                  │
  3830.     '│                                                                        │
  3831.     '│    x1% - Upper-left corner column of box                               │
  3832.     '│    y1% - Upper-left corner row of box                                  │
  3833.     '│    x2% - Lower-right corner column of box                              │
  3834.     '│    y2% - Lower-right corner column of box                              │
  3835.     '│    st$() - An array of strings to display as the message               │
  3836.     '│    numLines% - The number of lines in the array st$()                  │
  3837.     '│    justify% - 0 = Flush Left, 1 = Flush Right, 2 = Centered.  Also     │
  3838.     '│          there are constants defined in QBSCR.INC you may use in       │
  3839.     '│          place of these numbers: FLUSHLEFT, FLUSHRIGHT, and CENTERED.  │
  3840.     '│    fg% - The foreground color to use for display of the window and     │
  3841.     '│          text contained in it.                                         │
  3842.     '│    bg% - The background color to use for display of the window and     │
  3843.     '│          text contained in it.                                         │
  3844.     '│    bpfg% - The foreground color for a pressed button.                  │
  3845.     '│    bpbg% - The background color for a pressed button.                  │
  3846.     '│    frType% - The type of frame to use for the window.                  │
  3847.     '│    shadow% - Shadow type to use for window.                            │
  3848.     '│    explode% - Explode mode to use for window.                          │
  3849.     '│    label$ - The window label string.                                   │
  3850.     '│    useMouse% - 1 = use mouse support, 0 = don't.                       │
  3851.     '│                                                                        │
  3852.     '└────────────────────────────────────────────────────────────────────────┘
  3853.  
  3854.         enter$ = CHR$(13)
  3855.         esc$ = CHR$(27)
  3856.  
  3857.     '─────────────────────────────────────────────────────────────────────────
  3858.     ' First step is to turn off the mouse if we plan to use it.  This is to
  3859.     ' prevent us from screwing up the display by drawing over top of it.
  3860.     '─────────────────────────────────────────────────────────────────────────
  3861.         IF useMouse% THEN
  3862.             MouseHide
  3863.         END IF
  3864.  
  3865.     '─────────────────────────────────────────────────────────────────────────
  3866.     ' Now display the window that will contain the message.
  3867.     '─────────────────────────────────────────────────────────────────────────
  3868.         MakeWindow CSNG(y1%), CSNG(x1%), CSNG(y2%), CSNG(x2%), fg%, bg%, 0, frType%, shadow%, explode%, label$
  3869.  
  3870.     '─────────────────────────────────────────────────────────────────────────
  3871.     ' Now display the text passed in as a message.  These constants are
  3872.     ' defined in QBSCR.INC.
  3873.     '─────────────────────────────────────────────────────────────────────────
  3874.         COLOR fg%, bg%
  3875.         SELECT CASE justify%
  3876.      
  3877.         CASE FLUSHLEFT
  3878.             FOR i% = 1 TO numLines%
  3879.                 LOCATE y1% + 1 + i%, x1% + 3, 0
  3880.                 PRINT st$(i%);
  3881.             NEXT i%
  3882.  
  3883.         CASE FLUSHRIGHT
  3884.             FOR i% = 1 TO numLines%
  3885.                 st$(i%) = RIGHT$(SPACE$(LEN(st$(i%))) + st$(i%), x2% - x1% - 5)
  3886.                 LOCATE y1% + 1 + i%, x1% + 3, 0
  3887.                 PRINT st$(i%);
  3888.             NEXT i%
  3889.  
  3890.         CASE CENTERED
  3891.             FOR i% = 1 TO numLines%
  3892.                 OffCenter st$(i%), y1% + i% + 1, x1%, x2%
  3893.             NEXT i%
  3894.  
  3895.         CASE ELSE
  3896.         END SELECT
  3897.  
  3898.     '─────────────────────────────────────────────────────────────────────────
  3899.     ' If we are to use the mouse, display a bitton to click on.  If not, then
  3900.     ' Display a text message on the bottom line of the window.
  3901.     '─────────────────────────────────────────────────────────────────────────
  3902.         IF useMouse% THEN
  3903.             DrawButton buttonBorder%, x1% + 2, y2% - 3, x1% + 11, y2% - 1, fg%, bg%, "OK", buttonStyle%
  3904.         ELSE
  3905.             OffCenter " Hit any key ", y2% - 1, x1%, x2%
  3906.         END IF
  3907.  
  3908.     '─────────────────────────────────────────────────────────────────────────
  3909.     ' Now we wait.  If the mouse was clicked inside the button, then we're
  3910.     ' done.  If ESC or ENTER was hit, we're done.  If O (for OK) was hit, then
  3911.     ' we're done.  If the right mouse button is clicked, we're done.
  3912.     '─────────────────────────────────────────────────────────────────────────
  3913.         MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  3914.         MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  3915.         lmcCnt% = 0
  3916.         rmCnt% = 0
  3917.         done% = FALSE
  3918.         DO
  3919.  
  3920.         '───────────────────────────────────────────────────────────────────────
  3921.         ' Get some input, mouse or keyboard.
  3922.         '───────────────────────────────────────────────────────────────────────
  3923.             k$ = ""
  3924.             lmCnt% = 0
  3925.             rmCnt% = 0
  3926.             IF useMouse% THEN
  3927.  
  3928.                 MouseShow
  3929.  
  3930.             '─────────────────────────────────────────────────────────────────────
  3931.             ' Did we have any left mouse button presses?  If not, check the
  3932.             ' keyboard for input.
  3933.             '─────────────────────────────────────────────────────────────────────
  3934.                 MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  3935.                 MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  3936.                 mx% = (mx% / 8) + 1
  3937.                 my% = (my% / 8) + 1
  3938.                 IF (lmCnt% = 0) AND (rmCnt% = 0) THEN
  3939.                     k$ = UCASE$(INKEY$)
  3940.                 END IF
  3941.             ELSE
  3942.  
  3943.             '─────────────────────────────────────────────────────────────────────
  3944.             ' No mouse available, so wait for keyboard input.
  3945.             '─────────────────────────────────────────────────────────────────────
  3946.                 WHILE k$ = ""
  3947.                     k$ = UCASE$(INKEY$)
  3948.                 WEND
  3949.             END IF
  3950.  
  3951.         '───────────────────────────────────────────────────────────────────────
  3952.         ' Act based on user's input, if there was any.  First check the left
  3953.         ' mouse button for activity.
  3954.         '───────────────────────────────────────────────────────────────────────
  3955.             IF (lmCnt%) THEN
  3956.                 IF (mx% >= x1% + 2) AND (mx% <= x1% + 11) AND (my% >= y2% - 3) AND (my% <= y2% - 1) THEN
  3957.                     done% = TRUE
  3958.                     IF mouseExists% THEN
  3959.                         MouseHide
  3960.                     END IF
  3961.                     PressButton buttonBorder%, x1% + 2, y2% - 3, x1% + 11, y2% - 1, fg%, bg%, "OK", buttonStyle%
  3962.                     ' COLOR bpfg%, bpbg%
  3963.                     ' LOCATE y2% - 3, x1% + 2, 0: PRINT "┌────────╖";
  3964.                     ' LOCATE y2% - 2, x1% + 2, 0: PRINT "│   OK   ║";
  3965.                     ' LOCATE y2% - 1, x1% + 2, 0: PRINT "╘════════╝";
  3966.                 END IF
  3967.             END IF
  3968.  
  3969.         '───────────────────────────────────────────────────────────────────────
  3970.         ' Now see if the right mouse button was pressed.
  3971.         '───────────────────────────────────────────────────────────────────────
  3972.             IF (rmCnt%) THEN
  3973.                 done% = TRUE
  3974.             END IF
  3975.  
  3976.         '───────────────────────────────────────────────────────────────────────
  3977.         ' Were any of the exit keys pressed on the keyboard?
  3978.         '───────────────────────────────────────────────────────────────────────
  3979.             SELECT CASE k$
  3980.             CASE esc$, enter$, "O"
  3981.                 done% = TRUE
  3982.             CASE ELSE
  3983.             END SELECT
  3984.  
  3985.         LOOP UNTIL done%
  3986.  
  3987. END SUB
  3988.  
  3989. SUB PutScreen (file$)
  3990.     
  3991.     '┌──────────────────────────────────────────────────────────────────┐
  3992.     '│  This subprogram will copy the contents of a file that was saved │
  3993.     '│  using the QBSCR GetScreen subprogram (or Screen Builder)into    │
  3994.     '│  video RAM.  The result is a very fast retrieval and display of  │
  3995.     '│  a video screen.                                                 │
  3996.     '└──────────────────────────────────────────────────────────────────┘
  3997.     
  3998.     '────────────────────────────────────────────────────────────────────
  3999.     ' Set the memory segment to the address of screen memory
  4000.     '────────────────────────────────────────────────────────────────────
  4001.         DEF SEG = GetVideoSegment!
  4002.     
  4003.     '────────────────────────────────────────────────────────────────────
  4004.     ' Use the BASIC BLOAD statement to load the saved screen to video RAM
  4005.     '────────────────────────────────────────────────────────────────────
  4006.         LOCATE 1, 1, 0
  4007.         BLOAD file$, 0
  4008.     
  4009.     '────────────────────────────────────────────────────────────────────
  4010.     ' Restore BASIC's default data segment
  4011.     '────────────────────────────────────────────────────────────────────
  4012.         DEF SEG
  4013.     
  4014. END SUB
  4015.  
  4016. SUB QBPrint (st$, row%, col%, fore%, back%)
  4017.     
  4018.     '──────────────────────────────────────────────────────────────────────
  4019.     ' Calculate video memory offset, where display will begin
  4020.     '──────────────────────────────────────────────────────────────────────
  4021.         offset% = 160 * (row% - 1) + 2 * (col% - 1)
  4022.     
  4023.     '──────────────────────────────────────────────────────────────────────
  4024.     ' Calculate color byte for string
  4025.     '──────────────────────────────────────────────────────────────────────
  4026.         IF fore% > 15 THEN
  4027.             blinkingFore% = TRUE
  4028.             fore% = fore% - 16
  4029.         ELSE
  4030.             blinkingFore% = FALSE
  4031.         END IF
  4032.         attribute% = (back% * 16) + fore%
  4033.         IF blinkingFore% THEN
  4034.             attribute% = attribute% + 128
  4035.         END IF
  4036.     
  4037.     '──────────────────────────────────────────────────────────────────────
  4038.     ' Set default data segment to screen memory
  4039.     '──────────────────────────────────────────────────────────────────────
  4040.         DEF SEG = GetVideoSegment
  4041.     
  4042.     '──────────────────────────────────────────────────────────────────────
  4043.     ' Place the string into video memory, along with the color
  4044.     '──────────────────────────────────────────────────────────────────────
  4045.         stPos% = 1
  4046.         FOR x% = 0 TO ((LEN(st$) - 1) * 2) STEP 2
  4047.             POKE x% + offset%, ASC(MID$(st$, stPos%, 1))
  4048.             POKE x% + offset% + 1, attribute%
  4049.             stPos% = stPos% + 1
  4050.         NEXT x%
  4051.     
  4052.     '──────────────────────────────────────────────────────────────────────
  4053.     ' Restore BASIC's default data segment
  4054.     '──────────────────────────────────────────────────────────────────────
  4055.         DEF SEG
  4056.     
  4057. END SUB
  4058.  
  4059. FUNCTION ScreenBlank$ (delay!, useMouse%)
  4060.     
  4061.     '┌────────────────────────────────────────────────────────────────────────┐
  4062.     '│  This routine blanks out the screen and displays a message informing   │
  4063.     '│  the user of this.  To prevent this message from burning into the      │
  4064.     '│  screen, it changes place periodically.  The Delay parameter is a      │
  4065.     '│  numerical variable used in a dummy wait loop.  Change this value      │
  4066.     '│  based on the speed of your machine.  This routine returns the key     │
  4067.     '│  the user pressed to restore the screen, in case you want to use it.   │
  4068.     '│                                                                        │
  4069.     '│  Parameters are as follows:                                            │
  4070.     '│                                                                        │
  4071.     '│      delay - Numerical delay value.                                    │
  4072.     '│      useMouse% - 1 = use mouse support, 0 = don't.                     │
  4073.     '└────────────────────────────────────────────────────────────────────────┘
  4074.     
  4075.     '─────────────────────────────────────────────────────────────────────────
  4076.     ' Drain keys and mouse presses from buffers.
  4077.     '─────────────────────────────────────────────────────────────────────────
  4078.         WHILE INKEY$ <> ""
  4079.         WEND
  4080.         MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  4081.         MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  4082.         MouseButtonPressInfo CENTERBUTTON, cmCnt%, mx%, my%
  4083.         MousePosition oldMouseX%, oldMouseY%
  4084.         lmCnt% = 0
  4085.         rmCnt% = 0
  4086.         cmCnt% = 0
  4087.         MouseHide
  4088.  
  4089.     '─────────────────────────────────────────────────────────────────────────
  4090.     ' Seed the random number generator with the TIMER function
  4091.     '─────────────────────────────────────────────────────────────────────────
  4092.         RANDOMIZE TIMER
  4093.     
  4094.     '─────────────────────────────────────────────────────────────────────────
  4095.     ' Initialize local variables, set colors and clear the screen
  4096.     '─────────────────────────────────────────────────────────────────────────
  4097.         blankCount! = 0: key$ = "": COLOR 7, 0: CLS
  4098.     
  4099.     '─────────────────────────────────────────────────────────────────────────
  4100.     ' Display the informational message
  4101.     '─────────────────────────────────────────────────────────────────────────
  4102.         GOSUB BounceMessage
  4103.     
  4104.     '─────────────────────────────────────────────────────────────────────────
  4105.     ' While the user has not hit a key, increment our delay counter
  4106.     '─────────────────────────────────────────────────────────────────────────
  4107.         WHILE key$ = "" AND lmCnt% = 0 AND rmCnt% = 0 AND cmCnt% = 0 AND mx% = oldMouseX% AND my% = oldMouseY%
  4108.         
  4109.             MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  4110.             MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  4111.             MouseButtonPressInfo CENTERBUTTON, cmCnt%, mx%, my%
  4112.             MousePosition mx%, my%
  4113.             key$ = INKEY$
  4114.             blankCount! = blankCount! + 1
  4115.         
  4116.         '─────────────────────────────────────────────────────────────────────
  4117.         ' If our counter reaches our delay, then move the screen message
  4118.         '─────────────────────────────────────────────────────────────────────
  4119.             IF blankCount! > delay! THEN
  4120.             
  4121.                 blankCount! = 0: CLS
  4122.                 GOSUB BounceMessage
  4123.             
  4124.             END IF
  4125.         
  4126.         WEND
  4127.     
  4128.     '─────────────────────────────────────────────────────────────────────────
  4129.     ' Assign the key hit to the function and exit
  4130.     '─────────────────────────────────────────────────────────────────────────
  4131.         ScreenBlank$ = key$
  4132.         EXIT FUNCTION
  4133.     
  4134.     '─────────────────────────────────────────────────────────────────────────
  4135.     ' This little subroutine moves the informational message to a new
  4136.     ' location on the screen
  4137.     '─────────────────────────────────────────────────────────────────────────
  4138. BounceMessage:
  4139.     
  4140.     '─────────────────────────────────────────────────────────────────────────
  4141.     ' Clear the screen
  4142.     '─────────────────────────────────────────────────────────────────────────
  4143.         CLS
  4144.     
  4145.     '─────────────────────────────────────────────────────────────────────────
  4146.     ' Calculate new X and Y coordinates for the message randomly
  4147.     '─────────────────────────────────────────────────────────────────────────
  4148.         xCoord% = INT(RND(1) * 38) + 1
  4149.         yCoord% = INT(RND(1) * 24) + 1
  4150.     
  4151.     '─────────────────────────────────────────────────────────────────────────
  4152.     ' Display the message at the new X and Y coordinates
  4153.     '─────────────────────────────────────────────────────────────────────────
  4154.         LOCATE yCoord%, xCoord%, 0: PRINT "Screen has been blanked to prevent burn-in.";
  4155.         IF useMouse% THEN
  4156.             LOCATE yCoord% + 1, xCoord%, 0: PRINT "  Hit any key or mouse button to return...";
  4157.         ELSE
  4158.             LOCATE yCoord% + 1, xCoord%, 0: PRINT "         Hit any key to return...";
  4159.         END IF
  4160.  
  4161.     '─────────────────────────────────────────────────────────────────────────
  4162.     ' Return to the wait loop
  4163.     '─────────────────────────────────────────────────────────────────────────
  4164.         RETURN
  4165.     
  4166. END FUNCTION
  4167.  
  4168. SUB ScrnRestore (firstLine%, lastLine%, scrArray%(), segment)
  4169.     
  4170.     '┌────────────────────────────────────────────────────────────────────────┐
  4171.     '│  This routine will restore all or a portion of the screen display from │
  4172.     '│  an integer array.  For more implementation details, see the QBSCR     │
  4173.     '│  reference manual.                                                     │
  4174.     '│                                                                        │
  4175.     '│  Parameters are as follows:                                            │
  4176.     '│                                                                        │
  4177.     '│      firstLine%  - The first line of the display where restore should  │
  4178.     '│                    begin.  Top line is 1, bottom is 25.                │
  4179.     '│      lastLine%   - The last line of the display where restore should   │
  4180.     '│                    end, LastLine% being included.                      │
  4181.     '│      scrArray%() - The array in which the display contents will be     │
  4182.     '│                    restored.  Must be integer, and must be dimensioned │
  4183.     '│                    to 3999 (or 4000) elements.                         │
  4184.     '└────────────────────────────────────────────────────────────────────────┘
  4185.     
  4186.     '──────────────────────────────────────────────────────────────────────────
  4187.     ' Determine the starting address in the video memory (start%).  Must use
  4188.     ' 160 for the length of a line, since an attribute byte is stored for each
  4189.     ' character on the screen (80 characters + 80 attributes = 160)
  4190.     '──────────────────────────────────────────────────────────────────────────
  4191.         start% = (firstLine% - 1) * 160
  4192.     
  4193.     '──────────────────────────────────────────────────────────────────────────
  4194.     ' Calculate the length of the block of addresses we must restore (length%).
  4195.     ' 1 is subtracted since the array starts with element 0.
  4196.     '──────────────────────────────────────────────────────────────────────────
  4197.         length% = (((lastLine% - firstLine%) + 1) * 160) - 1
  4198.     
  4199.     '──────────────────────────────────────────────────────────────────────────
  4200.     ' Set the default segment to the video memory segment.
  4201.     '──────────────────────────────────────────────────────────────────────────
  4202.         DEF SEG = segment
  4203.     
  4204.     '──────────────────────────────────────────────────────────────────────────
  4205.     ' Restore information (characters and attributes) to video memory.
  4206.     '──────────────────────────────────────────────────────────────────────────
  4207.         FOR i% = 0 TO length%
  4208.             POKE start% + i%, scrArray%(start% + i%)
  4209.         NEXT i%
  4210.     
  4211.     '──────────────────────────────────────────────────────────────────────────
  4212.     ' Restore default segment to BASIC's segment.
  4213.     '──────────────────────────────────────────────────────────────────────────
  4214.         DEF SEG
  4215.     
  4216. END SUB
  4217.  
  4218. SUB ScrnSave (firstLine%, lastLine%, scrArray%(), segment)
  4219.     
  4220.     '┌────────────────────────────────────────────────────────────────────────┐
  4221.     '│  This routine will save all or a portion of the screen display to an   │
  4222.     '│  integer array.  For more implementation details, see the QBSCR        │
  4223.     '│  reference manual.                                                     │
  4224.     '│                                                                        │
  4225.     '│  Parameters are as follows:                                            │
  4226.     '│                                                                        │
  4227.     '│      firstLine%  - The first line of the display where saving should   │
  4228.     '│                    begin.  Top line is 1, bottom is 25.                │
  4229.     '│      lastLine%   - The last line of the display where saving should    │
  4230.     '│                    end, LastLine% being included.                      │
  4231.     '│      scrArray%() - The array in which the display contents will be     │
  4232.     '│                    stored.  Must be integer, and must be dimensioned   │
  4233.     '│                    to 3999 (or 4000) elements.                         │
  4234.     '└────────────────────────────────────────────────────────────────────────┘
  4235.     
  4236.     '──────────────────────────────────────────────────────────────────────────
  4237.     ' Determine the starting address in the video memory (start%).  Must use
  4238.     ' 160 for the length of a line, since an attribute byte is stored for each
  4239.     ' character on the screen (80 characters + 80 attributes = 160)
  4240.     '──────────────────────────────────────────────────────────────────────────
  4241.         start% = (firstLine% - 1) * 160
  4242.     
  4243.     '──────────────────────────────────────────────────────────────────────────
  4244.     ' Calculate the length of the block of addresses we must retrieve and
  4245.     ' store (length%).  1 is subtracted since the array starts with element 0.
  4246.     '──────────────────────────────────────────────────────────────────────────
  4247.         length% = (((lastLine% - firstLine%) + 1) * 160) - 1
  4248.     
  4249.     '──────────────────────────────────────────────────────────────────────────
  4250.     ' Set the default segment to the video memory segment.
  4251.     '──────────────────────────────────────────────────────────────────────────
  4252.         DEF SEG = segment
  4253.     
  4254.     '──────────────────────────────────────────────────────────────────────────
  4255.     ' Get information (characters and attributes) from video memory.
  4256.     '──────────────────────────────────────────────────────────────────────────
  4257.         FOR i% = 0 TO length%
  4258.             scrArray%(start% + i%) = PEEK(start% + i%)
  4259.         NEXT i%
  4260.     
  4261.     '──────────────────────────────────────────────────────────────────────────
  4262.     ' Restore default segment to BASIC's segment.
  4263.     '──────────────────────────────────────────────────────────────────────────
  4264.         DEF SEG
  4265.     
  4266. END SUB
  4267.  
  4268. FUNCTION SelectList$ (items$(), numItems%, topRow%, botRow%, leftCol%, maxWidth%, normFG%, normBG%, hiFG%, hiBG%, frameType%, explode%, shadow%, label$, useMouse%)
  4269.     
  4270.     '┌────────────────────────────────────────────────────────────────────────┐
  4271.     '│  This function accepts a list of string items and from it creates a    │
  4272.     '│  scrolling list with a selection bar.  The function will return the    │
  4273.     '│  item selected by a user.                                              │
  4274.     '│                                                                        │
  4275.     '│  Parameters are as follows:                                            │
  4276.     '│                                                                        │
  4277.     '│       items$() - an array containing the items from which a selection  │
  4278.     '│                  will be made                                          │
  4279.     '│       numItems% - the number of items in the list (items$())           │
  4280.     '│       topRow% - the top-most screen row of the list                    │
  4281.     '│       botRow% - the bottom-most screen row of the list                 │
  4282.     '│       leftCol% - the left-most screen column of the list               │
  4283.     '│       maxWidth% - the width of the widest entry in the list            │
  4284.     '│       normFG% - Foreground color of unhighlighted entries in the list  │
  4285.     '│       normBG% - Background color of unhighlighted entries in the list  │
  4286.     '│       hiFG% - Foreground color of highlighted entry in the list        │
  4287.     '│       hiBG% - Background color of highlighted entry in the list        │
  4288.     '│       useMouse% - 1 = use mouse support, 0 = don't                     │
  4289.     '└────────────────────────────────────────────────────────────────────────┘
  4290.  
  4291.     '──────────────────────────────────────────────────────────────────────────
  4292.     ' Define keys that will be used in this function
  4293.     '──────────────────────────────────────────────────────────────────────────
  4294.         enter$ = CHR$(13)
  4295.         esc$ = CHR$(27)
  4296.         UpArrowKey$ = CHR$(0) + CHR$(72)
  4297.         DownArrowKey$ = CHR$(0) + CHR$(80)
  4298.         PgUpKey$ = CHR$(0) + CHR$(73)
  4299.         PgDnKey$ = CHR$(0) + CHR$(81)
  4300.         HomeKee$ = CHR$(0) + CHR$(71)
  4301.         EndKee$ = CHR$(0) + CHR$(79)
  4302.     
  4303.     '──────────────────────────────────────────────────────────────────────────
  4304.     ' Define errortone string to use with PLAY
  4305.     '──────────────────────────────────────────────────────────────────────────
  4306.         errorTone$ = "L60 N1 N0 N1"
  4307.     
  4308.     '──────────────────────────────────────────────────────────────────────────
  4309.     ' Set up our top, bottom, and highlight pointers for the list.
  4310.     '
  4311.     '  - topPtr% will maintain the top of the screen position in the overall
  4312.     '    list.  Values will range from 1 to numItems%-numPerScreen%.
  4313.     '  - botPtr% will maintain the bottom of the screen position in the overall
  4314.     '    list.  Values will range from numPerScreen% to numItems%.
  4315.     '  - hiPtr% will maintain the position of the highlight in the overall
  4316.     '    list.  Values range from 1 to numItems%.
  4317.     '──────────────────────────────────────────────────────────────────────────
  4318.         numPerScreen% = botRow% - topRow% + 1
  4319.         topPtr% = 1
  4320.         botPtr% = numPerScreen%
  4321.         hiPtr% = 1
  4322.         elevatorPos% = 1
  4323.  
  4324.     '──────────────────────────────────────────────────────────────────────────
  4325.     ' Determine widest entry in list.
  4326.     '──────────────────────────────────────────────────────────────────────────
  4327.     longest% = 0
  4328.     FOR i% = 1 TO numItems%
  4329.         IF LEN(items$(i%)) > longest% THEN
  4330.             longest% = LEN(items$(i%))
  4331.         END IF
  4332.     NEXT i%
  4333.  
  4334.     '──────────────────────────────────────────────────────────────────────────
  4335.     ' If using the mouse, turn it off before we display.
  4336.     '──────────────────────────────────────────────────────────────────────────
  4337.     IF useMouse% THEN
  4338.         MouseHide
  4339.     END IF
  4340.  
  4341.     '──────────────────────────────────────────────────────────────────────────
  4342.     ' Calculate and display a box AROUND the list.
  4343.     '──────────────────────────────────────────────────────────────────────────
  4344.     MakeWindow topRow% - 1, leftCol% - 1, botRow% + 1, leftCol% + longest% + 2, normFG%, normBG%, 0, frameType%, shadow%, explode%, label$
  4345.  
  4346.     '──────────────────────────────────────────────────────────────────────────
  4347.     ' If we are using a mouse, and the number of rows is 2 or more, then we
  4348.     ' will build a scroll bar for the window.
  4349.     '──────────────────────────────────────────────────────────────────────────
  4350.     COLOR normFG%, normBG%
  4351.     IF (numPerScreen% >= 2) AND (useMouse%) THEN
  4352.         scrollBarFlag% = TRUE
  4353.         FOR i% = topRow% + 1 TO botRow% - 1
  4354.             LOCATE i%, leftCol% + longest% + 2, 0
  4355.             PRINT CHR$(177);
  4356.         NEXT i%
  4357.         elevatorFloors% = (botRow% - 1) - (topRow% + 1) + 1
  4358.         QBPrint CHR$(30), topRow%, leftCol% + longest% + 2, normFG%, normBG%
  4359.         QBPrint CHR$(31), botRow%, leftCol% + longest% + 2, normFG%, normBG%
  4360.         QBPrint CHR$(219), topRow% + 1, leftCol% + longest% + 2, normFG%, normBG%
  4361.     END IF
  4362.     
  4363.     '──────────────────────────────────────────────────────────────────────────
  4364.     ' Display first screen's worth of entries
  4365.     '──────────────────────────────────────────────────────────────────────────
  4366.         COLOR normFG%, normBG%
  4367.         FOR i% = 1 TO numPerScreen%
  4368.             LOCATE topRow% + i% - 1, leftCol% + 1, 0
  4369.             PRINT items$(i%);
  4370.         NEXT i%
  4371.     
  4372.     '──────────────────────────────────────────────────────────────────────────
  4373.     ' Locate the highlight bar to top position
  4374.     '──────────────────────────────────────────────────────────────────────────
  4375.         COLOR hiFG%, hiBG%
  4376.         LOCATE topRow% + topPtr% - 1, leftCol%, 0
  4377.         PRINT SPACE$(maxWidth% + 2);
  4378.         LOCATE topRow% + topPtr% - 1, leftCol% + 1, 0
  4379.         PRINT items$(hiPtr%);
  4380.     
  4381.     '──────────────────────────────────────────────────────────────────────────
  4382.     ' Sit in a loop whle the user hits keys.  If the ESC key is hit, then set
  4383.     ' function to NUL string and exit.  If ENTER is hit, set function to the
  4384.     ' entry pointed to by highlight (hiPtr%) and exit.
  4385.     '──────────────────────────────────────────────────────────────────────────
  4386.         updateList% = FALSE
  4387.         done% = FALSE
  4388.         MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  4389.         MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  4390.         DO
  4391.  
  4392.         '────────────────────────────────────────────────────────────────────────
  4393.         ' If mouse is around, show it.
  4394.         '────────────────────────────────────────────────────────────────────────
  4395.             IF useMouse% THEN
  4396.                 MouseShow
  4397.             END IF
  4398.         
  4399.         '────────────────────────────────────────────────────────────────────────
  4400.         ' Get a key from the user
  4401.         '────────────────────────────────────────────────────────────────────────
  4402.             k$ = ""
  4403.             lmCnt% = 0
  4404.             rmCnt% = 0
  4405.             IF useMouse% THEN
  4406.  
  4407.                 MouseButtonStatus lmCnt%, rmCnt%, bc%
  4408.                 IF lmCnt% OR rmCnt% THEN
  4409.                     MousePosition mx%, my%
  4410.                 END IF
  4411.  
  4412.             '──────────────────────────────────────────────────────────────────────
  4413.             ' Did we have any left mouse button presses?  If not, check the
  4414.             ' keyboard for input.
  4415.             '──────────────────────────────────────────────────────────────────────
  4416.                 IF lmCnt% = 0 THEN
  4417.                     k$ = UCASE$(INKEY$)
  4418.                 END IF
  4419.             ELSE
  4420.  
  4421.             '──────────────────────────────────────────────────────────────────────
  4422.             ' No mouse available, so wait for keyboard input.
  4423.             '──────────────────────────────────────────────────────────────────────
  4424.                 WHILE k$ = ""
  4425.                     k$ = UCASE$(INKEY$)
  4426.                 WEND
  4427.             END IF
  4428.  
  4429.         '────────────────────────────────────────────────────────────────────────
  4430.         ' If left mouse was clicked, then see if it was clicked on certain
  4431.         ' "hot spots" we understand.
  4432.         '────────────────────────────────────────────────────────────────────────
  4433.             IF (lmCnt%) THEN
  4434.  
  4435.                 mx% = (mx% / 8) + 1
  4436.                 my% = (my% / 8) + 1
  4437.  
  4438.             '────────────────────────────────────────────────────────────────────────
  4439.             ' First, check to see if a list item was selected.
  4440.             '────────────────────────────────────────────────────────────────────────
  4441.                 IF (mx% > leftCol%) AND (mx% < leftCol% + longest% + 2) AND (my% >= topRow%) AND (my% <= botRow%) THEN
  4442.                     hiPtr% = topPtr% + (my% - topRow%)
  4443.                     done% = TRUE
  4444.                     updateList% = TRUE
  4445.                 END IF
  4446.                     
  4447.             '────────────────────────────────────────────────────────────────────────
  4448.             ' Now check to see if the left button was clicked on the up arrow part
  4449.             ' of the scroll bar.  Is so, decrement the pointers.
  4450.             '────────────────────────────────────────────────────────────────────────
  4451.                 IF (my% = topRow%) AND (mx% = leftCol% + longest% + 2) THEN
  4452.                     k$ = UpArrowKey$
  4453.                 END IF
  4454.  
  4455.             '────────────────────────────────────────────────────────────────────────
  4456.             ' Now check to see if the left button was clicked on the down arrow part
  4457.             ' of the scroll bar.  Is so, increment the pointers.
  4458.             '────────────────────────────────────────────────────────────────────────
  4459.                 IF (my% = botRow%) AND (mx% = leftCol% + longest% + 2) THEN
  4460.                     k$ = DownArrowKey$
  4461.                 END IF
  4462.  
  4463.             '────────────────────────────────────────────────────────────────────────
  4464.             ' If the left mouse button was clicked on the scroll bar itself, then
  4465.             ' execute a PgUp or PgDn, based on where the elevator is.
  4466.             '────────────────────────────────────────────────────────────────────────
  4467.                 IF (mx% = leftCol% + longest% + 2) AND (my% < elevatorPos% + (topRow% - 1)) AND (my% >= topRow% + 1) THEN
  4468.                     k$ = PgUpKey$
  4469.                 END IF
  4470.                 IF (mx% = leftCol% + longest% + 2) AND (my% > elevatorPos% + (topRow% - 1)) AND (my% <= botRow% - 1) THEN
  4471.                     k$ = PgDnKey$
  4472.                 END IF
  4473.             END IF
  4474.  
  4475.         '────────────────────────────────────────────────────────────────────────
  4476.         ' If right mouse button was pressed, then exit.
  4477.         '────────────────────────────────────────────────────────────────────────
  4478.             IF rmCnt% THEN
  4479.                 k$ = esc$
  4480.             END IF
  4481.  
  4482.         '────────────────────────────────────────────────────────────────────────
  4483.         ' Decide what to do based on the user's keystroke
  4484.         '────────────────────────────────────────────────────────────────────────
  4485.             SELECT CASE k$
  4486.             
  4487.             CASE "A" TO "Z", "a" TO "z", "0" TO "9"   ' First character search
  4488.                 k$ = UCASE$(k$)
  4489.             
  4490.             ' Look for the user-entered character in the first pos of all list items
  4491.                 foundPos% = 0
  4492.             ' First check from current position plus one to end of screen
  4493.                 FOR i% = hiPtr% + 1 TO botPtr%
  4494.                     IF LEFT$(items$(i%), 1) = k$ THEN
  4495.                         foundPos% = i%
  4496.                         EXIT FOR
  4497.                     END IF
  4498.                 NEXT i%
  4499.             
  4500.             ' If not found, check from current position plus one to end of screen
  4501.                 IF foundPos% = 0 THEN
  4502.                     FOR i% = hiPtr% + 1 TO numItems%
  4503.                         IF LEFT$(items$(i%), 1) = k$ THEN
  4504.                             foundPos% = i%
  4505.                             EXIT FOR
  4506.                         END IF
  4507.                     NEXT i%
  4508.                 END IF
  4509.             
  4510.             ' If item was not found, then check from top of list to current pos
  4511.                 IF foundPos% = 0 THEN
  4512.                     FOR i% = 1 TO hiPtr%
  4513.                         IF LEFT$(items$(i%), 1) = k$ THEN
  4514.                             foundPos% = i%
  4515.                             EXIT FOR
  4516.                         END IF
  4517.                     NEXT i%
  4518.                 END IF
  4519.             
  4520.             ' If letter was found, update pointers for new list display
  4521.                 IF foundPos% THEN       ' Is foundPos% something other than 0?
  4522.                 ' --- Yes
  4523.                 
  4524.                 ' If the letter was found on the existing screen list, then
  4525.                 ' don't move the list - only the hilight pointer.  Otherwise,
  4526.                 ' move the list and the pointer.
  4527.                     IF foundPos% <= botPtr% AND foundPos% >= topPtr% THEN
  4528.                         hiPtr% = foundPos%
  4529.                     ELSE
  4530.                     ' Make sure the list will fill the whole screen
  4531.                         IF foundPos% > numItems% - numPerScreen% + 1 THEN
  4532.                             topPtr% = numItems% - numPerScreen% + 1
  4533.                             botPtr% = numItems%
  4534.                             hiPtr% = foundPos%
  4535.                         ELSE
  4536.                             topPtr% = foundPos%
  4537.                             botPtr% = topPtr% + numPerScreen% - 1
  4538.                             hiPtr% = foundPos%
  4539.                         END IF
  4540.                     END IF
  4541.                 
  4542.                 ' Tell routine to update list
  4543.                     updateList% = TRUE
  4544.                 
  4545.                 END IF
  4546.             
  4547.             CASE UpArrowKey$             ' Move list and/or highlight up one
  4548.                 IF hiPtr% > topPtr% THEN                ' Is highlight at top of screen list?
  4549.                     hiPtr% = hiPtr% - 1   ' --- No
  4550.                 ELSE    ' --- Yes
  4551.                     IF hiPtr% > 1 THEN    ' Is highlight at top of overall list?
  4552.                         topPtr% = topPtr% - 1               ' --- No
  4553.                         botPtr% = botPtr% - 1
  4554.                         hiPtr% = hiPtr% - 1
  4555.                     ELSE  ' --- Yes
  4556.                         topPtr% = numItems% - numPerScreen% + 1
  4557.                         botPtr% = numItems%
  4558.                         hiPtr% = numItems%
  4559.                     END IF
  4560.                 END IF
  4561.                 updateList% = TRUE
  4562.             
  4563.             CASE DownArrowKey$           ' Move list and/or highlight down one
  4564.                 IF hiPtr% < botPtr% THEN                ' Is highlight at bottom of screen list?
  4565.                     hiPtr% = hiPtr% + 1   ' --- No
  4566.                 ELSE    ' --- Yes
  4567.                     IF hiPtr% < numItems% THEN            ' Is highlight at bottom of overall list?
  4568.                         topPtr% = topPtr% + 1               ' --- No
  4569.                         botPtr% = botPtr% + 1
  4570.                         hiPtr% = hiPtr% + 1
  4571.                     ELSE  ' --- Yes
  4572.                         topPtr% = 1
  4573.                         botPtr% = numPerScreen%
  4574.                         hiPtr% = 1
  4575.                     END IF
  4576.                 END IF
  4577.                 updateList% = TRUE
  4578.             
  4579.             CASE PgUpKey$                ' Move up one screen's worth
  4580.                 IF topPtr% > numPerScreen% THEN         ' Got a whole screen's worth?
  4581.                     topPtr% = topPtr% - numPerScreen%     ' --- Yes
  4582.                     botPtr% = botPtr% - numPerScreen%
  4583.                     hiPtr% = hiPtr% - numPerScreen%
  4584.                 ELSE    ' --- No
  4585.                     IF topPtr% > 1 THEN   ' Need to move list on screen?
  4586.                         hiPtr% = hiPtr% - topPtr% + 1
  4587.                         topPtr% = 1
  4588.                         botPtr% = numPerScreen%
  4589.                     ELSE  ' --- No
  4590.                         hiPtr% = 1          ' Move highlight to top of list
  4591.                     END IF
  4592.                 END IF
  4593.                 updateList% = TRUE
  4594.             
  4595.             CASE PgDnKey$                ' Move down one screen's worth
  4596.                 IF botPtr% <= numItems% - numPerScreen% THEN            ' Got a whole screen's worth?
  4597.                     topPtr% = topPtr% + numPerScreen%     ' --- Yes
  4598.                     botPtr% = botPtr% + numPerScreen%
  4599.                     hiPtr% = hiPtr% + numPerScreen%
  4600.                 ELSE    ' --- No
  4601.                     IF botPtr% < numItems% THEN           ' Need to move the list on screen?
  4602.                         hiPtr% = hiPtr% + (numItems% - numPerScreen% + 1 - topPtr%)
  4603.                         topPtr% = numItems% - numPerScreen% + 1
  4604.                         botPtr% = numItems%
  4605.                     ELSE  ' --- No
  4606.                         hiPtr% = numItems%  ' Move highlight to end of list
  4607.                     END IF
  4608.                 END IF
  4609.                 updateList% = TRUE
  4610.             
  4611.             CASE HomeKee$             ' Move to top of overall list
  4612.                 topPtr% = 1
  4613.                 botPtr% = numPerScreen%
  4614.                 hiPtr% = 1
  4615.                 updateList% = TRUE
  4616.             
  4617.             CASE EndKee$              ' Move to bottom of overall list
  4618.                 topPtr% = numItems% - numPerScreen% + 1
  4619.                 botPtr% = numItems%
  4620.                 hiPtr% = numItems%
  4621.                 updateList% = TRUE
  4622.             
  4623.             CASE esc$ ' User wants out
  4624.                 SelectList$ = ""
  4625.                 done% = TRUE
  4626.             
  4627.             CASE enter$               ' User is done and has made selection
  4628.                 SelectList$ = items$(hiPtr%)
  4629.                 done% = TRUE
  4630.             
  4631.             CASE ELSE ' Invalid key was hit
  4632.                 IF k$ <> "" THEN
  4633.                     PLAY errorTone$
  4634.                 END IF
  4635.             
  4636.             END SELECT
  4637.      
  4638.         '────────────────────────────────────────────────────────────────────────
  4639.         ' If required, update the scroll bar display.
  4640.         '────────────────────────────────────────────────────────────────────────
  4641.             COLOR normFG%, normBG%
  4642.             IF (scrollBarFlag%) AND (updateList%) THEN
  4643.                 IF useMouse% THEN
  4644.                     MouseHide
  4645.                 END IF
  4646.                 FOR i% = topRow% + 1 TO botRow% - 1
  4647.                     LOCATE i%, leftCol% + longest% + 2, 0
  4648.                     PRINT CHR$(177);
  4649.                 NEXT i%
  4650.                 elevatorPos% = CalcScrollPos%(numItems%, elevatorFloors%, hiPtr%)
  4651.                 QBPrint CHR$(219), topRow% + elevatorPos%, leftCol% + longest% + 2, normFG%, normBG%
  4652.             END IF
  4653.  
  4654.         '────────────────────────────────────────────────────────────────────────
  4655.         ' If required, update the display list and hilight position
  4656.         '────────────────────────────────────────────────────────────────────────
  4657.             IF updateList% THEN
  4658.                 IF useMouse% THEN
  4659.                     MouseHide
  4660.                 END IF
  4661.             ' Update the list
  4662.                 COLOR normFG%, normBG%
  4663.                 FOR i% = topPtr% TO botPtr%
  4664.                     LOCATE topRow% + i% - topPtr%, leftCol%, 0
  4665.                     PRINT LEFT$(" " + items$(i%) + SPACE$(maxWidth%), maxWidth% + 1) + " ";
  4666.                 NEXT i%
  4667.             ' Update the highlight
  4668.                 COLOR hiFG%, hiBG%
  4669.                 LOCATE hiPtr% - topPtr% + topRow%, leftCol%, 0
  4670.                 PRINT SPACE$(maxWidth% + 2);
  4671.                 LOCATE hiPtr% - topPtr% + topRow%, leftCol% + 1, 0
  4672.                 PRINT items$(hiPtr%);
  4673.                 updateList% = FALSE
  4674.             END IF
  4675.         
  4676.         LOOP UNTIL done%
  4677.  
  4678.         IF lmCnt% THEN
  4679.             SelectList$ = items$(hiPtr%)
  4680.         END IF
  4681.  
  4682.     '──────────────────────────────────────────────────────────────────────────
  4683.     ' Wait here until the mouse buttons are no longer down.  This is useful
  4684.     ' in case this function is called successively.
  4685.     '──────────────────────────────────────────────────────────────────────────
  4686.     MouseButtonStatus l%, r%, c%
  4687.     WHILE (l% OR r%)
  4688.         MouseButtonStatus l%, r%, c%
  4689.     WEND
  4690.  
  4691. END FUNCTION
  4692.  
  4693. FUNCTION SubMenu% (choice$(), currentMenu%, numOfChoices%, justify$, leftColumn, rightColumn, row%, marker$, divider$, fg%, bg%, hfg%, hBG%, qfg%, qbg%, useMouse%, mx%, my%)
  4694.     
  4695.     '┌───────────────────────────────────────────────────────────────────────┐
  4696.     '│  This function is a special version of MakeMenu% and is used only by  │
  4697.     '│  the MultiMenu routine.  It is not intended to be called by itself.   │
  4698.     '│  See the MakeMenu% function if you need a single menu, or want to     │
  4699.     '│  know more about the parameters of this function.                     │
  4700.     '└───────────────────────────────────────────────────────────────────────┘
  4701.     
  4702.     '─────────────────────────────────────────────────────────────────────────
  4703.     ' Set local variables - extended scan codes for keypad keys
  4704.     '─────────────────────────────────────────────────────────────────────────
  4705.         up$ = CHR$(0) + CHR$(72)
  4706.         down$ = CHR$(0) + CHR$(80)
  4707.         enter$ = CHR$(13)
  4708.         home$ = CHR$(0) + CHR$(71)
  4709.         EndKee$ = CHR$(0) + CHR$(79)
  4710.         PgUpKey$ = CHR$(0) + CHR$(73)
  4711.         PgDnKey$ = CHR$(0) + CHR$(81)
  4712.         LeftArrowKey$ = CHR$(0) + CHR$(75)
  4713.         RightArrowKey$ = CHR$(0) + CHR$(77)
  4714.         esc$ = CHR$(27)
  4715.  
  4716.     '─────────────────────────────────────────────────────────────────────────
  4717.     ' Define other local variables.
  4718.     '─────────────────────────────────────────────────────────────────────────
  4719.         mx% = 0
  4720.         my% = 0
  4721.         lmCnt% = 0
  4722.         rmCnt% = 0
  4723.         returnIt% = FALSE
  4724.         updateMenu% = FALSE
  4725.  
  4726.     '─────────────────────────────────────────────────────────────────────────
  4727.     ' Define the error tone string to use with PLAY
  4728.     '─────────────────────────────────────────────────────────────────────────
  4729.         errorTone$ = "MB T120 L50 O3 AF"
  4730.  
  4731.     '─────────────────────────────────────────────────────────────────────────
  4732.     ' Set type of justification to uppercase
  4733.     '─────────────────────────────────────────────────────────────────────────
  4734.         justify$ = UCASE$(justify$)
  4735.         wdth% = (rightColumn - leftColumn - 1)
  4736.  
  4737.     '─────────────────────────────────────────────────────────────────────────
  4738.     ' Check for out-of-bounds parameters.  If any are out of range,
  4739.     ' quit the function
  4740.     '─────────────────────────────────────────────────────────────────────────
  4741.         IF numOfChoices% < 1 OR numOfChoices% > 25 THEN EXIT FUNCTION
  4742.         IF justify$ <> "C" AND justify$ <> "L" AND justify$ <> "R" THEN EXIT FUNCTION
  4743.         IF leftColumn < 1 OR rightColumn > 80 THEN EXIT FUNCTION
  4744.         IF row% < 1 OR row% > 24 THEN EXIT FUNCTION
  4745.  
  4746.     '─────────────────────────────────────────────────────────────────────────
  4747.     ' Calculate the array of character identifiers
  4748.     '─────────────────────────────────────────────────────────────────────────
  4749.         REDIM charID(numOfChoices%) AS STRING * 1
  4750.         FOR x% = 1 TO numOfChoices%
  4751.             FOR y% = 1 TO LEN(choice$(currentMenu%, x%))
  4752.                 IF MID$(choice$(currentMenu%, x%), y%, 1) = marker$ THEN
  4753.                     charID(x%) = UCASE$(MID$(choice$(currentMenu%, x%), y% + 1, 1))
  4754.                     EXIT FOR
  4755.                 END IF
  4756.             NEXT y%
  4757.         NEXT x%
  4758.  
  4759.     '─────────────────────────────────────────────────────────────────────────
  4760.     ' Calculate length of longest menu choice and store value in ChoiceLen%
  4761.     '─────────────────────────────────────────────────────────────────────────
  4762.         choiceLen% = 0
  4763.         FOR x% = 1 TO numOfChoices%
  4764.             IF LEN(choice$(currentMenu%, x%)) > choiceLen% THEN
  4765.                 IF INSTR(choice$(currentMenu%, x%), marker$) THEN
  4766.                     choiceLen% = LEN(choice$(currentMenu%, x%))
  4767.                 ELSE
  4768.                     choiceLen% = LEN(choice$(currentMenu%, x%)) + 1
  4769.                 END IF
  4770.             END IF
  4771.         NEXT x%
  4772.         choiceLen% = choiceLen% - 1
  4773.  
  4774.     '─────────────────────────────────────────────────────────────────────────
  4775.     ' Determine left-most column to display highlight bar on
  4776.     '─────────────────────────────────────────────────────────────────────────
  4777.         col = (((rightColumn - leftColumn - 1) - choiceLen%) / 2) + leftColumn
  4778.  
  4779.     '─────────────────────────────────────────────────────────────────────────
  4780.     ' At this point, we must turn off the mouse cursor if it's available.  We
  4781.     ' don't want to write overtop of it, leaving a hole when it's moved later.
  4782.     '─────────────────────────────────────────────────────────────────────────
  4783.         IF useMouse% THEN
  4784.             MouseHide
  4785.         END IF
  4786.  
  4787.     '─────────────────────────────────────────────────────────────────────────
  4788.     ' Print menu choices to screen based on the type of Justification
  4789.     ' selected (Center, Left, Right).
  4790.     '─────────────────────────────────────────────────────────────────────────
  4791.         COLOR fg%, bg%
  4792.         SELECT CASE justify$
  4793.         CASE "C"
  4794.             FOR x% = 1 TO numOfChoices%
  4795.                 xCol% = ((wdth% - (LEN(choice$(currentMenu%, x%))) - 1) \ 2 + leftColumn) + 1
  4796.                 LOCATE (row% - 1) + x%, leftColumn - 1, 0
  4797.                 PRINT SPACE$(choiceLen% + 2);
  4798.                 LOCATE (row% - 1) + x%, xCol%, 0
  4799.                 DisplayEntry choice$(currentMenu%, x%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  4800.             NEXT x%
  4801.         CASE "R"
  4802.             FOR x% = 1 TO numOfChoices%
  4803.                 LOCATE (row% - 1) + x%, leftColumn - 1, 0
  4804.                 PRINT SPACE$(choiceLen% + 2);
  4805.                 LOCATE (row% - 1) + x%, (rightColumn - LEN(choice$(currentMenu%, x%)))
  4806.                 DisplayEntry choice$(currentMenu%, x%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  4807.             NEXT x%
  4808.         CASE "L"
  4809.             FOR x% = 1 TO numOfChoices%
  4810.                 LOCATE (row% - 1) + x%, leftColumn - 1, 0
  4811.                 PRINT SPACE$(choiceLen% + 2);
  4812.                 LOCATE (row% - 1) + x%, leftColumn, 0
  4813.                 DisplayEntry choice$(currentMenu%, x%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  4814.             NEXT x%
  4815.         END SELECT
  4816.  
  4817.     '─────────────────────────────────────────────────────────────────────────
  4818.     ' Highlight the first entry in the list.  Must take into account the
  4819.     ' justification type.
  4820.     '─────────────────────────────────────────────────────────────────────────
  4821.         currentLocation% = 1
  4822.         oldLocation% = 1
  4823.         COLOR hfg%, hBG%
  4824.         LOCATE row%, leftColumn - 1: PRINT SPACE$(choiceLen% + 2);
  4825.         SELECT CASE justify$
  4826.         CASE "C"
  4827.             xCol% = ((wdth% - (LEN(choice$(currentMenu%, currentLocation%))) - 1) \ 2 + leftColumn) + 1
  4828.             LOCATE (row% - 1 + currentLocation%), xCol%, 0
  4829.             DisplayEntry choice$(currentMenu%, currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  4830.         CASE "R"
  4831.             LOCATE (row% - 1) + currentLocation%, (rightColumn - LEN(choice$(currentMenu%, currentLocation%)))
  4832.             DisplayEntry choice$(currentMenu%, currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  4833.         CASE "L"
  4834.             LOCATE (row% - 1) + currentLocation%, leftColumn
  4835.             DisplayEntry choice$(currentMenu%, currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  4836.         END SELECT
  4837.  
  4838.     '─────────────────────────────────────────────────────────────────────────
  4839.     ' Read keystrokes and change the highlighted entry appropriately.  Also
  4840.     ' drain out any pending mouse button presses if the mouse is available.
  4841.     '─────────────────────────────────────────────────────────────────────────
  4842.         exitCode% = FALSE
  4843.         IF useMouse% THEN
  4844.             MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  4845.             MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  4846.             lmCnt% = 0
  4847.             rmCnt% = 0
  4848.         END IF
  4849.         WHILE exitCode% = FALSE
  4850.  
  4851.         '─────────────────────────────────────────────────────────────────────
  4852.         ' If we're using the mouse, turn it on.
  4853.         '─────────────────────────────────────────────────────────────────────
  4854.             IF useMouse% THEN
  4855.                 MouseShow
  4856.             END IF
  4857.      
  4858.         '─────────────────────────────────────────────────────────────────────
  4859.         ' Read keystrokes and/or mouse strokes.
  4860.         '─────────────────────────────────────────────────────────────────────
  4861.             key$ = ""
  4862.             lmCnt% = 0
  4863.             rmCnt% = 0
  4864.             IF useMouse% THEN
  4865.                 MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  4866.                 MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  4867.  
  4868.             '───────────────────────────────────────────────────────────────────
  4869.             ' Did we have any left mouse button presses?  If not, check the
  4870.             ' keyboard for input.
  4871.             '───────────────────────────────────────────────────────────────────
  4872.                 IF lmCnt% = 0 THEN
  4873.                     key$ = UCASE$(INKEY$)
  4874.                 END IF
  4875.             ELSE
  4876.  
  4877.             '───────────────────────────────────────────────────────────────────
  4878.             ' No mouse available, so wait for keyboard input.
  4879.             '───────────────────────────────────────────────────────────────────
  4880.                 WHILE key$ = ""
  4881.                     key$ = UCASE$(INKEY$)
  4882.                 WEND
  4883.             END IF
  4884.  
  4885.         '─────────────────────────────────────────────────────────────────────
  4886.         ' If the left mouse button was pressed, check to see if a menu item
  4887.         ' was selected by it.
  4888.         '─────────────────────────────────────────────────────────────────────
  4889.             IF (useMouse%) AND (lmCnt% > 0) THEN
  4890.  
  4891.             '───────────────────────────────────────────────────────────────────
  4892.             ' Convert virtual screen mouse coordinates to real 80x25 coords.
  4893.             '───────────────────────────────────────────────────────────────────
  4894.                 mx% = (mx% \ 8) + 1
  4895.                 my% = (my% \ 8) + 1
  4896.  
  4897.             '───────────────────────────────────────────────────────────────────
  4898.             ' If mouse was inside menu window then return the item pointed to.
  4899.             '───────────────────────────────────────────────────────────────────
  4900.                 IF (mx% >= leftColumn) AND (mx% <= rightColumn) AND (my% >= row%) AND (my% <= row% + numOfChoices% - 1) THEN
  4901.                     IF (choice$(currentMenu%, my% - row% + 1) <> divider$) THEN
  4902.                         exitCode% = TRUE
  4903.                         updateMenu% = TRUE
  4904.                         currentLocation% = my% - row% + 1
  4905.                         key$ = charID(currentLocation%)
  4906.                         returnIt% = TRUE
  4907.                     END IF
  4908.                 ELSE
  4909.  
  4910.                 '─────────────────────────────────────────────────────────────────
  4911.                 ' See if the mouse was clicked on the row two above the top row.
  4912.                 ' If so, it was clicked on the menu bar - return mx.
  4913.                 '─────────────────────────────────────────────────────────────────
  4914.                     IF (my% = row% - 2) THEN
  4915.                         SubMenu% = LEFTMOUSEEXIT
  4916.                         EXIT FUNCTION
  4917.                     END IF
  4918.                 END IF
  4919.             END IF
  4920.  
  4921.         '─────────────────────────────────────────────────────────────────────
  4922.         ' If right mouse button was pressed, then exit as if ESC were pressed.
  4923.         '─────────────────────────────────────────────────────────────────────
  4924.         IF (useMouse%) AND (rmCnt% > 0) THEN
  4925.             SubMenu% = RIGHTMOUSEEXIT
  4926.             EXIT FUNCTION
  4927.         END IF
  4928.  
  4929.         '───────────────────────────────────────────────────────────────────
  4930.         ' Update currentLocation based on what user did, key-wise.
  4931.         '───────────────────────────────────────────────────────────────────
  4932.             SELECT CASE key$
  4933.  
  4934.             CASE up$
  4935.                 IF currentLocation% > 1 THEN
  4936.                     currentLocation% = currentLocation% - 1
  4937.                     IF (choice$(currentMenu%, currentLocation%) = divider$) AND (currentLocation% > 0) THEN
  4938.                         currentLocation% = currentLocation% - 1
  4939.                     END IF
  4940.                 ELSE
  4941.                     currentLocation% = numOfChoices%
  4942.                 END IF
  4943.                 updateMenu% = TRUE
  4944.  
  4945.             CASE down$
  4946.                 IF currentLocation% < numOfChoices% THEN
  4947.                     currentLocation% = currentLocation% + 1
  4948.                     IF (choice$(currentMenu%, currentLocation%) = divider$) AND (currentLocation% < numOfChoices%) THEN
  4949.                         currentLocation% = currentLocation% + 1
  4950.                     END IF
  4951.                 ELSE
  4952.                     currentLocation% = 1
  4953.                 END IF
  4954.                 updateMenu% = TRUE
  4955.  
  4956.             CASE home$, PgUpKey$
  4957.                 IF currentLocation% <> 1 THEN
  4958.                     currentLocation% = 1
  4959.                     updateMenu% = TRUE
  4960.                 END IF
  4961.  
  4962.             CASE LeftArrowKey$
  4963.                 SubMenu% = LEFTARROWCODE
  4964.                 EXIT FUNCTION
  4965.  
  4966.             CASE RightArrowKey$
  4967.                 SubMenu% = RIGHTARROWCODE
  4968.                 EXIT FUNCTION
  4969.          
  4970.             CASE EndKee$, PgDnKey$
  4971.                 IF currentLocation% <> numOfChoices% THEN
  4972.                     currentLocation% = numOfChoices%
  4973.                     updateMenu% = TRUE
  4974.                 END IF
  4975.  
  4976.             CASE enter$
  4977.                 SubMenu% = currentLocation%
  4978.                 exitCode% = TRUE
  4979.  
  4980.             CASE esc$
  4981.                 SubMenu% = 27
  4982.                 exitCode% = TRUE
  4983.  
  4984.             CASE ELSE
  4985.             '───────────────────────────────────────────────────────────────────
  4986.             ' Check hot quick access keys.
  4987.             '───────────────────────────────────────────────────────────────────
  4988.                 FOR i% = 1 TO numOfChoices%
  4989.                     IF charID(i%) = key$ THEN
  4990.                         currentLocation% = i%
  4991.                         updateMenu% = TRUE
  4992.                         SubMenu% = i%
  4993.                         exitCode% = TRUE
  4994.                     END IF
  4995.                 NEXT i%
  4996.  
  4997.             END SELECT
  4998.  
  4999.             '───────────────────────────────────────────────────────────────────
  5000.             ' If required, update the display.
  5001.             '───────────────────────────────────────────────────────────────────
  5002.             IF updateMenu% THEN
  5003.  
  5004.             '───────────────────────────────────────────────────────────────────
  5005.             ' If mouse is around, turn it off, since we'll be displaying.
  5006.             '───────────────────────────────────────────────────────────────────
  5007.                 IF useMouse% THEN
  5008.                     MouseHide
  5009.                 END IF
  5010.  
  5011.             '─────────────────────────────────────────────────────────────────
  5012.             ' Restore the old highlighted item to normal colors.
  5013.             '─────────────────────────────────────────────────────────────────
  5014.                 COLOR fg%, bg%
  5015.                 LOCATE row% + oldLocation% - 1, leftColumn - 1: PRINT SPACE$(choiceLen% + 2);
  5016.                 SELECT CASE justify$
  5017.                 CASE "C"
  5018.                     xCol% = ((wdth% - (LEN(choice$(currentMenu%, oldLocation%))) - 1) \ 2 + leftColumn) + 1
  5019.                     LOCATE (row% - 1 + oldLocation%), xCol%, 0
  5020.                     DisplayEntry choice$(currentMenu%, oldLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  5021.                 CASE "R"
  5022.                     LOCATE (row% - 1) + oldLocation%, (rightColumn - LEN(choice$(currentMenu%, oldLocation%)))
  5023.                     DisplayEntry choice$(currentMenu%, oldLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  5024.                 CASE "L"
  5025.                     LOCATE (row% - 1) + oldLocation%, leftColumn
  5026.                     DisplayEntry choice$(currentMenu%, oldLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 1
  5027.                 END SELECT
  5028.  
  5029.             '─────────────────────────────────────────────────────────────────
  5030.             ' Display newly highlighted item in highlight colors.
  5031.             '─────────────────────────────────────────────────────────────────
  5032.                 COLOR hfg%, hBG%
  5033.                 LOCATE row% + currentLocation% - 1, leftColumn - 1: PRINT SPACE$(choiceLen% + 2);
  5034.                 SELECT CASE justify$
  5035.                 CASE "C"
  5036.                     xCol% = ((wdth% - (LEN(choice$(currentMenu%, currentLocation%))) - 1) \ 2 + leftColumn) + 1
  5037.                     LOCATE (row% - 1 + currentLocation%), xCol%, 0
  5038.                     DisplayEntry choice$(currentMenu%, currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  5039.                 CASE "R"
  5040.                     LOCATE (row% - 1) + currentLocation%, (rightColumn - LEN(choice$(currentMenu%, currentLocation%)))
  5041.                     DisplayEntry choice$(currentMenu%, currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  5042.                 CASE "L"
  5043.                     LOCATE (row% - 1) + currentLocation%, leftColumn
  5044.                     DisplayEntry choice$(currentMenu%, currentLocation%), qfg%, qbg%, hfg%, hBG%, fg%, bg%, marker$, divider$, wdth%, 2
  5045.                 END SELECT
  5046.  
  5047.             '─────────────────────────────────────────────────────────────────
  5048.             ' Reset old location to current.
  5049.             '─────────────────────────────────────────────────────────────────
  5050.                 oldLocation% = currentLocation%
  5051.                 updateMenu% = FALSE
  5052.  
  5053.             END IF
  5054.          
  5055.         '───────────────────────────────────────────────────────────────────
  5056.         ' If the mouse was used to click on a menu choice, then return it
  5057.         ' and exit now.
  5058.         '───────────────────────────────────────────────────────────────────
  5059.             IF returnIt% THEN
  5060.                 SubMenu% = currentLocation%
  5061.                 EXIT FUNCTION
  5062.             END IF
  5063.      
  5064.         WEND
  5065.  
  5066. END FUNCTION
  5067.  
  5068. SUB ViewList (list$(), listLen%, maxWidth%, topRow%, botRow%, leftCol%, fg%, bg%, frameType%, explode%, shadow%, label$, useMouse%)
  5069.     
  5070.     '┌────────────────────────────────────────────────────────────────────────┐
  5071.     '│  This function accepts a list of string items and from it creates a    │
  5072.     '│  scrolling list.                                                       │
  5073.     '│                                                                        │
  5074.     '│  Parameters are as follows:                                            │
  5075.     '│                                                                        │
  5076.     '│       list$() - an array containing the list of items to scroll thru   │
  5077.     '│       listLen% - the number of items in the list                       │
  5078.     '│       maxWidth% - the width of the widest entry in the list            │
  5079.     '│       topRow% - the top-most screen row of the list                    │
  5080.     '│       botRow% - the bottom-most screen row of the list                 │
  5081.     '│       leftCol% - the left-most screen column of the list               │
  5082.     '│       fg% - Foreground color of text in the list                       │
  5083.     '│       bg% - Background color of text in the list                       │
  5084.     '│       frameType% - Type of window frame to use.                        │
  5085.     '│       explode% - Explode mode to use for window.                       │
  5086.     '│       shadow% - Shadow type for main window.                           │
  5087.     '│       label$ - TExt label for window.                                  │
  5088.     '│       useMouse% - 1 = use mouse support, 0 = don't.                    │
  5089.     '└────────────────────────────────────────────────────────────────────────┘
  5090.     
  5091.     '──────────────────────────────────────────────────────────────────────────
  5092.     ' Define keys that will be used in this function
  5093.     '──────────────────────────────────────────────────────────────────────────
  5094.         enter$ = CHR$(13)
  5095.         esc$ = CHR$(27)
  5096.         UpArrowKey$ = CHR$(0) + CHR$(72)
  5097.         DownArrowKey$ = CHR$(0) + CHR$(80)
  5098.         PgUpKey$ = CHR$(0) + CHR$(73)
  5099.         PgDnKey$ = CHR$(0) + CHR$(81)
  5100.         HomeKee$ = CHR$(0) + CHR$(71)
  5101.         EndKee$ = CHR$(0) + CHR$(79)
  5102.  
  5103.     '──────────────────────────────────────────────────────────────────────────
  5104.     ' Define errortone string to use with PLAY
  5105.     '──────────────────────────────────────────────────────────────────────────
  5106.         errorTone$ = "L60 N1 N0 N1"
  5107.  
  5108.     '──────────────────────────────────────────────────────────────────────────
  5109.     ' Set up our top, bottom, and highlight pointers for the list, as well as
  5110.     ' the starting point for the scroll bar elevator.
  5111.     '
  5112.     '  - topPtr% will maintain the top of the screen position in the overall
  5113.     '    list.  Values will range from 1 to numItems%-numPerScreen%.
  5114.     '  - botPtr% will maintain the bottom of the screen position in the overall
  5115.     '    list.  Values will range from numPerScreen% to numItems%.
  5116.     '──────────────────────────────────────────────────────────────────────────
  5117.         numPerScreen% = botRow% - topRow% + 1
  5118.         topPtr% = 1
  5119.         botPtr% = numPerScreen%
  5120.         elevatorPos% = 1
  5121.  
  5122.     '──────────────────────────────────────────────────────────────────────────
  5123.     ' If using the mouse, turn it off before we display.
  5124.     '──────────────────────────────────────────────────────────────────────────
  5125.     IF useMouse% THEN
  5126.         MouseHide
  5127.     END IF
  5128.  
  5129.     '──────────────────────────────────────────────────────────────────────────
  5130.     ' Calculate and display a box AROUND the list.
  5131.     '──────────────────────────────────────────────────────────────────────────
  5132.     MakeWindow topRow% - 1, leftCol% - 1, botRow% + 1, leftCol% + maxWidth% + 2, fg%, bg%, 0, frameType%, shadow%, explode%, label$
  5133.  
  5134.     '──────────────────────────────────────────────────────────────────────────
  5135.     ' If we are using a mouse, and the number of rows is 2 or more, then we
  5136.     ' will build a scroll bar for the window.  The variable 'elevatorFloors%'
  5137.     ' stores the number of possible elevator positions on the scroll bar.
  5138.     '──────────────────────────────────────────────────────────────────────────
  5139.     COLOR fg%, bg%
  5140.     IF ((botRow% - topRow%) + 1 >= 2) AND (useMouse%) THEN
  5141.         scrollBarFlag% = TRUE
  5142.         FOR i% = topRow% + 1 TO botRow% - 1
  5143.             LOCATE i%, leftCol% + maxWidth% + 2, 0
  5144.             PRINT CHR$(177);
  5145.         NEXT i%
  5146.         elevatorFloors% = (botRow% - 1) - (topRow% + 1) + 1
  5147.         QBPrint CHR$(30), topRow%, leftCol% + maxWidth% + 2, fg%, bg%
  5148.         QBPrint CHR$(31), botRow%, leftCol% + maxWidth% + 2, fg%, bg%
  5149.         QBPrint CHR$(219), topRow% + 1, leftCol% + maxWidth% + 2, fg%, bg%
  5150.     END IF
  5151.  
  5152.     '──────────────────────────────────────────────────────────────────────────
  5153.     ' Display first screen's worth of entries
  5154.     '──────────────────────────────────────────────────────────────────────────
  5155.         COLOR fg%, bg%
  5156.         FOR i% = 1 TO (botRow% - topRow%) + 1
  5157.             LOCATE topRow% + i% - 1, leftCol% + 1, 0
  5158.             PRINT list$(i%);
  5159.         NEXT i%
  5160.  
  5161.     '──────────────────────────────────────────────────────────────────────────
  5162.     ' Sit in a loop whle the user hits keys.  If the ESC key is hit, then set
  5163.     ' function to NULL string and exit.
  5164.     '──────────────────────────────────────────────────────────────────────────
  5165.         updateList% = FALSE
  5166.         done% = FALSE
  5167.         MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  5168.         MouseButtonPressInfo LEFTBUTTON, lmCnt%, mx%, my%
  5169.         DO
  5170.  
  5171.         '────────────────────────────────────────────────────────────────────────
  5172.         ' If mouse is around, show it.
  5173.         '────────────────────────────────────────────────────────────────────────
  5174.             IF useMouse% THEN
  5175.                 MouseShow
  5176.             END IF
  5177.      
  5178.         '────────────────────────────────────────────────────────────────────────
  5179.         ' Get a key from the user
  5180.         '────────────────────────────────────────────────────────────────────────
  5181.             k$ = ""
  5182.             lmCnt% = 0
  5183.             rmCnt% = 0
  5184.             IF useMouse% THEN
  5185.          
  5186.             '──────────────────────────────────────────────────────────────────────
  5187.             ' Determine the status of the mouse buttons, as well as position, if
  5188.             ' a button was down.
  5189.             '──────────────────────────────────────────────────────────────────────
  5190.                 MouseButtonStatus lmCnt%, rmCnt%, bc%
  5191.                 IF lmCnt% OR rmCnt% THEN
  5192.                     MousePosition mx%, my%
  5193.                 END IF
  5194.  
  5195.             '──────────────────────────────────────────────────────────────────────
  5196.             ' Did we have any left mouse button presses?  If not, check the
  5197.             ' keyboard for input.
  5198.             '──────────────────────────────────────────────────────────────────────
  5199.                 IF lmCnt% = 0 THEN
  5200.                     k$ = UCASE$(INKEY$)
  5201.                 END IF
  5202.             ELSE
  5203.  
  5204.             '──────────────────────────────────────────────────────────────────────
  5205.             ' No mouse available, so wait for keyboard input.
  5206.             '──────────────────────────────────────────────────────────────────────
  5207.                 WHILE k$ = ""
  5208.                     k$ = UCASE$(INKEY$)
  5209.                 WEND
  5210.             END IF
  5211.  
  5212.         '────────────────────────────────────────────────────────────────────────
  5213.         ' If left mouse was clicked, then see if it was clicked on certain
  5214.         ' "hot spots" we understand.
  5215.         '────────────────────────────────────────────────────────────────────────
  5216.             IF (lmCnt%) THEN
  5217.  
  5218.             '──────────────────────────────────────────────────────────────────────
  5219.             ' Convert virtual screen mouse coordinates to real 80x25 coordinates.
  5220.             '──────────────────────────────────────────────────────────────────────
  5221.                 mx% = (mx% / 8) + 1
  5222.                 my% = (my% / 8) + 1
  5223.  
  5224.             '──────────────────────────────────────────────────────────────────────
  5225.             ' Check to see if the left button was clicked on the up arrow part
  5226.             ' of the scroll bar.  Is so, decrement the pointers.
  5227.             '──────────────────────────────────────────────────────────────────────
  5228.                 IF (my% = topRow%) AND (mx% = leftCol% + maxWidth% + 2) THEN
  5229.                     k$ = UpArrowKey$
  5230.                 END IF
  5231.  
  5232.             '──────────────────────────────────────────────────────────────────────
  5233.             ' Now check to see if the left button was clicked on the down arrow part
  5234.             ' of the scroll bar.  Is so, increment the pointers.
  5235.             '──────────────────────────────────────────────────────────────────────
  5236.                 IF (my% = botRow%) AND (mx% = leftCol% + maxWidth% + 2) THEN
  5237.                     k$ = DownArrowKey$
  5238.                 END IF
  5239.  
  5240.             '──────────────────────────────────────────────────────────────────────
  5241.             ' If the left mouse button was clicked on the scroll bar itself, then
  5242.             ' execute a PgUp or PgDn, based on where the elevator is.
  5243.             '──────────────────────────────────────────────────────────────────────
  5244.                 IF (mx% = leftCol% + maxWidth% + 2) AND (my% < elevatorPos% + (topRow% - 1)) AND (my% >= topRow% + 1) THEN
  5245.                     k$ = PgUpKey$
  5246.                 END IF
  5247.                 IF (mx% = leftCol% + maxWidth% + 2) AND (my% > elevatorPos% + (topRow% - 1)) AND (my% <= botRow% - 1) THEN
  5248.                     k$ = PgDnKey$
  5249.                 END IF
  5250.             END IF
  5251.  
  5252.         '────────────────────────────────────────────────────────────────────────
  5253.         ' If the right mouse button was pressed, then get outta here.  First,
  5254.         ' though, just to be tidy, we're going to drain that right button press
  5255.         ' from the mouse click buffer.
  5256.         '────────────────────────────────────────────────────────────────────────
  5257.             IF (rmCnt%) THEN
  5258.                 MouseButtonPressInfo RIGHTBUTTON, rmCnt%, mx%, my%
  5259.                 done% = TRUE
  5260.             END IF
  5261.  
  5262.         '────────────────────────────────────────────────────────────────────────
  5263.         ' Decide what to do based on the user's keystroke
  5264.         '────────────────────────────────────────────────────────────────────────
  5265.             SELECT CASE k$
  5266.          
  5267.             CASE UpArrowKey$             ' Move list up one
  5268.                 IF (topPtr% > 1) THEN
  5269.                     topPtr% = topPtr% - 1
  5270.                     botPtr% = botPtr% - 1
  5271.                     ' botPtr% = topPtr% + (botRow% - topRow%)
  5272.                     updateList% = TRUE
  5273.                 END IF
  5274.          
  5275.             CASE DownArrowKey$           ' Move list down one
  5276.                 IF (botPtr% < listLen%) THEN
  5277.                     botPtr% = botPtr% + 1
  5278.                     topPtr% = topPtr% + 1
  5279.                     updateList% = TRUE
  5280.                 END IF
  5281.          
  5282.             CASE PgUpKey$                ' Move up one screen's worth
  5283.                 IF topPtr% > (botRow% - topRow%) THEN         ' Got a whole screen's worth?
  5284.                     topPtr% = topPtr% - (botRow% - topRow%)
  5285.                     botPtr% = botPtr% - (botRow% - topRow%)
  5286.                     updateList% = TRUE
  5287.                 ELSE
  5288.                     topPtr% = 1
  5289.                     botPtr% = (botRow% - topRow%) + 1
  5290.                     updateList% = TRUE
  5291.                 END IF
  5292.          
  5293.             CASE PgDnKey$, enter$        ' Move down one screen's worth
  5294.                 IF botPtr% <= listLen% - (botRow% - topRow%) THEN            ' Got a whole screen's worth?
  5295.                     topPtr% = topPtr% + (botRow% - topRow%)
  5296.                     botPtr% = botPtr% + (botRow% - topRow%)
  5297.                     updateList% = TRUE
  5298.                 ELSE    ' --- No
  5299.                     topPtr% = listLen% - (botRow% - topRow%)
  5300.                     botPtr% = listLen%
  5301.                     updateList% = TRUE
  5302.                 END IF
  5303.          
  5304.             CASE HomeKee$             ' Move to top of overall list
  5305.                 IF (topPtr% > 1) THEN
  5306.                     topPtr% = 1
  5307.                     botPtr% = (botRow% - topRow%) + 1
  5308.                     updateList% = TRUE
  5309.                 END IF
  5310.          
  5311.             CASE EndKee$              ' Move to bottom of overall list
  5312.                 IF (botPtr% < listLen%) THEN
  5313.                     topPtr% = listLen% - (botRow% - topRow%)
  5314.                     botPtr% = listLen%
  5315.                     updateList% = TRUE
  5316.                 END IF
  5317.          
  5318.             CASE esc$ ' User wants out
  5319.                 done% = TRUE
  5320.          
  5321.             CASE ELSE ' Invalid key was hit
  5322.                 IF k$ <> "" THEN
  5323.                     PLAY errorTone$
  5324.                 END IF
  5325.          
  5326.             END SELECT
  5327.     
  5328.         '────────────────────────────────────────────────────────────────────────
  5329.         ' If required, update the scroll bar display.
  5330.         '────────────────────────────────────────────────────────────────────────
  5331.             IF (scrollBarFlag%) AND (updateList%) THEN
  5332.                 IF useMouse% THEN
  5333.                     MouseHide
  5334.                 END IF
  5335.                 FOR i% = topRow% + 1 TO botRow% - 1
  5336.                     LOCATE i%, leftCol% + maxWidth% + 2, 0
  5337.                     PRINT CHR$(177);
  5338.                 NEXT i%
  5339.                 elevatorPos% = CalcScrollPos%(listLen% - (botPtr% - topPtr%), elevatorFloors%, topPtr%)
  5340.                 QBPrint CHR$(219), topRow% + elevatorPos%, leftCol% + maxWidth% + 2, fg%, bg%
  5341.             END IF
  5342.  
  5343.         '────────────────────────────────────────────────────────────────────────
  5344.         ' If required, update the display.
  5345.         '────────────────────────────────────────────────────────────────────────
  5346.             IF updateList% THEN
  5347.                 IF useMouse% THEN
  5348.                     MouseHide
  5349.                 END IF
  5350.             ' Update the list
  5351.                 COLOR fg%, bg%
  5352.                 FOR i% = topPtr% TO botPtr%
  5353.                     LOCATE topRow% + i% - topPtr%, leftCol%, 0
  5354.                     PRINT LEFT$(" " + list$(i%) + SPACE$(maxWidth%), maxWidth% + 1) + " ";
  5355.                 NEXT i%
  5356.                 updateList% = FALSE
  5357.             END IF
  5358.      
  5359.         LOOP UNTIL done%
  5360.  
  5361. END SUB
  5362.  
  5363. SUB Wipe (top%, bottom%, lft%, rght%, back%)
  5364.     
  5365.     '┌────────────────────────────────────────────────────────────────────────┐
  5366.     '│  This routine clears off a selected portion of the screen.  Note that  │
  5367.     '│  the area cleared by this routine is always INSIDE the box defined by  │
  5368.     '│  coordinates passed in.  This allows you to use the same values used   │
  5369.     '│  for the window being WIPEd, without having to adjust them by one to   │
  5370.     '│  avoid erasing your window border.                                     │
  5371.     '│  The passed parameters are:                                            │
  5372.     '│                                                                        │
  5373.     '│      top% - The top-most row to clear.  Allowable range is 1 to 25.    │
  5374.     '│      bottom% - The bottom-most row to clear.  Allowable range is       │
  5375.     '│                1 to 25.                                                │
  5376.     '│      lft% - The left-most column to clear.  Allowable range is 1 to    │
  5377.     '│             80.                                                        │
  5378.     '│      rght% - The right-most column to clear.  Allowable range is       │
  5379.     '│              1 to 80.                                                  │
  5380.     '│      back% - The background color to clear with.  Allowable range is   │
  5381.     '│              0 to 7.                                                   │
  5382.     '└────────────────────────────────────────────────────────────────────────┘
  5383.     
  5384.     '─────────────────────────────────────────────────────────────────────────
  5385.     ' Change to the passed background color
  5386.     '─────────────────────────────────────────────────────────────────────────
  5387.         COLOR , back%
  5388.     
  5389.     '─────────────────────────────────────────────────────────────────────────
  5390.     ' Clear the selected portion of the screen by overwriting with spaces
  5391.     '─────────────────────────────────────────────────────────────────────────
  5392.         FOR x% = top% + 1 TO bottom% - 1
  5393.             LOCATE x%, lft% + 1, 0
  5394.             PRINT SPACE$(rght% - lft% - 1);
  5395.         NEXT x%
  5396.     
  5397. END SUB
  5398.  
  5399.